Skip to main content

Audit B — DTO Contract Diff Report

Generated: 2026-04-26 by automated audit. Part of Production Readiness Audit Plan.

Scope: 13 backend services × 47 dashboard API files. Focused on POST/PUT/PATCH/DELETE mutations.

Header injection audit​

Sound — src/lib/api/client.ts correctly injects Authorization, X-Property-ID, X-Tenant-ID on every authenticated request. Issues are payload shape, not headers.

Concerns flagged:

  • getInvoicePdfUrl returns a URL for <a href> — won't carry JWT, may 401 in prod
  • propertyId read directly from localStorage['hmp-app-store'] in 4 modules — parallel source of truth, lags state on impersonate/property-switch

CRITICAL — payload will be rejected (14 findings)​

1. POS Create Menu Item — isAvailable silently stripped​

  • Frontend form sends isAvailable: boolean but backend CreateMenuItemDto doesn't have it. Item created but toggle ignored. (HIGH actually — silent data loss.)

2. Housekeeping Create Task — assignedTo free-text input vs @IsUUID() (issue #8 root cause)​

  • …/housekeeping/tasks/page.tsx:485 is <Input placeholder="Staff name (optional)">. Backend assignedTo? requires UUID. Any non-empty value 400s.

3. Settings Add User — missing required tenantId​

  • CreateUserDto lacks tenantId; backend requires @IsUUID() @IsNotEmpty(). Every Add User fails.

4. Reservation Cancel — DELETE with body (gateway may strip)​

  • Works locally; QA-stack-dependent.

5. Settings Add User — role: string vs backend UserRole enum​

  • Case mismatch fails IsEnum.

6. CRM Create Campaign — 5 simultaneous gaps (issue #3 root cause)​

  • Missing: propertyId, channel, segmentRules, createdBy. Frontend type values are channel codes, fail IsIn on backend type enum.

7. Guests Create — missing propertyId, lowercase gender​

  • Backend reads only @Body() CreateGuestDto, no header fallback. Every Create Guest fails.

8. Settings Create Rate Plan — missing propertyId, bare-string enums​

  • mealPlan/paymentPolicy are enums on backend.

9. Settings Create Cancellation Policy — missing propertyId, lowercase chargeType​

  • Backend ChargeType is PERCENTAGE/FLAT/FIRST_NIGHT/FULL_STAY; frontend sends 'percentage'/'fixed'/'nights'.

10. Frontdesk Check-In — multiple field mismatches​

  • idDocumentType, idDocumentNumber REQUIRED on backend but OPTIONAL on frontend
  • reservationId REQUIRED in body, frontend only puts in URL
  • Field name mismatch: signature vs signatureImageUrl
  • Extra fields silently stripped: guestPhoto, idExpiryDate, idIssuingCountry, arrivalTime, skipInspection, formC.passportNo

11. Frontdesk Post Charge — folioId missing from body, transactionCode regex (issue #17 path)​

  • Backend requires folioId in body (DTO @IsUUID @IsNotEmpty). transactionCode must match /^[A-Z0-9_]+$/. Lowercase or hyphens fail.

12. Frontdesk Post Payment — folioId missing, paymentMode enum​

  • Same path-vs-body issue + lowercase enum strings fail.

13. Frontdesk Void Transaction — extra folioId, reason required min 3 chars​

  • Backend strips folioId, requires reason.

14. Rooms Update Status — wrong endpoint path (issue #6 root cause)​

  • Frontend hits PATCH /rooms/:id but backend only exposes @Put('rooms/:id') and @Patch('rooms/:id/status'). Returns 404/405.
  • Also: frontend HousekeepingStatus enum has 6 values (includes OUT_OF_ORDER, OUT_OF_SERVICE) but backend has 4. Selecting OOO/OOS fails IsEnum.

19. CRM Drip Step — lowercase channel (issue #3 part)​

  • Frontend 'email'/'sms'/'whatsapp'/'push'/'wait'; backend uppercase. Every Drip Step send fails.

HIGH — likely to break edge cases (16 findings)​

15-18, 20-30: see full audit. Banquet, spa, channel manager, maintenance, linen, loyalty, folio merge — all have similar weak typing or enum case issues.

Summary stats​

  • Total mutation paths sampled: ~95
  • Confirmed-broken end-to-end: 14
  • Critical missing-required-field gaps: 8 (tenantId, propertyId, channel, segmentRules, createdBy, folioId, idDocumentType/Number, reservationId)
  • Enum-case mismatch gaps: 6 (gender, campaign type/channel, drip channel, charge type, payment mode, room status)
  • Header-injection problems: 0 critical (interceptor is sound)

Top 10 prioritized fixes​

  1. Frontdesk Check-In DTO — required fields, field rename, body vs URL.
  2. Settings Add User — add tenantId, type role enum, validate password length.
  3. CRM Create Campaign + Drip Step — split type/channel, add propertyId/segmentRules/createdBy, uppercase enums.
  4. Guest Create — inject propertyId, uppercase Gender.
  5. Settings Create Rate Plan / Cancellation Policy — add propertyId, uppercase enums.
  6. Folio Charge / Payment / Void — include folioId in body, uppercase enums, sanitize transactionCode.
  7. Housekeeping Create Task — Assignee — replace free-text Input with user picker.
  8. Room Update Status — fix path to /rooms/:id/status, align HousekeepingStatus enum.
  9. POS Create Menu Item — isAvailable — either remove from form or add to backend DTO.
  10. Banquet/Spa weak typing — replace Record<string, unknown> with strong types.