Skip to content

feat: atomic proposal acceptance → contract creation flow - #256

Open
CodeNinjaStephen wants to merge 2 commits into
Lumina-eX:mainfrom
CodeNinjaStephen:feature/proposal-acceptance-contract-creation
Open

CodeNinjaStephen wants to merge 2 commits into
Lumina-eX:mainfrom
CodeNinjaStephen:feature/proposal-acceptance-contract-creation

Conversation

@CodeNinjaStephen

Copy link
Copy Markdown

Summary

Implements the full proposal acceptance → contract creation flow as an atomic database
transaction, ensuring no partial state is ever persisted if any step fails.

When a client accepts a freelancer's proposal, the system now:

  1. Validates the proposal is still pending and the project is active
  2. Verifies the caller is the project's client (freelancers are blocked)
  3. Marks the proposal accepted and rejects all other pending proposals on the same project
  4. Creates a contract record (status=pending, escrow_status=unfunded)
  5. Back-references proposal.contract_id for traceability
  6. Initializes milestone rows from the proposal's breakdown
  7. Advances the project to in_progress
  8. Writes an audit log entry

All 8 steps run inside a single BEGIN/COMMIT/ROLLBACK — if anything fails, the entire
transaction rolls back.

───────────────────────────────────────────────────────────────────────────────────────────────

Changes

  • scripts/012-proposals.sql — Proposals table with UUID PKs, proposal_status enum, JSONB
    milestone_breakdown, and a partial unique index (WHERE status = 'accepted') enforcing one
    accepted proposal per project at the DB level
  • lib/proposals.ts — Service layer with acceptProposal() atomic transaction and typed domain
    errors (ProposalNotFoundError, ProposalAlreadyAcceptedError, ProjectNotActiveError,
    UnauthorizedAcceptanceError, etc.)
  • app/api/proposals/[id]/accept/route.ts — POST /api/proposals/:id/accept with auth middleware,
    client/admin-only role guard, and deterministic HTTP error codes
  • lib/validations/index.ts — AcceptProposalSchema, CreateProposalSchema,
    ProposalMilestoneSchema
  • tests/api/proposals.test.ts — 29 unit tests covering the full acceptance criteria

───────────────────────────────────────────────────────────────────────────────────────────────

Testing

pnpm test tests/api/proposals.test.ts

29/29 passed

pnpm build

exit 0 — route compiled as ƒ /api/proposals/[id]/accept

Test coverage includes: happy path, milestone creation, duplicate acceptance prevention,
unauthorized freelancer access, non-active project rejection, transaction rollback on DB error,
concurrent race condition (unique constraint violation), and all HTTP error codes.

───────────────────────────────────────────────────────────────────────────────────────────────

Acceptance Criteria

  • ✅ Database transaction for atomicity
  • ✅ Prevent multiple accepted proposals per project
  • ✅ Proposal → contract contract_id back-reference
  • ✅ Contract status=pending, escrow_status=unfunded on creation
  • ✅ Error handling with full transaction rollback
  • ✅ Client-only authorization (freelancers return 403)
  • ✅ Audit trail via admin_audit_logs

closes #217

- Add scripts/012-proposals.sql: proposals table with UUID PKs,
  proposal_status enum, JSONB milestone_breakdown, and a partial
  unique index (WHERE status='accepted') enforcing one accepted
  proposal per project at the DB level

- Add lib/proposals.ts: service layer with acceptProposal() running
  a 10-step atomic BEGIN/COMMIT/ROLLBACK transaction:
  validate proposal, verify client ownership, check project state,
  mark accepted, reject other proposals, create contract
  (status=pending, escrow_status=unfunded), back-reference
  proposal.contract_id, create milestone rows, advance project to
  in_progress, insert audit log

- Add app/api/proposals/[id]/accept/route.ts: POST handler with
  withAuthCtx, UUID path validation, client/admin-only role guard,
  wallet→userId resolution, deterministic HTTP codes for all domain
  errors (400/401/403/404/409/422/500)

- Add lib/validations/index.ts: AcceptProposalSchema,
  CreateProposalSchema, ProposalMilestoneSchema

- Add __tests__/api/proposals.test.ts: 29 tests covering atomicity
  rollback, duplicate prevention, unauthorized access, project state
  validation, milestone creation, escrow init, concurrent race
  (unique constraint), and full success shape
@drips-wave

drips-wave Bot commented Oct 16, 2026

Copy link
Copy Markdown

@CodeNinjaStephen Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@SudiptaPaul-31

Copy link
Copy Markdown
Collaborator

@CodeNinjaStephen build fails

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Proposal Acceptance & Contract Creation Service

2 participants