Skip to content

fix: reject negative/zero work hours in attendance regularization (#1337)#1407

Open
Xenon010101 wants to merge 2 commits into
Sachinchaurasiya360:mainfrom
Xenon010101:fix/negative-work-hours
Open

fix: reject negative/zero work hours in attendance regularization (#1337)#1407
Xenon010101 wants to merge 2 commits into
Sachinchaurasiya360:mainfrom
Xenon010101:fix/negative-work-hours

Conversation

@Xenon010101
Copy link
Copy Markdown
Contributor

@Xenon010101 Xenon010101 commented Jun 4, 2026

fix: reject negative/zero work hours in attendance regularization (#1337)

Description

Rejects negative and zero work hours in attendance regularization requests.

Changes

  • Added Zod .refine() to regularizeSchema: ensures checkOut > checkIn and work hours ≤ 24
  • Added runtime guard in regularize() service method: throws if workHours ≤ 0 or > 24

Testing

  • checkOut before checkIn → validation error
  • checkOut equal to checkIn → validation error
  • workHours > 24 → validation error
  • Normal valid times → proceeds as before

Closes #1337

Summary by CodeRabbit

Release Notes

  • Bug Fixes
    • Attendance records now validate that check-out times occur after check-in times
    • Attendance records now enforce a maximum 24-hour work duration limit to prevent invalid submissions

…chinchaurasiya360#1337)

- Add Zod refinements to regularizeSchema: checkOut > checkIn, workHours <= 24
- Add runtime guards in regularize() service method
- Clamp HALF_DAY/PRESENT logic to positive work hours only
@github-actions github-actions Bot added gssoc scope:backend Changes to server-side / API code labels Jun 4, 2026
@github-actions github-actions Bot added quality:clean Clean and well-structured contribution level:beginner Good for first-time contributors labels Jun 4, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 4, 2026

Review Change Stack

Warning

Review limit reached

@Xenon010101, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 5 minutes and 4 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c9d20219-6629-4611-b729-e0b1771808d9

📥 Commits

Reviewing files that changed from the base of the PR and between 7abdbaa and b74504b.

📒 Files selected for processing (4)
  • server/src/module/attendance/attendance.constants.ts
  • server/src/module/attendance/attendance.controller.ts
  • server/src/module/attendance/attendance.service.ts
  • server/src/module/attendance/attendance.validation.ts
📝 Walkthrough

Walkthrough

Work hours validation constraints are enforced at both schema and service layers in the attendance regularization flow. The Zod schema now validates checkOut > checkIn and duration ≤ 24 hours; the service method adds matching runtime guard clauses that throw errors before upserting invalid attendance records.

Changes

Work Hours Validation

Layer / File(s) Summary
Work hours validation in schema and service
server/src/module/attendance/attendance.validation.ts, server/src/module/attendance/attendance.service.ts
Zod schema defines MAX_WORK_HOURS and adds .refine validations ensuring checkOut > checkIn and duration ≤ 24 hours; service method regularize adds matching guard clauses that reject invalid work hours before persisting attendance records.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Possibly related issues

  • #1337: Addresses the same validation gap by preventing negative workHours when checkOut precedes checkIn.
  • #1338: Directly resolves the bug where AttendanceService.regularize now errors on invalid checkOut–checkIn relationships and exceeds-24-hours conditions.

Suggested labels

bug, type:bug, scope:backend

Suggested reviewers

  • Sachinchaurasiya360

Poem

🐰 Hop, hop, check the time!
When checkout trails checkin, that's no crime—
But validation's here to keep it right,
No negative hours, no work past twilight!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: rejecting negative/zero work hours in attendance regularization, which directly matches the changeset.
Description check ✅ Passed The description includes the required sections: clear description of changes, implementation details, testing approach, and issue reference; no required UI screenshots needed.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions Bot added gssoc:approved Approved for GSSoC scoring type:bug Bug fixes labels Jun 4, 2026
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 4, 2026

Hi @Xenon010101, thanks for contributing to InternHack! 🎉

I have automatically:

  • 👤 Assigned this PR to you.
  • 🏷️ Applied the gssoc:approved label.

Our workflows will now analyze your changes to classify:

  • 📈 PR Difficulty: level:*
  • 🧩 PR Type: type:*
  • 🌟 PR Quality: quality:*

Tip

Ensure your PR description references the issue it resolves (e.g. Closes #123). This allows the bot to inherit any additional labels from that issue!

Happy coding! 🚀

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
server/src/module/attendance/attendance.validation.ts (1)

21-27: ⚡ Quick win

Consolidate duplicate workHours calculations into one validation pass.

The two .refine() blocks duplicate date parsing and duration math. Use a single .superRefine() so the rule logic stays in sync and easier to maintain.

Proposed refactor
-export const regularizeSchema = z.object({
+export const regularizeSchema = z.object({
   employeeId: z.number().int().positive(),
   date: z.string().datetime(),
   checkIn: z.string().datetime(),
   checkOut: z.string().datetime(),
   notes: z.string().min(1, "Reason for regularization is required").max(500),
-}).refine((data) => {
-  const workHours = (new Date(data.checkOut).getTime() - new Date(data.checkIn).getTime()) / 3600000;
-  return workHours > 0;
-}, { message: "checkOut must be after checkIn" }).refine((data) => {
-  const workHours = (new Date(data.checkOut).getTime() - new Date(data.checkIn).getTime()) / 3600000;
-  return workHours <= MAX_WORK_HOURS;
-}, { message: `Work hours must not exceed ${MAX_WORK_HOURS}` });
+}).superRefine((data, ctx) => {
+  const workHours = (new Date(data.checkOut).getTime() - new Date(data.checkIn).getTime()) / 3600000;
+
+  if (workHours <= 0) {
+    ctx.addIssue({
+      code: z.ZodIssueCode.custom,
+      message: "checkOut must be after checkIn",
+      path: ["checkOut"],
+    });
+  }
+
+  if (workHours > MAX_WORK_HOURS) {
+    ctx.addIssue({
+      code: z.ZodIssueCode.custom,
+      message: `Work hours must not exceed ${MAX_WORK_HOURS}`,
+      path: ["checkOut"],
+    });
+  }
+});

As per coding guidelines: "Apply DRY principle: no duplicate helpers, shared animation variants per file".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/src/module/attendance/attendance.validation.ts` around lines 21 - 27,
Duplicate workHours calculation in the two .refine() calls should be
consolidated into a single .superRefine() on the same Zod schema: compute
workHours once from data.checkIn and data.checkOut, then add two checks inside
the superRefine callback—(1) if workHours <= 0 addIssue({ path: ["checkOut"],
message: "checkOut must be after checkIn" }) and (2) if workHours >
MAX_WORK_HOURS addIssue({ path: ["checkOut"], message: `Work hours must not
exceed ${MAX_WORK_HOURS}` }); update the schema where those .refine() calls
exist to use .superRefine() so the parsing and error reporting for
checkIn/checkOut are centralized and DRY.
server/src/module/attendance/attendance.service.ts (1)

136-137: ⚡ Quick win

Avoid cross-layer drift by sharing the max-hours constant.

24 is enforced in both schema and service with separate literals/constants. Move it to a shared attendance constant and import in both places to keep the contract synchronized.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/src/module/attendance/attendance.service.ts` around lines 136 - 137,
The service currently hardcodes 24 in the work-hours validation (see the
workHours checks in attendance.service.ts: "if (workHours > 24) ..."), which
duplicates the schema's limit; extract a single shared constant (e.g.
ATTENDANCE_MAX_HOURS or MAX_WORK_HOURS) into the attendance constants module,
import that constant into both the schema and the attendance service, and
replace the literal 24 in the service (and schema) with the imported constant so
the max-hours constraint is defined in one place and reused.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@server/src/module/attendance/attendance.service.ts`:
- Around line 136-137: The service currently hardcodes 24 in the work-hours
validation (see the workHours checks in attendance.service.ts: "if (workHours >
24) ..."), which duplicates the schema's limit; extract a single shared constant
(e.g. ATTENDANCE_MAX_HOURS or MAX_WORK_HOURS) into the attendance constants
module, import that constant into both the schema and the attendance service,
and replace the literal 24 in the service (and schema) with the imported
constant so the max-hours constraint is defined in one place and reused.

In `@server/src/module/attendance/attendance.validation.ts`:
- Around line 21-27: Duplicate workHours calculation in the two .refine() calls
should be consolidated into a single .superRefine() on the same Zod schema:
compute workHours once from data.checkIn and data.checkOut, then add two checks
inside the superRefine callback—(1) if workHours <= 0 addIssue({ path:
["checkOut"], message: "checkOut must be after checkIn" }) and (2) if workHours
> MAX_WORK_HOURS addIssue({ path: ["checkOut"], message: `Work hours must not
exceed ${MAX_WORK_HOURS}` }); update the schema where those .refine() calls
exist to use .superRefine() so the parsing and error reporting for
checkIn/checkOut are centralized and DRY.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 75667374-c57d-41f8-a59f-60f16249f4ed

📥 Commits

Reviewing files that changed from the base of the PR and between e15a36c and 7abdbaa.

📒 Files selected for processing (2)
  • server/src/module/attendance/attendance.service.ts
  • server/src/module/attendance/attendance.validation.ts

- Replace duplicate .refine() with single .superRefine()
- Extract MAX_WORK_HOURS to shared attendance.constants.ts
- Import constant in both validation.ts and service.ts
- Return HTTP 400 for service validation errors in controller
@github-actions github-actions Bot added level:intermediate Requires moderate project understanding and removed level:beginner Good for first-time contributors labels Jun 4, 2026
@Sachinchaurasiya360
Copy link
Copy Markdown
Owner

Code Review — PR #1407: Reject negative/zero work hours in attendance regularization

Hi @Xenon010101, the MAX_WORK_HOURS constant is a clean extraction. Found the same fragile controller pattern as PR #1408 and a duplication issue.


🔴 Fragile error-message string matching in the controller (same pattern as #1408)

// attendance.controller.ts
if (msg.includes("must be after") || msg.includes("must not exceed") || ...)
  return res.status(400).json({ message: msg });

This PR adds "must be after" and "must not exceed" to the substring list, and both PRs (#1407 and #1408) modify the same controller method. These two PRs will conflict at the controller level.

Beyond the conflict, the substring matching is fragile — if either error message is ever changed, the controller silently returns 500 instead of 400. Use a typed error class instead (same fix as described in #1408).


🟡 Validation logic duplicated between Zod schema and service layer

Both regularizeSchema (via superRefine) and attendanceService.regularize() check:

  • workHours <= 0 → "checkOut must be after checkIn"
  • workHours > MAX_WORK_HOURS → "Work hours must not exceed 24"

Since Zod runs first at the request boundary, the service-level checks are dead code in practice. This creates maintenance overhead — both places need to stay in sync when the rules change.

Recommendation: Keep validation in Zod (single source of truth at the boundary), remove the duplicate guards from the service layer, and rely on the typed error class approach for any service-layer business logic errors.


🟡 Merge conflict with PR #1408

Both PRs modify attendance.controller.ts, attendance.service.ts, and attendance.validation.ts. They need to be merged in sequence. Coordinate with the other author and rebase whichever merges second.

Copy link
Copy Markdown
Owner

@Sachinchaurasiya360 Sachinchaurasiya360 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Zod superRefine is correct and well-placed. Two issues before merge.

Issue 1 (Medium) - Fragile substring matching in controller catch block:

attendance.controller.ts lines 92-95 use msg.includes("must be after"), msg.includes("must not exceed") etc. This couples service error strings to the controller with no type safety - any future typo fix in the error message silently turns a 400 into a 500. The same file already uses exact string equality (===) for the checkOut handler - use that pattern here too.

Issue 2 (Low) - Duplicate validation logic in service layer:

The workHours guard (if (workHours <= 0) and if (workHours > MAX_WORK_HOURS)) exists in both attendance.validation.ts (via superRefine, which runs first) and in attendance.service.ts. Since Zod always runs at the controller boundary before the service is called, the service-layer guards are dead code in normal execution. Remove them from the service. The existing date > today guard in the service is a legitimate business rule and should stay - this note only applies to the workHours arithmetic checks.

Note: This PR and PR #1408 both modify the regularize catch block in attendance.controller.ts. They will conflict - please rebase onto whichever merges first.

The Zod superRefine logic itself, the MAX_WORK_HOURS constant extraction, and the 24-hour upper bound are all correct. Fix these two issues and it is ready to merge.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gssoc:approved Approved for GSSoC scoring gssoc level:intermediate Requires moderate project understanding quality:clean Clean and well-structured contribution scope:backend Changes to server-side / API code type:bug Bug fixes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix: Negative or Invalid Work Hours in Attendance Regularization

2 participants