critical priority low complexity backend pending backend specialist Tier 1

Acceptance Criteria

fetchPresetsForOrg(orgId) returns a typed List<PeriodPreset> from Supabase when online
fetchPresetsForOrg returns cached presets from local storage when the network is unavailable
Cache is populated on every successful Supabase fetch and invalidated when savePreset or deletePreset is called
savePreset(preset) inserts or upserts a preset row scoped to the given organisation and returns the saved entity
deletePreset(presetId) removes the row from Supabase and removes the corresponding entry from the local cache
clearCache() removes all locally cached preset data for the organisation without affecting Supabase
All methods throw typed domain exceptions (e.g. NetworkException, PermissionException) rather than raw Supabase errors
RLS policy violations result in a PermissionException with a user-readable message, not an unhandled crash
Repository is injectable via Riverpod and can be replaced with a mock in tests
All public method signatures are documented with Dart doc comments

Technical Requirements

frameworks
Flutter
Riverpod
supabase_flutter
apis
Supabase REST API (period_presets table)
Supabase Realtime (optional live sync)
data models
PeriodPreset
Organisation
performance requirements
Cache reads must complete in under 5 ms (SharedPreferences or Hive)
Supabase fetch must complete within 3 seconds on a 4G connection
Cached data must not exceed 500 KB per organisation
security requirements
All Supabase queries must be scoped to the authenticated user's organisation via RLS
Preset data must not be written to unencrypted storage if it contains personally identifiable information
Access tokens must be refreshed automatically by supabase_flutter before expiry

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

Define a PeriodPresetLocalCache abstraction (backed by SharedPreferences or Hive) so the caching strategy can be swapped independently. Store cached presets as JSON-encoded strings keyed by orgId. Use supabase_flutter's `.from('period_presets').select().eq('org_id', orgId)` pattern and map rows to strongly typed Dart models using a fromJson factory. Wrap all Supabase calls in try/catch blocks and translate PostgrestException codes to domain exceptions.

Register the repository as a Riverpod Provider so it participates in the dependency graph established in task-001. Avoid storing Supabase client references as instance fields — inject via constructor for testability.

Testing Requirements

Unit tests using flutter_test and Mockito. Mock the Supabase client at the interface boundary. Test cases: (1) online fetch returns mapped PeriodPreset list, (2) offline path returns cached data, (3) cache miss + network error throws NetworkException, (4) savePreset upserts and updates cache, (5) deletePreset removes from Supabase and cache, (6) RLS-denied response (403) maps to PermissionException, (7) empty result set returns empty list without error. Target 90%+ line coverage on the repository class.

Component
Period Configuration Repository
data low
Epic Risks (3)
high impact medium prob security

Supabase RLS policies for period preset configuration may be missing or incorrectly scoped, causing one organisation's presets to leak to another or write operations to fail silently.

Mitigation & Contingency

Mitigation: Define and review RLS policies for the bufdir_period_presets table in the migration file before any repository code is written. Include an integration test that verifies cross-organisation isolation using two distinct org credentials.

Contingency: If RLS is misconfigured in production, immediately disable the period preset fetch endpoint and fall back to hardcoded global presets until the policy is corrected and redeployed.

medium impact medium prob technical

The activities table may lack a composite index on (organisation_id, activity_date), causing the range count query in BufdirAggregationRepository to perform a full table scan and exceed acceptable response time for large organisations.

Mitigation & Contingency

Mitigation: Add a migration that creates a composite index on (organisation_id, activity_date) as part of this epic. Benchmark the count query against a representative dataset (10 000+ rows) before marking the epic complete.

Contingency: If query latency is unacceptable after indexing, move the count query to a Supabase RPC function that leverages a materialised view or partial index, accepting a slight staleness window.

medium impact medium prob technical

Flutter's native date picker widgets have known accessibility gaps (missing semantic labels, non-standard focus traversal) that may prevent WCAG 2.2 AA compliance out of the box, requiring a custom implementation.

Mitigation & Contingency

Mitigation: Evaluate third-party accessible date picker packages (e.g., table_calendar with custom semantics) against WCAG 2.2 AA criteria before beginning implementation. Document the chosen approach in the epic kick-off.

Contingency: If no package meets accessibility requirements, implement a simple text-field-based date entry with explicit semantic labels and format hints as an accessible fallback, deferring a fully visual calendar to a later iteration.