add sale order
This commit is contained in:
parent
2de83644b8
commit
5604f34e97
@ -21,27 +21,27 @@ func authMiddleware(tokenMaker token.Maker) gin.HandlerFunc {
|
||||
authorizationHeader := ctx.GetHeader(authorizationHeaderKey)
|
||||
if len(authorizationHeader) == 0 {
|
||||
err := errors.New("authorization header is not provided")
|
||||
ctx.AbortWithStatusJSON(http.StatusUnauthorized, errorResponse(err))
|
||||
ctx.AbortWithStatusJSON(http.StatusUnauthorized, errorResponse(err, ""))
|
||||
return
|
||||
}
|
||||
|
||||
fields := strings.Fields(authorizationHeader)
|
||||
if len(fields) < 2 {
|
||||
err := errors.New("Invalid authorization header format")
|
||||
ctx.AbortWithStatusJSON(http.StatusUnauthorized, errorResponse(err))
|
||||
ctx.AbortWithStatusJSON(http.StatusUnauthorized, errorResponse(err, ""))
|
||||
return
|
||||
}
|
||||
|
||||
authorizationType := strings.ToLower(fields[0])
|
||||
if authorizationType != authorizationTypeBearer {
|
||||
err := fmt.Errorf("Authorization only accept bearer type")
|
||||
ctx.AbortWithStatusJSON(http.StatusUnauthorized, errorResponse(err))
|
||||
ctx.AbortWithStatusJSON(http.StatusUnauthorized, errorResponse(err, ""))
|
||||
}
|
||||
|
||||
accessToken := fields[1]
|
||||
payload, err := tokenMaker.VerifyToken(accessToken)
|
||||
if err != nil {
|
||||
ctx.AbortWithStatusJSON(http.StatusUnauthorized, errorResponse(err))
|
||||
ctx.AbortWithStatusJSON(http.StatusUnauthorized, errorResponse(err, ""))
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -24,7 +24,7 @@ func (server *Server) createProduct(ctx *gin.Context) {
|
||||
var req createProductRequest
|
||||
|
||||
if err := ctx.ShouldBindJSON(&req); err != nil {
|
||||
ctx.JSON(http.StatusBadRequest, errorResponse(err))
|
||||
ctx.JSON(http.StatusBadRequest, errorResponse(err, ""))
|
||||
return
|
||||
}
|
||||
|
||||
@ -40,7 +40,7 @@ func (server *Server) createProduct(ctx *gin.Context) {
|
||||
|
||||
product, err := server.store.CreateProduct(ctx, arg)
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, errorResponse(err))
|
||||
ctx.JSON(http.StatusInternalServerError, errorResponse(err, ""))
|
||||
return
|
||||
}
|
||||
|
||||
@ -54,24 +54,24 @@ type getProductRequest struct {
|
||||
func (server *Server) getProduct(ctx *gin.Context) {
|
||||
var req getProductRequest
|
||||
if err := ctx.ShouldBindUri(&req); err != nil {
|
||||
ctx.JSON(http.StatusBadRequest, errorResponse(err))
|
||||
ctx.JSON(http.StatusBadRequest, errorResponse(err, ""))
|
||||
return
|
||||
}
|
||||
|
||||
product, err := server.store.GetProduct(ctx, uuid.MustParse(req.ID))
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
ctx.JSON(http.StatusNotFound, errorResponse(err))
|
||||
ctx.JSON(http.StatusNotFound, errorResponse(err, ""))
|
||||
return
|
||||
}
|
||||
ctx.JSON(http.StatusInternalServerError, errorResponse(err))
|
||||
ctx.JSON(http.StatusInternalServerError, errorResponse(err, ""))
|
||||
return
|
||||
}
|
||||
|
||||
authPayload := ctx.MustGet(authorizationPayloadKey).(*token.Payload)
|
||||
if product.MerchantID != authPayload.MerchantID {
|
||||
err := errors.New("Product doesn't belong to the user")
|
||||
ctx.JSON(http.StatusUnauthorized, errorResponse(err))
|
||||
ctx.JSON(http.StatusUnauthorized, errorResponse(err, ""))
|
||||
return
|
||||
}
|
||||
|
||||
@ -86,7 +86,7 @@ type listProductsRequest struct {
|
||||
func (server *Server) listProducts(ctx *gin.Context) {
|
||||
var req listProductsRequest
|
||||
if err := ctx.ShouldBindQuery(&req); err != nil {
|
||||
ctx.JSON(http.StatusBadRequest, errorResponse(err))
|
||||
ctx.JSON(http.StatusBadRequest, errorResponse(err, ""))
|
||||
return
|
||||
}
|
||||
|
||||
@ -99,7 +99,7 @@ func (server *Server) listProducts(ctx *gin.Context) {
|
||||
|
||||
products, err := server.store.ListProducts(ctx, arg)
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, errorResponse(err))
|
||||
ctx.JSON(http.StatusInternalServerError, errorResponse(err, ""))
|
||||
return
|
||||
}
|
||||
|
||||
@ -116,7 +116,7 @@ type updateProductRequest struct {
|
||||
func (server *Server) updateProduct(ctx *gin.Context) {
|
||||
var req updateProductRequest
|
||||
if err := ctx.ShouldBindJSON(&req); err != nil {
|
||||
ctx.JSON(http.StatusBadRequest, errorResponse(err))
|
||||
ctx.JSON(http.StatusBadRequest, errorResponse(err, ""))
|
||||
return
|
||||
}
|
||||
|
||||
@ -131,10 +131,10 @@ func (server *Server) updateProduct(ctx *gin.Context) {
|
||||
product, err := server.store.UpdateProduct(ctx, arg)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
ctx.JSON(http.StatusNotFound, errorResponse(err))
|
||||
ctx.JSON(http.StatusNotFound, errorResponse(err, ""))
|
||||
return
|
||||
}
|
||||
ctx.JSON(http.StatusInternalServerError, errorResponse(err))
|
||||
ctx.JSON(http.StatusInternalServerError, errorResponse(err, ""))
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,7 @@ func (server *Server) createProductCategory(ctx *gin.Context) {
|
||||
var req createProductCategoryRequest
|
||||
|
||||
if err := ctx.ShouldBindJSON(&req); err != nil {
|
||||
ctx.JSON(http.StatusBadRequest, errorResponse(err))
|
||||
ctx.JSON(http.StatusBadRequest, errorResponse(err, ""))
|
||||
return
|
||||
}
|
||||
|
||||
@ -31,7 +31,7 @@ func (server *Server) createProductCategory(ctx *gin.Context) {
|
||||
|
||||
ProductCategory, err := server.store.CreateProductCategory(ctx, arg)
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, errorResponse(err))
|
||||
ctx.JSON(http.StatusInternalServerError, errorResponse(err, ""))
|
||||
return
|
||||
}
|
||||
|
||||
@ -46,7 +46,7 @@ type listProductCategoriesRequest struct {
|
||||
func (server *Server) listProductCategories(ctx *gin.Context) {
|
||||
var req listProductCategoriesRequest
|
||||
if err := ctx.ShouldBindQuery(&req); err != nil {
|
||||
ctx.JSON(http.StatusBadRequest, errorResponse(err))
|
||||
ctx.JSON(http.StatusBadRequest, errorResponse(err, ""))
|
||||
return
|
||||
}
|
||||
|
||||
@ -59,7 +59,7 @@ func (server *Server) listProductCategories(ctx *gin.Context) {
|
||||
|
||||
ProductCategories, err := server.store.ListProductCategoriesByMerchantID(ctx, arg)
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, errorResponse(err))
|
||||
ctx.JSON(http.StatusInternalServerError, errorResponse(err, ""))
|
||||
return
|
||||
}
|
||||
|
||||
@ -74,7 +74,7 @@ type updateProductCategoryRequest struct {
|
||||
func (server *Server) updateProductCategory(ctx *gin.Context) {
|
||||
var req updateProductCategoryRequest
|
||||
if err := ctx.ShouldBindJSON(&req); err != nil {
|
||||
ctx.JSON(http.StatusBadRequest, errorResponse(err))
|
||||
ctx.JSON(http.StatusBadRequest, errorResponse(err, ""))
|
||||
return
|
||||
}
|
||||
|
||||
@ -86,10 +86,10 @@ func (server *Server) updateProductCategory(ctx *gin.Context) {
|
||||
ProductCategory, err := server.store.UpdateProductCategory(ctx, arg)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
ctx.JSON(http.StatusNotFound, errorResponse(err))
|
||||
ctx.JSON(http.StatusNotFound, errorResponse(err, ""))
|
||||
return
|
||||
}
|
||||
ctx.JSON(http.StatusInternalServerError, errorResponse(err))
|
||||
ctx.JSON(http.StatusInternalServerError, errorResponse(err, ""))
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -35,11 +35,11 @@ func TestCreateProductCategory(t *testing.T) {
|
||||
"merchant_id": productCategory.MerchantID,
|
||||
},
|
||||
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) {
|
||||
arg := db.CreateProductCategoryParams{
|
||||
MerchantID: uuid.MustParse("a848090f-0409-4386-9caa-929ae6874dbb"),
|
||||
MerchantID: uuid.MustParse("54b8a2d9-16be-4239-8828-5daa317028dc"),
|
||||
Name: productCategory.Name,
|
||||
}
|
||||
store.EXPECT().
|
||||
@ -83,7 +83,7 @@ func TestCreateProductCategory(t *testing.T) {
|
||||
func createRandomProductCategory() db.ProductCategory {
|
||||
return db.ProductCategory{
|
||||
ID: uuid.New(),
|
||||
MerchantID: uuid.MustParse("a848090f-0409-4386-9caa-929ae6874dbb"),
|
||||
MerchantID: uuid.MustParse("54b8a2d9-16be-4239-8828-5daa317028dc"),
|
||||
Name: util.RandomString(5),
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var MERCHANTID = "a848090f-0409-4386-9caa-929ae6874dbb"
|
||||
var MERCHANTID = "54b8a2d9-16be-4239-8828-5daa317028dc"
|
||||
|
||||
func TestGetProductApi(t *testing.T) {
|
||||
product := randomProduct(MERCHANTID)
|
||||
|
@ -34,7 +34,7 @@ func (server Server) createPurchase(ctx *gin.Context) {
|
||||
}
|
||||
|
||||
if err := ctx.ShouldBindJSON(&req); err != nil {
|
||||
ctx.JSON(http.StatusBadRequest, errorResponse(err))
|
||||
ctx.JSON(http.StatusBadRequest, errorResponse(err, ""))
|
||||
return
|
||||
}
|
||||
|
||||
@ -52,7 +52,7 @@ func (server Server) createPurchase(ctx *gin.Context) {
|
||||
|
||||
result, err := server.store.PurchaseOrderTx(ctx, arg)
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, errorResponse(err))
|
||||
ctx.JSON(http.StatusInternalServerError, errorResponse(err, ""))
|
||||
return
|
||||
}
|
||||
|
||||
|
78
api/sale_order.go
Normal file
78
api/sale_order.go
Normal 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)
|
||||
}
|
@ -42,6 +42,7 @@ func (server *Server) getRoutes() {
|
||||
|
||||
apiRoutes.POST("/products", server.createProduct)
|
||||
apiRoutes.PATCH("/products", server.updateProduct)
|
||||
apiRoutes.GET("/products", server.listProducts)
|
||||
apiRoutes.GET("/product/:id", server.getProduct)
|
||||
|
||||
apiRoutes.POST("/product/category", server.createProductCategory)
|
||||
@ -53,6 +54,8 @@ func (server *Server) getRoutes() {
|
||||
|
||||
apiRoutes.POST("/purchase-products", server.createPurchase)
|
||||
|
||||
apiRoutes.POST("/sale-order", server.createSaleOrder)
|
||||
|
||||
server.router = router
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,7 @@ func (server *Server) createSupplier(ctx *gin.Context) {
|
||||
var supDetail pqtype.NullRawMessage
|
||||
|
||||
if err := ctx.ShouldBindJSON(&req); err != nil {
|
||||
ctx.JSON(http.StatusBadRequest, errorResponse(err))
|
||||
ctx.JSON(http.StatusBadRequest, errorResponse(err, ""))
|
||||
return
|
||||
}
|
||||
|
||||
@ -44,7 +44,7 @@ func (server *Server) createSupplier(ctx *gin.Context) {
|
||||
|
||||
supplier, err := server.store.CreateSuppliers(ctx, arg)
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, errorResponse(err))
|
||||
ctx.JSON(http.StatusInternalServerError, errorResponse(err, ""))
|
||||
return
|
||||
}
|
||||
|
||||
|
24
api/token.go
24
api/token.go
@ -21,63 +21,63 @@ type renewAccessResponse struct {
|
||||
func (server *Server) renewAccessToken(ctx *gin.Context) {
|
||||
var req renewAccessRequest
|
||||
if err := ctx.ShouldBindJSON(&req); err != nil {
|
||||
ctx.JSON(http.StatusBadRequest, errorResponse(err))
|
||||
ctx.JSON(http.StatusBadRequest, errorResponse(err, ""))
|
||||
return
|
||||
}
|
||||
|
||||
refreshPayload, err := server.tokenMaker.VerifyToken(req.RefreshToken)
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusUnauthorized, errorResponse(err))
|
||||
ctx.JSON(http.StatusUnauthorized, errorResponse(err, ""))
|
||||
return
|
||||
}
|
||||
|
||||
session, err := server.store.GetSession(ctx, refreshPayload.ID)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
ctx.JSON(http.StatusNotFound, errorResponse(err))
|
||||
ctx.JSON(http.StatusNotFound, errorResponse(err, ""))
|
||||
return
|
||||
}
|
||||
ctx.JSON(http.StatusInternalServerError, errorResponse(err))
|
||||
ctx.JSON(http.StatusInternalServerError, errorResponse(err, ""))
|
||||
return
|
||||
}
|
||||
|
||||
if session.IsBlocked {
|
||||
err := fmt.Errorf("blocked session")
|
||||
ctx.JSON(http.StatusUnauthorized, errorResponse(err))
|
||||
ctx.JSON(http.StatusUnauthorized, errorResponse(err, ""))
|
||||
return
|
||||
}
|
||||
|
||||
if session.Email != refreshPayload.Email {
|
||||
err := fmt.Errorf("incorrect session user")
|
||||
ctx.JSON(http.StatusUnauthorized, errorResponse(err))
|
||||
ctx.JSON(http.StatusUnauthorized, errorResponse(err, ""))
|
||||
return
|
||||
}
|
||||
|
||||
if session.RefreshToken != req.RefreshToken {
|
||||
err := fmt.Errorf("mismatched session token")
|
||||
ctx.JSON(http.StatusUnauthorized, errorResponse(err))
|
||||
ctx.JSON(http.StatusUnauthorized, errorResponse(err, ""))
|
||||
return
|
||||
}
|
||||
|
||||
if time.Now().After(refreshPayload.ExpiredAt) {
|
||||
err := fmt.Errorf("Exprired session")
|
||||
ctx.JSON(http.StatusUnauthorized, errorResponse(err))
|
||||
ctx.JSON(http.StatusUnauthorized, errorResponse(err, ""))
|
||||
return
|
||||
}
|
||||
|
||||
user, err := server.store.GetUserByEmail(ctx, refreshPayload.Email)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
ctx.JSON(http.StatusNotFound, errorResponse(err))
|
||||
ctx.JSON(http.StatusNotFound, errorResponse(err, ""))
|
||||
return
|
||||
}
|
||||
ctx.JSON(http.StatusInternalServerError, errorResponse(err))
|
||||
ctx.JSON(http.StatusInternalServerError, errorResponse(err, ""))
|
||||
return
|
||||
}
|
||||
|
||||
merchant, err := server.store.GetMerchantByUserId(ctx, user.ID)
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, errorResponse(err))
|
||||
ctx.JSON(http.StatusInternalServerError, errorResponse(err, ""))
|
||||
return
|
||||
}
|
||||
|
||||
@ -88,7 +88,7 @@ func (server *Server) renewAccessToken(ctx *gin.Context) {
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, errorResponse(err))
|
||||
ctx.JSON(http.StatusInternalServerError, errorResponse(err, ""))
|
||||
return
|
||||
}
|
||||
|
||||
|
20
api/user.go
20
api/user.go
@ -50,13 +50,13 @@ func newUserMerchantResponse(user db.GetUserByEmailRow) userMerchantResponse {
|
||||
func (server *Server) createUserMerchant(ctx *gin.Context) {
|
||||
var req createUserMerchantRequest
|
||||
if err := ctx.ShouldBindJSON(&req); err != nil {
|
||||
ctx.JSON(http.StatusBadRequest, errorResponse(err))
|
||||
ctx.JSON(http.StatusBadRequest, errorResponse(err, ""))
|
||||
return
|
||||
}
|
||||
|
||||
hashedPassword, err := util.HashPassword(req.Password)
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, errorResponse(err))
|
||||
ctx.JSON(http.StatusInternalServerError, errorResponse(err, ""))
|
||||
return
|
||||
}
|
||||
|
||||
@ -72,11 +72,11 @@ func (server *Server) createUserMerchant(ctx *gin.Context) {
|
||||
if pqErr, ok := err.(*pq.Error); ok {
|
||||
switch pqErr.Code.Name() {
|
||||
case "foreign_key_violation", "unique_violation":
|
||||
ctx.JSON(http.StatusConflict, errorResponse(err))
|
||||
ctx.JSON(http.StatusConflict, errorResponse(err, ""))
|
||||
return
|
||||
}
|
||||
}
|
||||
ctx.JSON(http.StatusInternalServerError, errorResponse(err))
|
||||
ctx.JSON(http.StatusInternalServerError, errorResponse(err, ""))
|
||||
return
|
||||
}
|
||||
|
||||
@ -117,23 +117,23 @@ type userLoginResponse struct {
|
||||
func (server *Server) loginUser(ctx *gin.Context) {
|
||||
var req userLoginRequest
|
||||
if err := ctx.ShouldBindJSON(&req); err != nil {
|
||||
ctx.JSON(http.StatusBadRequest, errorResponse(err))
|
||||
ctx.JSON(http.StatusBadRequest, errorResponse(err, ""))
|
||||
return
|
||||
}
|
||||
|
||||
user, err := server.store.GetUserByEmail(ctx, req.Email)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
ctx.JSON(http.StatusNotFound, errorResponse(err))
|
||||
ctx.JSON(http.StatusNotFound, errorResponse(err, ""))
|
||||
return
|
||||
}
|
||||
ctx.JSON(http.StatusInternalServerError, errorResponse(err))
|
||||
ctx.JSON(http.StatusInternalServerError, errorResponse(err, ""))
|
||||
return
|
||||
}
|
||||
|
||||
err = util.CheckPassword(req.Password, user.Password)
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusUnauthorized, errorResponse(err))
|
||||
ctx.JSON(http.StatusUnauthorized, errorResponse(err, ""))
|
||||
return
|
||||
}
|
||||
|
||||
@ -144,7 +144,7 @@ func (server *Server) loginUser(ctx *gin.Context) {
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, errorResponse(err))
|
||||
ctx.JSON(http.StatusInternalServerError, errorResponse(err, ""))
|
||||
return
|
||||
}
|
||||
|
||||
@ -165,7 +165,7 @@ func (server *Server) loginUser(ctx *gin.Context) {
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, errorResponse(err))
|
||||
ctx.JSON(http.StatusInternalServerError, errorResponse(err, ""))
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -44,7 +44,7 @@ CREATE TABLE products (
|
||||
"name" varchar not null,
|
||||
"selling_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()),
|
||||
"updated_at" timestamp default(now())
|
||||
);
|
||||
@ -94,6 +94,7 @@ CREATE TABLE sale_order (
|
||||
"created_at" timestamp default(now()),
|
||||
"updated_at" timestamp default(now())
|
||||
);
|
||||
|
||||
CREATE TABLE sale_order_detail (
|
||||
"id" uuid default gen_random_uuid() primary key not null,
|
||||
"index_id" bigserial not null,
|
||||
|
@ -0,0 +1 @@
|
||||
ALTER TABLE sale_order DROP COLUMN IF EXISTS is_keep;
|
@ -0,0 +1 @@
|
||||
ALTER TABLE sale_order ADD COLUMN is_keep boolean not null default false;
|
@ -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;
|
@ -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;
|
@ -127,10 +127,10 @@ func (mr *MockStoreMockRecorder) CreatePurchaseOrderDetail(arg0, arg1 interface{
|
||||
}
|
||||
|
||||
// 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()
|
||||
ret := m.ctrl.Call(m, "CreateSaleOrder", arg0, arg1)
|
||||
ret0, _ := ret[0].(db.PurchaseOrder)
|
||||
ret0, _ := ret[0].(db.SaleOrder)
|
||||
ret1, _ := ret[1].(error)
|
||||
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)
|
||||
}
|
||||
|
||||
// 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.
|
||||
func (m *MockStore) CreateSession(arg0 context.Context, arg1 db.CreateSessionParams) (db.UserSession, error) {
|
||||
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)
|
||||
}
|
||||
|
||||
// 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.
|
||||
func (m *MockStore) SuppliersList(arg0 context.Context, arg1 db.SuppliersListParams) ([]db.Supplier, error) {
|
||||
m.ctrl.T.Helper()
|
||||
|
16
db/query/sale_order.sql
Normal file
16
db/query/sale_order.sql
Normal 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 *;
|
13
db/query/sale_order_detail.sql
Normal file
13
db/query/sale_order_detail.sql
Normal 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
34
db/sqlc/customer_test.go
Normal 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)
|
||||
|
||||
}
|
@ -128,16 +128,18 @@ type PurchaseOrderDetail struct {
|
||||
type SaleOrder struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
IndexID int64 `json:"index_id"`
|
||||
Code sql.NullString `json:"code"`
|
||||
Code string `json:"code"`
|
||||
CreatedBy uuid.UUID `json:"created_by"`
|
||||
MerchantID uuid.UUID `json:"merchant_id"`
|
||||
CustomerID uuid.NullUUID `json:"customer_id"`
|
||||
IsPaid sql.NullBool `json:"is_paid"`
|
||||
IsPaid bool `json:"is_paid"`
|
||||
Total float64 `json:"total"`
|
||||
PaidNominal float64 `json:"paid_nominal"`
|
||||
Note sql.NullString `json:"note"`
|
||||
CreatedAt sql.NullTime `json:"created_at"`
|
||||
UpdatedAt sql.NullTime `json:"updated_at"`
|
||||
IsKeep bool `json:"is_keep"`
|
||||
Change float64 `json:"change"`
|
||||
}
|
||||
|
||||
type SaleOrderDetail struct {
|
||||
|
@ -9,7 +9,7 @@ import (
|
||||
"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 {
|
||||
arg := CreateProductCategoryParams{
|
||||
|
@ -19,7 +19,7 @@ func createRandomProduct(t *testing.T) (Product, CreateProductParams) {
|
||||
productCategory := createRandomProductCategory(t)
|
||||
|
||||
arg := CreateProductParams{
|
||||
MerchantID: uuid.MustParse("04a1b0a7-69b4-41da-a053-2f6b95c93195"),
|
||||
MerchantID: uuid.MustParse("54b8a2d9-16be-4239-8828-5daa317028dc"),
|
||||
Name: util.RandomString(10),
|
||||
SellingPrice: sellingPrice,
|
||||
PurchasePrice: purchasePrice,
|
||||
|
@ -17,7 +17,8 @@ type Querier interface {
|
||||
CreateProductCategory(ctx context.Context, arg CreateProductCategoryParams) (ProductCategory, error)
|
||||
CreatePurchaseOrder(ctx context.Context, arg CreatePurchaseOrderParams) (PurchaseOrder, 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)
|
||||
CreateStockLogs(ctx context.Context, arg CreateStockLogsParams) (StockLog, error)
|
||||
CreateSuppliers(ctx context.Context, arg CreateSuppliersParams) (Supplier, error)
|
||||
|
77
db/sqlc/sale_order.sql.go
Normal file
77
db/sqlc/sale_order.sql.go
Normal 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
|
||||
}
|
64
db/sqlc/sale_order_detail.sql.go
Normal file
64
db/sqlc/sale_order_detail.sql.go
Normal 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
|
||||
}
|
119
db/sqlc/store.go
119
db/sqlc/store.go
@ -13,6 +13,7 @@ type Store interface {
|
||||
Querier
|
||||
PurchaseOrderTx(ctx context.Context, arg PurchasoOrderTxParams) (PurchaseOrderTxResult, error)
|
||||
CreateUserMerchantTx(ctx context.Context, arg UserMerchantTxParams) (UserMerchantTxResult, error)
|
||||
SaleOrderTx(ctx context.Context, arg SaleOrderTxParams) (SaleOrderTxResult, error)
|
||||
}
|
||||
|
||||
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 {
|
||||
ProductID uuid.UUID `json:"product_id"`
|
||||
Quantity float64 `json:"quantity"`
|
||||
@ -127,11 +130,11 @@ func (store *SQLStore) PurchaseOrderTx(ctx context.Context, arg PurchasoOrderTxP
|
||||
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),
|
||||
TransactionActionType: "purchase_order",
|
||||
TransactionDescription: fmt.Sprintf("Penambahan stok produk %s dari pembelian dengan code %s", product.Name, arg.Code.String),
|
||||
Type: util.STOCK_LOG_IN,
|
||||
SellingPrice: product.SellingPrice,
|
||||
PurchasePrice: product.PurchasePrice,
|
||||
PurchasePrice: arg.Products[i].Price,
|
||||
Quantity: arg.Products[i].Quantity,
|
||||
})
|
||||
|
||||
@ -147,6 +150,116 @@ func (store *SQLStore) PurchaseOrderTx(ctx context.Context, arg PurchasoOrderTxP
|
||||
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 {
|
||||
Email string `json:"email"`
|
||||
Fullname string `json:"fullname"`
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"git.nochill.in/nochill/naice_pos/util"
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@ -13,7 +14,7 @@ func TestPurchaseOrder(t *testing.T) {
|
||||
var products []PurchaseOrderProduct
|
||||
|
||||
store := NewStore(testDB)
|
||||
user := createRandomUser(t)
|
||||
user := createRandomUser(t) // TODO: Change to use UserMerchantTx
|
||||
supplier, _ := createRandomSupplier(t)
|
||||
product1, _ := createRandomProduct(t)
|
||||
product2, _ := createRandomProduct(t)
|
||||
@ -50,8 +51,8 @@ func TestPurchaseOrder(t *testing.T) {
|
||||
for i := 0; i < tries; i++ {
|
||||
go func() {
|
||||
result, err := store.PurchaseOrderTx(context.Background(), PurchasoOrderTxParams{
|
||||
MerchantID: supplier.MerchantID,
|
||||
CreatedBy: user.ID,
|
||||
MerchantID: supplier.MerchantID, // GET FROM UserMerchantTx
|
||||
CreatedBy: user.ID, // TODO: GET FROM UserMerchantTx
|
||||
SupplierID: supplier.ID,
|
||||
Code: sql.NullString{Valid: true, String: util.RandomTransactionCode("P", util.RandomInt(1, 10))},
|
||||
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) {
|
||||
store := NewStore(testDB)
|
||||
|
||||
|
@ -11,7 +11,7 @@ import (
|
||||
|
||||
func createRandomSupplier(t *testing.T) (Supplier, CreateSuppliersParams) {
|
||||
arg := CreateSuppliersParams{
|
||||
MerchantID: uuid.MustParse("04a1b0a7-69b4-41da-a053-2f6b95c93195"),
|
||||
MerchantID: uuid.MustParse("54b8a2d9-16be-4239-8828-5daa317028dc"),
|
||||
Name: util.RandomString(10),
|
||||
}
|
||||
|
||||
|
8
util/validate.go
Normal file
8
util/validate.go
Normal file
@ -0,0 +1,8 @@
|
||||
package util
|
||||
|
||||
import "github.com/google/uuid"
|
||||
|
||||
func IsValidUUID(u string) bool {
|
||||
_, err := uuid.Parse(u)
|
||||
return err == nil
|
||||
}
|
Loading…
Reference in New Issue
Block a user