add purchase product transaction
This commit is contained in:
parent
287eb2f205
commit
068a6d9320
@ -10,11 +10,16 @@ INSERT INTO products (
|
||||
)
|
||||
RETURNING *;
|
||||
|
||||
-- name: UpdateProductStock :one
|
||||
-- name: GetStockForUpdateStock :one
|
||||
SELECT * FROM products
|
||||
WHERE id = $1
|
||||
LIMIT 1
|
||||
FOR NO KEY UPDATE;
|
||||
|
||||
-- name: UpdateProductStock :exec
|
||||
UPDATE products
|
||||
SET stock = $1
|
||||
WHERE id = $2
|
||||
RETURNING stock;
|
||||
WHERE id = $2;
|
||||
|
||||
-- name: GetProduct :one
|
||||
SELECT * FROM products
|
||||
|
@ -87,6 +87,30 @@ func (q *Queries) GetProduct(ctx context.Context, id uuid.UUID) (Product, error)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getStockForUpdateStock = `-- name: GetStockForUpdateStock :one
|
||||
SELECT id, merchant_id, index_id, name, selling_price, purchase_price, stock, created_at, updated_at FROM products
|
||||
WHERE id = $1
|
||||
LIMIT 1
|
||||
FOR NO KEY UPDATE
|
||||
`
|
||||
|
||||
func (q *Queries) GetStockForUpdateStock(ctx context.Context, id uuid.UUID) (Product, error) {
|
||||
row := q.db.QueryRowContext(ctx, getStockForUpdateStock, id)
|
||||
var i Product
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.MerchantID,
|
||||
&i.IndexID,
|
||||
&i.Name,
|
||||
&i.SellingPrice,
|
||||
&i.PurchasePrice,
|
||||
&i.Stock,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const listProducts = `-- name: ListProducts :many
|
||||
SELECT id, merchant_id, index_id, name, selling_price, purchase_price, stock, created_at, updated_at FROM products
|
||||
ORDER BY index_id
|
||||
@ -172,11 +196,10 @@ func (q *Queries) UpdateProduct(ctx context.Context, arg UpdateProductParams) (P
|
||||
return i, err
|
||||
}
|
||||
|
||||
const updateProductStock = `-- name: UpdateProductStock :one
|
||||
const updateProductStock = `-- name: UpdateProductStock :exec
|
||||
UPDATE products
|
||||
SET stock = $1
|
||||
WHERE id = $2
|
||||
RETURNING stock
|
||||
`
|
||||
|
||||
type UpdateProductStockParams struct {
|
||||
@ -184,9 +207,7 @@ type UpdateProductStockParams struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateProductStock(ctx context.Context, arg UpdateProductStockParams) (float64, error) {
|
||||
row := q.db.QueryRowContext(ctx, updateProductStock, arg.Stock, arg.ID)
|
||||
var stock float64
|
||||
err := row.Scan(&stock)
|
||||
return stock, err
|
||||
func (q *Queries) UpdateProductStock(ctx context.Context, arg UpdateProductStockParams) error {
|
||||
_, err := q.db.ExecContext(ctx, updateProductStock, arg.Stock, arg.ID)
|
||||
return err
|
||||
}
|
||||
|
@ -13,12 +13,16 @@ import (
|
||||
|
||||
func createRandomProduct(t *testing.T) (Product, CreateProductParams) {
|
||||
|
||||
sellingPrice, _ := util.RandomFloat(999, 99999)
|
||||
purchasePrice, _ := util.RandomFloat(999, 9999)
|
||||
stock, _ := util.RandomFloat(10, 10000)
|
||||
|
||||
arg := CreateProductParams{
|
||||
MerchantID: uuid.MustParse("1f81d072-0c98-4e7e-9ceb-233d2eadb674"),
|
||||
Name: util.RandomString(10),
|
||||
SellingPrice: float64(123),
|
||||
PurchasePrice: float64(123),
|
||||
Stock: float64(120),
|
||||
SellingPrice: sellingPrice,
|
||||
PurchasePrice: purchasePrice,
|
||||
Stock: stock,
|
||||
}
|
||||
|
||||
product, err := testQueries.CreateProduct(context.Background(), arg)
|
||||
|
@ -58,8 +58,8 @@ type PurchasoOrderTxParams struct {
|
||||
}
|
||||
|
||||
type PurchaseOrderTxResult struct {
|
||||
PurchaseOrder PurchaseOrder `json:"purchase_order"`
|
||||
// PurchaseOrderDetail []PurchaseOrderDetail `json:"detail"`
|
||||
PurchaseOrder PurchaseOrder `json:"purchase_order"`
|
||||
PurchaseOrderDetail []PurchaseOrderDetail `json:"detail"`
|
||||
}
|
||||
|
||||
func (store *Store) PurchaseOrderTx(ctx context.Context, arg PurchasoOrderTxParams) (PurchaseOrderTxResult, error) {
|
||||
@ -67,6 +67,8 @@ func (store *Store) PurchaseOrderTx(ctx context.Context, arg PurchasoOrderTxPara
|
||||
|
||||
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,
|
||||
@ -81,6 +83,39 @@ func (store *Store) PurchaseOrderTx(ctx context.Context, arg PurchasoOrderTxPara
|
||||
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
|
||||
})
|
||||
|
||||
|
@ -5,10 +5,13 @@ import (
|
||||
"database/sql"
|
||||
"testing"
|
||||
|
||||
"git.nochill.in/nochill/nice_pos/util"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestPurchaseOrder(t *testing.T) {
|
||||
var products []PurchaseOrderProduct
|
||||
|
||||
store := NewStore(testDB)
|
||||
supplier, _ := createRandomSupplier(t)
|
||||
product1, _ := createRandomProduct(t)
|
||||
@ -17,29 +20,58 @@ func TestPurchaseOrder(t *testing.T) {
|
||||
errs := make(chan error)
|
||||
results := make(chan PurchaseOrderTxResult)
|
||||
|
||||
// for i := 0; i < testIteration; i++ {
|
||||
go func() {
|
||||
result, err := store.PurchaseOrderTx(context.Background(), PurchasoOrderTxParams{
|
||||
MerchantID: supplier.MerchantID,
|
||||
SupplierID: supplier.ID,
|
||||
Code: sql.NullString{Valid: true, String: ""},
|
||||
IsPaid: true,
|
||||
Total: product1.PurchasePrice + product2.PurchasePrice,
|
||||
PaidNominal: product1.PurchasePrice + product2.PurchasePrice,
|
||||
Note: sql.NullString{Valid: true, String: ""},
|
||||
// Products: products,
|
||||
purchaseProducts1Quantity, _ := util.RandomFloat(1, 99)
|
||||
purchaseProducts1Price, _ := util.RandomFloat(999, 9999)
|
||||
purchaseProducts1SubTotal := purchaseProducts1Price * purchaseProducts1Quantity
|
||||
|
||||
})
|
||||
errs <- err
|
||||
results <- result
|
||||
}()
|
||||
// }
|
||||
purchaseProducts2Quantity, _ := util.RandomFloat(1, 99)
|
||||
purchaseProducts2Price, _ := util.RandomFloat(999, 9999)
|
||||
purchaseProducts2SubTotal := purchaseProducts2Price * purchaseProducts2Quantity
|
||||
|
||||
// for i := 0; i < testIteration; i++ {
|
||||
err := <-errs
|
||||
require.NoError(t, err)
|
||||
purchaseProducts_1 := PurchaseOrderProduct{
|
||||
ProductID: product1.ID,
|
||||
Quantity: purchaseProducts1Quantity,
|
||||
Sub_total: purchaseProducts1SubTotal,
|
||||
Price: purchaseProducts1Price,
|
||||
}
|
||||
|
||||
result := <-results
|
||||
require.NotEmpty(t, result)
|
||||
purchaseProducts_2 := PurchaseOrderProduct{
|
||||
ProductID: product2.ID,
|
||||
Quantity: purchaseProducts2Quantity,
|
||||
Sub_total: purchaseProducts2SubTotal,
|
||||
Price: purchaseProducts2Price,
|
||||
}
|
||||
|
||||
products = append(products, purchaseProducts_1, purchaseProducts_2)
|
||||
|
||||
for i := 0; i < 5; i++ {
|
||||
go func() {
|
||||
result, err := store.PurchaseOrderTx(context.Background(), PurchasoOrderTxParams{
|
||||
MerchantID: supplier.MerchantID,
|
||||
SupplierID: supplier.ID,
|
||||
Code: sql.NullString{Valid: true, String: ""},
|
||||
IsPaid: true,
|
||||
Total: product1.PurchasePrice + product2.PurchasePrice,
|
||||
PaidNominal: product1.PurchasePrice + product2.PurchasePrice,
|
||||
Note: sql.NullString{Valid: true, String: ""},
|
||||
Products: products,
|
||||
})
|
||||
errs <- err
|
||||
results <- result
|
||||
}()
|
||||
}
|
||||
|
||||
for i := 0; i < 5; i++ {
|
||||
err := <-errs
|
||||
require.NoError(t, err)
|
||||
|
||||
result := <-results
|
||||
require.NotEmpty(t, result)
|
||||
|
||||
purchaseOrder := result.PurchaseOrder
|
||||
require.NotEmpty(t, purchaseOrder)
|
||||
require.Equal(t, purchaseOrder.MerchantID, supplier.MerchantID)
|
||||
require.Equal(t, purchaseOrder.SupplierID, supplier.ID)
|
||||
require.NotZero(t, purchaseOrder.PaidNominal, product1.PurchasePrice+product2.PurchasePrice)
|
||||
}
|
||||
}
|
||||
|
1
go.mod
1
go.mod
@ -5,6 +5,7 @@ go 1.20
|
||||
require (
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/lib/pq v1.10.7
|
||||
github.com/shopspring/decimal v1.3.1
|
||||
github.com/stretchr/testify v1.8.2
|
||||
github.com/tabbed/pqtype v0.1.1
|
||||
)
|
||||
|
2
go.sum
2
go.sum
@ -7,6 +7,8 @@ github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw=
|
||||
github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
|
||||
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
|
@ -4,6 +4,8 @@ import (
|
||||
"math/rand"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
const alphabet = "abcdefghijklmnopqrstuvwxyz"
|
||||
@ -17,8 +19,10 @@ func RandomInt(min, max int64) int64 {
|
||||
return min + rand.Int63n(max-min+1)
|
||||
}
|
||||
|
||||
func RandomFloat(max, div float64) float64 {
|
||||
return (rand.Float64() * max) * div
|
||||
// RandomFloat with rounding for testing
|
||||
func RandomFloat(min, max float64) (float64, bool) {
|
||||
val := (min + rand.Float64()*(max-min)) * 100
|
||||
return decimal.NewFromFloat(val).Truncate(2).Float64()
|
||||
}
|
||||
|
||||
// RandomString generates a random string of length n
|
||||
|
Loading…
Reference in New Issue
Block a user