package membership import ( "context" "errors" "fuj-management/go/internal/domain/reconcile" ) // ErrIOPending is returned by stub loader methods until the M4 IO layer lands. var ErrIOPending = errors.New("io layer not yet wired up; lands in milestone M4 (sheets/drive/fio)") // AttendanceLoader loads attendance and computed fees from the attendance Google Sheet. type AttendanceLoader interface { LoadAdults(ctx context.Context) (members []reconcile.Member, sortedMonths []string, err error) LoadJuniors(ctx context.Context) (members []reconcile.Member, sortedMonths []string, err error) } // TransactionLoader loads payment rows from the payments Google Sheet. type TransactionLoader interface { LoadTransactions(ctx context.Context) ([]reconcile.Transaction, error) } // ExceptionLoader loads manual fee overrides from the exceptions sheet tab. type ExceptionLoader interface { LoadExceptions(ctx context.Context) (map[reconcile.ExceptionKey]reconcile.Exception, error) } // CacheFlusher can invalidate all cached data. type CacheFlusher interface { FlushCache() (int, error) } // Sources is the aggregate interface required by ReconcileReport. type Sources interface { AttendanceLoader TransactionLoader ExceptionLoader CacheFlusher } // NewStubSources returns a Sources whose every method returns ErrIOPending. func NewStubSources() Sources { return stubSources{} } type stubSources struct{} func (stubSources) LoadAdults(_ context.Context) ([]reconcile.Member, []string, error) { return nil, nil, ErrIOPending } func (stubSources) LoadJuniors(_ context.Context) ([]reconcile.Member, []string, error) { return nil, nil, ErrIOPending } func (stubSources) LoadTransactions(_ context.Context) ([]reconcile.Transaction, error) { return nil, ErrIOPending } func (stubSources) LoadExceptions(_ context.Context) (map[reconcile.ExceptionKey]reconcile.Exception, error) { return nil, ErrIOPending } func (stubSources) FlushCache() (int, error) { return 0, nil }