package db import ( "context" "database/sql" "fmt" "github.com/google/uuid" ) type Store interface { Querier PurchaseOrderTx(ctx context.Context, arg PurchasoOrderTxParams) (PurchaseOrderTxResult, error) CreateUserMerchantTx(ctx context.Context, arg UserMerchantTxParams) (UserMerchantTxResult, error) } type SQLStore struct { *Queries db *sql.DB } func NewStore(db *sql.DB) Store { return &SQLStore{ db: db, Queries: New(db), } } func (store *SQLStore) execTx(ctx context.Context, fn func(*Queries) error) error { tx, err := store.db.BeginTx(ctx, nil) if err != nil { return err } q := New(tx) err = fn(q) if err != nil { if rbErr := tx.Rollback(); rbErr != nil { return fmt.Errorf("tx err: %v, rb err : %v", err, rbErr) } return err } return tx.Commit() } type PurchaseOrderProduct struct { ProductID uuid.UUID `json:"product_id"` Quantity float64 `json:"quantity"` Sub_total float64 `json:"sub_total"` Price float64 `json:"price"` } type PurchasoOrderTxParams struct { MerchantID uuid.UUID `json:"merchant_id"` SupplierID uuid.UUID `json:"supplier_id"` Code sql.NullString `json:"code"` IsPaid bool `json:"is_paid"` Total float64 `json:"total"` PaidNominal float64 `json:"paid_nominal"` Note sql.NullString `json:"note"` Products []PurchaseOrderProduct `json:"products"` } type PurchaseOrderTxResult struct { PurchaseOrder PurchaseOrder `json:"purchase_order"` PurchaseOrderDetail []PurchaseOrderDetail `json:"detail"` } func (store *SQLStore) PurchaseOrderTx(ctx context.Context, arg PurchasoOrderTxParams) (PurchaseOrderTxResult, error) { var result PurchaseOrderTxResult err := store.execTx(ctx, func(q *Queries) error { var err error // create purchase order result.PurchaseOrder, err = q.CreatePurchaseOrder(ctx, CreatePurchaseOrderParams{ MerchantID: arg.MerchantID, SupplierID: arg.SupplierID, Code: arg.Code, IsPaid: arg.IsPaid, Total: arg.Total, PaidNominal: arg.PaidNominal, Note: arg.Note, }) if err != nil { return err } // create purchase order detail for each products in purchase order for i := 0; i < len(arg.Products); i++ { purchaseOrderDetail, err := q.CreatePurchaseOrderDetail(ctx, CreatePurchaseOrderDetailParams{ PurchaseOrderID: result.PurchaseOrder.ID, MerchantID: arg.MerchantID, ProductID: arg.Products[i].ProductID, Quantity: arg.Products[i].Quantity, SubTotal: arg.Products[i].Sub_total, ProductPrice: arg.Products[i].Price, }) if err != nil { return err } result.PurchaseOrderDetail = append(result.PurchaseOrderDetail, purchaseOrderDetail) product, err := q.GetStockForUpdateStock(ctx, arg.Products[i].ProductID) if err != nil { return err } err = q.UpdateProductStock(ctx, UpdateProductStockParams{ ID: product.ID, Stock: product.Stock + arg.Products[i].Quantity, }) if err != nil { return err } } return nil }) return result, err } type UserMerchantTxParams struct { Email string `json:"email"` Fullname string `json:"fullname"` Password string `json:"password"` OutletName string `json:"outlet_name"` } type UserMerchantTxResult struct { OwnerProfile User `json:"owner_profile"` OutletProfile Merchant `json:"outlet_profile"` } func (store *SQLStore) CreateUserMerchantTx(ctx context.Context, arg UserMerchantTxParams) (UserMerchantTxResult, error) { var result UserMerchantTxResult err := store.execTx(ctx, func(q *Queries) error { var err error result.OwnerProfile, err = q.CreateUser(ctx, CreateUserParams{ Email: arg.Email, Password: arg.Password, Fullname: arg.Fullname, }) if err != nil { return err } result.OutletProfile, err = q.CreateMerchant(ctx, CreateMerchantParams{ Name: arg.OutletName, OwnerID: result.OwnerProfile.ID, }) if err != nil { return err } return nil }) return result, err }