Orders with pay at closing show agreement unsigned

Objective

  • Fix Pay at Close (PAC) orders where staff and worklists show agreement unsigned and reports stay locked, even though clients and CSRs see agreements as signed (or complete) in the portal.
  • Restore a trustworthy single source of truth for “agreements done” vs “payment done” on PAC jobs so reports release when the business expects them to.
  • Reduce failed PAC journeys where the only workaround is cancel PAC and pay by card.

Background

  • Product feedback reports multiple markets affected: PAC selected, order/worklist shows unsigned agreement, but opening agreements on the inspection shows signed documents.
  • In the client portal, agreements can appear complete while PAC remains pending on the order and reports do not release.
  • Reported examples:
  • WPI inspection 1008593904Example inspection
  • GTC — 130 Thrushwood Dr, Fayetteville, Georgia 30215
  • RIA1639
  • Reported workaround: client cancels PAC and pays by card, then flow can proceed.
  • Code review points to three separate systems that can disagree:
  1. Attik agreements (Agreement collection, client route /client/job/[slug]/agreement/[id])
  2. FlexFund / ISN PAC agreement (separate signing link on the payment; status via webhooks)
  3. Denormalized job fields inspection.agreement.signed / inspection.paid used for badges and report lock
  • Stale agreement rule (intentional today): agreementLockSync.ts treats only stale signed active agreements as not signed at the job level (only stale signed → not signed in unit tests). Individual rows can still show Signed / Outdated in the Agreements UI while the order chip stays unsigned.
  • Report lock rule: checkReportAccess.ts unlocks when paid (unless toBeInvoiced) and non-stale agreements are satisfied (DB query preferred over stale inspection.agreement sync).
  • PAC pending rule: Pending PAC does not contribute to inspection.paid (payAtClosePaymentContributesToTotalPaid); payment should move to completed on FlexFund statuses such as SIGNATURE_RECEIVED via POST /webhooks/flexfund and totals sync.
  • Screenshots from the field:

image.png

image.png

image.png

Scope

Backend

  • Job-level agreement sync: attik-backend/src/events/streamHandlers/agreementStream.ts updates inspection.agreement.present / inspection.agreement.signed via attik-backend/src/util/functions/agreements/agreementLockSync.ts (non-stale rows must be signed; stale-only signed → job signed: false).
  • Report access: attik-backend/src/util/functions/inspection/checkReportAccess.ts (paid + agreement rules; uses Agreement DB when query succeeds).
  • Stale worker: attik-backend/src/events/bullmq/agreementStaleWorker.ts and signing hash logic under attik-backend/src/util/functions/agreements/.
  • PAC payment lifecycle: attik-backend/src/routes/webhooks/flexfund/flexfundWebhook.ts, status mapping in attik-backend/src/util/functions/payments/pacIsnFlexFundStatuses.ts and paymentStatusMapping.ts, transitions in paymentStatusValidator.ts.
  • PAC totals / paid flag: attik-backend/src/util/functions/isn/isnPayAtCloseOrderTotal.ts (payAtClosePaymentContributesToTotalPaid), attik-backend/src/util/functions/forecast/syncTotalsOnInspections.ts.
  • Decision needed: For affected inspection IDs, determine which failure mode applies — stale-only lock, unsigned non-stale agreement, inspection.agreement desync, or PAC webhook/status stuck in pending.
  • Decision needed: Whether stale-only signed agreements should continue to show as unsigned on staff badges or should surface as outdated with different lock/report behavior.

Frontend

  • Staff unsigned/signed chip: attik-frontend/src/components/ui/StatusBadges.tsx uses job.agreement.signed (not per-document status).
  • Worklist filter: attik-frontend/src/components/task-check/AgreementsNeeded.tsx (agreementSigned: 'false', !job.agreement.signed).
  • Inspection agreements tab: attik-frontend/src/app/tools/inspections/[id]/components/Agreements.tsx (per-row status, stale/outdated UI).
  • Client portal agreements list: attik-frontend/src/app/client/job/[slug]/components/AttikAgreementList.tsx (row-level Signed / Outdated).
  • Client report lock: attik-frontend/src/app/client/job/[slug]/components/ReportList.tsx with attik-frontend/src/util/functions/report/computeAgreementSignedForReportLock.ts (can prefer agreements array over synced fields).
  • PAC UX: attik-frontend/src/app/client/job/[slug]/pay-beta/PaymentBetaClient.tsx, PendingPACAlert.tsx, helpers in attik-frontend/src/util/functions/payments/pacStatusHelpers.ts (isPACPaymentPending, Pending Signature labels).
  • Decision needed: Clearer distinction in UI between Attik agreement unsigned/outdated vs FlexFund PAC signature still required.

Out of scope unless expanded

  • Mobile app agreement badges (similar inspection.agreement.signed pattern exists in attik-mobile but not reported in examples).

References

  • Example inspection: https://www.attik.ai/inspections/1008593904?tab=activity-feed
  • Agreement lock rules: attik-backend/src/util/functions/agreements/agreementLockSync.ts, attik-backend/tests/unit/agreementLockSync.test.ts
  • Report lock: attik-backend/src/util/functions/inspection/checkReportAccess.ts, attik-backend/tests/integration/lockReport.test.ts
  • FlexFund webhook: attik-backend/src/routes/webhooks/flexfund/flexfundWebhook.ts

Please authenticate to join the conversation.

Upvoters
Status

Completed

Board
🏠

Main App

Date

7 days ago

Author

Linear

Subscribe to post

Get notified by email when there are changes.