Added transaction feature, fixed some mistakes
This commit is contained in:
@@ -93,7 +93,6 @@ func (r *FamilySQLRepository) Update(ctx context.Context, family *domain.Family)
|
||||
family.Name,
|
||||
family.TelegramChatID,
|
||||
family.TelegramChatName,
|
||||
family.UpdatedAt,
|
||||
family.ID,
|
||||
).Scan(&family.UpdatedAt)
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ func (r *ReceiptsSQLRepository) Create(ctx context.Context, receipt *domain.Rece
|
||||
}
|
||||
res, err := tx.ExecContext(ctx, `
|
||||
INSERT INTO receipts (
|
||||
receipt_number, ui, status, issued_at,
|
||||
transaction_id, receipt_number, ui, status, issued_at,
|
||||
total_amount, payment_amount, cash_amount,
|
||||
another_amount, clearing_amount, margin,
|
||||
currency, payment_type,
|
||||
@@ -46,8 +46,9 @@ func (r *ReceiptsSQLRepository) Create(ctx context.Context, receipt *domain.Rece
|
||||
kod_soato, oblast_soato, rayon_soato, selsovet_soato,
|
||||
doc_num, skno_number, unp,
|
||||
success
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`,
|
||||
receipt.TransactionID,
|
||||
receipt.ReceiptNumber,
|
||||
receipt.UI,
|
||||
receipt.Status,
|
||||
@@ -144,6 +145,7 @@ func (r *ReceiptsSQLRepository) GetByID(ctx context.Context, id int64) (*domain.
|
||||
err := r.db.QueryRowContext(ctx, `
|
||||
SELECT
|
||||
id,
|
||||
transaction_id,
|
||||
receipt_number, ui, status, issued_at,
|
||||
total_amount, payment_amount, cash_amount,
|
||||
another_amount, clearing_amount, margin,
|
||||
@@ -158,6 +160,7 @@ func (r *ReceiptsSQLRepository) GetByID(ctx context.Context, id int64) (*domain.
|
||||
WHERE id = ?
|
||||
`, id).Scan(
|
||||
&receipt.ID,
|
||||
&receipt.TransactionID,
|
||||
&receipt.ReceiptNumber,
|
||||
&receipt.UI,
|
||||
&receipt.Status,
|
||||
@@ -205,6 +208,7 @@ func (r *ReceiptsSQLRepository) GetByID(ctx context.Context, id int64) (*domain.
|
||||
|
||||
rows, err := r.db.QueryContext(ctx, `
|
||||
SELECT
|
||||
id, receipt_id,
|
||||
section_number, gtin_code, product_name,
|
||||
product_count, amount,
|
||||
discount, surcharge,
|
||||
@@ -219,6 +223,8 @@ func (r *ReceiptsSQLRepository) GetByID(ctx context.Context, id int64) (*domain.
|
||||
for rows.Next() {
|
||||
var p domain.Position
|
||||
if err := rows.Scan(
|
||||
&p.ID,
|
||||
&p.ReceiptID,
|
||||
&p.SectionNumber,
|
||||
&p.GTINCode,
|
||||
&p.ProductName,
|
||||
@@ -241,7 +247,7 @@ func (r *ReceiptsSQLRepository) GetByID(ctx context.Context, id int64) (*domain.
|
||||
func (r *ReceiptsSQLRepository) GetAll(ctx context.Context, limit, offset int) ([]*domain.Receipt, error) {
|
||||
|
||||
rows, err := r.db.QueryContext(ctx, `
|
||||
SELECT id, receipt_number, issued_at, total_amount, currency
|
||||
SELECT id, transaction_id, receipt_number, issued_at, total_amount, currency
|
||||
FROM receipts
|
||||
ORDER BY issued_at DESC
|
||||
LIMIT ? OFFSET ?
|
||||
@@ -257,6 +263,7 @@ func (r *ReceiptsSQLRepository) GetAll(ctx context.Context, limit, offset int) (
|
||||
var rct domain.Receipt
|
||||
if err := rows.Scan(
|
||||
&rct.ID,
|
||||
&rct.TransactionID,
|
||||
&rct.ReceiptNumber,
|
||||
&rct.IssuedAt,
|
||||
&rct.TotalAmount,
|
||||
@@ -280,11 +287,13 @@ func (r *ReceiptsSQLRepository) Update(ctx context.Context, receipt *domain.Rece
|
||||
|
||||
_, err = tx.ExecContext(ctx, `
|
||||
UPDATE receipts SET
|
||||
transaction_id = ?,
|
||||
issued_at = ?,
|
||||
total_amount = ?,
|
||||
currency = ?
|
||||
WHERE id = ?
|
||||
`,
|
||||
receipt.TransactionID,
|
||||
receipt.IssuedAt,
|
||||
receipt.TotalAmount,
|
||||
receipt.Currency,
|
||||
|
||||
@@ -0,0 +1,279 @@
|
||||
package repositories
|
||||
|
||||
import (
|
||||
"FamilyHub/src/domain"
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var ErrReceiptNotFound = errors.New("receipt not found")
|
||||
|
||||
type TransactionRepository interface {
|
||||
Create(ctx context.Context, transaction *domain.Transaction) error
|
||||
GetByID(ctx context.Context, id int64) (*domain.Transaction, error)
|
||||
List(ctx context.Context, filter domain.TransactionListFilter) ([]*domain.Transaction, error)
|
||||
Update(ctx context.Context, transaction *domain.Transaction, syncReceipt bool) error
|
||||
Delete(ctx context.Context, id int64) error
|
||||
}
|
||||
|
||||
type TransactionsSQLRepository struct {
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
func NewTransactionsSQLRepository(db *sql.DB) *TransactionsSQLRepository {
|
||||
return &TransactionsSQLRepository{db: db}
|
||||
}
|
||||
|
||||
func (r *TransactionsSQLRepository) Create(ctx context.Context, transaction *domain.Transaction) error {
|
||||
tx, err := r.db.BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
query := `
|
||||
INSERT INTO transactions
|
||||
(family_id, description, type, datetime, category, amount, created_by)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||
RETURNING id, created_at
|
||||
`
|
||||
|
||||
if err := tx.QueryRowContext(
|
||||
ctx,
|
||||
query,
|
||||
transaction.FamilyID,
|
||||
transaction.Description,
|
||||
transaction.Type,
|
||||
transaction.DateTime,
|
||||
transaction.Category,
|
||||
transaction.Amount,
|
||||
transaction.CreatedBy,
|
||||
).Scan(&transaction.ID, &transaction.CreatedAt); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := r.rebindReceipt(ctx, tx, transaction.ID, transaction.ReceiptID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return tx.Commit()
|
||||
}
|
||||
|
||||
func (r *TransactionsSQLRepository) GetByID(ctx context.Context, id int64) (*domain.Transaction, error) {
|
||||
query := `
|
||||
SELECT
|
||||
t.id,
|
||||
t.family_id,
|
||||
t.description,
|
||||
t.type,
|
||||
t.datetime,
|
||||
t.category,
|
||||
t.amount,
|
||||
t.created_at,
|
||||
t.created_by,
|
||||
r.id
|
||||
FROM transactions t
|
||||
LEFT JOIN receipts r ON r.transaction_id = t.id
|
||||
WHERE t.id = $1
|
||||
`
|
||||
|
||||
var transaction domain.Transaction
|
||||
err := r.db.QueryRowContext(ctx, query, id).Scan(
|
||||
&transaction.ID,
|
||||
&transaction.FamilyID,
|
||||
&transaction.Description,
|
||||
&transaction.Type,
|
||||
&transaction.DateTime,
|
||||
&transaction.Category,
|
||||
&transaction.Amount,
|
||||
&transaction.CreatedAt,
|
||||
&transaction.CreatedBy,
|
||||
&transaction.ReceiptID,
|
||||
)
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &transaction, nil
|
||||
}
|
||||
|
||||
func (r *TransactionsSQLRepository) List(ctx context.Context, filter domain.TransactionListFilter) ([]*domain.Transaction, error) {
|
||||
var (
|
||||
whereClauses []string
|
||||
args []any
|
||||
)
|
||||
|
||||
baseQuery := `
|
||||
SELECT
|
||||
t.id,
|
||||
t.family_id,
|
||||
t.description,
|
||||
t.type,
|
||||
t.datetime,
|
||||
t.category,
|
||||
t.amount,
|
||||
t.created_at,
|
||||
t.created_by,
|
||||
r.id
|
||||
FROM transactions t
|
||||
LEFT JOIN receipts r ON r.transaction_id = t.id
|
||||
`
|
||||
|
||||
appendFilter := func(condition string, value any) {
|
||||
args = append(args, value)
|
||||
whereClauses = append(whereClauses, fmt.Sprintf(condition, len(args)))
|
||||
}
|
||||
|
||||
if filter.FamilyID != nil {
|
||||
appendFilter("t.family_id = $%d", *filter.FamilyID)
|
||||
}
|
||||
if filter.CreatedBy != nil {
|
||||
appendFilter("t.created_by = $%d", *filter.CreatedBy)
|
||||
}
|
||||
if filter.Type != nil {
|
||||
appendFilter("t.type = $%d", *filter.Type)
|
||||
}
|
||||
if filter.Category != nil {
|
||||
appendFilter("t.category = $%d", *filter.Category)
|
||||
}
|
||||
if filter.DateFrom != nil {
|
||||
appendFilter("t.datetime >= $%d", *filter.DateFrom)
|
||||
}
|
||||
if filter.DateTo != nil {
|
||||
appendFilter("t.datetime <= $%d", *filter.DateTo)
|
||||
}
|
||||
|
||||
var queryBuilder strings.Builder
|
||||
queryBuilder.WriteString(baseQuery)
|
||||
if len(whereClauses) > 0 {
|
||||
queryBuilder.WriteString(" WHERE ")
|
||||
queryBuilder.WriteString(strings.Join(whereClauses, " AND "))
|
||||
}
|
||||
|
||||
args = append(args, filter.Limit, filter.Offset)
|
||||
queryBuilder.WriteString(fmt.Sprintf(" ORDER BY t.datetime DESC LIMIT $%d OFFSET $%d", len(args)-1, len(args)))
|
||||
|
||||
rows, err := r.db.QueryContext(ctx, queryBuilder.String(), args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var transactions []*domain.Transaction
|
||||
for rows.Next() {
|
||||
var transaction domain.Transaction
|
||||
if err := rows.Scan(
|
||||
&transaction.ID,
|
||||
&transaction.FamilyID,
|
||||
&transaction.Description,
|
||||
&transaction.Type,
|
||||
&transaction.DateTime,
|
||||
&transaction.Category,
|
||||
&transaction.Amount,
|
||||
&transaction.CreatedAt,
|
||||
&transaction.CreatedBy,
|
||||
&transaction.ReceiptID,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
transactions = append(transactions, &transaction)
|
||||
}
|
||||
|
||||
return transactions, rows.Err()
|
||||
}
|
||||
|
||||
func (r *TransactionsSQLRepository) Update(ctx context.Context, transaction *domain.Transaction, syncReceipt bool) error {
|
||||
tx, err := r.db.BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
query := `
|
||||
UPDATE transactions SET
|
||||
description = $1,
|
||||
type = $2,
|
||||
datetime = $3,
|
||||
category = $4,
|
||||
amount = $5
|
||||
WHERE id = $6
|
||||
`
|
||||
|
||||
result, err := tx.ExecContext(
|
||||
ctx,
|
||||
query,
|
||||
transaction.Description,
|
||||
transaction.Type,
|
||||
transaction.DateTime,
|
||||
transaction.Category,
|
||||
transaction.Amount,
|
||||
transaction.ID,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rowsAffected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rowsAffected == 0 {
|
||||
return sql.ErrNoRows
|
||||
}
|
||||
|
||||
if syncReceipt {
|
||||
if err := r.rebindReceipt(ctx, tx, transaction.ID, transaction.ReceiptID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return tx.Commit()
|
||||
}
|
||||
|
||||
func (r *TransactionsSQLRepository) Delete(ctx context.Context, id int64) error {
|
||||
result, err := r.db.ExecContext(ctx, `DELETE FROM transactions WHERE id = $1`, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rowsAffected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rowsAffected == 0 {
|
||||
return sql.ErrNoRows
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *TransactionsSQLRepository) rebindReceipt(ctx context.Context, tx *sql.Tx, transactionID int64, receiptID *int64) error {
|
||||
if _, err := tx.ExecContext(ctx, `UPDATE receipts SET transaction_id = NULL WHERE transaction_id = $1`, transactionID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if receiptID == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
result, err := tx.ExecContext(ctx, `UPDATE receipts SET transaction_id = $1 WHERE id = $2`, transactionID, *receiptID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rowsAffected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rowsAffected == 0 {
|
||||
return ErrReceiptNotFound
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user