diff --git a/billing/credit/mocks/transaction_repository.go b/billing/credit/mocks/transaction_repository.go index e954105b3..76453bfda 100644 --- a/billing/credit/mocks/transaction_repository.go +++ b/billing/credit/mocks/transaction_repository.go @@ -4,11 +4,10 @@ package mocks import ( context "context" + time "time" credit "github.com/raystack/frontier/billing/credit" mock "github.com/stretchr/testify/mock" - - time "time" ) // TransactionRepository is an autogenerated mock type for the TransactionRepository type @@ -84,6 +83,53 @@ func (_c *TransactionRepository_CreateEntry_Call) RunAndReturn(run func(context. return _c } +// DeleteByAccountID provides a mock function with given fields: ctx, accountID +func (_m *TransactionRepository) DeleteByAccountID(ctx context.Context, accountID string) error { + ret := _m.Called(ctx, accountID) + + if len(ret) == 0 { + panic("no return value specified for DeleteByAccountID") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { + r0 = rf(ctx, accountID) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// TransactionRepository_DeleteByAccountID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DeleteByAccountID' +type TransactionRepository_DeleteByAccountID_Call struct { + *mock.Call +} + +// DeleteByAccountID is a helper method to define mock.On call +// - ctx context.Context +// - accountID string +func (_e *TransactionRepository_Expecter) DeleteByAccountID(ctx interface{}, accountID interface{}) *TransactionRepository_DeleteByAccountID_Call { + return &TransactionRepository_DeleteByAccountID_Call{Call: _e.mock.On("DeleteByAccountID", ctx, accountID)} +} + +func (_c *TransactionRepository_DeleteByAccountID_Call) Run(run func(ctx context.Context, accountID string)) *TransactionRepository_DeleteByAccountID_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string)) + }) + return _c +} + +func (_c *TransactionRepository_DeleteByAccountID_Call) Return(_a0 error) *TransactionRepository_DeleteByAccountID_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *TransactionRepository_DeleteByAccountID_Call) RunAndReturn(run func(context.Context, string) error) *TransactionRepository_DeleteByAccountID_Call { + _c.Call.Return(run) + return _c +} + // GetBalance provides a mock function with given fields: ctx, id func (_m *TransactionRepository) GetBalance(ctx context.Context, id string) (int64, error) { ret := _m.Called(ctx, id) diff --git a/billing/credit/service.go b/billing/credit/service.go index 5ff4ee123..0996271b7 100644 --- a/billing/credit/service.go +++ b/billing/credit/service.go @@ -20,6 +20,7 @@ type TransactionRepository interface { GetByID(ctx context.Context, id string) (Transaction, error) GetBalanceForRange(ctx context.Context, accountID string, start time.Time, end time.Time) (int64, error) GetBalanceForRangeWithoutOverdraft(ctx context.Context, accountID string, start time.Time, end time.Time) (int64, error) + DeleteByAccountID(ctx context.Context, accountID string) error } type CustomerRepository interface { @@ -175,6 +176,12 @@ func (s Service) GetByID(ctx context.Context, id string) (Transaction, error) { return s.transactionRepository.GetByID(ctx, id) } +// DeleteByAccountID removes all credit transactions of a billing account. It is +// meant for account teardown; the deletion is recorded in audit records. +func (s Service) DeleteByAccountID(ctx context.Context, accountID string) error { + return s.transactionRepository.DeleteByAccountID(ctx, accountID) +} + // createAuditRecord creates an audit record for billing transaction events. func (s Service) createAuditRecord(ctx context.Context, customerID string, eventType pkgAuditRecord.Event, txID string, txEntry Transaction) error { customerAcc, err := s.customerRepository.GetByID(ctx, customerID) diff --git a/internal/store/postgres/billing_transactions_repository.go b/internal/store/postgres/billing_transactions_repository.go index f3004044d..36e465658 100644 --- a/internal/store/postgres/billing_transactions_repository.go +++ b/internal/store/postgres/billing_transactions_repository.go @@ -305,6 +305,23 @@ func (r BillingTransactionRepository) UpdateByID(ctx context.Context, toUpdate c return customerModel.transform() } +func (r BillingTransactionRepository) DeleteByAccountID(ctx context.Context, accountID string) error { + query, params, err := dialect.Delete(TABLE_BILLING_TRANSACTIONS).Where(goqu.Ex{ + "account_id": accountID, + }).ToSQL() + if err != nil { + return fmt.Errorf("%w: %w", errParse, err) + } + + if err = r.dbc.WithTimeout(ctx, TABLE_BILLING_TRANSACTIONS, "DeleteByAccountID", func(ctx context.Context) error { + _, err := r.dbc.ExecContext(ctx, query, params...) + return err + }); err != nil { + return fmt.Errorf("%w: %w", errTxn, err) + } + return nil +} + func (r BillingTransactionRepository) List(ctx context.Context, filter credit.Filter) ([]credit.Transaction, error) { stmt := dialect.Select().From(TABLE_BILLING_TRANSACTIONS).Order(goqu.I("created_at").Desc()) if filter.CustomerID != "" {