add sale order

This commit is contained in:
nochill 2023-03-22 12:06:09 +07:00
parent 2de83644b8
commit 5604f34e97
30 changed files with 592 additions and 67 deletions

View File

@ -21,27 +21,27 @@ func authMiddleware(tokenMaker token.Maker) gin.HandlerFunc {
authorizationHeader := ctx.GetHeader(authorizationHeaderKey) authorizationHeader := ctx.GetHeader(authorizationHeaderKey)
if len(authorizationHeader) == 0 { if len(authorizationHeader) == 0 {
err := errors.New("authorization header is not provided") err := errors.New("authorization header is not provided")
ctx.AbortWithStatusJSON(http.StatusUnauthorized, errorResponse(err)) ctx.AbortWithStatusJSON(http.StatusUnauthorized, errorResponse(err, ""))
return return
} }
fields := strings.Fields(authorizationHeader) fields := strings.Fields(authorizationHeader)
if len(fields) < 2 { if len(fields) < 2 {
err := errors.New("Invalid authorization header format") err := errors.New("Invalid authorization header format")
ctx.AbortWithStatusJSON(http.StatusUnauthorized, errorResponse(err)) ctx.AbortWithStatusJSON(http.StatusUnauthorized, errorResponse(err, ""))
return return
} }
authorizationType := strings.ToLower(fields[0]) authorizationType := strings.ToLower(fields[0])
if authorizationType != authorizationTypeBearer { if authorizationType != authorizationTypeBearer {
err := fmt.Errorf("Authorization only accept bearer type") err := fmt.Errorf("Authorization only accept bearer type")
ctx.AbortWithStatusJSON(http.StatusUnauthorized, errorResponse(err)) ctx.AbortWithStatusJSON(http.StatusUnauthorized, errorResponse(err, ""))
} }
accessToken := fields[1] accessToken := fields[1]
payload, err := tokenMaker.VerifyToken(accessToken) payload, err := tokenMaker.VerifyToken(accessToken)
if err != nil { if err != nil {
ctx.AbortWithStatusJSON(http.StatusUnauthorized, errorResponse(err)) ctx.AbortWithStatusJSON(http.StatusUnauthorized, errorResponse(err, ""))
return return
} }

View File

@ -24,7 +24,7 @@ func (server *Server) createProduct(ctx *gin.Context) {
var req createProductRequest var req createProductRequest
if err := ctx.ShouldBindJSON(&req); err != nil { if err := ctx.ShouldBindJSON(&req); err != nil {
ctx.JSON(http.StatusBadRequest, errorResponse(err)) ctx.JSON(http.StatusBadRequest, errorResponse(err, ""))
return return
} }
@ -40,7 +40,7 @@ func (server *Server) createProduct(ctx *gin.Context) {
product, err := server.store.CreateProduct(ctx, arg) product, err := server.store.CreateProduct(ctx, arg)
if err != nil { if err != nil {
ctx.JSON(http.StatusInternalServerError, errorResponse(err)) ctx.JSON(http.StatusInternalServerError, errorResponse(err, ""))
return return
} }
@ -54,24 +54,24 @@ type getProductRequest struct {
func (server *Server) getProduct(ctx *gin.Context) { func (server *Server) getProduct(ctx *gin.Context) {
var req getProductRequest var req getProductRequest
if err := ctx.ShouldBindUri(&req); err != nil { if err := ctx.ShouldBindUri(&req); err != nil {
ctx.JSON(http.StatusBadRequest, errorResponse(err)) ctx.JSON(http.StatusBadRequest, errorResponse(err, ""))
return return
} }
product, err := server.store.GetProduct(ctx, uuid.MustParse(req.ID)) product, err := server.store.GetProduct(ctx, uuid.MustParse(req.ID))
if err != nil { if err != nil {
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
ctx.JSON(http.StatusNotFound, errorResponse(err)) ctx.JSON(http.StatusNotFound, errorResponse(err, ""))
return return
} }
ctx.JSON(http.StatusInternalServerError, errorResponse(err)) ctx.JSON(http.StatusInternalServerError, errorResponse(err, ""))
return return
} }
authPayload := ctx.MustGet(authorizationPayloadKey).(*token.Payload) authPayload := ctx.MustGet(authorizationPayloadKey).(*token.Payload)
if product.MerchantID != authPayload.MerchantID { if product.MerchantID != authPayload.MerchantID {
err := errors.New("Product doesn't belong to the user") err := errors.New("Product doesn't belong to the user")
ctx.JSON(http.StatusUnauthorized, errorResponse(err)) ctx.JSON(http.StatusUnauthorized, errorResponse(err, ""))
return return
} }
@ -86,7 +86,7 @@ type listProductsRequest struct {
func (server *Server) listProducts(ctx *gin.Context) { func (server *Server) listProducts(ctx *gin.Context) {
var req listProductsRequest var req listProductsRequest
if err := ctx.ShouldBindQuery(&req); err != nil { if err := ctx.ShouldBindQuery(&req); err != nil {
ctx.JSON(http.StatusBadRequest, errorResponse(err)) ctx.JSON(http.StatusBadRequest, errorResponse(err, ""))
return return
} }
@ -99,7 +99,7 @@ func (server *Server) listProducts(ctx *gin.Context) {
products, err := server.store.ListProducts(ctx, arg) products, err := server.store.ListProducts(ctx, arg)
if err != nil { if err != nil {
ctx.JSON(http.StatusInternalServerError, errorResponse(err)) ctx.JSON(http.StatusInternalServerError, errorResponse(err, ""))
return return
} }
@ -116,7 +116,7 @@ type updateProductRequest struct {
func (server *Server) updateProduct(ctx *gin.Context) { func (server *Server) updateProduct(ctx *gin.Context) {
var req updateProductRequest var req updateProductRequest
if err := ctx.ShouldBindJSON(&req); err != nil { if err := ctx.ShouldBindJSON(&req); err != nil {
ctx.JSON(http.StatusBadRequest, errorResponse(err)) ctx.JSON(http.StatusBadRequest, errorResponse(err, ""))
return return
} }
@ -131,10 +131,10 @@ func (server *Server) updateProduct(ctx *gin.Context) {
product, err := server.store.UpdateProduct(ctx, arg) product, err := server.store.UpdateProduct(ctx, arg)
if err != nil { if err != nil {
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
ctx.JSON(http.StatusNotFound, errorResponse(err)) ctx.JSON(http.StatusNotFound, errorResponse(err, ""))
return return
} }
ctx.JSON(http.StatusInternalServerError, errorResponse(err)) ctx.JSON(http.StatusInternalServerError, errorResponse(err, ""))
return return
} }

View File

@ -19,7 +19,7 @@ func (server *Server) createProductCategory(ctx *gin.Context) {
var req createProductCategoryRequest var req createProductCategoryRequest
if err := ctx.ShouldBindJSON(&req); err != nil { if err := ctx.ShouldBindJSON(&req); err != nil {
ctx.JSON(http.StatusBadRequest, errorResponse(err)) ctx.JSON(http.StatusBadRequest, errorResponse(err, ""))
return return
} }
@ -31,7 +31,7 @@ func (server *Server) createProductCategory(ctx *gin.Context) {
ProductCategory, err := server.store.CreateProductCategory(ctx, arg) ProductCategory, err := server.store.CreateProductCategory(ctx, arg)
if err != nil { if err != nil {
ctx.JSON(http.StatusInternalServerError, errorResponse(err)) ctx.JSON(http.StatusInternalServerError, errorResponse(err, ""))
return return
} }
@ -46,7 +46,7 @@ type listProductCategoriesRequest struct {
func (server *Server) listProductCategories(ctx *gin.Context) { func (server *Server) listProductCategories(ctx *gin.Context) {
var req listProductCategoriesRequest var req listProductCategoriesRequest
if err := ctx.ShouldBindQuery(&req); err != nil { if err := ctx.ShouldBindQuery(&req); err != nil {
ctx.JSON(http.StatusBadRequest, errorResponse(err)) ctx.JSON(http.StatusBadRequest, errorResponse(err, ""))
return return
} }
@ -59,7 +59,7 @@ func (server *Server) listProductCategories(ctx *gin.Context) {
ProductCategories, err := server.store.ListProductCategoriesByMerchantID(ctx, arg) ProductCategories, err := server.store.ListProductCategoriesByMerchantID(ctx, arg)
if err != nil { if err != nil {
ctx.JSON(http.StatusInternalServerError, errorResponse(err)) ctx.JSON(http.StatusInternalServerError, errorResponse(err, ""))
return return
} }
@ -74,7 +74,7 @@ type updateProductCategoryRequest struct {
func (server *Server) updateProductCategory(ctx *gin.Context) { func (server *Server) updateProductCategory(ctx *gin.Context) {
var req updateProductCategoryRequest var req updateProductCategoryRequest
if err := ctx.ShouldBindJSON(&req); err != nil { if err := ctx.ShouldBindJSON(&req); err != nil {
ctx.JSON(http.StatusBadRequest, errorResponse(err)) ctx.JSON(http.StatusBadRequest, errorResponse(err, ""))
return return
} }
@ -86,10 +86,10 @@ func (server *Server) updateProductCategory(ctx *gin.Context) {
ProductCategory, err := server.store.UpdateProductCategory(ctx, arg) ProductCategory, err := server.store.UpdateProductCategory(ctx, arg)
if err != nil { if err != nil {
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
ctx.JSON(http.StatusNotFound, errorResponse(err)) ctx.JSON(http.StatusNotFound, errorResponse(err, ""))
return return
} }
ctx.JSON(http.StatusInternalServerError, errorResponse(err)) ctx.JSON(http.StatusInternalServerError, errorResponse(err, ""))
return return
} }

View File

@ -35,11 +35,11 @@ func TestCreateProductCategory(t *testing.T) {
"merchant_id": productCategory.MerchantID, "merchant_id": productCategory.MerchantID,
}, },
setupAuth: func(t *testing.T, request *http.Request, tokenMaker token.Maker) { setupAuth: func(t *testing.T, request *http.Request, tokenMaker token.Maker) {
addAuthorization(t, request, tokenMaker, authorizationTypeBearer, util.RandomEmail(), "a848090f-0409-4386-9caa-929ae6874dbb", time.Minute) addAuthorization(t, request, tokenMaker, authorizationTypeBearer, util.RandomEmail(), "54b8a2d9-16be-4239-8828-5daa317028dc", time.Minute)
}, },
buildStubs: func(store *mockdb.MockStore) { buildStubs: func(store *mockdb.MockStore) {
arg := db.CreateProductCategoryParams{ arg := db.CreateProductCategoryParams{
MerchantID: uuid.MustParse("a848090f-0409-4386-9caa-929ae6874dbb"), MerchantID: uuid.MustParse("54b8a2d9-16be-4239-8828-5daa317028dc"),
Name: productCategory.Name, Name: productCategory.Name,
} }
store.EXPECT(). store.EXPECT().
@ -83,7 +83,7 @@ func TestCreateProductCategory(t *testing.T) {
func createRandomProductCategory() db.ProductCategory { func createRandomProductCategory() db.ProductCategory {
return db.ProductCategory{ return db.ProductCategory{
ID: uuid.New(), ID: uuid.New(),
MerchantID: uuid.MustParse("a848090f-0409-4386-9caa-929ae6874dbb"), MerchantID: uuid.MustParse("54b8a2d9-16be-4239-8828-5daa317028dc"),
Name: util.RandomString(5), Name: util.RandomString(5),
} }
} }

View File

@ -21,7 +21,7 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
var MERCHANTID = "a848090f-0409-4386-9caa-929ae6874dbb" var MERCHANTID = "54b8a2d9-16be-4239-8828-5daa317028dc"
func TestGetProductApi(t *testing.T) { func TestGetProductApi(t *testing.T) {
product := randomProduct(MERCHANTID) product := randomProduct(MERCHANTID)

View File

@ -34,7 +34,7 @@ func (server Server) createPurchase(ctx *gin.Context) {
} }
if err := ctx.ShouldBindJSON(&req); err != nil { if err := ctx.ShouldBindJSON(&req); err != nil {
ctx.JSON(http.StatusBadRequest, errorResponse(err)) ctx.JSON(http.StatusBadRequest, errorResponse(err, ""))
return return
} }
@ -52,7 +52,7 @@ func (server Server) createPurchase(ctx *gin.Context) {
result, err := server.store.PurchaseOrderTx(ctx, arg) result, err := server.store.PurchaseOrderTx(ctx, arg)
if err != nil { if err != nil {
ctx.JSON(http.StatusInternalServerError, errorResponse(err)) ctx.JSON(http.StatusInternalServerError, errorResponse(err, ""))
return return
} }

78
api/sale_order.go Normal file
View File

@ -0,0 +1,78 @@
package api
import (
"database/sql"
"net/http"
db "git.nochill.in/nochill/naice_pos/db/sqlc"
"git.nochill.in/nochill/naice_pos/util"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"github.com/lib/pq"
)
type createSaleOrderRequest struct {
MerchantID uuid.UUID `json:"merchant_id" binding:"required"`
MerchantIDX int64 `json:"merchant_index" binding:"required"`
CustomerID string `json:"customer_id"`
CreatedBy uuid.UUID `json:"created_by" binding:"required"`
Code string `json:"code"`
IsPaid bool `json:"is_paid" binding:"required"`
Total float64 `json:"total" binding:"required"`
PaidNominal float64 `json:"paid_nominal" binding:"required"`
Change float64 `json:"change"`
Note string `json:"note"`
IsKeep bool `json:"is_keep"`
Products []db.SaleOrderProduct `json:"products" binding:"required"`
}
func (server Server) createSaleOrder(ctx *gin.Context) {
var req createSaleOrderRequest
code := util.RandomTransactionCode("S", req.MerchantIDX)
customerID := uuid.NullUUID{Valid: false, UUID: uuid.Nil}
if err := ctx.ShouldBindJSON(&req); err != nil {
ctx.JSON(http.StatusBadRequest, errorResponse(err, ""))
return
}
if len(req.CustomerID) > 0 {
customerID = uuid.NullUUID{Valid: true, UUID: uuid.MustParse(req.CustomerID)}
}
if len(req.Code) > 0 {
code = req.Code
}
arg := db.SaleOrderTxParams{
MerchantID: req.MerchantID,
CreatedBy: req.CreatedBy,
CustomerID: customerID,
Code: code,
IsPaid: req.IsPaid,
Total: req.Total,
PaidNominal: req.PaidNominal,
Note: sql.NullString{Valid: len(req.Note) > 0, String: req.Note},
IsKeep: req.IsKeep,
Change: req.Change,
Products: req.Products,
}
result, err := server.store.SaleOrderTx(ctx, arg)
if err != nil {
if pqErr, ok := err.(*pq.Error); ok {
switch pqErr.Code.Name() {
case "check_violation":
ctx.JSON(http.StatusConflict, errorResponse(err, "Stok tidak bisa kurang dari 0"))
return
}
}
ctx.JSON(http.StatusInternalServerError, errorResponse(err, ""))
return
}
ctx.JSON(http.StatusOK, result)
}

View File

@ -42,6 +42,7 @@ func (server *Server) getRoutes() {
apiRoutes.POST("/products", server.createProduct) apiRoutes.POST("/products", server.createProduct)
apiRoutes.PATCH("/products", server.updateProduct) apiRoutes.PATCH("/products", server.updateProduct)
apiRoutes.GET("/products", server.listProducts)
apiRoutes.GET("/product/:id", server.getProduct) apiRoutes.GET("/product/:id", server.getProduct)
apiRoutes.POST("/product/category", server.createProductCategory) apiRoutes.POST("/product/category", server.createProductCategory)
@ -53,6 +54,8 @@ func (server *Server) getRoutes() {
apiRoutes.POST("/purchase-products", server.createPurchase) apiRoutes.POST("/purchase-products", server.createPurchase)
apiRoutes.POST("/sale-order", server.createSaleOrder)
server.router = router server.router = router
} }

View File

@ -26,7 +26,7 @@ func (server *Server) createSupplier(ctx *gin.Context) {
var supDetail pqtype.NullRawMessage var supDetail pqtype.NullRawMessage
if err := ctx.ShouldBindJSON(&req); err != nil { if err := ctx.ShouldBindJSON(&req); err != nil {
ctx.JSON(http.StatusBadRequest, errorResponse(err)) ctx.JSON(http.StatusBadRequest, errorResponse(err, ""))
return return
} }
@ -44,7 +44,7 @@ func (server *Server) createSupplier(ctx *gin.Context) {
supplier, err := server.store.CreateSuppliers(ctx, arg) supplier, err := server.store.CreateSuppliers(ctx, arg)
if err != nil { if err != nil {
ctx.JSON(http.StatusInternalServerError, errorResponse(err)) ctx.JSON(http.StatusInternalServerError, errorResponse(err, ""))
return return
} }

View File

@ -21,63 +21,63 @@ type renewAccessResponse struct {
func (server *Server) renewAccessToken(ctx *gin.Context) { func (server *Server) renewAccessToken(ctx *gin.Context) {
var req renewAccessRequest var req renewAccessRequest
if err := ctx.ShouldBindJSON(&req); err != nil { if err := ctx.ShouldBindJSON(&req); err != nil {
ctx.JSON(http.StatusBadRequest, errorResponse(err)) ctx.JSON(http.StatusBadRequest, errorResponse(err, ""))
return return
} }
refreshPayload, err := server.tokenMaker.VerifyToken(req.RefreshToken) refreshPayload, err := server.tokenMaker.VerifyToken(req.RefreshToken)
if err != nil { if err != nil {
ctx.JSON(http.StatusUnauthorized, errorResponse(err)) ctx.JSON(http.StatusUnauthorized, errorResponse(err, ""))
return return
} }
session, err := server.store.GetSession(ctx, refreshPayload.ID) session, err := server.store.GetSession(ctx, refreshPayload.ID)
if err != nil { if err != nil {
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
ctx.JSON(http.StatusNotFound, errorResponse(err)) ctx.JSON(http.StatusNotFound, errorResponse(err, ""))
return return
} }
ctx.JSON(http.StatusInternalServerError, errorResponse(err)) ctx.JSON(http.StatusInternalServerError, errorResponse(err, ""))
return return
} }
if session.IsBlocked { if session.IsBlocked {
err := fmt.Errorf("blocked session") err := fmt.Errorf("blocked session")
ctx.JSON(http.StatusUnauthorized, errorResponse(err)) ctx.JSON(http.StatusUnauthorized, errorResponse(err, ""))
return return
} }
if session.Email != refreshPayload.Email { if session.Email != refreshPayload.Email {
err := fmt.Errorf("incorrect session user") err := fmt.Errorf("incorrect session user")
ctx.JSON(http.StatusUnauthorized, errorResponse(err)) ctx.JSON(http.StatusUnauthorized, errorResponse(err, ""))
return return
} }
if session.RefreshToken != req.RefreshToken { if session.RefreshToken != req.RefreshToken {
err := fmt.Errorf("mismatched session token") err := fmt.Errorf("mismatched session token")
ctx.JSON(http.StatusUnauthorized, errorResponse(err)) ctx.JSON(http.StatusUnauthorized, errorResponse(err, ""))
return return
} }
if time.Now().After(refreshPayload.ExpiredAt) { if time.Now().After(refreshPayload.ExpiredAt) {
err := fmt.Errorf("Exprired session") err := fmt.Errorf("Exprired session")
ctx.JSON(http.StatusUnauthorized, errorResponse(err)) ctx.JSON(http.StatusUnauthorized, errorResponse(err, ""))
return return
} }
user, err := server.store.GetUserByEmail(ctx, refreshPayload.Email) user, err := server.store.GetUserByEmail(ctx, refreshPayload.Email)
if err != nil { if err != nil {
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
ctx.JSON(http.StatusNotFound, errorResponse(err)) ctx.JSON(http.StatusNotFound, errorResponse(err, ""))
return return
} }
ctx.JSON(http.StatusInternalServerError, errorResponse(err)) ctx.JSON(http.StatusInternalServerError, errorResponse(err, ""))
return return
} }
merchant, err := server.store.GetMerchantByUserId(ctx, user.ID) merchant, err := server.store.GetMerchantByUserId(ctx, user.ID)
if err != nil { if err != nil {
ctx.JSON(http.StatusInternalServerError, errorResponse(err)) ctx.JSON(http.StatusInternalServerError, errorResponse(err, ""))
return return
} }
@ -88,7 +88,7 @@ func (server *Server) renewAccessToken(ctx *gin.Context) {
) )
if err != nil { if err != nil {
ctx.JSON(http.StatusInternalServerError, errorResponse(err)) ctx.JSON(http.StatusInternalServerError, errorResponse(err, ""))
return return
} }

View File

@ -50,13 +50,13 @@ func newUserMerchantResponse(user db.GetUserByEmailRow) userMerchantResponse {
func (server *Server) createUserMerchant(ctx *gin.Context) { func (server *Server) createUserMerchant(ctx *gin.Context) {
var req createUserMerchantRequest var req createUserMerchantRequest
if err := ctx.ShouldBindJSON(&req); err != nil { if err := ctx.ShouldBindJSON(&req); err != nil {
ctx.JSON(http.StatusBadRequest, errorResponse(err)) ctx.JSON(http.StatusBadRequest, errorResponse(err, ""))
return return
} }
hashedPassword, err := util.HashPassword(req.Password) hashedPassword, err := util.HashPassword(req.Password)
if err != nil { if err != nil {
ctx.JSON(http.StatusInternalServerError, errorResponse(err)) ctx.JSON(http.StatusInternalServerError, errorResponse(err, ""))
return return
} }
@ -72,11 +72,11 @@ func (server *Server) createUserMerchant(ctx *gin.Context) {
if pqErr, ok := err.(*pq.Error); ok { if pqErr, ok := err.(*pq.Error); ok {
switch pqErr.Code.Name() { switch pqErr.Code.Name() {
case "foreign_key_violation", "unique_violation": case "foreign_key_violation", "unique_violation":
ctx.JSON(http.StatusConflict, errorResponse(err)) ctx.JSON(http.StatusConflict, errorResponse(err, ""))
return return
} }
} }
ctx.JSON(http.StatusInternalServerError, errorResponse(err)) ctx.JSON(http.StatusInternalServerError, errorResponse(err, ""))
return return
} }
@ -117,23 +117,23 @@ type userLoginResponse struct {
func (server *Server) loginUser(ctx *gin.Context) { func (server *Server) loginUser(ctx *gin.Context) {
var req userLoginRequest var req userLoginRequest
if err := ctx.ShouldBindJSON(&req); err != nil { if err := ctx.ShouldBindJSON(&req); err != nil {
ctx.JSON(http.StatusBadRequest, errorResponse(err)) ctx.JSON(http.StatusBadRequest, errorResponse(err, ""))
return return
} }
user, err := server.store.GetUserByEmail(ctx, req.Email) user, err := server.store.GetUserByEmail(ctx, req.Email)
if err != nil { if err != nil {
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
ctx.JSON(http.StatusNotFound, errorResponse(err)) ctx.JSON(http.StatusNotFound, errorResponse(err, ""))
return return
} }
ctx.JSON(http.StatusInternalServerError, errorResponse(err)) ctx.JSON(http.StatusInternalServerError, errorResponse(err, ""))
return return
} }
err = util.CheckPassword(req.Password, user.Password) err = util.CheckPassword(req.Password, user.Password)
if err != nil { if err != nil {
ctx.JSON(http.StatusUnauthorized, errorResponse(err)) ctx.JSON(http.StatusUnauthorized, errorResponse(err, ""))
return return
} }
@ -144,7 +144,7 @@ func (server *Server) loginUser(ctx *gin.Context) {
) )
if err != nil { if err != nil {
ctx.JSON(http.StatusInternalServerError, errorResponse(err)) ctx.JSON(http.StatusInternalServerError, errorResponse(err, ""))
return return
} }
@ -165,7 +165,7 @@ func (server *Server) loginUser(ctx *gin.Context) {
}) })
if err != nil { if err != nil {
ctx.JSON(http.StatusInternalServerError, errorResponse(err)) ctx.JSON(http.StatusInternalServerError, errorResponse(err, ""))
return return
} }

View File

@ -44,7 +44,7 @@ CREATE TABLE products (
"name" varchar not null, "name" varchar not null,
"selling_price" double precision default(0::double precision) NOT NULL, "selling_price" double precision default(0::double precision) NOT NULL,
"purchase_price" double precision default(0:: double precision) NOT NULL, "purchase_price" double precision default(0:: double precision) NOT NULL,
"stock" double precision default(0::double precision) NOT NULL, "stock" double precision default(0::double precision) NOT NULL CHECK("stock" >= 0),
"created_at" timestamp default(now()), "created_at" timestamp default(now()),
"updated_at" timestamp default(now()) "updated_at" timestamp default(now())
); );
@ -94,6 +94,7 @@ CREATE TABLE sale_order (
"created_at" timestamp default(now()), "created_at" timestamp default(now()),
"updated_at" timestamp default(now()) "updated_at" timestamp default(now())
); );
CREATE TABLE sale_order_detail ( CREATE TABLE sale_order_detail (
"id" uuid default gen_random_uuid() primary key not null, "id" uuid default gen_random_uuid() primary key not null,
"index_id" bigserial not null, "index_id" bigserial not null,

View File

@ -0,0 +1 @@
ALTER TABLE sale_order DROP COLUMN IF EXISTS is_keep;

View File

@ -0,0 +1 @@
ALTER TABLE sale_order ADD COLUMN is_keep boolean not null default false;

View File

@ -0,0 +1,3 @@
ALTER TABLE sale_order DROP COLUMN change;
ALTER TABLE sale_order ALTER COLUMN is_paid DROP NOT null;
ALTER TABLE sale_order ALTER COLUMN code DROP NOT null;

View File

@ -0,0 +1,3 @@
ALTER TABLE sale_order ADD COLUMN change double precision default(0::double precision) NOT NULL;
ALTER TABLE sale_order ALTER COLUMN is_paid SET not null;
ALTER TABLE sale_order ALTER COLUMN code SET not null;

View File

@ -127,10 +127,10 @@ func (mr *MockStoreMockRecorder) CreatePurchaseOrderDetail(arg0, arg1 interface{
} }
// CreateSaleOrder mocks base method. // CreateSaleOrder mocks base method.
func (m *MockStore) CreateSaleOrder(arg0 context.Context, arg1 db.CreateSaleOrderParams) (db.PurchaseOrder, error) { func (m *MockStore) CreateSaleOrder(arg0 context.Context, arg1 db.CreateSaleOrderParams) (db.SaleOrder, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CreateSaleOrder", arg0, arg1) ret := m.ctrl.Call(m, "CreateSaleOrder", arg0, arg1)
ret0, _ := ret[0].(db.PurchaseOrder) ret0, _ := ret[0].(db.SaleOrder)
ret1, _ := ret[1].(error) ret1, _ := ret[1].(error)
return ret0, ret1 return ret0, ret1
} }
@ -141,6 +141,21 @@ func (mr *MockStoreMockRecorder) CreateSaleOrder(arg0, arg1 interface{}) *gomock
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateSaleOrder", reflect.TypeOf((*MockStore)(nil).CreateSaleOrder), arg0, arg1) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateSaleOrder", reflect.TypeOf((*MockStore)(nil).CreateSaleOrder), arg0, arg1)
} }
// CreateSaleOrderDetail mocks base method.
func (m *MockStore) CreateSaleOrderDetail(arg0 context.Context, arg1 db.CreateSaleOrderDetailParams) (db.SaleOrderDetail, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CreateSaleOrderDetail", arg0, arg1)
ret0, _ := ret[0].(db.SaleOrderDetail)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// CreateSaleOrderDetail indicates an expected call of CreateSaleOrderDetail.
func (mr *MockStoreMockRecorder) CreateSaleOrderDetail(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateSaleOrderDetail", reflect.TypeOf((*MockStore)(nil).CreateSaleOrderDetail), arg0, arg1)
}
// CreateSession mocks base method. // CreateSession mocks base method.
func (m *MockStore) CreateSession(arg0 context.Context, arg1 db.CreateSessionParams) (db.UserSession, error) { func (m *MockStore) CreateSession(arg0 context.Context, arg1 db.CreateSessionParams) (db.UserSession, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
@ -497,6 +512,21 @@ func (mr *MockStoreMockRecorder) PurchaseOrderTx(arg0, arg1 interface{}) *gomock
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PurchaseOrderTx", reflect.TypeOf((*MockStore)(nil).PurchaseOrderTx), arg0, arg1) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PurchaseOrderTx", reflect.TypeOf((*MockStore)(nil).PurchaseOrderTx), arg0, arg1)
} }
// SaleOrderTx mocks base method.
func (m *MockStore) SaleOrderTx(arg0 context.Context, arg1 db.SaleOrderTxParams) (db.SaleOrderTxResult, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "SaleOrderTx", arg0, arg1)
ret0, _ := ret[0].(db.SaleOrderTxResult)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// SaleOrderTx indicates an expected call of SaleOrderTx.
func (mr *MockStoreMockRecorder) SaleOrderTx(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaleOrderTx", reflect.TypeOf((*MockStore)(nil).SaleOrderTx), arg0, arg1)
}
// SuppliersList mocks base method. // SuppliersList mocks base method.
func (m *MockStore) SuppliersList(arg0 context.Context, arg1 db.SuppliersListParams) ([]db.Supplier, error) { func (m *MockStore) SuppliersList(arg0 context.Context, arg1 db.SuppliersListParams) ([]db.Supplier, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()

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

@ -0,0 +1,16 @@
-- name: CreateSaleOrder :one
INSERT INTO sale_order (
merchant_id,
customer_id,
code,
created_by,
is_paid,
total,
paid_nominal,
note,
change,
is_keep
) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10
)
RETURNING *;

View File

@ -0,0 +1,13 @@
-- name: CreateSaleOrderDetail :one
INSERT INTO sale_order_detail (
sale_order_id,
product_id,
product_name,
quantity,
sub_total,
product_price,
profit
) VALUES(
$1, $2, $3, $4, $5, $6, $7
)
RETURNING *;

34
db/sqlc/customer_test.go Normal file
View File

@ -0,0 +1,34 @@
package db
import (
"context"
"testing"
"git.nochill.in/nochill/naice_pos/util"
"github.com/google/uuid"
"github.com/stretchr/testify/require"
)
func createRandomCustomer(t *testing.T) (Customer, CreateCustomersParams) {
arg := CreateCustomersParams{
MerchantID: uuid.MustParse("54b8a2d9-16be-4239-8828-5daa317028dc"),
Name: util.RandomString(10),
}
customer, err := testQueries.CreateCustomers(context.Background(), arg)
require.NoError(t, err)
return customer, arg
}
func TestCreateCustomer(t *testing.T) {
supplier, arg := createRandomCustomer(t)
require.Equal(t, arg.Name, supplier.Name)
require.Equal(t, arg.MerchantID, supplier.MerchantID)
require.NotZero(t, supplier.ID)
require.NotZero(t, supplier.CreatedAt)
require.NotZero(t, supplier.UpdatedAt)
}

View File

@ -128,16 +128,18 @@ type PurchaseOrderDetail struct {
type SaleOrder struct { type SaleOrder struct {
ID uuid.UUID `json:"id"` ID uuid.UUID `json:"id"`
IndexID int64 `json:"index_id"` IndexID int64 `json:"index_id"`
Code sql.NullString `json:"code"` Code string `json:"code"`
CreatedBy uuid.UUID `json:"created_by"` CreatedBy uuid.UUID `json:"created_by"`
MerchantID uuid.UUID `json:"merchant_id"` MerchantID uuid.UUID `json:"merchant_id"`
CustomerID uuid.NullUUID `json:"customer_id"` CustomerID uuid.NullUUID `json:"customer_id"`
IsPaid sql.NullBool `json:"is_paid"` IsPaid bool `json:"is_paid"`
Total float64 `json:"total"` Total float64 `json:"total"`
PaidNominal float64 `json:"paid_nominal"` PaidNominal float64 `json:"paid_nominal"`
Note sql.NullString `json:"note"` Note sql.NullString `json:"note"`
CreatedAt sql.NullTime `json:"created_at"` CreatedAt sql.NullTime `json:"created_at"`
UpdatedAt sql.NullTime `json:"updated_at"` UpdatedAt sql.NullTime `json:"updated_at"`
IsKeep bool `json:"is_keep"`
Change float64 `json:"change"`
} }
type SaleOrderDetail struct { type SaleOrderDetail struct {

View File

@ -9,7 +9,7 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
var merchantID = uuid.MustParse("04a1b0a7-69b4-41da-a053-2f6b95c93195") var merchantID = uuid.MustParse("54b8a2d9-16be-4239-8828-5daa317028dc")
func createRandomProductCategory(t *testing.T) ProductCategory { func createRandomProductCategory(t *testing.T) ProductCategory {
arg := CreateProductCategoryParams{ arg := CreateProductCategoryParams{

View File

@ -19,7 +19,7 @@ func createRandomProduct(t *testing.T) (Product, CreateProductParams) {
productCategory := createRandomProductCategory(t) productCategory := createRandomProductCategory(t)
arg := CreateProductParams{ arg := CreateProductParams{
MerchantID: uuid.MustParse("04a1b0a7-69b4-41da-a053-2f6b95c93195"), MerchantID: uuid.MustParse("54b8a2d9-16be-4239-8828-5daa317028dc"),
Name: util.RandomString(10), Name: util.RandomString(10),
SellingPrice: sellingPrice, SellingPrice: sellingPrice,
PurchasePrice: purchasePrice, PurchasePrice: purchasePrice,

View File

@ -17,7 +17,8 @@ type Querier interface {
CreateProductCategory(ctx context.Context, arg CreateProductCategoryParams) (ProductCategory, error) CreateProductCategory(ctx context.Context, arg CreateProductCategoryParams) (ProductCategory, error)
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)
CreateSaleOrder(ctx context.Context, arg CreateSaleOrderParams) (PurchaseOrder, error) CreateSaleOrder(ctx context.Context, arg CreateSaleOrderParams) (SaleOrder, error)
CreateSaleOrderDetail(ctx context.Context, arg CreateSaleOrderDetailParams) (SaleOrderDetail, 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) CreateStockLogs(ctx context.Context, arg CreateStockLogsParams) (StockLog, error)
CreateSuppliers(ctx context.Context, arg CreateSuppliersParams) (Supplier, error) CreateSuppliers(ctx context.Context, arg CreateSuppliersParams) (Supplier, error)

77
db/sqlc/sale_order.sql.go Normal file
View File

@ -0,0 +1,77 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.17.2
// source: sale_order.sql
package db
import (
"context"
"database/sql"
"github.com/google/uuid"
)
const createSaleOrder = `-- name: CreateSaleOrder :one
INSERT INTO sale_order (
merchant_id,
customer_id,
code,
created_by,
is_paid,
total,
paid_nominal,
note,
change,
is_keep
) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10
)
RETURNING id, index_id, code, created_by, merchant_id, customer_id, is_paid, total, paid_nominal, note, created_at, updated_at, is_keep, change
`
type CreateSaleOrderParams struct {
MerchantID uuid.UUID `json:"merchant_id"`
CustomerID uuid.NullUUID `json:"customer_id"`
Code string `json:"code"`
CreatedBy uuid.UUID `json:"created_by"`
IsPaid bool `json:"is_paid"`
Total float64 `json:"total"`
PaidNominal float64 `json:"paid_nominal"`
Note sql.NullString `json:"note"`
Change float64 `json:"change"`
IsKeep bool `json:"is_keep"`
}
func (q *Queries) CreateSaleOrder(ctx context.Context, arg CreateSaleOrderParams) (SaleOrder, error) {
row := q.db.QueryRowContext(ctx, createSaleOrder,
arg.MerchantID,
arg.CustomerID,
arg.Code,
arg.CreatedBy,
arg.IsPaid,
arg.Total,
arg.PaidNominal,
arg.Note,
arg.Change,
arg.IsKeep,
)
var i SaleOrder
err := row.Scan(
&i.ID,
&i.IndexID,
&i.Code,
&i.CreatedBy,
&i.MerchantID,
&i.CustomerID,
&i.IsPaid,
&i.Total,
&i.PaidNominal,
&i.Note,
&i.CreatedAt,
&i.UpdatedAt,
&i.IsKeep,
&i.Change,
)
return i, err
}

View File

@ -0,0 +1,64 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.17.2
// source: sale_order_detail.sql
package db
import (
"context"
"github.com/google/uuid"
)
const createSaleOrderDetail = `-- name: CreateSaleOrderDetail :one
INSERT INTO sale_order_detail (
sale_order_id,
product_id,
product_name,
quantity,
sub_total,
product_price,
profit
) VALUES(
$1, $2, $3, $4, $5, $6, $7
)
RETURNING id, index_id, sale_order_id, product_id, product_name, quantity, sub_total, product_price, profit, created_at, updated_at
`
type CreateSaleOrderDetailParams struct {
SaleOrderID uuid.UUID `json:"sale_order_id"`
ProductID uuid.UUID `json:"product_id"`
ProductName string `json:"product_name"`
Quantity float64 `json:"quantity"`
SubTotal float64 `json:"sub_total"`
ProductPrice float64 `json:"product_price"`
Profit float64 `json:"profit"`
}
func (q *Queries) CreateSaleOrderDetail(ctx context.Context, arg CreateSaleOrderDetailParams) (SaleOrderDetail, error) {
row := q.db.QueryRowContext(ctx, createSaleOrderDetail,
arg.SaleOrderID,
arg.ProductID,
arg.ProductName,
arg.Quantity,
arg.SubTotal,
arg.ProductPrice,
arg.Profit,
)
var i SaleOrderDetail
err := row.Scan(
&i.ID,
&i.IndexID,
&i.SaleOrderID,
&i.ProductID,
&i.ProductName,
&i.Quantity,
&i.SubTotal,
&i.ProductPrice,
&i.Profit,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}

View File

@ -13,6 +13,7 @@ type Store interface {
Querier Querier
PurchaseOrderTx(ctx context.Context, arg PurchasoOrderTxParams) (PurchaseOrderTxResult, error) PurchaseOrderTx(ctx context.Context, arg PurchasoOrderTxParams) (PurchaseOrderTxResult, error)
CreateUserMerchantTx(ctx context.Context, arg UserMerchantTxParams) (UserMerchantTxResult, error) CreateUserMerchantTx(ctx context.Context, arg UserMerchantTxParams) (UserMerchantTxResult, error)
SaleOrderTx(ctx context.Context, arg SaleOrderTxParams) (SaleOrderTxResult, error)
} }
type SQLStore struct { type SQLStore struct {
@ -46,6 +47,8 @@ func (store *SQLStore) execTx(ctx context.Context, fn func(*Queries) error) erro
} }
// -------- PURCHASE ORDER TRANSACTION --------- //
type PurchaseOrderProduct struct { type PurchaseOrderProduct struct {
ProductID uuid.UUID `json:"product_id"` ProductID uuid.UUID `json:"product_id"`
Quantity float64 `json:"quantity"` Quantity float64 `json:"quantity"`
@ -127,11 +130,11 @@ func (store *SQLStore) PurchaseOrderTx(ctx context.Context, arg PurchasoOrderTxP
MerchantID: arg.MerchantID, MerchantID: arg.MerchantID,
CreatedBy: arg.CreatedBy, CreatedBy: arg.CreatedBy,
TransactionID: uuid.NullUUID{UUID: result.PurchaseOrder.ID, Valid: true}, TransactionID: uuid.NullUUID{UUID: result.PurchaseOrder.ID, Valid: true},
TransactionActionType: "Purchase_order", TransactionActionType: "purchase_order",
TransactionDescription: fmt.Sprintf("Penambahan produk %s dari pembelian dengan code %s", product.Name, arg.Code.String), TransactionDescription: fmt.Sprintf("Penambahan stok produk %s dari pembelian dengan code %s", product.Name, arg.Code.String),
Type: util.STOCK_LOG_IN, Type: util.STOCK_LOG_IN,
SellingPrice: product.SellingPrice, SellingPrice: product.SellingPrice,
PurchasePrice: product.PurchasePrice, PurchasePrice: arg.Products[i].Price,
Quantity: arg.Products[i].Quantity, Quantity: arg.Products[i].Quantity,
}) })
@ -147,6 +150,116 @@ func (store *SQLStore) PurchaseOrderTx(ctx context.Context, arg PurchasoOrderTxP
return result, err return result, err
} }
// ------ SALE ORDER TRANSACTION ----------- //
type SaleOrderProduct struct {
ProductID uuid.UUID `json:"product_id"`
ProductName string `json:"product_name"`
Quantity float64 `json:"quantity"`
Sub_total float64 `json:"sub_total"`
Price float64 `json:"price"`
Profit float64 `json:"profit"`
}
type SaleOrderTxParams struct {
MerchantID uuid.UUID `json:"merchant_id"`
CreatedBy uuid.UUID `json:"created_by"` // user_id
CustomerID uuid.NullUUID `json:"customer_id"`
Code string `json:"code"`
IsPaid bool `json:"is_paid"`
Total float64 `json:"total"`
PaidNominal float64 `json:"paid_nominal"`
Note sql.NullString `json:"note"`
IsKeep bool `json:"is_keep"`
Change float64 `json:"change"`
Products []SaleOrderProduct `json:"products"`
}
type SaleOrderTxResult struct {
SaleOrder SaleOrder `json:"sale_order"`
SaleOrderDetail []SaleOrderDetail `json:"detail"`
}
func (store *SQLStore) SaleOrderTx(ctx context.Context, arg SaleOrderTxParams) (SaleOrderTxResult, error) {
var result SaleOrderTxResult
err := store.execTx(ctx, func(q *Queries) error {
var err error
result.SaleOrder, err = q.CreateSaleOrder(ctx, CreateSaleOrderParams{
MerchantID: arg.MerchantID,
CustomerID: uuid.NullUUID{Valid: arg.CustomerID.Valid, UUID: arg.CustomerID.UUID},
Code: arg.Code,
CreatedBy: arg.CreatedBy,
IsPaid: arg.IsPaid,
Total: arg.Total,
PaidNominal: arg.PaidNominal,
Note: sql.NullString{Valid: len(arg.Note.String) > 0, String: arg.Note.String},
IsKeep: arg.IsKeep,
})
if err != nil {
return err
}
for i := 0; i < len(arg.Products); i++ {
saleOrderDetail, err := q.CreateSaleOrderDetail(ctx, CreateSaleOrderDetailParams{
SaleOrderID: result.SaleOrder.ID,
ProductID: arg.Products[i].ProductID,
ProductName: arg.Products[i].ProductName,
Quantity: arg.Products[i].Quantity,
SubTotal: arg.Products[i].Sub_total,
ProductPrice: arg.Products[i].Price,
Profit: arg.Products[i].Profit,
})
if err != nil {
return err
}
result.SaleOrderDetail = append(result.SaleOrderDetail, saleOrderDetail)
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
}
_, err = q.CreateStockLogs(ctx, CreateStockLogsParams{
ProductID: arg.Products[i].ProductID,
MerchantID: arg.MerchantID,
CreatedBy: arg.CreatedBy,
TransactionID: uuid.NullUUID{UUID: result.SaleOrder.ID, Valid: true},
TransactionActionType: "sale_order",
TransactionDescription: fmt.Sprintf("Pengurangan stok produk %s dari penjualan dengan code %s", product.Name, arg.Code),
Type: util.STOCK_LOG_OUT,
SellingPrice: arg.Products[i].Price,
PurchasePrice: product.PurchasePrice,
Quantity: arg.Products[i].Quantity,
})
if err != nil {
return err
}
}
return nil
})
return result, err
}
// ---------- CREATE USER MERCHANT TRANSACTION ------------ //
type UserMerchantTxParams struct { type UserMerchantTxParams struct {
Email string `json:"email"` Email string `json:"email"`
Fullname string `json:"fullname"` Fullname string `json:"fullname"`

View File

@ -6,6 +6,7 @@ import (
"testing" "testing"
"git.nochill.in/nochill/naice_pos/util" "git.nochill.in/nochill/naice_pos/util"
"github.com/google/uuid"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -13,7 +14,7 @@ func TestPurchaseOrder(t *testing.T) {
var products []PurchaseOrderProduct var products []PurchaseOrderProduct
store := NewStore(testDB) store := NewStore(testDB)
user := createRandomUser(t) user := createRandomUser(t) // TODO: Change to use UserMerchantTx
supplier, _ := createRandomSupplier(t) supplier, _ := createRandomSupplier(t)
product1, _ := createRandomProduct(t) product1, _ := createRandomProduct(t)
product2, _ := createRandomProduct(t) product2, _ := createRandomProduct(t)
@ -50,8 +51,8 @@ func TestPurchaseOrder(t *testing.T) {
for i := 0; i < tries; i++ { for i := 0; i < tries; i++ {
go func() { go func() {
result, err := store.PurchaseOrderTx(context.Background(), PurchasoOrderTxParams{ result, err := store.PurchaseOrderTx(context.Background(), PurchasoOrderTxParams{
MerchantID: supplier.MerchantID, MerchantID: supplier.MerchantID, // GET FROM UserMerchantTx
CreatedBy: user.ID, CreatedBy: user.ID, // TODO: GET FROM UserMerchantTx
SupplierID: supplier.ID, SupplierID: supplier.ID,
Code: sql.NullString{Valid: true, String: util.RandomTransactionCode("P", util.RandomInt(1, 10))}, Code: sql.NullString{Valid: true, String: util.RandomTransactionCode("P", util.RandomInt(1, 10))},
IsPaid: true, IsPaid: true,
@ -80,6 +81,82 @@ func TestPurchaseOrder(t *testing.T) {
} }
} }
func TestCreateSaleOrder(t *testing.T) {
var products []SaleOrderProduct
store := NewStore(testDB)
user := createRandomUser(t)
customer, _ := createRandomCustomer(t)
product1, _ := createRandomProduct(t)
product2, _ := createRandomProduct(t)
errs := make(chan error)
results := make(chan SaleOrderTxResult)
saleProducts1Quantity := util.RandomFloat(1, 99)
saleProducts1Price := util.RandomFloat(999, 9999)
saleProducts1SubTotal := saleProducts1Price * saleProducts1Quantity
saleProducts1Profit := saleProducts1SubTotal - saleProducts1Quantity*product1.SellingPrice
saleProducts2Quantity := util.RandomFloat(1, 99)
saleProducts2Price := util.RandomFloat(999, 9999)
saleProducts2SubTotal := saleProducts2Price * saleProducts2Quantity
saleProducts2Profit := saleProducts2SubTotal - saleProducts2Quantity*product2.SellingPrice
saleProducts_1 := SaleOrderProduct{
ProductID: product1.ID,
Quantity: saleProducts1Quantity,
Sub_total: saleProducts1SubTotal,
Price: saleProducts1Price,
ProductName: product1.Name,
Profit: saleProducts1Profit,
}
saleProducts_2 := SaleOrderProduct{
ProductID: product2.ID,
Quantity: saleProducts2Quantity,
Sub_total: saleProducts2SubTotal,
Price: saleProducts2Price,
ProductName: product2.Name,
Profit: saleProducts2Profit,
}
products = append(products, saleProducts_1, saleProducts_2)
tries := 3
for i := 0; i < tries; i++ {
go func() {
result, err := store.SaleOrderTx(context.Background(), SaleOrderTxParams{
MerchantID: customer.MerchantID,
CreatedBy: user.ID,
CustomerID: uuid.NullUUID{Valid: true, UUID: customer.ID},
Code: util.RandomTransactionCode("S", util.RandomInt(1, 10)),
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 < tries; i++ {
err := <-errs
require.NoError(t, err)
result := <-results
require.NotEmpty(t, result)
saleOrder := result.SaleOrder
require.NotEmpty(t, saleOrder)
require.Equal(t, saleOrder.MerchantID, customer.MerchantID) // TODO: Change to use UserMerchantTx result
}
}
func TestCreateUserMerchant(t *testing.T) { func TestCreateUserMerchant(t *testing.T) {
store := NewStore(testDB) store := NewStore(testDB)

View File

@ -11,7 +11,7 @@ import (
func createRandomSupplier(t *testing.T) (Supplier, CreateSuppliersParams) { func createRandomSupplier(t *testing.T) (Supplier, CreateSuppliersParams) {
arg := CreateSuppliersParams{ arg := CreateSuppliersParams{
MerchantID: uuid.MustParse("04a1b0a7-69b4-41da-a053-2f6b95c93195"), MerchantID: uuid.MustParse("54b8a2d9-16be-4239-8828-5daa317028dc"),
Name: util.RandomString(10), Name: util.RandomString(10),
} }

8
util/validate.go Normal file
View File

@ -0,0 +1,8 @@
package util
import "github.com/google/uuid"
func IsValidUUID(u string) bool {
_, err := uuid.Parse(u)
return err == nil
}