Record checkbox e-signature with UTC timestamp
epic-driver-and-confidentiality-management-core-services-task-010 — Implement the acknowledgement recording method in the declaration acknowledgement service. When a driver submits their acknowledgement, the service must persist: driver ID, declaration ID, UTC timestamp of acknowledgement, scroll completion flag, and a hash of the document version acknowledged. This record is the legally defensible e-signature and must be immutable after creation.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 5 - 253 tasks
Can start after Tier 4 completes
Implementation Notes
Implement the atomic insert + declaration status update as a Postgres function (Supabase Edge Function alternative): CREATE OR REPLACE FUNCTION record_declaration_acknowledgement(p_declaration_id UUID, p_scroll_completed BOOLEAN) RETURNS JSON AS $$ ... $$ — this function resolves driver_id from auth.uid(), copies document_hash from declarations, inserts into declaration_acknowledgements with DEFAULT timestamps, and updates declarations.status = 'ACKNOWLEDGED' in a single transaction. Call it via supabase.rpc('record_declaration_acknowledgement', {...}). The immutability requirement (no UPDATE/DELETE) is enforced by two separate RLS policies.
Document in a code comment that this record constitutes the legally defensible e-signature under Norwegian electronic signature law (eSignatur-loven) — future developers must not remove the immutability policies without legal review. The AcknowledgementReceipt returned to the Flutter layer should be persisted locally (Hive or shared_preferences) as a client-side confirmation copy in case the driver later disputes the acknowledgement.
Testing Requirements
Unit tests (flutter_test) with mocked AcknowledgementRepository: happy path returns correct receipt with document_hash and acknowledged_at, declarationId not found (ForeignKeyViolationException), duplicate acknowledgement (DuplicateAcknowledgementException), scroll_completed false (record still persisted). Integration tests against Supabase test project: insert acknowledgement and verify all fields in the returned row, verify declaration.status is ACKNOWLEDGED after the call, attempt duplicate acknowledgement and verify DB constraint fires, attempt to UPDATE the acknowledgement row (verify RLS blocks it), attempt acknowledgement by a different driver (verify RLS blocks it). Legal audit test: verify document_hash in acknowledgement matches the content_hash on the declaration record exactly.
Org-scoped encryption key management is complex. If keys are not correctly isolated per organization, a breach in one org's key could expose another org's declarations. Additionally, key rotation is not specified but may be needed for compliance, and the current implementation may not support it.
Mitigation & Contingency
Mitigation: Use Supabase Vault or a dedicated secrets management approach for org-scoped key storage. Define the key derivation strategy (per-org master key) in a security design document reviewed before implementation begins. Include key isolation tests in the test suite.
Contingency: If a full per-org key management system cannot be safely implemented within the sprint, fall back to a single platform-level encryption key with strict RLS isolation as a temporary measure, flagging the key rotation gap as a security debt item with a defined resolution milestone.
Push notification delivery to drivers depends on FCM token availability and device connectivity. If a driver has not granted notification permissions or has an expired FCM token, the declaration delivery notification will silently fail, leaving the coordinator unaware and the declaration unacknowledged.
Mitigation & Contingency
Mitigation: Implement delivery status tracking in declaration-notification-service. Fall back to in-app notification and SMS (if configured) when push delivery fails. Expose delivery failure status in the declaration status badge so coordinators can identify and manually follow up.
Contingency: If push delivery proves unreliable, implement a polling-based in-app notification fallback where drivers see pending declarations on next app open, ensuring the workflow can complete even without push notifications.
The acknowledgement service is meant to validate that the driver has fully scrolled through the declaration before confirming. Implementing reliable scroll completion detection in Flutter across different screen sizes and font sizes is technically non-trivial and could be bypassed.
Mitigation & Contingency
Mitigation: Implement scroll position tracking using ScrollController with a threshold (e.g., 95% of content height reached) and record the validated state server-side before allowing acknowledgement submission. Document the approach in the legal sign-off checkpoint noted in the feature documentation.
Contingency: If reliable scroll detection cannot be implemented within the sprint, add a mandatory reading delay timer (e.g., estimated reading time based on word count) as an alternative validation mechanism, pending legal review of the approach.
The driver assignment service must coordinate with the threshold-based expense approval workflow for fees above configured thresholds. If the expense approval workflow interface changes or is not yet stable, the integration point could break or produce incorrect routing behavior.
Mitigation & Contingency
Mitigation: Define a clear interface contract between driver-assignment-service and the expense approval workflow before implementation. Use dependency injection so the expense workflow client can be mocked in tests. Monitor the expense approval feature for interface changes.
Contingency: If the expense approval workflow interface is not stable, implement a direct database insert to the expense records table as a temporary bypass, with a flag indicating manual review is needed, until the stable interface is available.