314 lines
8.5 KiB
Go
314 lines
8.5 KiB
Go
package pgdb
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
|
|
dberrors "git-molva.ru/Molva/molva-backend/services/api_gateway/internal/database/errors"
|
|
dbtypes "git-molva.ru/Molva/molva-backend/services/api_gateway/internal/database/types"
|
|
"github.com/Masterminds/squirrel"
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
// ALERT: intergrate safety checks
|
|
|
|
func (c *client) GetBankAccountList(
|
|
ctx context.Context,
|
|
request *dbtypes.BankAccountListGetRequest,
|
|
) (*dbtypes.BankAccountListGetResponse, error) {
|
|
if request == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
var (
|
|
psql = squirrel.StatementBuilder.PlaceholderFormat(squirrel.Dollar)
|
|
|
|
bankAccountsTable = fmt.Sprintf("%s.%s", c.config.Schema, BankAccountsTableName)
|
|
)
|
|
|
|
getAccounts := psql.Select(
|
|
"id", "owner_id", "account_number", "bank_name", "bik",
|
|
"correspondent_account", "is_primary", "created_at", "updated_at",
|
|
).From(bankAccountsTable).
|
|
Where(squirrel.Eq{"owner_id": request.OwnerId})
|
|
|
|
query, args, err := getAccounts.ToSql()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%w: error building get bank accounts query: %v", dberrors.ErrInternal, err)
|
|
}
|
|
|
|
rows, err := c.db.QueryContext(ctx, query, args...)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%w: error executing get bank accounts query: %v", dberrors.ErrInternal, err)
|
|
}
|
|
|
|
defer rows.Close()
|
|
|
|
var accounts []dbtypes.BankAccount
|
|
|
|
for rows.Next() {
|
|
var (
|
|
accountNumber, bankName, bik, correspondentAccount sql.NullString
|
|
bankAccount dbtypes.BankAccount
|
|
)
|
|
|
|
if err := rows.Scan(
|
|
&bankAccount.Id, &bankAccount.OwnerId, &accountNumber, &bankName, &bik,
|
|
&correspondentAccount, &bankAccount.IsPrimary, &bankAccount.CreatedAt, &bankAccount.UpdatedAt,
|
|
); err != nil {
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
return nil, dberrors.ErrNotFound
|
|
}
|
|
|
|
return nil, fmt.Errorf("%w: error scanning row: %v", dberrors.ErrInternal, err)
|
|
}
|
|
|
|
if accountNumber.Valid {
|
|
bankAccount.AccountName = accountNumber.String
|
|
}
|
|
|
|
if bankName.Valid {
|
|
bankAccount.BankName = bankName.String
|
|
}
|
|
|
|
if bik.Valid {
|
|
bankAccount.Bik = bik.String
|
|
}
|
|
|
|
if correspondentAccount.Valid {
|
|
bankAccount.CorrespondentAccount = correspondentAccount.String
|
|
}
|
|
|
|
accounts = append(accounts, bankAccount)
|
|
}
|
|
|
|
return &dbtypes.BankAccountListGetResponse{
|
|
BankAccounts: accounts,
|
|
}, nil
|
|
}
|
|
|
|
func (c *client) CreateBankAccount(
|
|
ctx context.Context,
|
|
request *dbtypes.BankAccountCreateRequest,
|
|
) (*dbtypes.BankAccountCreateResponse, error) {
|
|
if request == nil {
|
|
return nil, fmt.Errorf("%w: request is nil", dberrors.ErrBadRequest)
|
|
}
|
|
|
|
var (
|
|
psql = squirrel.StatementBuilder.PlaceholderFormat(squirrel.Dollar)
|
|
|
|
bankAccountsTable = fmt.Sprintf("%s.%s", c.config.Schema, BankAccountsTableName)
|
|
)
|
|
|
|
// TODO: use normal uuid after DB reengineering
|
|
bankAccountId := fmt.Sprintf("%sBNK", strings.ReplaceAll(uuid.NewString(), "-", ""))
|
|
|
|
createBankAccount := psql.Insert(bankAccountsTable).
|
|
Columns(
|
|
"id", "owner_id", "account_number", "bank_name", "bik", "correspondent_account", "is_primary",
|
|
).
|
|
Values(
|
|
bankAccountId, request.OwnerId, request.AccountNumber, request.BankName,
|
|
request.Bik, request.CorrespondentAccount, request.IsPrimary,
|
|
)
|
|
|
|
query, args, err := createBankAccount.ToSql()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%w: error building create bank account query: %v", dberrors.ErrInternal, err)
|
|
}
|
|
|
|
res, err := c.db.ExecContext(ctx, query, args...)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%w: error executing create bank account query: %v", dberrors.ErrInternal, err)
|
|
}
|
|
|
|
rowsAffected, err := res.RowsAffected()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%w: error getting rows affected for create bank account query: %v", dberrors.ErrInternal, err)
|
|
}
|
|
|
|
if rowsAffected == 0 {
|
|
return nil, dberrors.ErrInternal
|
|
}
|
|
|
|
return &dbtypes.BankAccountCreateResponse{
|
|
Id: bankAccountId,
|
|
}, nil
|
|
}
|
|
|
|
func (c *client) UpdateBankAccount(
|
|
ctx context.Context,
|
|
request *dbtypes.BankAccountUpdateRequest,
|
|
) (*dbtypes.BankAccountUpdateResponse, error) {
|
|
if request == nil {
|
|
return nil, fmt.Errorf("%w: request is nil", dberrors.ErrBadRequest)
|
|
}
|
|
|
|
tx, err := c.db.BeginTx(ctx, nil)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%w: error starting transaction: %v", dberrors.ErrInternal, err)
|
|
}
|
|
|
|
defer func() { _ = tx.Rollback() }()
|
|
|
|
result, err := c.updateBankAccount(ctx, tx, request)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error updating bank account: %w", err)
|
|
}
|
|
|
|
if err := tx.Commit(); err != nil {
|
|
return nil, fmt.Errorf("%w: error committing transaction: %w", dberrors.ErrInternal, err)
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
func (c *client) updateBankAccount(
|
|
ctx context.Context,
|
|
driver Driver,
|
|
request *dbtypes.BankAccountUpdateRequest,
|
|
) (*dbtypes.BankAccountUpdateResponse, error) {
|
|
var (
|
|
psql = squirrel.StatementBuilder.PlaceholderFormat(squirrel.Dollar)
|
|
|
|
bankAccountsTable = fmt.Sprintf("%s.%s", c.config.Schema, BankAccountsTableName)
|
|
)
|
|
|
|
updateAccount := psql.Update(bankAccountsTable).
|
|
Where(squirrel.Eq{"id": request.Id})
|
|
|
|
if request.AccountNumber != nil {
|
|
updateAccount = updateAccount.Set("account_number", *request.AccountNumber)
|
|
}
|
|
|
|
// TODO: uncomment when DB supports it
|
|
// if request.AccountName != nil {
|
|
// updateAccount = updateAccount.Set("account_name", *request.AccountName)
|
|
// }
|
|
|
|
if request.BankName != nil {
|
|
updateAccount = updateAccount.Set("bank_name", *request.BankName)
|
|
}
|
|
|
|
if request.Bik != nil {
|
|
updateAccount = updateAccount.Set("bik", *request.Bik)
|
|
}
|
|
|
|
if request.CorrespondentAccount != nil {
|
|
updateAccount = updateAccount.Set("correspondent_account", *request.CorrespondentAccount)
|
|
}
|
|
|
|
if request.IsPrimary != nil {
|
|
if *request.IsPrimary {
|
|
if err := c.unmarkPrimaryBankAccount(ctx, driver, request.Id); err != nil {
|
|
return nil, fmt.Errorf("error unmarking currently primary bank account: %w", err)
|
|
}
|
|
}
|
|
|
|
// QnA: what if the update makes all BAs non-primary?
|
|
updateAccount = updateAccount.Set("is_primary", *request.IsPrimary)
|
|
}
|
|
|
|
query, args, err := updateAccount.ToSql()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%w: error building 'update bank account' query: %v", dberrors.ErrInternal, err)
|
|
}
|
|
|
|
res, err := driver.ExecContext(ctx, query, args...)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%w: error executing 'update bank account' query: %v", dberrors.ErrInternal, err)
|
|
}
|
|
|
|
rowsAffected, err := res.RowsAffected()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%w: error getting rows affected for 'update bank account' query: %v", dberrors.ErrInternal, err)
|
|
}
|
|
|
|
if rowsAffected == 0 {
|
|
return nil, dberrors.ErrNotFound
|
|
}
|
|
|
|
return &dbtypes.BankAccountUpdateResponse{}, nil
|
|
}
|
|
|
|
func (c *client) unmarkPrimaryBankAccount(
|
|
ctx context.Context,
|
|
driver Driver,
|
|
bankAccountId string,
|
|
) error {
|
|
var (
|
|
psql = squirrel.StatementBuilder.PlaceholderFormat(squirrel.Dollar)
|
|
|
|
bankAccountsTable = fmt.Sprintf("%s.%s", c.config.Schema, BankAccountsTableName)
|
|
)
|
|
|
|
getOwnerId := psql.Select(
|
|
"owner_id", "is_primary",
|
|
).From(bankAccountsTable).
|
|
Where(squirrel.Eq{"id": bankAccountId}).
|
|
Suffix("FOR UPDATE")
|
|
|
|
query, args, err := getOwnerId.ToSql()
|
|
if err != nil {
|
|
return fmt.Errorf("%w: error building 'get owner id of bank account' query: %v", dberrors.ErrInternal, err)
|
|
}
|
|
|
|
row := driver.QueryRowContext(ctx, query, args...)
|
|
|
|
var (
|
|
ownerId string
|
|
isPrimary bool
|
|
)
|
|
|
|
if err := row.Scan(&ownerId, &isPrimary); err != nil {
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
return dberrors.ErrNotFound
|
|
}
|
|
|
|
return fmt.Errorf("%w: error scanning row for 'get owner id of bank account' query: %v", dberrors.ErrInternal, err)
|
|
}
|
|
|
|
if isPrimary {
|
|
return nil
|
|
}
|
|
|
|
unmarkPrimaryBA := psql.Update(bankAccountsTable).
|
|
Set("is_primary", false).
|
|
Where(squirrel.Eq{
|
|
"owner_id": ownerId,
|
|
"is_primary": true,
|
|
})
|
|
|
|
query, args, err = unmarkPrimaryBA.ToSql()
|
|
if err != nil {
|
|
return fmt.Errorf("%w: error building 'unmark primary bank account' query: %v", dberrors.ErrInternal, err)
|
|
}
|
|
|
|
res, err := driver.ExecContext(ctx, query, args...)
|
|
if err != nil {
|
|
return fmt.Errorf("%w: error executing 'unmark primary bank account' query: %v", dberrors.ErrInternal, err)
|
|
}
|
|
|
|
rowsAffected, err := res.RowsAffected()
|
|
if err != nil {
|
|
return fmt.Errorf("%w: error getting rows affected for 'unmark primary bank account' query: %v", dberrors.ErrInternal, err)
|
|
}
|
|
|
|
if rowsAffected == 0 {
|
|
return fmt.Errorf("%w: error unmarking primary bank account: no rows affected", dberrors.ErrInternal)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c *client) DeleteBankAccount(
|
|
ctx context.Context,
|
|
request *dbtypes.BankAccountDeleteRequest,
|
|
) (*dbtypes.BankAccountDeleteResponse, error) {
|
|
return nil, dberrors.ErrUnimplemented
|
|
}
|