add create stock logs

This commit is contained in:
nochill 2023-03-19 18:17:17 +07:00
parent edddee0cb4
commit cb14cd40a4
15 changed files with 358 additions and 23 deletions

View File

@ -0,0 +1,2 @@
DROP TABLE IF EXISTS stock_logs;
DROP TYPE IF EXISTS stock_logs_type;

View File

@ -0,0 +1,24 @@
CREATE TYPE stock_logs_type AS ENUM (
'in',
'out'
);
CREATE TABLE stock_logs (
"id" uuid default gen_random_uuid() not null primary key,
"index_id" bigserial not null,
"merchant_id" uuid references "merchants"("id") not null,
"product_id" uuid references "products"("id") not null,
"created_by" uuid references "users"("id") not null,
"transaction_id" uuid,
"transaction_action_type" varchar not null , -- "penjualan / pembelian / pengaturan stok"
"transaction_description" varchar not null,
"type" stock_logs_type not null,
"selling_price" double precision default(0::double precision) NOT NULL,
"purchase_price" double precision default(0:: double precision) NOT NULL,
"quantity" double precision default(0::double precision) NOT NULL,
"created_at" timestamp default(now()),
"updated_at" timestamp default(now())
);
CREATE INDEX ON "stock_logs"("index_id");

View File

@ -141,6 +141,21 @@ func (mr *MockStoreMockRecorder) CreateSession(arg0, arg1 interface{}) *gomock.C
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateSession", reflect.TypeOf((*MockStore)(nil).CreateSession), arg0, arg1) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateSession", reflect.TypeOf((*MockStore)(nil).CreateSession), arg0, arg1)
} }
// CreateStockLogs mocks base method.
func (m *MockStore) CreateStockLogs(arg0 context.Context, arg1 db.CreateStockLogsParams) (db.StockLog, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CreateStockLogs", arg0, arg1)
ret0, _ := ret[0].(db.StockLog)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// CreateStockLogs indicates an expected call of CreateStockLogs.
func (mr *MockStoreMockRecorder) CreateStockLogs(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateStockLogs", reflect.TypeOf((*MockStore)(nil).CreateStockLogs), arg0, arg1)
}
// CreateSuppliers mocks base method. // CreateSuppliers mocks base method.
func (m *MockStore) CreateSuppliers(arg0 context.Context, arg1 db.CreateSuppliersParams) (db.Supplier, error) { func (m *MockStore) CreateSuppliers(arg0 context.Context, arg1 db.CreateSuppliersParams) (db.Supplier, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
@ -378,10 +393,10 @@ func (mr *MockStoreMockRecorder) GetStockForUpdateStock(arg0, arg1 interface{})
} }
// GetUserByEmail mocks base method. // GetUserByEmail mocks base method.
func (m *MockStore) GetUserByEmail(arg0 context.Context, arg1 string) (db.User, error) { func (m *MockStore) GetUserByEmail(arg0 context.Context, arg1 string) (db.GetUserByEmailRow, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetUserByEmail", arg0, arg1) ret := m.ctrl.Call(m, "GetUserByEmail", arg0, arg1)
ret0, _ := ret[0].(db.User) ret0, _ := ret[0].(db.GetUserByEmailRow)
ret1, _ := ret[1].(error) ret1, _ := ret[1].(error)
return ret0, ret1 return ret0, ret1
} }
@ -393,10 +408,10 @@ func (mr *MockStoreMockRecorder) GetUserByEmail(arg0, arg1 interface{}) *gomock.
} }
// GetUserById mocks base method. // GetUserById mocks base method.
func (m *MockStore) GetUserById(arg0 context.Context, arg1 uuid.UUID) (db.User, error) { func (m *MockStore) GetUserById(arg0 context.Context, arg1 uuid.UUID) (db.GetUserByIdRow, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetUserById", arg0, arg1) ret := m.ctrl.Call(m, "GetUserById", arg0, arg1)
ret0, _ := ret[0].(db.User) ret0, _ := ret[0].(db.GetUserByIdRow)
ret1, _ := ret[1].(error) ret1, _ := ret[1].(error)
return ret0, ret1 return ret0, ret1
} }

16
db/query/stock_log.sql Normal file
View File

@ -0,0 +1,16 @@
-- name: CreateStockLogs :one
INSERT INTO stock_logs (
product_id,
merchant_id,
created_by,
transaction_id,
transaction_action_type,
transaction_description,
type,
selling_price,
purchase_price,
quantity
) VALUES (
$1, $2, $3, $4, $5 ,$6, $7, $8, $9, $10
)
RETURNING *;

View File

@ -13,10 +13,12 @@ WHERE email = $1;
-- name: GetUserById :one -- name: GetUserById :one
SELECT * SELECT *
FROM users FROM users u
WHERE id = $1; JOIN merchants m on u.id = m.owner_id
WHERE u.id = $1;
-- name: GetUserByEmail :one -- name: GetUserByEmail :one
SELECT * SELECT *
FROM users FROM users u
JOIN merchants m on u.id = m.owner_id
WHERE email = $1; WHERE email = $1;

View File

@ -6,13 +6,57 @@ package db
import ( import (
"database/sql" "database/sql"
"database/sql/driver"
"encoding/json" "encoding/json"
"fmt"
"time" "time"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/tabbed/pqtype" "github.com/tabbed/pqtype"
) )
type StockLogsType string
const (
StockLogsTypeIn StockLogsType = "in"
StockLogsTypeOut StockLogsType = "out"
)
func (e *StockLogsType) Scan(src interface{}) error {
switch s := src.(type) {
case []byte:
*e = StockLogsType(s)
case string:
*e = StockLogsType(s)
default:
return fmt.Errorf("unsupported scan type for StockLogsType: %T", src)
}
return nil
}
type NullStockLogsType struct {
StockLogsType StockLogsType
Valid bool // Valid is true if StockLogsType is not NULL
}
// Scan implements the Scanner interface.
func (ns *NullStockLogsType) Scan(value interface{}) error {
if value == nil {
ns.StockLogsType, ns.Valid = "", false
return nil
}
ns.Valid = true
return ns.StockLogsType.Scan(value)
}
// Value implements the driver Valuer interface.
func (ns NullStockLogsType) Value() (driver.Value, error) {
if !ns.Valid {
return nil, nil
}
return string(ns.StockLogsType), nil
}
type Customer struct { type Customer struct {
ID uuid.UUID `json:"id"` ID uuid.UUID `json:"id"`
IndexID int64 `json:"index_id"` IndexID int64 `json:"index_id"`
@ -108,6 +152,23 @@ type SaleOrderDetail struct {
UpdatedAt sql.NullTime `json:"updated_at"` UpdatedAt sql.NullTime `json:"updated_at"`
} }
type StockLog struct {
ID uuid.UUID `json:"id"`
IndexID int64 `json:"index_id"`
MerchantID uuid.UUID `json:"merchant_id"`
ProductID uuid.UUID `json:"product_id"`
CreatedBy uuid.UUID `json:"created_by"`
TransactionID uuid.NullUUID `json:"transaction_id"`
TransactionActionType string `json:"transaction_action_type"`
TransactionDescription string `json:"transaction_description"`
Type StockLogsType `json:"type"`
SellingPrice float64 `json:"selling_price"`
PurchasePrice float64 `json:"purchase_price"`
Quantity float64 `json:"quantity"`
CreatedAt sql.NullTime `json:"created_at"`
UpdatedAt sql.NullTime `json:"updated_at"`
}
type Supplier struct { type Supplier struct {
ID uuid.UUID `json:"id"` ID uuid.UUID `json:"id"`
IndexID int64 `json:"index_id"` IndexID int64 `json:"index_id"`

View File

@ -18,6 +18,7 @@ type Querier interface {
CreatePurchaseOrder(ctx context.Context, arg CreatePurchaseOrderParams) (PurchaseOrder, error) CreatePurchaseOrder(ctx context.Context, arg CreatePurchaseOrderParams) (PurchaseOrder, error)
CreatePurchaseOrderDetail(ctx context.Context, arg CreatePurchaseOrderDetailParams) (PurchaseOrderDetail, error) CreatePurchaseOrderDetail(ctx context.Context, arg CreatePurchaseOrderDetailParams) (PurchaseOrderDetail, error)
CreateSession(ctx context.Context, arg CreateSessionParams) (UserSession, error) CreateSession(ctx context.Context, arg CreateSessionParams) (UserSession, error)
CreateStockLogs(ctx context.Context, arg CreateStockLogsParams) (StockLog, error)
CreateSuppliers(ctx context.Context, arg CreateSuppliersParams) (Supplier, error) CreateSuppliers(ctx context.Context, arg CreateSuppliersParams) (Supplier, error)
CreateUser(ctx context.Context, arg CreateUserParams) (User, error) CreateUser(ctx context.Context, arg CreateUserParams) (User, error)
CustomersList(ctx context.Context, arg CustomersListParams) ([]Customer, error) CustomersList(ctx context.Context, arg CustomersListParams) ([]Customer, error)
@ -33,8 +34,8 @@ type Querier interface {
GetPurchaseOrderDetailsByPuchaseOrderId(ctx context.Context, purchaseOrderID uuid.UUID) (PurchaseOrderDetail, error) GetPurchaseOrderDetailsByPuchaseOrderId(ctx context.Context, purchaseOrderID uuid.UUID) (PurchaseOrderDetail, error)
GetSession(ctx context.Context, id uuid.UUID) (UserSession, error) GetSession(ctx context.Context, id uuid.UUID) (UserSession, error)
GetStockForUpdateStock(ctx context.Context, id uuid.UUID) (Product, error) GetStockForUpdateStock(ctx context.Context, id uuid.UUID) (Product, error)
GetUserByEmail(ctx context.Context, email string) (User, error) GetUserByEmail(ctx context.Context, email string) (GetUserByEmailRow, error)
GetUserById(ctx context.Context, id uuid.UUID) (User, error) GetUserById(ctx context.Context, id uuid.UUID) (GetUserByIdRow, error)
ListProductCategoriesByMerchantID(ctx context.Context, arg ListProductCategoriesByMerchantIDParams) ([]ProductCategory, error) ListProductCategoriesByMerchantID(ctx context.Context, arg ListProductCategoriesByMerchantIDParams) ([]ProductCategory, error)
ListProducts(ctx context.Context, arg ListProductsParams) ([]Product, error) ListProducts(ctx context.Context, arg ListProductsParams) ([]Product, error)
ListPurchaseOrderByMerchantId(ctx context.Context, merchantID uuid.UUID) ([]PurchaseOrder, error) ListPurchaseOrderByMerchantId(ctx context.Context, merchantID uuid.UUID) ([]PurchaseOrder, error)

76
db/sqlc/stock_log.sql.go Normal file
View File

@ -0,0 +1,76 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.17.2
// source: stock_log.sql
package db
import (
"context"
"github.com/google/uuid"
)
const createStockLogs = `-- name: CreateStockLogs :one
INSERT INTO stock_logs (
product_id,
merchant_id,
created_by,
transaction_id,
transaction_action_type,
transaction_description,
type,
selling_price,
purchase_price,
quantity
) VALUES (
$1, $2, $3, $4, $5 ,$6, $7, $8, $9, $10
)
RETURNING id, index_id, merchant_id, product_id, created_by, transaction_id, transaction_action_type, transaction_description, type, selling_price, purchase_price, quantity, created_at, updated_at
`
type CreateStockLogsParams struct {
ProductID uuid.UUID `json:"product_id"`
MerchantID uuid.UUID `json:"merchant_id"`
CreatedBy uuid.UUID `json:"created_by"`
TransactionID uuid.NullUUID `json:"transaction_id"`
TransactionActionType string `json:"transaction_action_type"`
TransactionDescription string `json:"transaction_description"`
Type StockLogsType `json:"type"`
SellingPrice float64 `json:"selling_price"`
PurchasePrice float64 `json:"purchase_price"`
Quantity float64 `json:"quantity"`
}
func (q *Queries) CreateStockLogs(ctx context.Context, arg CreateStockLogsParams) (StockLog, error) {
row := q.db.QueryRowContext(ctx, createStockLogs,
arg.ProductID,
arg.MerchantID,
arg.CreatedBy,
arg.TransactionID,
arg.TransactionActionType,
arg.TransactionDescription,
arg.Type,
arg.SellingPrice,
arg.PurchasePrice,
arg.Quantity,
)
var i StockLog
err := row.Scan(
&i.ID,
&i.IndexID,
&i.MerchantID,
&i.ProductID,
&i.CreatedBy,
&i.TransactionID,
&i.TransactionActionType,
&i.TransactionDescription,
&i.Type,
&i.SellingPrice,
&i.PurchasePrice,
&i.Quantity,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}

37
db/sqlc/stock_log_test.go Normal file
View File

@ -0,0 +1,37 @@
package db
import (
"context"
"fmt"
"testing"
"git.nochill.in/nochill/naice_pos/util"
"github.com/google/uuid"
"github.com/stretchr/testify/require"
)
func createRandomStockLog(t *testing.T) {
product, _ := createRandomProduct(t)
user := createRandomUser(t)
arg := CreateStockLogsParams{
ProductID: product.ID,
MerchantID: product.MerchantID,
CreatedBy: user.ID,
TransactionID: uuid.NullUUID{Valid: true, UUID: uuid.New()},
TransactionActionType: "Stock_log",
TransactionDescription: fmt.Sprintf("Penyesuaian stok %s", util.RandomTransactionCode("P", user.IndexID)),
SellingPrice: util.RandomFloat(10000, 99999),
PurchasePrice: util.RandomFloat(5000, 50000),
Quantity: util.RandomFloat(1, 100),
Type: util.STOCK_LOG_IN,
}
stockLog, err := testQueries.CreateStockLogs(context.Background(), arg)
require.NoError(t, err)
require.NotEmpty(t, stockLog)
}
func TestCreateStockLog(t *testing.T) {
createRandomStockLog(t)
}

View File

@ -5,6 +5,7 @@ import (
"database/sql" "database/sql"
"fmt" "fmt"
"git.nochill.in/nochill/naice_pos/util"
"github.com/google/uuid" "github.com/google/uuid"
) )
@ -54,6 +55,7 @@ type PurchaseOrderProduct struct {
type PurchasoOrderTxParams struct { type PurchasoOrderTxParams struct {
MerchantID uuid.UUID `json:"merchant_id"` MerchantID uuid.UUID `json:"merchant_id"`
CreatedBy uuid.UUID `json:"created_by"` // user_id
SupplierID uuid.UUID `json:"supplier_id"` SupplierID uuid.UUID `json:"supplier_id"`
Code sql.NullString `json:"code"` Code sql.NullString `json:"code"`
IsPaid bool `json:"is_paid"` IsPaid bool `json:"is_paid"`
@ -115,6 +117,22 @@ func (store *SQLStore) PurchaseOrderTx(ctx context.Context, arg PurchasoOrderTxP
ID: product.ID, ID: product.ID,
Stock: product.Stock + arg.Products[i].Quantity, Stock: product.Stock + arg.Products[i].Quantity,
}) })
if err != nil {
return err
}
_, err = q.CreateStockLogs(ctx, CreateStockLogsParams{
ProductID: arg.Products[i].ProductID,
MerchantID: arg.MerchantID,
CreatedBy: arg.CreatedBy,
TransactionID: uuid.NullUUID{UUID: result.PurchaseOrder.ID, Valid: true},
TransactionActionType: "Purchase_order",
TransactionDescription: fmt.Sprintf("Penambahan produk %s dari pembelian dengan code %s", product.Name, arg.Code.String),
Type: util.STOCK_LOG_IN,
SellingPrice: product.SellingPrice,
PurchasePrice: product.PurchasePrice,
Quantity: arg.Products[i].Quantity,
})
if err != nil { if err != nil {
return err return err

View File

@ -13,6 +13,7 @@ func TestPurchaseOrder(t *testing.T) {
var products []PurchaseOrderProduct var products []PurchaseOrderProduct
store := NewStore(testDB) store := NewStore(testDB)
user := createRandomUser(t)
supplier, _ := createRandomSupplier(t) supplier, _ := createRandomSupplier(t)
product1, _ := createRandomProduct(t) product1, _ := createRandomProduct(t)
product2, _ := createRandomProduct(t) product2, _ := createRandomProduct(t)
@ -50,8 +51,9 @@ func TestPurchaseOrder(t *testing.T) {
go func() { go func() {
result, err := store.PurchaseOrderTx(context.Background(), PurchasoOrderTxParams{ result, err := store.PurchaseOrderTx(context.Background(), PurchasoOrderTxParams{
MerchantID: supplier.MerchantID, MerchantID: supplier.MerchantID,
CreatedBy: user.ID,
SupplierID: supplier.ID, SupplierID: supplier.ID,
Code: sql.NullString{Valid: true, String: ""}, Code: sql.NullString{Valid: true, String: util.RandomTransactionCode("P", util.RandomInt(1, 10))},
IsPaid: true, IsPaid: true,
Total: product1.PurchasePrice + product2.PurchasePrice, Total: product1.PurchasePrice + product2.PurchasePrice,
PaidNominal: product1.PurchasePrice + product2.PurchasePrice, PaidNominal: product1.PurchasePrice + product2.PurchasePrice,

View File

@ -7,6 +7,7 @@ package db
import ( import (
"context" "context"
"database/sql"
"github.com/google/uuid" "github.com/google/uuid"
) )
@ -55,14 +56,31 @@ func (q *Queries) GetPasswordByEmail(ctx context.Context, email string) (string,
} }
const getUserByEmail = `-- name: GetUserByEmail :one const getUserByEmail = `-- name: GetUserByEmail :one
SELECT id, index_id, email, password, fullname, created_at, updated_at SELECT u.id, u.index_id, email, password, fullname, u.created_at, u.updated_at, m.id, m.index_id, name, owner_id, m.created_at, m.updated_at
FROM users FROM users u
JOIN merchants m on u.id = m.owner_id
WHERE email = $1 WHERE email = $1
` `
func (q *Queries) GetUserByEmail(ctx context.Context, email string) (User, error) { type GetUserByEmailRow struct {
ID uuid.UUID `json:"id"`
IndexID int64 `json:"index_id"`
Email string `json:"email"`
Password string `json:"password"`
Fullname string `json:"fullname"`
CreatedAt sql.NullTime `json:"created_at"`
UpdatedAt sql.NullTime `json:"updated_at"`
ID_2 uuid.UUID `json:"id_2"`
IndexID_2 int64 `json:"index_id_2"`
Name string `json:"name"`
OwnerID uuid.UUID `json:"owner_id"`
CreatedAt_2 sql.NullTime `json:"created_at_2"`
UpdatedAt_2 sql.NullTime `json:"updated_at_2"`
}
func (q *Queries) GetUserByEmail(ctx context.Context, email string) (GetUserByEmailRow, error) {
row := q.db.QueryRowContext(ctx, getUserByEmail, email) row := q.db.QueryRowContext(ctx, getUserByEmail, email)
var i User var i GetUserByEmailRow
err := row.Scan( err := row.Scan(
&i.ID, &i.ID,
&i.IndexID, &i.IndexID,
@ -71,19 +89,42 @@ func (q *Queries) GetUserByEmail(ctx context.Context, email string) (User, error
&i.Fullname, &i.Fullname,
&i.CreatedAt, &i.CreatedAt,
&i.UpdatedAt, &i.UpdatedAt,
&i.ID_2,
&i.IndexID_2,
&i.Name,
&i.OwnerID,
&i.CreatedAt_2,
&i.UpdatedAt_2,
) )
return i, err return i, err
} }
const getUserById = `-- name: GetUserById :one const getUserById = `-- name: GetUserById :one
SELECT id, index_id, email, password, fullname, created_at, updated_at SELECT u.id, u.index_id, email, password, fullname, u.created_at, u.updated_at, m.id, m.index_id, name, owner_id, m.created_at, m.updated_at
FROM users FROM users u
WHERE id = $1 JOIN merchants m on u.id = m.owner_id
WHERE u.id = $1
` `
func (q *Queries) GetUserById(ctx context.Context, id uuid.UUID) (User, error) { type GetUserByIdRow struct {
ID uuid.UUID `json:"id"`
IndexID int64 `json:"index_id"`
Email string `json:"email"`
Password string `json:"password"`
Fullname string `json:"fullname"`
CreatedAt sql.NullTime `json:"created_at"`
UpdatedAt sql.NullTime `json:"updated_at"`
ID_2 uuid.UUID `json:"id_2"`
IndexID_2 int64 `json:"index_id_2"`
Name string `json:"name"`
OwnerID uuid.UUID `json:"owner_id"`
CreatedAt_2 sql.NullTime `json:"created_at_2"`
UpdatedAt_2 sql.NullTime `json:"updated_at_2"`
}
func (q *Queries) GetUserById(ctx context.Context, id uuid.UUID) (GetUserByIdRow, error) {
row := q.db.QueryRowContext(ctx, getUserById, id) row := q.db.QueryRowContext(ctx, getUserById, id)
var i User var i GetUserByIdRow
err := row.Scan( err := row.Scan(
&i.ID, &i.ID,
&i.IndexID, &i.IndexID,
@ -92,6 +133,12 @@ func (q *Queries) GetUserById(ctx context.Context, id uuid.UUID) (User, error) {
&i.Fullname, &i.Fullname,
&i.CreatedAt, &i.CreatedAt,
&i.UpdatedAt, &i.UpdatedAt,
&i.ID_2,
&i.IndexID_2,
&i.Name,
&i.OwnerID,
&i.CreatedAt_2,
&i.UpdatedAt_2,
) )
return i, err return i, err
} }

View File

@ -1,7 +1,30 @@
package db package db
import "testing" import (
"context"
"testing"
"git.nochill.in/nochill/naice_pos/util"
"github.com/stretchr/testify/require"
)
func createRandomUser(t *testing.T) User {
arg := CreateUserParams{
Email: util.RandomEmail(),
Password: util.RandomString(5),
Fullname: util.RandomString(6),
}
user, err := testQueries.CreateUser(context.Background(), arg)
require.NoError(t, err)
require.Equal(t, arg.Email, user.Email)
require.Equal(t, arg.Password, user.Password)
require.Equal(t, arg.Fullname, user.Fullname)
return user
}
func TestGetUserByID(t *testing.T) { func TestGetUserByID(t *testing.T) {
createRandomUser(t)
} }

6
util/enums.go Normal file
View File

@ -0,0 +1,6 @@
package util
const (
STOCK_LOG_IN = "in"
STOCK_LOG_OUT = "out"
)

View File

@ -43,3 +43,8 @@ func RandomString(n int) string {
func RandomEmail() string { func RandomEmail() string {
return fmt.Sprintf("%s@mail.com", RandomString(5)) return fmt.Sprintf("%s@mail.com", RandomString(5))
} }
func RandomTransactionCode(prefix string, merchant_index int64) string {
time_now := time.Now().Unix()
return fmt.Sprintf("%s%d%d", prefix, merchant_index, time_now)
}