Add refund order

This commit is contained in:
aditya.siregar 2025-06-14 21:17:13 +07:00
parent 58d3b32c40
commit ebb33186b8
42 changed files with 1152 additions and 2094 deletions

View File

@ -0,0 +1,27 @@
package entity
import "time"
type CashierSession struct {
ID int64
CashierID int64
OpenedAt time.Time
ClosedAt *time.Time
OpeningAmount float64
ClosingAmount *float64
ExpectedAmount *float64
Status string
}
type PaymentSummary struct {
PaymentType string
PaymentProvider string
TotalAmount float64
}
type CashierSessionReport struct {
SessionID int64
ExpectedAmount float64
ClosingAmount float64
Payments []PaymentSummary
}

View File

@ -0,0 +1,9 @@
package entity
type Category struct {
ID int64
PartnerID int64
Name string
CreatedAt int64
UpdatedAt int64
}

View File

@ -26,6 +26,7 @@ type Order struct {
User User `gorm:"foreignKey:CreatedBy;constraint:OnDelete:CASCADE;"` User User `gorm:"foreignKey:CreatedBy;constraint:OnDelete:CASCADE;"`
Source string `gorm:"type:varchar;column:source"` Source string `gorm:"type:varchar;column:source"`
OrderType string `gorm:"type:varchar;column:order_type"` OrderType string `gorm:"type:varchar;column:order_type"`
CashierSessionID int64 `gorm:"type:varchar;column:cashier_session_id"`
TableNumber string TableNumber string
InProgressOrderID int64 InProgressOrderID int64
} }
@ -113,6 +114,7 @@ type OrderRequest struct {
PaymentProvider string PaymentProvider string
OrderType string OrderType string
ID int64 ID int64
CashierSessionID int64
} }
type OrderItemRequest struct { type OrderItemRequest struct {

View File

@ -26,6 +26,7 @@ type OrderInquiry struct {
PaymentProvider string `json:"payment_provider"` PaymentProvider string `json:"payment_provider"`
TableNumber string `json:"table_number"` TableNumber string `json:"table_number"`
OrderType string `json:"order_type"` OrderType string `json:"order_type"`
CashierSessionID int64 `json:"cashier_session_id"`
} }
type OrderCalculation struct { type OrderCalculation struct {
@ -54,6 +55,7 @@ func NewOrderInquiry(
paymentProvider string, paymentProvider string,
tableNumber string, tableNumber string,
orderType string, orderType string,
cashierSessionID int64,
) *OrderInquiry { ) *OrderInquiry {
return &OrderInquiry{ return &OrderInquiry{
ID: constants.GenerateUUID(), ID: constants.GenerateUUID(),
@ -75,6 +77,7 @@ func NewOrderInquiry(
PaymentProvider: paymentProvider, PaymentProvider: paymentProvider,
TableNumber: tableNumber, TableNumber: tableNumber,
OrderType: orderType, OrderType: orderType,
CashierSessionID: cashierSessionID,
} }
} }
@ -95,22 +98,23 @@ func (i *OrderInquiry) ToOrder(paymentMethod, paymentProvider string) *Order {
now := time.Now() now := time.Now()
order := &Order{ order := &Order{
PartnerID: i.PartnerID, PartnerID: i.PartnerID,
CustomerID: &i.CustomerID, CustomerID: &i.CustomerID,
InquiryID: &i.ID, InquiryID: &i.ID,
Status: constants.StatusPaid, Status: constants.StatusPaid,
Amount: i.Amount, Amount: i.Amount,
Tax: i.Tax, Tax: i.Tax,
Total: i.Total, Total: i.Total,
PaymentType: paymentMethod, PaymentType: paymentMethod,
PaymentProvider: paymentProvider, PaymentProvider: paymentProvider,
Source: i.Source, Source: i.Source,
CreatedBy: i.CreatedBy, CreatedBy: i.CreatedBy,
CreatedAt: now, CreatedAt: now,
OrderItems: make([]OrderItem, len(i.OrderItems)), OrderItems: make([]OrderItem, len(i.OrderItems)),
OrderType: i.OrderType, OrderType: i.OrderType,
CustomerName: i.CustomerName, CustomerName: i.CustomerName,
TableNumber: i.TableNumber, TableNumber: i.TableNumber,
CashierSessionID: i.CashierSessionID,
} }
for idx, item := range i.OrderItems { for idx, item := range i.OrderItems {

View File

@ -2,23 +2,26 @@ package entity
import ( import (
"enaklo-pos-be/internal/constants/product" "enaklo-pos-be/internal/constants/product"
"enaklo-pos-be/internal/repository/models"
"time" "time"
) )
type Product struct { type Product struct {
ID int64 `gorm:"primaryKey;autoIncrement;column:id"` ID int64 `gorm:"primaryKey;autoIncrement;column:id"`
PartnerID int64 `gorm:"type:int;column:partner_id"` PartnerID int64 `gorm:"type:int;column:partner_id"`
Name string `gorm:"type:varchar(255);not null;column:name"` Name string `gorm:"type:varchar(255);not null;column:name"`
Type string `gorm:"type:varchar;column:type"` Type string `gorm:"type:varchar;column:type"`
Price float64 `gorm:"type:decimal;column:price"` Price float64 `gorm:"type:decimal;column:price"`
Status string `gorm:"type:varchar;column:status"` Status string `gorm:"type:varchar;column:status"`
Description string `gorm:"type:varchar(255);not null;column:description"` Description string `gorm:"type:varchar(255);not null;column:description"`
CreatedAt time.Time `gorm:"autoCreateTime;column:created_at"` CreatedAt time.Time `gorm:"autoCreateTime;column:created_at"`
UpdatedAt time.Time `gorm:"autoUpdateTime;column:updated_at"` UpdatedAt time.Time `gorm:"autoUpdateTime;column:updated_at"`
DeletedAt *time.Time `gorm:"column:deleted_at"` DeletedAt *time.Time `gorm:"column:deleted_at"`
CreatedBy int64 `gorm:"type:int;column:created_by"` CreatedBy int64 `gorm:"type:int;column:created_by"`
UpdatedBy int64 `gorm:"type:int;column:updated_by"` UpdatedBy int64 `gorm:"type:int;column:updated_by"`
Image string `gorm:"type:varchar;column:image"` Image string `gorm:"type:varchar;column:image"`
CategoryID *int64 `gorm:"column:category_id"`
Category *models.CategoryDB `gorm:"foreignKey:CategoryID;references:ID"`
} }
func (Product) TableName() string { func (Product) TableName() string {
@ -26,14 +29,15 @@ func (Product) TableName() string {
} }
type ProductSearch struct { type ProductSearch struct {
Search string Search string
Name string Name string
Type product.ProductType Type product.ProductType
BranchID int64 BranchID int64
PartnerID int64 PartnerID int64
Available product.ProductStock Available product.ProductStock
Limit int Limit int
Offset int Offset int
CategoryID int64
} }
type ProductPOS struct { type ProductPOS struct {
@ -72,6 +76,8 @@ func (e *ProductDB) ToProduct() *Product {
CreatedBy: e.CreatedBy, CreatedBy: e.CreatedBy,
UpdatedBy: e.UpdatedBy, UpdatedBy: e.UpdatedBy,
Image: e.Image, Image: e.Image,
Category: e.Category,
CategoryID: e.CategoryID,
} }
} }

View File

@ -0,0 +1,123 @@
package http
import (
"enaklo-pos-be/internal/common/errors"
"enaklo-pos-be/internal/handlers/request"
"enaklo-pos-be/internal/handlers/response"
"enaklo-pos-be/internal/services/v2/cashier_session"
"github.com/gin-gonic/gin"
"net/http"
"strconv"
)
type CashierSessionHandler struct {
service cashier_session.Service
}
func NewCashierSession(service cashier_session.Service) *CashierSessionHandler {
return &CashierSessionHandler{service: service}
}
func (h *CashierSessionHandler) Route(group *gin.RouterGroup, jwt gin.HandlerFunc) {
route := group.Group("/cashier-sessions")
route.Use(jwt)
route.POST("/open", h.OpenSession)
route.POST("/close/:id", h.CloseSession)
route.GET("/open", h.GetOpenSession)
route.GET("/report/:id", h.GetSessionReport)
}
func (h *CashierSessionHandler) OpenSession(c *gin.Context) {
ctx := request.GetMyContext(c)
var req request.OpenCashierSessionRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.ErrorWrapper(c, errors.ErrorBadRequest)
return
}
session, err := h.service.OpenSession(ctx, req.ToEntity(ctx.RequestedBy()))
if err != nil {
response.ErrorWrapper(c, err)
return
}
c.JSON(http.StatusOK, response.BaseResponse{
Success: true,
Status: http.StatusOK,
Data: response.MapToCashierSessionResponse(session),
})
}
func (h *CashierSessionHandler) CloseSession(c *gin.Context) {
ctx := request.GetMyContext(c)
idStr := c.Param("id")
sessionID, err := strconv.ParseInt(idStr, 10, 64)
if err != nil {
response.ErrorWrapper(c, errors.ErrorBadRequest)
return
}
var body struct {
ClosingAmount float64 `json:"closing_amount"`
}
if err := c.ShouldBindJSON(&body); err != nil {
response.ErrorWrapper(c, errors.ErrorBadRequest)
return
}
report, err := h.service.CloseSession(ctx, sessionID, body.ClosingAmount)
if err != nil {
response.ErrorWrapper(c, err)
return
}
c.JSON(http.StatusOK, response.BaseResponse{
Success: true,
Status: http.StatusOK,
Data: response.MapToCashierSessionReport(report),
})
}
func (h *CashierSessionHandler) GetOpenSession(c *gin.Context) {
ctx := request.GetMyContext(c)
cashierID := ctx.RequestedBy()
session, err := h.service.GetOpenSession(ctx, cashierID)
if err != nil {
response.ErrorWrapper(c, err)
return
}
c.JSON(http.StatusOK, response.BaseResponse{
Success: true,
Status: http.StatusOK,
Data: response.MapToCashierSessionResponse(session),
})
}
func (h *CashierSessionHandler) GetSessionReport(c *gin.Context) {
ctx := request.GetMyContext(c)
idStr := c.Param("id")
sessionID, err := strconv.ParseInt(idStr, 10, 64)
if err != nil {
response.ErrorWrapper(c, errors.ErrorBadRequest)
return
}
report, err := h.service.GetSessionReport(ctx, sessionID)
if err != nil {
response.ErrorWrapper(c, err)
return
}
c.JSON(http.StatusOK, response.BaseResponse{
Success: true,
Status: http.StatusOK,
Data: response.MapToCashierSessionReport(report),
})
}

View File

@ -0,0 +1,139 @@
package http
import (
"enaklo-pos-be/internal/common/errors"
"enaklo-pos-be/internal/handlers/request"
"enaklo-pos-be/internal/handlers/response"
category "enaklo-pos-be/internal/services/v2/categories"
"github.com/gin-gonic/gin"
"net/http"
"strconv"
)
type CategoryHandler struct {
service category.Service
}
func NewCategoryHandler(service category.Service) *CategoryHandler {
return &CategoryHandler{service: service}
}
func (h *CategoryHandler) Route(group *gin.RouterGroup, jwt gin.HandlerFunc) {
route := group.Group("/categories")
route.Use(jwt)
route.POST("/create", h.Create)
route.GET("/list", h.GetByPartner)
route.GET("/:id", h.GetByID)
route.PUT("/:id", h.Update)
route.DELETE("/:id", h.Delete)
}
func (h *CategoryHandler) Create(c *gin.Context) {
ctx := request.GetMyContext(c)
var req request.CategoryRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.ErrorWrapper(c, errors.ErrorBadRequest)
return
}
category, err := h.service.Create(ctx, req.ToEntity(ctx.RequestedBy()))
if err != nil {
response.ErrorWrapper(c, err)
return
}
c.JSON(http.StatusOK, response.BaseResponse{
Success: true,
Status: http.StatusOK,
Data: response.MapToCategoryResponse(category),
})
}
func (h *CategoryHandler) GetByPartner(c *gin.Context) {
ctx := request.GetMyContext(c)
categories, err := h.service.GetByPartnerID(ctx, ctx.RequestedBy())
if err != nil {
response.ErrorWrapper(c, err)
return
}
c.JSON(http.StatusOK, response.BaseResponse{
Success: true,
Status: http.StatusOK,
Data: response.MapToCategoryListResponse(categories),
})
}
func (h *CategoryHandler) GetByID(c *gin.Context) {
ctx := request.GetMyContext(c)
id, err := strconv.ParseInt(c.Param("id"), 10, 64)
if err != nil {
response.ErrorWrapper(c, errors.ErrorBadRequest)
return
}
category, err := h.service.GetByID(ctx, id)
if err != nil {
response.ErrorWrapper(c, err)
return
}
c.JSON(http.StatusOK, response.BaseResponse{
Success: true,
Status: http.StatusOK,
Data: response.MapToCategoryResponse(category),
})
}
func (h *CategoryHandler) Update(c *gin.Context) {
ctx := request.GetMyContext(c)
id, err := strconv.ParseInt(c.Param("id"), 10, 64)
if err != nil {
response.ErrorWrapper(c, errors.ErrorBadRequest)
return
}
var req request.CategoryRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.ErrorWrapper(c, errors.ErrorBadRequest)
return
}
category := req.ToEntity(ctx.RequestedBy())
category.ID = id
if err := h.service.Update(ctx, category); err != nil {
response.ErrorWrapper(c, err)
return
}
c.JSON(http.StatusOK, response.BaseResponse{
Success: true,
Status: http.StatusOK,
})
}
func (h *CategoryHandler) Delete(c *gin.Context) {
ctx := request.GetMyContext(c)
id, err := strconv.ParseInt(c.Param("id"), 10, 64)
if err != nil {
response.ErrorWrapper(c, errors.ErrorBadRequest)
return
}
if err := h.service.Delete(ctx, id); err != nil {
response.ErrorWrapper(c, err)
return
}
c.JSON(http.StatusOK, response.BaseResponse{
Success: true,
Status: http.StatusOK,
})
}

View File

@ -1,268 +0,0 @@
package customerorder
import (
"enaklo-pos-be/internal/common/errors"
"enaklo-pos-be/internal/entity"
"enaklo-pos-be/internal/handlers/request"
"enaklo-pos-be/internal/handlers/response"
"enaklo-pos-be/internal/services"
"encoding/json"
"net/http"
"time"
"github.com/gin-gonic/gin"
"github.com/go-playground/validator/v10"
)
type Handler struct {
service services.Order
}
func (h *Handler) Route(group *gin.RouterGroup, jwt gin.HandlerFunc) {
route := group.Group("/order")
route.POST("/inquiry", h.Inquiry)
route.POST("/execute", jwt, h.Execute)
route.GET("/history", jwt, h.History)
route.GET("/detail", jwt, h.Detail)
}
func NewHandler(service services.Order) *Handler {
return &Handler{
service: service,
}
}
func (h *Handler) Inquiry(c *gin.Context) {
ctx := request.GetMyContext(c)
var req request.CustomerOrder
if err := c.ShouldBindJSON(&req); err != nil {
response.ErrorWrapper(c, errors.ErrorBadRequest)
return
}
if err := request.ValidateAndHandleError(req); err != nil {
response.ErrorWrapper(c, errors.NewErrorMessage(errors.ErrorBadRequest, err.Error()))
return
}
order, err := h.service.CreateOrder(ctx, req.ToEntity(ctx.RequestedBy()))
if err != nil {
response.ErrorWrapper(c, err)
return
}
c.JSON(http.StatusOK, response.BaseResponse{
Success: true,
Status: http.StatusOK,
Data: MapOrderToCreateOrderResponse(order, req),
})
}
func (h *Handler) Execute(c *gin.Context) {
ctx := request.GetMyContext(c)
var req request.Execute
if err := c.ShouldBindJSON(&req); err != nil {
response.ErrorWrapper(c, errors.ErrorBadRequest)
return
}
validate := validator.New()
if err := validate.Struct(req); err != nil {
response.ErrorWrapper(c, err)
return
}
order, err := h.service.Execute(ctx, req.ToOrderExecuteRequest(ctx.RequestedBy()))
if err != nil {
response.ErrorWrapper(c, err)
return
}
c.JSON(http.StatusOK, response.BaseResponse{
Success: true,
Status: http.StatusOK,
Data: MapOrderToExecuteOrderResponse(order),
})
}
func MapOrderToCreateOrderResponse(orderResponse *entity.OrderResponse, req request.CustomerOrder) response.CreateOrderResponse {
order := orderResponse.Order
orderItems := make([]response.CreateOrderItemResponse, len(order.OrderItems))
for i, item := range order.OrderItems {
orderItems[i] = response.CreateOrderItemResponse{
ID: item.ID,
ItemID: item.ItemID,
Quantity: item.Quantity,
Price: item.Price,
Name: item.Product.Name,
}
}
return response.CreateOrderResponse{
ID: order.ID,
PartnerID: order.PartnerID,
Status: order.Status,
Amount: order.Amount,
PaymentType: order.PaymentType,
CreatedAt: order.CreatedAt,
OrderItems: orderItems,
Tax: order.Tax,
Total: order.Total,
}
}
func MapOrderToExecuteOrderResponse(orderResponse *entity.ExecuteOrderResponse) response.ExecuteOrderResponse {
order := orderResponse.Order
orderItems := make([]response.CreateOrderItemResponse, len(order.OrderItems))
for i, item := range order.OrderItems {
orderItems[i] = response.CreateOrderItemResponse{
ID: item.ID,
ItemID: item.ItemID,
Quantity: item.Quantity,
Price: item.Price,
Name: item.Product.Name,
}
}
return response.ExecuteOrderResponse{
ID: order.ID,
PartnerID: order.PartnerID,
Status: order.Status,
Amount: order.Amount,
PaymentType: order.PaymentType,
CreatedAt: order.CreatedAt,
OrderItems: orderItems,
PaymentToken: orderResponse.PaymentToken,
RedirectURL: orderResponse.RedirectURL,
QRcode: orderResponse.QRCode,
VirtualAccount: orderResponse.VirtualAccount,
BankName: orderResponse.BankName,
BankCode: orderResponse.BankCode,
}
}
func (h *Handler) toHistoryOrderResponse(resp *entity.HistoryOrder) response.HistoryOrder {
return response.HistoryOrder{
ID: resp.ID,
Employee: resp.Employee,
Site: resp.Site,
Timestamp: resp.Timestamp.Format(time.RFC3339),
BookingTime: resp.BookingTime.Format(time.RFC3339),
Tickets: resp.Tickets,
PaymentType: resp.PaymentType,
Status: resp.GetPaymentStatus(),
Amount: resp.Amount,
}
}
func (h *Handler) toHistoryOrderList(resp []*entity.HistoryOrder, total int64, req request.OrderParamCustomer) response.HistoryOrderList {
var orders []response.HistoryOrder
for _, b := range resp {
orders = append(orders, h.toHistoryOrderResponse(b))
}
return response.HistoryOrderList{
Orders: orders,
Total: total,
Limit: req.Limit,
Offset: req.Offset,
}
}
func (h *Handler) History(c *gin.Context) {
var req request.OrderParamCustomer
if err := c.ShouldBindQuery(&req); err != nil {
response.ErrorWrapper(c, errors.ErrorBadRequest)
return
}
ctx := request.GetMyContext(c)
orders, total, err := h.service.GetAllHistoryOrders(ctx, req.ToOrderEntity(ctx))
if err != nil {
response.ErrorWrapper(c, err)
return
}
c.JSON(http.StatusOK, response.BaseResponse{
Success: true,
Status: http.StatusOK,
Data: h.toHistoryOrderList(orders, int64(total), req),
})
}
func (h *Handler) Detail(c *gin.Context) {
var req request.OrderParamCustomer
if err := c.ShouldBindQuery(&req); err != nil {
response.ErrorWrapper(c, errors.ErrorBadRequest)
return
}
ctx := request.GetMyContext(c)
order, err := h.service.GetByID(ctx, req.ID, req.ReferenceID)
if err != nil {
response.ErrorWrapper(c, err)
return
}
c.JSON(http.StatusOK, response.BaseResponse{
Success: true,
Status: http.StatusOK,
Data: h.toOrderDetail(order),
})
}
func (h *Handler) toOrderDetail(order *entity.Order) *response.OrderDetail {
if order == nil {
return nil
}
payment := map[string]string{}
paymentLink := ""
paymentToken := ""
if order.Payment.RequestMetadata != nil && order.Status != "EXPIRED" {
json.Unmarshal(order.Payment.RequestMetadata, &payment)
paymentLink = payment["payment_redirect_url"]
paymentToken = payment["payment_token"]
}
qrCode := ""
var siteName string
if order.Site != nil {
siteName = order.Site.Name
}
orderDetail := &response.OrderDetail{
ID: order.ID,
QRCode: qrCode,
FullName: order.User.Name,
Email: order.User.Email,
PhoneNumber: order.User.PhoneNumber,
TotalAmount: order.Total,
CreatedAt: order.CreatedAt,
Status: order.Status,
PaymentLink: paymentLink,
PaymentToken: paymentToken,
SiteName: siteName,
Fee: order.Tax,
}
orderDetail.OrderItems = make([]response.OrderDetailItem, len(order.OrderItems))
for i, item := range order.OrderItems {
orderDetail.OrderItems[i] = response.OrderDetailItem{
Name: item.Product.Name,
ItemType: item.ItemType,
Description: "",
Quantity: int(item.Quantity),
UnitPrice: item.Price,
TotalPrice: float64(item.Quantity) * item.Price,
}
}
return orderDetail
}

View File

@ -1,229 +0,0 @@
package discovery
import (
"enaklo-pos-be/internal/common/errors"
"enaklo-pos-be/internal/entity"
"enaklo-pos-be/internal/handlers/request"
"enaklo-pos-be/internal/handlers/response"
"enaklo-pos-be/internal/services"
"github.com/gin-gonic/gin"
"net/http"
)
type Handler struct {
service services.DiscoverService
}
func (h *Handler) Route(group *gin.RouterGroup, jwt gin.HandlerFunc) {
route := group.Group("/discovery")
route.GET("/home", h.DisoveryHome)
route.GET("/search", h.DisoverySearch)
route.GET("/site/detail", h.DiscoveryGetByID)
route.GET("/site/products", h.DiscoveryProducts)
}
func NewHandler(service services.DiscoverService) *Handler {
return &Handler{
service: service,
}
}
func (h *Handler) DisoveryHome(c *gin.Context) {
var req request.DiscoveryHomeParam
if err := c.ShouldBindQuery(&req); err != nil {
response.ErrorWrapper(c, errors.ErrorBadRequest)
return
}
res, err := h.service.Home(c.Request.Context(), req.ToEntity())
if err != nil {
response.ErrorWrapper(c, err)
return
}
c.JSON(http.StatusOK, response.BaseResponse{
Success: true,
Status: http.StatusOK,
Data: ConvertEntityToResponse(res),
})
}
func (h *Handler) DisoverySearch(c *gin.Context) {
var req request.DiscoveryHomeParam
if err := c.ShouldBindQuery(&req); err != nil {
response.ErrorWrapper(c, errors.ErrorBadRequest)
return
}
res, total, err := h.service.Search(c.Request.Context(), req.ToEntity())
if err != nil {
response.ErrorWrapper(c, err)
return
}
c.JSON(http.StatusOK, response.BaseResponse{
Success: true,
Status: http.StatusOK,
Data: ConvertEntityToSearchResponse(res, total, req),
})
}
func (h *Handler) DiscoveryGetByID(c *gin.Context) {
ctx := request.GetMyContext(c)
var req request.DiscoverySearchByID
if err := c.ShouldBindQuery(&req); err != nil {
response.ErrorWrapper(c, errors.ErrorBadRequest)
return
}
res, err := h.service.GetByID(ctx, req.ID)
if err != nil {
response.ErrorWrapper(c, err)
return
}
c.JSON(http.StatusOK, response.BaseResponse{
Success: true,
Status: http.StatusOK,
Data: ConvertEntityToGetByIDResp(res),
})
}
func (h *Handler) DiscoveryProducts(c *gin.Context) {
ctx := request.GetMyContext(c)
var req request.DiscoverySearchByID
if err := c.ShouldBindQuery(&req); err != nil {
response.ErrorWrapper(c, errors.ErrorBadRequest)
return
}
res, err := h.service.GetProductsByID(ctx, req.ID)
if err != nil {
response.ErrorWrapper(c, err)
return
}
c.JSON(http.StatusOK, response.BaseResponse{
Success: true,
Status: http.StatusOK,
Data: ConvertToProductResp(res),
})
}
func ConvertEntityToResponse(entityResp *entity.DiscoverySearchResp) *response.ExploreResponse {
// Convert ExploreRegions
exploreRegions := make([]response.Region, len(entityResp.ExploreRegions))
for i, region := range entityResp.ExploreRegions {
exploreRegions[i] = response.Region{
Name: region.Name,
}
}
// Convert ExploreDestinations
exploreDestinations := make([]response.Destination, len(entityResp.ExploreDestinations))
for i, destination := range entityResp.ExploreDestinations {
exploreDestinations[i] = response.Destination{
Name: destination.Name,
ImageURL: destination.ImageURL,
}
}
mustVisit := make([]response.MustVisit, len(entityResp.MustVisit))
for i, mv := range entityResp.MustVisit {
mustVisit[i] = response.MustVisit{
Name: mv.Name,
Region: mv.Region,
Rating: mv.Rating,
ReviewCount: mv.ReviewCount,
Price: mv.Price,
ImageURL: mv.ImageURL,
SiteID: mv.SiteID,
Regency: mv.Regency,
}
}
return &response.ExploreResponse{
ExploreRegions: exploreRegions,
ExploreDestinations: exploreDestinations,
MustVisit: mustVisit,
}
}
func ConvertEntityToSearchResponse(entityResp *entity.DiscoverySearchResp, total int64, req request.DiscoveryHomeParam) *response.SearchResponse {
data := make([]response.SiteSeach, len(entityResp.MustVisit))
for i, mv := range entityResp.MustVisit {
data[i] = response.SiteSeach{
Name: mv.Name,
Region: mv.Region,
Rating: mv.Rating,
ReviewCount: mv.ReviewCount,
Price: mv.Price,
ImageURL: mv.ImageURL,
SiteID: mv.SiteID,
Regency: mv.Regency,
}
}
return &response.SearchResponse{
Data: data,
Total: int(total),
Limit: req.Limit,
Offset: req.Offset,
}
}
func ConvertEntityToGetByIDResp(resp *entity.Site) *response.SearchSiteByIDResponse {
if resp == nil {
return nil
}
return &response.SearchSiteByIDResponse{
ID: resp.ID,
Name: resp.Name,
Image: resp.Image,
Address: resp.Address,
LocationLink: resp.LocationLink,
Description: resp.Description,
Highlight: resp.Highlight,
ContactPerson: resp.ContactPerson,
TnC: resp.TnC,
AdditionalInfo: resp.AdditionalInfo,
PartnerID: resp.PartnerID,
Regency: resp.Regency,
Region: resp.Region,
}
}
func ConvertToProductResp(resp []*entity.Product) *response.SearchProductSiteResponse {
if resp == nil {
return nil
}
var productResp []response.SearchProductSiteByIDResponse
partnerID := int64(0)
for _, res := range resp {
productResp = append(productResp, response.SearchProductSiteByIDResponse{
ID: res.ID,
Name: res.Name,
Price: res.Price,
Description: res.Description,
Type: res.Type,
})
partnerID = res.PartnerID
}
return &response.SearchProductSiteResponse{
Product: productResp,
PartnerID: partnerID,
}
}

View File

@ -1,205 +0,0 @@
package event
import (
"net/http"
"strconv"
"time"
"github.com/gin-gonic/gin"
"enaklo-pos-be/internal/common/errors"
"enaklo-pos-be/internal/entity"
"enaklo-pos-be/internal/handlers/request"
"enaklo-pos-be/internal/handlers/response"
"enaklo-pos-be/internal/services"
)
type Handler struct {
service services.Event
}
func (h *Handler) Route(group *gin.RouterGroup, jwt gin.HandlerFunc) {
route := group.Group("/event")
route.POST("/", jwt, h.Create)
route.GET("/list", jwt, h.GetAll)
route.PUT("/:id", jwt, h.Update)
route.GET("/:id", jwt, h.GetByID)
route.DELETE("/:id", jwt, h.Delete)
}
func NewHandler(service services.Event) *Handler {
return &Handler{
service: service,
}
}
func (h *Handler) Create(c *gin.Context) {
var req request.Event
if err := c.ShouldBindJSON(&req); err != nil {
response.ErrorWrapper(c, errors.ErrorBadRequest)
return
}
if err := req.Validate(); err != nil {
response.ErrorWrapper(c, errors.ErrorInvalidRequest)
return
}
res, err := h.service.Create(c.Request.Context(), req.ToEntity())
if err != nil {
response.ErrorWrapper(c, err)
return
}
c.JSON(http.StatusOK, response.BaseResponse{
Success: true,
Status: http.StatusOK,
Data: h.toEventResponse(res),
})
}
func (h *Handler) Update(c *gin.Context) {
id := c.Param("id")
eventID, err := strconv.ParseInt(id, 10, 64)
if err != nil {
response.ErrorWrapper(c, errors.ErrorBadRequest)
return
}
var req request.Event
if err := c.ShouldBindJSON(&req); err != nil {
response.ErrorWrapper(c, errors.ErrorBadRequest)
return
}
if err := req.Validate(); err != nil {
response.ErrorWrapper(c, errors.ErrorBadRequest)
return
}
updatedEvent, err := h.service.Update(c.Request.Context(), eventID, req.ToEntity())
if err != nil {
response.ErrorWrapper(c, err)
return
}
c.JSON(http.StatusOK, response.BaseResponse{
Success: true,
Status: http.StatusOK,
Data: h.toEventResponse(updatedEvent),
})
}
func (h *Handler) GetAll(c *gin.Context) {
var req request.EventParam
if err := c.ShouldBindQuery(&req); err != nil {
response.ErrorWrapper(c, errors.ErrorBadRequest)
return
}
events, total, err := h.service.GetAll(c.Request.Context(), req.ToEntity())
if err != nil {
response.ErrorWrapper(c, err)
return
}
c.JSON(http.StatusOK, response.BaseResponse{
Success: true,
Status: http.StatusOK,
Data: h.toEventResponseList(events, int64(total), req),
})
}
func (h *Handler) Delete(c *gin.Context) {
id := c.Param("id")
// Parse the ID into a uint
eventID, err := strconv.ParseInt(id, 10, 64)
if err != nil {
response.ErrorWrapper(c, errors.ErrorBadRequest)
return
}
err = h.service.Delete(c.Request.Context(), eventID)
if err != nil {
c.JSON(http.StatusInternalServerError, response.BaseResponse{
Success: false,
Status: http.StatusInternalServerError,
Message: err.Error(),
Data: nil,
})
return
}
c.JSON(http.StatusOK, response.BaseResponse{
Success: true,
Status: http.StatusOK,
Data: nil,
})
}
func (h *Handler) GetByID(c *gin.Context) {
id := c.Param("id")
// Parse the ID into a uint
eventID, err := strconv.ParseInt(id, 10, 64)
if err != nil {
response.ErrorWrapper(c, errors.ErrorBadRequest)
return
}
res, err := h.service.GetByID(c.Request.Context(), eventID)
if err != nil {
c.JSON(http.StatusInternalServerError, response.BaseResponse{
Success: false,
Status: http.StatusInternalServerError,
Message: err.Error(),
Data: nil,
})
return
}
c.JSON(http.StatusOK, response.BaseResponse{
Success: true,
Status: http.StatusOK,
Data: h.toEventResponse(res),
})
}
func (h *Handler) toEventResponse(resp *entity.Event) response.Event {
return response.Event{
ID: resp.ID,
Name: resp.Name,
Description: resp.Description,
StartDate: resp.StartDate.Format("2006-01-02"),
EndDate: resp.EndDate.Format("2006-01-02"),
StartTime: resp.StartDate.Format("15:04:05"),
EndTime: resp.EndDate.Format("15:04:05"),
Location: resp.Location,
Level: resp.Level,
Included: resp.Included,
Price: resp.Price,
Paid: resp.Paid,
LocationID: resp.LocationID,
CreatedAt: resp.CreatedAt.Format(time.RFC3339),
UpdatedAt: resp.CreatedAt.Format(time.RFC3339),
Status: string(resp.Status),
}
}
func (h *Handler) toEventResponseList(resp []*entity.Event, total int64, req request.EventParam) response.EventList {
var events []response.Event
for _, evt := range resp {
events = append(events, h.toEventResponse(evt))
}
return response.EventList{
Events: events,
Total: total,
Limit: req.Limit,
Offset: req.Offset,
}
}

View File

@ -56,7 +56,7 @@ func (h *MenuHandler) GetProducts(c *gin.Context) {
return return
} }
searchParam := req.ToEntity() searchParam := req.ToEntity(partnerID)
searchParam.PartnerID = partnerID searchParam.PartnerID = partnerID
products, total, err := h.service.GetProductsByPartnerID(ctx, searchParam) products, total, err := h.service.GetProductsByPartnerID(ctx, searchParam)

View File

@ -29,6 +29,7 @@ func (h *Handler) Route(group *gin.RouterGroup, jwt gin.HandlerFunc) {
route.POST("/inquiry", jwt, h.Inquiry) route.POST("/inquiry", jwt, h.Inquiry)
route.POST("/execute", jwt, h.Execute) route.POST("/execute", jwt, h.Execute)
route.POST("/refund", jwt, h.Refund)
route.GET("/history", jwt, h.GetOrderHistory) route.GET("/history", jwt, h.GetOrderHistory)
route.GET("/payment-analysis", jwt, h.GetPaymentMethodAnalysis) route.GET("/payment-analysis", jwt, h.GetPaymentMethodAnalysis)
route.GET("/revenue-overview", jwt, h.GetRevenueOverview) route.GET("/revenue-overview", jwt, h.GetRevenueOverview)
@ -47,6 +48,7 @@ type InquiryRequest struct {
OrderType string `json:"order_type"` OrderType string `json:"order_type"`
PaymentProvider string `json:"payment_provider"` PaymentProvider string `json:"payment_provider"`
TableNumber string `json:"table_number"` TableNumber string `json:"table_number"`
CashierSessionID int64 `json:"cashier_session_id"`
} }
func (o *InquiryRequest) GetPaymentProvider() string { func (o *InquiryRequest) GetPaymentProvider() string {
@ -70,6 +72,11 @@ type ExecuteRequest struct {
Token string `json:"token"` Token string `json:"token"`
} }
type RefundRequest struct {
OrderID int64 `json:"order_id" validate:"required"`
Reason string `json:"reason" validate:"required"`
}
func (h *Handler) Inquiry(c *gin.Context) { func (h *Handler) Inquiry(c *gin.Context) {
ctx := request.GetMyContext(c) ctx := request.GetMyContext(c)
userID := ctx.RequestedBy() userID := ctx.RequestedBy()
@ -109,6 +116,7 @@ func (h *Handler) Inquiry(c *gin.Context) {
OrderType: req.OrderType, OrderType: req.OrderType,
PaymentProvider: req.GetPaymentProvider(), PaymentProvider: req.GetPaymentProvider(),
TableNumber: req.TableNumber, TableNumber: req.TableNumber,
CashierSessionID: req.CashierSessionID,
} }
result, err := h.service.CreateOrderInquiry(ctx, orderReq) result, err := h.service.CreateOrderInquiry(ctx, orderReq)
@ -152,6 +160,33 @@ func (h *Handler) Execute(c *gin.Context) {
}) })
} }
func (h *Handler) Refund(c *gin.Context) {
ctx := request.GetMyContext(c)
var req RefundRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.ErrorWrapper(c, errors.ErrorBadRequest)
return
}
validate := validator.New()
if err := validate.Struct(req); err != nil {
response.ErrorWrapper(c, err)
return
}
err := h.service.RefundRequest(ctx, *ctx.GetPartnerID(), req.OrderID, req.Reason)
if err != nil {
response.ErrorWrapper(c, err)
return
}
c.JSON(http.StatusOK, response.BaseResponse{
Success: true,
Status: http.StatusOK,
})
}
func (h *Handler) GetOrderHistory(c *gin.Context) { func (h *Handler) GetOrderHistory(c *gin.Context) {
ctx := request.GetMyContext(c) ctx := request.GetMyContext(c)
partnerID := ctx.GetPartnerID() partnerID := ctx.GetPartnerID()

View File

@ -1,461 +0,0 @@
package order
import (
"enaklo-pos-be/internal/common/errors"
"enaklo-pos-be/internal/entity"
"enaklo-pos-be/internal/handlers/request"
"enaklo-pos-be/internal/handlers/response"
"enaklo-pos-be/internal/services"
"net/http"
"time"
"github.com/gin-gonic/gin"
"github.com/go-playground/validator/v10"
)
type Handler struct {
service services.Order
}
func (h *Handler) Route(group *gin.RouterGroup, jwt gin.HandlerFunc) {
route := group.Group("/order")
route.POST("/inquiry", jwt, h.Inquiry)
route.GET("/print-detail", jwt, h.PrintDetail)
route.POST("/execute", jwt, h.Execute)
route.GET("/history", jwt, h.GetAllHistoryOrders)
route.GET("/ticket-sold", jwt, h.CountSoldOfTicket)
route.POST("/checkin/inquiry", jwt, h.CheckInInquiry)
route.POST("/checkin/execute", jwt, h.CheckInExecute)
route.GET("/sum-amount", jwt, h.SumAmount)
route.GET("/daily-sales", jwt, h.GetDailySalesTicket)
route.GET("/payment-distribution", jwt, h.GetPaymentDistributionChart)
}
func NewHandler(service services.Order) *Handler {
return &Handler{
service: service,
}
}
func (h *Handler) Inquiry(c *gin.Context) {
ctx := request.GetMyContext(c)
var req request.Order
if err := c.ShouldBindJSON(&req); err != nil {
response.ErrorWrapper(c, errors.ErrorBadRequest)
return
}
validate := validator.New()
if err := validate.Struct(req); err != nil {
response.ErrorWrapper(c, err)
return
}
orderRequest := req.ToEntity(*ctx.GetPartnerID(), ctx.RequestedBy())
order, err := h.service.CreateOrder(ctx, orderRequest)
if err != nil {
response.ErrorWrapper(c, err)
return
}
c.JSON(http.StatusOK, response.BaseResponse{
Success: true,
Status: http.StatusOK,
Data: MapOrderToCreateOrderResponse(order),
})
}
func (h *Handler) PrintDetail(c *gin.Context) {
ctx := request.GetMyContext(c)
var req request.OrderPrintDetail
if err := c.ShouldBindQuery(&req); err != nil {
response.ErrorWrapper(c, errors.ErrorBadRequest)
return
}
order, err := h.service.GetPrintDetail(ctx, req.ID)
if err != nil {
response.ErrorWrapper(c, err)
return
}
c.JSON(http.StatusOK, response.BaseResponse{
Success: true,
Status: http.StatusOK,
Data: MapOrderToPrintDetailResponse(order, ctx.GetName()),
})
}
func (h *Handler) Execute(c *gin.Context) {
ctx := request.GetMyContext(c)
var req request.Execute
if err := c.ShouldBindJSON(&req); err != nil {
response.ErrorWrapper(c, errors.ErrorBadRequest)
return
}
if !ctx.IsCasheer() {
response.ErrorWrapper(c, errors.ErrorBadRequest)
return
}
req.PartnerID = *ctx.GetPartnerID()
validate := validator.New()
if err := validate.Struct(req); err != nil {
response.ErrorWrapper(c, err)
return
}
order, err := h.service.Execute(ctx, req.ToOrderExecuteRequest(ctx.RequestedBy()))
if err != nil {
response.ErrorWrapper(c, err)
return
}
c.JSON(http.StatusOK, response.BaseResponse{
Success: true,
Status: http.StatusOK,
Data: MapOrderToExecuteOrderResponse(order),
})
}
func (h *Handler) CheckInInquiry(c *gin.Context) {
ctx := request.GetMyContext(c)
var req request.Checkin
if err := c.ShouldBindJSON(&req); err != nil {
response.ErrorWrapper(c, errors.ErrorBadRequest)
return
}
if !ctx.IsCasheer() || req.QRCode == "" {
response.ErrorWrapper(c, errors.ErrorBadRequest)
return
}
partnerID := ctx.GetPartnerID()
resp, err := h.service.CheckInInquiry(ctx, req.QRCode, partnerID)
if err != nil {
response.ErrorWrapper(c, err)
return
}
c.JSON(http.StatusOK, response.BaseResponse{
Success: true,
Status: http.StatusOK,
Data: response.CheckingInquiryResponse{
Token: resp.Token,
},
})
}
func (h *Handler) CheckInExecute(c *gin.Context) {
ctx := request.GetMyContext(c)
var req request.CheckinExecute
if err := c.ShouldBindJSON(&req); err != nil {
response.ErrorWrapper(c, errors.ErrorBadRequest)
return
}
if !ctx.IsCasheer() || req.Token == "" {
response.ErrorWrapper(c, errors.ErrorBadRequest)
return
}
partnerID := ctx.GetPartnerID()
resp, err := h.service.CheckInExecute(ctx, req.Token, partnerID)
if err != nil {
response.ErrorWrapper(c, err)
return
}
c.JSON(http.StatusOK, response.BaseResponse{
Success: true,
Status: http.StatusOK,
Data: MapOrderToExecuteCheckinResponse(resp.Order),
})
}
func MapOrderToCreateOrderResponse(orderResponse *entity.OrderResponse) response.CreateOrderResponse {
order := orderResponse.Order
orderItems := make([]response.CreateOrderItemResponse, len(order.OrderItems))
for i, item := range order.OrderItems {
orderItems[i] = response.CreateOrderItemResponse{
ID: item.ID,
ItemID: item.ItemID,
Quantity: item.Quantity,
Price: item.Price,
Name: item.Product.Name,
}
}
return response.CreateOrderResponse{
ID: order.ID,
PartnerID: order.PartnerID,
Status: order.Status,
Amount: order.Amount,
Total: order.Total,
Tax: order.Tax,
PaymentType: order.PaymentType,
CreatedAt: order.CreatedAt,
OrderItems: orderItems,
}
}
func MapOrderToExecuteOrderResponse(orderResponse *entity.ExecuteOrderResponse) response.ExecuteOrderResponse {
order := orderResponse.Order
orderItems := make([]response.CreateOrderItemResponse, len(order.OrderItems))
for i, item := range order.OrderItems {
orderItems[i] = response.CreateOrderItemResponse{
ID: item.ID,
ItemID: item.ItemID,
Quantity: item.Quantity,
Price: item.Price,
Name: item.Product.Name,
}
}
return response.ExecuteOrderResponse{
ID: order.ID,
PartnerID: order.PartnerID,
Status: order.Status,
Amount: order.Amount,
PaymentType: order.PaymentType,
CreatedAt: order.CreatedAt,
OrderItems: orderItems,
PaymentToken: orderResponse.PaymentToken,
RedirectURL: orderResponse.RedirectURL,
QRcode: orderResponse.QRCode,
}
}
func MapOrderToExecuteCheckinResponse(order *entity.Order) response.ExecuteCheckinResponse {
orderItems := make([]response.CreateOrderItemResponse, len(order.OrderItems))
for i, item := range order.OrderItems {
orderItems[i] = response.CreateOrderItemResponse{
ID: item.ID,
ItemID: item.ItemID,
Quantity: item.Quantity,
Price: item.Price,
Name: item.Product.Name,
}
}
return response.ExecuteCheckinResponse{
ID: order.ID,
PartnerID: order.PartnerID,
Status: order.Status,
Amount: order.Amount,
PaymentType: order.PaymentType,
CreatedAt: order.CreatedAt,
OrderItems: orderItems,
}
}
func (h *Handler) toHistoryOrderResponse(resp *entity.HistoryOrder) response.HistoryOrder {
return response.HistoryOrder{
ID: resp.ID,
Employee: resp.Employee,
Site: resp.Site,
Timestamp: resp.Timestamp.Format("2006-01-02 15:04:05"),
BookingTime: resp.BookingTime.Format("2006-01-02 15:04:05"),
Tickets: resp.Tickets,
PaymentType: resp.PaymentType,
Status: resp.Status,
Amount: resp.Amount,
VisitDate: resp.VisitDate.Format("2006-01-02"),
TicketStatus: resp.TicketStatus,
Source: resp.Source,
}
}
func (h *Handler) SumAmount(c *gin.Context) {
var req request.OrderParam
if err := c.ShouldBindQuery(&req); err != nil {
response.ErrorWrapper(c, errors.ErrorBadRequest)
return
}
ctx := request.GetMyContext(c)
order, err := h.service.SumAmount(ctx, req.ToOrderEntity(ctx))
if err != nil {
response.ErrorWrapper(c, err)
return
}
c.JSON(http.StatusOK, response.BaseResponse{
Success: true,
Status: http.StatusOK,
Data: response.OrderAmount{
Amount: order.Amount,
},
})
}
func (h *Handler) GetAllHistoryOrders(c *gin.Context) {
var req request.OrderParam
if err := c.ShouldBindQuery(&req); err != nil {
response.ErrorWrapper(c, errors.ErrorBadRequest)
return
}
ctx := request.GetMyContext(c)
orders, total, err := h.service.GetAllHistoryOrders(ctx, req.ToOrderEntity(ctx))
if err != nil {
response.ErrorWrapper(c, err)
return
}
c.JSON(http.StatusOK, response.BaseResponse{
Success: true,
Status: http.StatusOK,
Data: h.toHistoryOrderList(orders, int64(total), req),
})
}
func (h *Handler) CountSoldOfTicket(c *gin.Context) {
var req request.OrderParam
if err := c.ShouldBindQuery(&req); err != nil {
response.ErrorWrapper(c, errors.ErrorBadRequest)
return
}
ctx := request.GetMyContext(c)
res, err := h.service.CountSoldOfTicket(ctx, req.ToOrderEntity(ctx))
if err != nil {
response.ErrorWrapper(c, err)
return
}
c.JSON(http.StatusOK, response.BaseResponse{
Success: true,
Status: http.StatusOK,
Data: response.TicketSold{
Count: res.Count,
},
})
}
func (h *Handler) GetDailySalesTicket(c *gin.Context) {
var req request.OrderParam
if err := c.ShouldBindQuery(&req); err != nil {
response.ErrorWrapper(c, errors.ErrorBadRequest)
return
}
ctx := request.GetMyContext(c)
resp, err := h.service.GetDailySales(ctx, req.ToOrderEntity(ctx))
if err != nil {
response.ErrorWrapper(c, err)
return
}
c.JSON(http.StatusOK, response.BaseResponse{
Success: true,
Status: http.StatusOK,
Data: h.toDailySales(resp),
})
}
func (h *Handler) GetPaymentDistributionChart(c *gin.Context) {
var req request.OrderParam
if err := c.ShouldBindQuery(&req); err != nil {
response.ErrorWrapper(c, errors.ErrorBadRequest)
return
}
ctx := request.GetMyContext(c)
resp, err := h.service.GetPaymentDistribution(ctx, req.ToOrderEntity(ctx))
if err != nil {
response.ErrorWrapper(c, err)
return
}
c.JSON(http.StatusOK, response.BaseResponse{
Success: true,
Status: http.StatusOK,
Data: h.toPaymentDistributionChart(resp),
})
}
func (h *Handler) toHistoryOrderList(resp []*entity.HistoryOrder, total int64, req request.OrderParam) response.HistoryOrderList {
var orders []response.HistoryOrder
for _, b := range resp {
orders = append(orders, h.toHistoryOrderResponse(b))
}
return response.HistoryOrderList{
Orders: orders,
Total: total,
Limit: req.Limit,
Offset: req.Offset,
}
}
func (h *Handler) toDailySales(resp []entity.ProductDailySales) []response.ProductDailySales {
var dailySales []response.ProductDailySales
for _, b := range resp {
dailySales = append(dailySales, response.ProductDailySales{
Day: b.Day,
SiteID: b.SiteID,
Total: b.Total,
SiteName: b.SiteName,
PaymentType: b.PaymentType,
})
}
return dailySales
}
func (h *Handler) toPaymentDistributionChart(resp []entity.PaymentTypeDistribution) []response.PaymentDistribution {
var dailySales []response.PaymentDistribution
for _, b := range resp {
dailySales = append(dailySales, response.PaymentDistribution{
PaymentType: b.PaymentType,
Count: b.Count,
})
}
return dailySales
}
func MapOrderToPrintDetailResponse(order *entity.OrderPrintDetail, casherName string) response.PrintDetailResponse {
orderItems := make([]response.CreateOrderItemResponse, len(order.OrderItems))
for i, item := range order.OrderItems {
orderItems[i] = response.CreateOrderItemResponse{
ID: item.ID,
ItemID: item.ItemID,
Quantity: item.Quantity,
Price: item.Price,
Name: item.Product.Name,
}
}
return response.PrintDetailResponse{
ID: order.ID,
OrderID: order.OrderID,
Total: order.Total,
Fee: order.Fee,
PaymentType: order.GetPaymanetType(),
Source: order.Source,
VisitDateAt: order.VisitDate.Format("2006-01-02"),
VisitTime: time.Now().Format("15:04:05"),
OrderItems: orderItems,
CasheerName: casherName,
PartnerName: order.SiteName,
Logo: order.Logo,
}
}

View File

@ -55,7 +55,6 @@ func (h *Handler) Create(c *gin.Context) {
} }
req.PartnerID = *ctx.GetPartnerID() req.PartnerID = *ctx.GetPartnerID()
req.SiteID = *ctx.GetSiteID()
res, err := h.service.Create(ctx, req.ToEntity()) res, err := h.service.Create(ctx, req.ToEntity())
@ -140,7 +139,9 @@ func (h *Handler) GetAll(c *gin.Context) {
return return
} }
products, total, err := h.service.GetAll(c.Request.Context(), req.ToEntity()) ctx := request.GetMyContext(c)
products, total, err := h.service.GetAll(ctx, req.ToEntity(*ctx.GetPartnerID()))
if err != nil { if err != nil {
response.ErrorWrapper(c, err) response.ErrorWrapper(c, err)
return return
@ -266,6 +267,13 @@ func (h *Handler) GetByID(c *gin.Context) {
} }
func (h *Handler) toProductResponse(resp *entity.Product) response.Product { func (h *Handler) toProductResponse(resp *entity.Product) response.Product {
category := response.Category{}
if resp.Category != nil {
category.ID = resp.Category.ID
category.Name = resp.Category.Name
}
return response.Product{ return response.Product{
ID: resp.ID, ID: resp.ID,
Name: resp.Name, Name: resp.Name,
@ -274,6 +282,7 @@ func (h *Handler) toProductResponse(resp *entity.Product) response.Product {
Status: resp.Status, Status: resp.Status,
Description: resp.Description, Description: resp.Description,
Image: resp.Image, Image: resp.Image,
Category: category,
} }
} }

View File

@ -1,232 +0,0 @@
package studio
import (
"enaklo-pos-be/internal/common/errors"
"enaklo-pos-be/internal/entity"
"enaklo-pos-be/internal/handlers/request"
"enaklo-pos-be/internal/handlers/response"
"enaklo-pos-be/internal/services"
"encoding/json"
"net/http"
"strconv"
"time"
"github.com/gin-gonic/gin"
"github.com/go-playground/validator/v10"
)
type StudioHandler struct {
service services.Studio
}
func (h *StudioHandler) Route(group *gin.RouterGroup, jwt gin.HandlerFunc) {
route := group.Group("/studio")
route.POST("/", jwt, h.Create)
route.PUT("/:id", jwt, h.Update)
route.GET("/:id", jwt, h.GetByID)
route.GET("/search", jwt, h.Search)
}
func NewStudioHandler(service services.Studio) *StudioHandler {
return &StudioHandler{
service: service,
}
}
// Create handles the creation of a new studio.
// @Summary Create a new studio
// @Description Create a new studio based on the provided details.
// @Accept json
// @Produce json
// @Param Authorization header string true "JWT token"
// @Param req body request.Studio true "New studio details"
// @Success 200 {object} response.BaseResponse{data=response.Studio} "Studio created successfully"
// @Failure 400 {object} response.BaseResponse{data=errors.Error} "Bad request"
// @Failure 401 {object} response.BaseResponse{data=errors.Error} "Unauthorized"
// @Router /api/v1/studio [post]
// @Tags Studio APIs
func (h *StudioHandler) Create(c *gin.Context) {
ctx := request.GetMyContext(c)
var req request.Studio
if err := c.ShouldBindJSON(&req); err != nil {
response.ErrorWrapper(c, errors.ErrorBadRequest)
return
}
validate := validator.New()
if err := validate.Struct(req); err != nil {
response.ErrorWrapper(c, err)
return
}
res, err := h.service.Create(ctx, req.ToEntity())
if err != nil {
response.ErrorWrapper(c, err)
return
}
c.JSON(http.StatusOK, response.BaseResponse{
Success: true,
Status: http.StatusOK,
Data: h.toStudioResponse(res),
})
}
// Update handles the update of an existing studio.
// @Summary Update an existing studio
// @Description Update the details of an existing studio based on the provided ID.
// @Accept json
// @Produce json
// @Param Authorization header string true "JWT token"
// @Param id path int64 true "Studio ID to update"
// @Param req body request.Studio true "Updated studio details"
// @Success 200 {object} response.BaseResponse{data=response.Studio} "Studio updated successfully"
// @Failure 400 {object} response.BaseResponse{data=errors.Error} "Bad request"
// @Failure 401 {object} response.BaseResponse{data=errors.Error} "Unauthorized"
// @Router /api/v1/studio/{id} [put]
// @Tags Studio APIs
func (h *StudioHandler) Update(c *gin.Context) {
ctx := request.GetMyContext(c)
id := c.Param("id")
studioID, err := strconv.ParseInt(id, 10, 64)
if err != nil {
response.ErrorWrapper(c, errors.ErrorBadRequest)
return
}
var req request.Studio
if err := c.ShouldBindJSON(&req); err != nil {
response.ErrorWrapper(c, errors.ErrorBadRequest)
return
}
validate := validator.New()
if err := validate.Struct(req); err != nil {
response.ErrorWrapper(c, err)
return
}
updatedStudio, err := h.service.Update(ctx, studioID, req.ToEntity())
if err != nil {
response.ErrorWrapper(c, err)
return
}
c.JSON(http.StatusOK, response.BaseResponse{
Success: true,
Status: http.StatusOK,
Data: h.toStudioResponse(updatedStudio),
})
}
// Search retrieves a list of studios based on search criteria.
// @Summary Search for studios
// @Description Search for studios based on query parameters.
// @Accept json
// @Produce json
// @Param Authorization header string true "JWT token"
// @Param Name query string false "Studio name for search"
// @Param Status query string false "Studio status for search"
// @Param Limit query int false "Number of items to retrieve (default 10)"
// @Param Offset query int false "Offset for pagination (default 0)"
// @Success 200 {object} response.BaseResponse{data=response.StudioList} "List of studios"
// @Failure 400 {object} response.BaseResponse{data=errors.Error} "Bad request"
// @Failure 401 {object} response.BaseResponse{data=errors.Error} "Unauthorized"
// @Router /api/v1/studio/search [get]
// @Tags Studio APIs
func (h *StudioHandler) Search(c *gin.Context) {
var req request.StudioParam
if err := c.ShouldBindQuery(&req); err != nil {
response.ErrorWrapper(c, errors.ErrorBadRequest)
return
}
studios, total, err := h.service.Search(c.Request.Context(), req.ToEntity())
if err != nil {
response.ErrorWrapper(c, err)
return
}
c.JSON(http.StatusOK, response.BaseResponse{
Success: true,
Status: http.StatusOK,
Data: h.toStudioResponseList(studios, int64(total), req),
})
}
// GetByID retrieves details of a specific studio by ID.
// @Summary Get details of a studio by ID
// @Description Get details of a studio based on the provided ID.
// @Accept json
// @Produce json
// @Param Authorization header string true "JWT token"
// @Param id path int64 true "Studio ID to retrieve"
// @Success 200 {object} response.BaseResponse{data=response.Studio} "Studio details"
// @Failure 400 {object} response.BaseResponse{data=errors.Error} "Bad request"
// @Failure 401 {object} response.BaseResponse{data=errors.Error} "Unauthorized"
// @Router /api/v1/studio/{id} [get]
// @Tags Studio APIs
func (h *StudioHandler) GetByID(c *gin.Context) {
id := c.Param("id")
studioID, err := strconv.ParseInt(id, 10, 64)
if err != nil {
response.ErrorWrapper(c, errors.ErrorBadRequest)
return
}
res, err := h.service.GetByID(c.Request.Context(), studioID)
if err != nil {
c.JSON(http.StatusInternalServerError, response.BaseResponse{
Success: false,
Status: http.StatusInternalServerError,
Message: err.Error(),
Data: nil,
})
return
}
c.JSON(http.StatusOK, response.BaseResponse{
Success: true,
Status: http.StatusOK,
Data: h.toStudioResponse(res),
})
}
func (h *StudioHandler) toStudioResponse(resp *entity.Studio) response.Studio {
metadata := make(map[string]interface{})
if err := json.Unmarshal(resp.Metadata, &metadata); err != nil {
//TODO taufanvps
// Handle the error if the metadata cannot be unmarshaled.
}
return response.Studio{
ID: &resp.ID,
BranchId: &resp.BranchId,
Name: resp.Name,
Status: string(resp.Status),
Price: resp.Price,
Metadata: metadata,
CreatedAt: resp.CreatedAt.Format(time.RFC3339),
UpdatedAt: resp.CreatedAt.Format(time.RFC3339),
}
}
func (h *StudioHandler) toStudioResponseList(resp []*entity.Studio, total int64, req request.StudioParam) response.StudioList {
var studios []response.Studio
for _, b := range resp {
studios = append(studios, h.toStudioResponse(b))
}
return response.StudioList{
Studios: studios,
Total: total,
Limit: req.Limit,
Offset: req.Offset,
}
}

View File

@ -0,0 +1,18 @@
package request
import "enaklo-pos-be/internal/entity"
type OpenCashierSessionRequest struct {
OpeningAmount float64 `json:"opening_amount" validate:"required,gt=0"`
}
type CloseCashierSessionRequest struct {
ClosingAmount float64 `json:"closing_amount" validate:"required"`
}
func (o *OpenCashierSessionRequest) ToEntity(cashierID int64) *entity.CashierSession {
return &entity.CashierSession{
CashierID: cashierID,
OpeningAmount: o.OpeningAmount,
}
}

View File

@ -0,0 +1,14 @@
package request
import "enaklo-pos-be/internal/entity"
type CategoryRequest struct {
Name string `json:"name" binding:"required"`
}
func (r *CategoryRequest) ToEntity(partnerID int64) *entity.Category {
return &entity.Category{
PartnerID: partnerID,
Name: r.Name,
}
}

View File

@ -6,24 +6,26 @@ import (
) )
type ProductParam struct { type ProductParam struct {
Search string `form:"search" json:"search" example:"Nasi Goreng"` Search string `form:"search" json:"search" example:"Nasi Goreng"`
Name string `form:"name" json:"name" example:"Nasi Goreng"` Name string `form:"name" json:"name" example:"Nasi Goreng"`
Type product.ProductType `form:"type" json:"type" example:"FOOD/BEVERAGE"` Type product.ProductType `form:"type" json:"type" example:"FOOD/BEVERAGE"`
BranchID int64 `form:"branch_id" json:"branch_id" example:"1"` BranchID int64 `form:"branch_id" json:"branch_id" example:"1"`
Available product.ProductStock `form:"available" json:"available" example:"1" example:"AVAILABLE/UNAVAILABLE"` Available product.ProductStock `form:"available" json:"available" example:"1" example:"AVAILABLE/UNAVAILABLE"`
Limit int `form:"limit" json:"limit" example:"10"` Limit int `form:"limit" json:"limit" example:"10"`
Offset int `form:"offset" json:"offset" example:"0"` Offset int `form:"offset" json:"offset" example:"0"`
CategoryID int64 `form:"category_id" json:"category_id" example:"1"`
} }
func (p *ProductParam) ToEntity() entity.ProductSearch { func (p *ProductParam) ToEntity(partnerID int64) entity.ProductSearch {
return entity.ProductSearch{ return entity.ProductSearch{
Search: p.Search, Search: p.Search,
Name: p.Name, Name: p.Name,
Type: p.Type, Type: p.Type,
BranchID: p.BranchID, PartnerID: partnerID,
Available: p.Available, Available: p.Available,
Limit: p.Limit, Limit: p.Limit,
Offset: p.Offset, Offset: p.Offset,
CategoryID: p.CategoryID,
} }
} }
@ -40,6 +42,7 @@ type Product struct {
Description string `json:"description"` Description string `json:"description"`
Stock int64 `json:"stock"` Stock int64 `json:"stock"`
Image string `json:"image"` Image string `json:"image"`
CategoryID int64 `json:"category_id"`
} }
func (e *Product) ToEntity() *entity.Product { func (e *Product) ToEntity() *entity.Product {
@ -51,5 +54,6 @@ func (e *Product) ToEntity() *entity.Product {
Description: e.Description, Description: e.Description,
PartnerID: e.PartnerID, PartnerID: e.PartnerID,
Image: e.Image, Image: e.Image,
CategoryID: &e.CategoryID,
} }
} }

View File

@ -0,0 +1,62 @@
package response
import (
"enaklo-pos-be/internal/entity"
"time"
)
type CashierSessionResponse struct {
ID int64 `json:"id"`
CashierID int64 `json:"cashier_id"`
OpenedAt time.Time `json:"opened_at"`
ClosedAt *time.Time `json:"closed_at,omitempty"`
OpeningAmount float64 `json:"opening_amount"`
ClosingAmount *float64 `json:"closing_amount,omitempty"`
Status string `json:"status"`
}
type PaymentSummaryResponse struct {
PaymentType string `json:"payment_type"`
PaymentProvider string `json:"payment_provider"`
TotalAmount float64 `json:"total_amount"`
}
type CashierSessionReportResponse struct {
SessionID int64 `json:"session_id"`
ExpectedAmount float64 `json:"expected_amount"`
ClosingAmount float64 `json:"closing_amount"`
Payments []PaymentSummaryResponse `json:"payments"`
}
func MapToCashierSessionResponse(e *entity.CashierSession) *CashierSessionResponse {
if e == nil {
return nil
}
return &CashierSessionResponse{
ID: e.ID,
CashierID: e.CashierID,
OpenedAt: e.OpenedAt,
ClosedAt: e.ClosedAt,
OpeningAmount: e.OpeningAmount,
ClosingAmount: e.ClosingAmount,
Status: e.Status,
}
}
func MapToCashierSessionReport(e *entity.CashierSessionReport) *CashierSessionReportResponse {
payments := make([]PaymentSummaryResponse, len(e.Payments))
for i, p := range e.Payments {
payments[i] = PaymentSummaryResponse{
PaymentType: p.PaymentType,
PaymentProvider: p.PaymentProvider,
TotalAmount: p.TotalAmount,
}
}
return &CashierSessionReportResponse{
SessionID: e.SessionID,
ExpectedAmount: e.ExpectedAmount,
ClosingAmount: e.ClosingAmount,
Payments: payments,
}
}

View File

@ -0,0 +1,29 @@
package response
import "enaklo-pos-be/internal/entity"
type CategoryResponse struct {
ID int64 `json:"id"`
Name string `json:"name"`
PartnerID int64 `json:"partner_id"`
}
func MapToCategoryResponse(cat *entity.Category) *CategoryResponse {
if cat == nil {
return nil
}
return &CategoryResponse{
ID: cat.ID,
Name: cat.Name,
PartnerID: cat.PartnerID,
}
}
func MapToCategoryListResponse(cats []*entity.Category) []*CategoryResponse {
result := make([]*CategoryResponse, len(cats))
for i, c := range cats {
result[i] = MapToCategoryResponse(c)
}
return result
}

View File

@ -1,13 +1,19 @@
package response package response
type Product struct { type Product struct {
ID int64 `json:"id"` ID int64 `json:"id"`
Name string `json:"name"` Name string `json:"name"`
Type string `json:"type"` Type string `json:"type"`
Price float64 `json:"price"` Price float64 `json:"price"`
Status string `json:"status"` Status string `json:"status"`
Description string `json:"description"` Description string `json:"description"`
Image string `json:"image"` Image string `json:"image"`
Category Category `json:"category"`
}
type Category struct {
ID int64 `json:"id"`
Name string `json:"name"`
} }
type ProductList struct { type ProductList struct {

View File

@ -0,0 +1,137 @@
package repository
import (
"enaklo-pos-be/internal/common/mycontext"
"enaklo-pos-be/internal/entity"
"enaklo-pos-be/internal/repository/models"
"github.com/pkg/errors"
"gorm.io/gorm"
"time"
)
type CashierSessionRepository interface {
CreateSession(ctx mycontext.Context, session *entity.CashierSession) (*entity.CashierSession, error)
CloseSession(ctx mycontext.Context, sessionID int64, closingAmount, expectedAmount float64) error
GetOpenSessionByCashierID(ctx mycontext.Context, cashierID int64) (*entity.CashierSession, error)
GetSessionByID(ctx mycontext.Context, sessionID int64) (*entity.CashierSession, error)
GetPaymentSummaryBySessionID(ctx mycontext.Context, sessionID int64) ([]entity.PaymentSummary, error)
}
type cashierSessionRepository struct {
db *gorm.DB
}
func NewCashierSessionRepository(db *gorm.DB) CashierSessionRepository {
return &cashierSessionRepository{db: db}
}
func (r *cashierSessionRepository) CreateSession(ctx mycontext.Context, session *entity.CashierSession) (*entity.CashierSession, error) {
dbModel := models.CashierSessionDB{
CashierID: session.CashierID,
OpenedAt: time.Now(),
OpeningAmount: session.OpeningAmount,
Status: "open",
CreatedAt: time.Now(),
}
if err := r.db.Create(&dbModel).Error; err != nil {
return nil, errors.Wrap(err, "failed to create cashier session")
}
session.ID = dbModel.ID
session.Status = dbModel.Status
session.OpenedAt = dbModel.OpenedAt
return session, nil
}
func (r *cashierSessionRepository) CloseSession(ctx mycontext.Context, sessionID int64, closingAmount, expectedAmount float64) error {
result := r.db.Model(&models.CashierSessionDB{}).
Where("id = ?", sessionID).
Updates(map[string]interface{}{
"closed_at": time.Now(),
"closing_amount": closingAmount,
"expected_amount": expectedAmount,
"status": "closed",
})
if result.Error != nil {
return errors.Wrap(result.Error, "failed to close session")
}
if result.RowsAffected == 0 {
return errors.New("no session updated")
}
return nil
}
func (r *cashierSessionRepository) GetOpenSessionByCashierID(ctx mycontext.Context, cashierID int64) (*entity.CashierSession, error) {
var dbModel models.CashierSessionDB
if err := r.db.Where("cashier_id = ? AND status = 'open'", cashierID).
Order("opened_at DESC").First(&dbModel).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, errors.Wrap(err, "failed to get open session")
}
return r.toEntity(&dbModel), nil
}
func (r *cashierSessionRepository) GetSessionByID(ctx mycontext.Context, sessionID int64) (*entity.CashierSession, error) {
var dbModel models.CashierSessionDB
if err := r.db.First(&dbModel, sessionID).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, errors.Wrap(err, "failed to get session by ID")
}
return r.toEntity(&dbModel), nil
}
func (r *cashierSessionRepository) toEntity(db *models.CashierSessionDB) *entity.CashierSession {
return &entity.CashierSession{
ID: db.ID,
CashierID: db.CashierID,
OpenedAt: db.OpenedAt,
ClosedAt: db.ClosedAt,
OpeningAmount: db.OpeningAmount,
ClosingAmount: db.ClosingAmount,
ExpectedAmount: db.ExpectedAmount,
Status: db.Status,
}
}
func (r *cashierSessionRepository) GetPaymentSummaryBySessionID(ctx mycontext.Context, sessionID int64) ([]entity.PaymentSummary, error) {
type result struct {
PaymentType string
PaymentProvider string
TotalAmount float64
}
var rows []result
err := r.db.WithContext(ctx).
Table("orders").
Select("payment_type, payment_provider, SUM(total) AS total_amount").
Where("cashier_session_id = ?", sessionID).
Group("payment_type, payment_provider").
Scan(&rows).Error
if err != nil {
return nil, errors.Wrap(err, "failed to summarize payments from orders")
}
summary := make([]entity.PaymentSummary, len(rows))
for i, row := range rows {
summary[i] = entity.PaymentSummary{
PaymentType: row.PaymentType,
PaymentProvider: row.PaymentProvider,
TotalAmount: row.TotalAmount,
}
}
return summary, nil
}

View File

@ -0,0 +1,94 @@
package repository
import (
"enaklo-pos-be/internal/common/mycontext"
"enaklo-pos-be/internal/entity"
"enaklo-pos-be/internal/repository/models"
"github.com/pkg/errors"
"gorm.io/gorm"
"time"
)
type CategoryRepository interface {
Create(ctx mycontext.Context, category *entity.Category) (*entity.Category, error)
GetByPartnerID(ctx mycontext.Context, partnerID int64) ([]*entity.Category, error)
GetByID(ctx mycontext.Context, id int64) (*entity.Category, error)
Update(ctx mycontext.Context, category *entity.Category) error
Delete(ctx mycontext.Context, id int64) error
}
type categoryRepository struct {
db *gorm.DB
}
func NewCategoryRepository(db *gorm.DB) CategoryRepository {
return &categoryRepository{db: db}
}
func (r *categoryRepository) Create(ctx mycontext.Context, category *entity.Category) (*entity.Category, error) {
dbModel := &models.CategoryDB{
PartnerID: category.PartnerID,
Name: category.Name,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
if err := r.db.WithContext(ctx).Create(dbModel).Error; err != nil {
return nil, errors.Wrap(err, "failed to create category")
}
category.ID = dbModel.ID
return category, nil
}
func (r *categoryRepository) GetByPartnerID(ctx mycontext.Context, partnerID int64) ([]*entity.Category, error) {
var dbModels []models.CategoryDB
if err := r.db.WithContext(ctx).
Where("partner_id = ? AND deleted_at IS NULL", partnerID).
Find(&dbModels).Error; err != nil {
return nil, errors.Wrap(err, "failed to fetch categories by partner ID")
}
var result []*entity.Category
for _, db := range dbModels {
result = append(result, r.toEntity(&db))
}
return result, nil
}
func (r *categoryRepository) GetByID(ctx mycontext.Context, id int64) (*entity.Category, error) {
var db models.CategoryDB
if err := r.db.WithContext(ctx).
Where("id = ? AND deleted_at IS NULL", id).
First(&db).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, errors.Wrap(err, "failed to get category by ID")
}
return r.toEntity(&db), nil
}
func (r *categoryRepository) Update(ctx mycontext.Context, category *entity.Category) error {
return r.db.WithContext(ctx).Model(&models.CategoryDB{}).
Where("id = ?", category.ID).
Updates(map[string]interface{}{
"name": category.Name,
"updated_at": time.Now(),
}).Error
}
func (r *categoryRepository) Delete(ctx mycontext.Context, id int64) error {
return r.db.WithContext(ctx).
Model(&models.CategoryDB{}).
Where("id = ?", id).
Update("deleted_at", time.Now()).Error
}
func (r *categoryRepository) toEntity(db *models.CategoryDB) *entity.Category {
return &entity.Category{
ID: db.ID,
PartnerID: db.PartnerID,
Name: db.Name,
CreatedAt: db.CreatedAt.Unix(),
UpdatedAt: db.UpdatedAt.Unix(),
}
}

View File

@ -1,86 +0,0 @@
package event
import (
"context"
"go.uber.org/zap"
"gorm.io/gorm"
"enaklo-pos-be/internal/common/logger"
"enaklo-pos-be/internal/entity"
)
type EventRepoImpl struct {
DB *gorm.DB
}
func NewEventRepo(db *gorm.DB) *EventRepoImpl {
return &EventRepoImpl{DB: db}
}
func (e *EventRepoImpl) CreateEvent(ctx context.Context, event *entity.EventDB) (*entity.EventDB, error) {
err := e.DB.Create(event).Error
if err != nil {
logger.ContextLogger(ctx).Error("error when create event", zap.Error(err))
return nil, err
}
return event, nil
}
func (e *EventRepoImpl) UpdateEvent(ctx context.Context, event *entity.EventDB) (*entity.EventDB, error) {
if err := e.DB.Save(event).Error; err != nil {
logger.ContextLogger(ctx).Error("error when update event", zap.Error(err))
return nil, err
}
return event, nil
}
func (e *EventRepoImpl) GetEventByID(ctx context.Context, id int64) (*entity.EventDB, error) {
event := new(entity.EventDB)
if err := e.DB.First(event, id).Error; err != nil {
logger.ContextLogger(ctx).Error("error when get event by id", zap.Error(err))
return nil, err
}
return event, nil
}
func (e *EventRepoImpl) GetAllEvents(ctx context.Context, nameFilter string, limit, offset int) (entity.EventList, int, error) {
var events []*entity.EventDB
var total int64
query := e.DB
query = query.Where("deleted_at is null")
if nameFilter != "" {
query = query.Where("name LIKE ?", "%"+nameFilter+"%")
}
if limit > 0 {
query = query.Limit(limit)
}
if offset > 0 {
query = query.Offset(offset)
}
if err := query.Find(&events).Error; err != nil {
logger.ContextLogger(ctx).Error("error when get all events", zap.Error(err))
return nil, 0, err
}
if err := e.DB.Model(&entity.EventDB{}).Where(query).Count(&total).Error; err != nil {
logger.ContextLogger(ctx).Error("error when count event", zap.Error(err))
return nil, 0, err
}
return events, int(total), nil
}
func (e *EventRepoImpl) DeleteEvent(ctx context.Context, id int64) error {
event := new(entity.EventDB)
event.ID = id
if err := e.DB.Delete(event).Error; err != nil {
logger.ContextLogger(ctx).Error("error when get all events", zap.Error(err))
return err
}
return nil
}

View File

@ -0,0 +1,20 @@
package models
import "time"
type CashierSessionDB struct {
ID int64 `gorm:"primaryKey"`
CashierID int64 `gorm:"not null"`
OpenedAt time.Time `gorm:"not null"`
ClosedAt *time.Time
OpeningAmount float64 `gorm:"not null"`
ClosingAmount *float64
ExpectedAmount *float64
Notes *string
Status string `gorm:"not null"`
CreatedAt time.Time
}
func (CashierSessionDB) TableName() string {
return "cashier_sessions"
}

View File

@ -0,0 +1,16 @@
package models
import "time"
type CategoryDB struct {
ID int64 `gorm:"primaryKey;autoIncrement"`
PartnerID int64 `gorm:"not null"`
Name string `gorm:"type:varchar(255);not null"`
CreatedAt time.Time `gorm:"autoCreateTime"`
UpdatedAt time.Time `gorm:"autoUpdateTime"`
DeletedAt *time.Time
}
func (CategoryDB) TableName() string {
return "categories"
}

View File

@ -5,24 +5,26 @@ import (
) )
type OrderDB struct { type OrderDB struct {
ID int64 `gorm:"primaryKey;column:id"` ID int64 `gorm:"primaryKey;column:id"`
PartnerID int64 `gorm:"column:partner_id"` PartnerID int64 `gorm:"column:partner_id"`
CustomerID *int64 `gorm:"column:customer_id"` CustomerID *int64 `gorm:"column:customer_id"`
InquiryID *string `gorm:"column:inquiry_id"` InquiryID *string `gorm:"column:inquiry_id"`
Status string `gorm:"column:status"` Status string `gorm:"column:status"`
Amount float64 `gorm:"column:amount"` Amount float64 `gorm:"column:amount"`
Tax float64 `gorm:"column:tax"` Tax float64 `gorm:"column:tax"`
Total float64 `gorm:"column:total"` Total float64 `gorm:"column:total"`
PaymentType string `gorm:"column:payment_type"` PaymentType string `gorm:"column:payment_type"`
Source string `gorm:"column:source"` Source string `gorm:"column:source"`
CreatedBy int64 `gorm:"column:created_by"` CreatedBy int64 `gorm:"column:created_by"`
CreatedAt time.Time `gorm:"column:created_at"` CreatedAt time.Time `gorm:"column:created_at"`
UpdatedAt time.Time `gorm:"column:updated_at"` UpdatedAt time.Time `gorm:"column:updated_at"`
OrderItems []OrderItemDB `gorm:"foreignKey:OrderID"` OrderItems []OrderItemDB `gorm:"foreignKey:OrderID"`
OrderType string `gorm:"column:order_type"` OrderType string `gorm:"column:order_type"`
TableNumber string `gorm:"column:table_number"` TableNumber string `gorm:"column:table_number"`
PaymentProvider string `gorm:"column:payment_provider"` PaymentProvider string `gorm:"column:payment_provider"`
CustomerName string `gorm:"column:customer_name"` CustomerName string `gorm:"column:customer_name"`
CashierSessionID int64 `gorm:"column:cashier_session_id"`
Description string `gorm:"column:description"`
} }
func (OrderDB) TableName() string { func (OrderDB) TableName() string {
@ -68,6 +70,7 @@ type OrderInquiryDB struct {
PaymentProvider string `gorm:"column:payment_provider"` PaymentProvider string `gorm:"column:payment_provider"`
TableNumber string `gorm:"column:table_number"` TableNumber string `gorm:"column:table_number"`
OrderType string `gorm:"column:order_type"` OrderType string `gorm:"column:order_type"`
CashierSessionID int64 `gorm:"column:cashier_session_id"`
} }
func (OrderInquiryDB) TableName() string { func (OrderInquiryDB) TableName() string {

View File

@ -40,6 +40,7 @@ type OrderRepository interface {
FindByIDAndPartnerID(ctx mycontext.Context, id int64, partnerID int64) (*entity.Order, error) FindByIDAndPartnerID(ctx mycontext.Context, id int64, partnerID int64) (*entity.Order, error)
GetOrderHistoryByUserID(ctx mycontext.Context, userID int64, req entity.SearchRequest) ([]*entity.Order, int64, error) GetOrderHistoryByUserID(ctx mycontext.Context, userID int64, req entity.SearchRequest) ([]*entity.Order, int64, error)
FindByIDAndCustomerID(ctx mycontext.Context, id int64, customerID int64) (*entity.Order, error) FindByIDAndCustomerID(ctx mycontext.Context, id int64, customerID int64) (*entity.Order, error)
UpdateOrder(ctx mycontext.Context, id int64, status string, description string) error
} }
type orderRepository struct { type orderRepository struct {
@ -232,25 +233,48 @@ func (r *orderRepository) UpdateInquiryStatus(ctx mycontext.Context, id string,
return nil return nil
} }
func (r *orderRepository) UpdateOrder(ctx mycontext.Context, id int64, status string, description string) error {
now := time.Now()
result := r.db.Model(&models.OrderDB{}).
Where("id = ?", id).
Updates(map[string]interface{}{
"status": status,
"updated_at": now,
"description": description,
})
if result.Error != nil {
return errors.Wrap(result.Error, "failed to update order status")
}
if result.RowsAffected == 0 {
logger.ContextLogger(ctx).Warn("no order updated")
}
return nil
}
func (r *orderRepository) toOrderDBModel(order *entity.Order) models.OrderDB { func (r *orderRepository) toOrderDBModel(order *entity.Order) models.OrderDB {
return models.OrderDB{ return models.OrderDB{
ID: order.ID, ID: order.ID,
PartnerID: order.PartnerID, PartnerID: order.PartnerID,
CustomerID: order.CustomerID, CustomerID: order.CustomerID,
InquiryID: order.InquiryID, InquiryID: order.InquiryID,
Status: order.Status, Status: order.Status,
Amount: order.Amount, Amount: order.Amount,
Tax: order.Tax, Tax: order.Tax,
Total: order.Total, Total: order.Total,
PaymentType: order.PaymentType, PaymentType: order.PaymentType,
Source: order.Source, Source: order.Source,
CreatedBy: order.CreatedBy, CreatedBy: order.CreatedBy,
CreatedAt: order.CreatedAt, CreatedAt: order.CreatedAt,
UpdatedAt: order.UpdatedAt, UpdatedAt: order.UpdatedAt,
OrderType: order.OrderType, OrderType: order.OrderType,
TableNumber: order.TableNumber, TableNumber: order.TableNumber,
PaymentProvider: order.PaymentProvider, PaymentProvider: order.PaymentProvider,
CustomerName: order.CustomerName, CustomerName: order.CustomerName,
CashierSessionID: order.CashierSessionID,
} }
} }
@ -346,27 +370,29 @@ func (r *orderRepository) toOrderInquiryDBModel(inquiry *entity.OrderInquiry) mo
PaymentProvider: inquiry.PaymentProvider, PaymentProvider: inquiry.PaymentProvider,
OrderType: inquiry.OrderType, OrderType: inquiry.OrderType,
TableNumber: inquiry.TableNumber, TableNumber: inquiry.TableNumber,
CashierSessionID: inquiry.CashierSessionID,
} }
} }
func (r *orderRepository) toDomainOrderInquiryModel(dbModel *models.OrderInquiryDB) *entity.OrderInquiry { func (r *orderRepository) toDomainOrderInquiryModel(dbModel *models.OrderInquiryDB) *entity.OrderInquiry {
inquiry := &entity.OrderInquiry{ inquiry := &entity.OrderInquiry{
ID: dbModel.ID, ID: dbModel.ID,
PartnerID: dbModel.PartnerID, PartnerID: dbModel.PartnerID,
Status: dbModel.Status, Status: dbModel.Status,
Amount: dbModel.Amount, Amount: dbModel.Amount,
Tax: dbModel.Tax, Tax: dbModel.Tax,
Total: dbModel.Total, Total: dbModel.Total,
PaymentType: dbModel.PaymentType, PaymentType: dbModel.PaymentType,
Source: dbModel.Source, Source: dbModel.Source,
CreatedBy: dbModel.CreatedBy, CreatedBy: dbModel.CreatedBy,
CreatedAt: dbModel.CreatedAt, CreatedAt: dbModel.CreatedAt,
ExpiresAt: dbModel.ExpiresAt, ExpiresAt: dbModel.ExpiresAt,
OrderItems: []entity.OrderItem{}, OrderItems: []entity.OrderItem{},
OrderType: dbModel.OrderType, OrderType: dbModel.OrderType,
CustomerName: dbModel.CustomerName, CustomerName: dbModel.CustomerName,
PaymentProvider: dbModel.PaymentProvider, PaymentProvider: dbModel.PaymentProvider,
TableNumber: dbModel.TableNumber, TableNumber: dbModel.TableNumber,
CashierSessionID: dbModel.CashierSessionID,
} }
if dbModel.CustomerID != nil { if dbModel.CustomerID != nil {
@ -718,7 +744,7 @@ func (r *orderRepository) GetRevenueOverview(
ctx mycontext.Context, ctx mycontext.Context,
req entity.RevenueOverviewRequest, req entity.RevenueOverviewRequest,
) ([]entity.RevenueOverviewItem, error) { ) ([]entity.RevenueOverviewItem, error) {
var overview []entity.RevenueOverviewItem overview := []entity.RevenueOverviewItem{}
baseQuery := r.db.Model(&models.OrderDB{}). baseQuery := r.db.Model(&models.OrderDB{}).
Where("partner_id = ?", req.PartnerID). Where("partner_id = ?", req.PartnerID).

View File

@ -38,7 +38,7 @@ func (b *ProductRepository) UpdateProduct(ctx context.Context, product *entity.P
func (b *ProductRepository) GetProductByID(ctx context.Context, id int64) (*entity.ProductDB, error) { func (b *ProductRepository) GetProductByID(ctx context.Context, id int64) (*entity.ProductDB, error) {
product := new(entity.ProductDB) product := new(entity.ProductDB)
if err := b.db.First(product, id).Error; err != nil { if err := b.db.Preload("Category").First(product, id).Error; err != nil {
logger.ContextLogger(ctx).Error("error when get by id product", zap.Error(err)) logger.ContextLogger(ctx).Error("error when get by id product", zap.Error(err))
return nil, err return nil, err
} }
@ -84,8 +84,12 @@ func (b *ProductRepository) GetAllProducts(ctx context.Context, req entity.Produ
query = query.Where("type = ? ", req.Type) query = query.Where("type = ? ", req.Type)
} }
if req.BranchID > 0 { if req.CategoryID > 0 {
query = query.Where("branch_id = ? ", req.BranchID) query = query.Where("category_id = ? ", req.CategoryID)
}
if req.PartnerID > 0 {
query = query.Where("partner_id = ? ", req.PartnerID)
} }
if req.Limit > 0 { if req.Limit > 0 {
@ -96,7 +100,7 @@ func (b *ProductRepository) GetAllProducts(ctx context.Context, req entity.Produ
query = query.Offset(req.Offset) query = query.Offset(req.Offset)
} }
if err := query.Find(&products).Error; err != nil { if err := query.Preload("Category").Find(&products).Order("id ASC").Error; err != nil {
logger.ContextLogger(ctx).Error("error when get all products", zap.Error(err)) logger.ContextLogger(ctx).Error("error when get all products", zap.Error(err))
return nil, 0, err return nil, 0, err
} }

View File

@ -15,7 +15,6 @@ import (
pg "enaklo-pos-be/internal/repository/payment_gateway" pg "enaklo-pos-be/internal/repository/payment_gateway"
"enaklo-pos-be/internal/repository/products" "enaklo-pos-be/internal/repository/products"
"enaklo-pos-be/internal/repository/sites" "enaklo-pos-be/internal/repository/sites"
"enaklo-pos-be/internal/repository/studios"
transactions "enaklo-pos-be/internal/repository/transaction" transactions "enaklo-pos-be/internal/repository/transaction"
"enaklo-pos-be/internal/repository/trx" "enaklo-pos-be/internal/repository/trx"
"enaklo-pos-be/internal/repository/users" "enaklo-pos-be/internal/repository/users"
@ -28,15 +27,12 @@ import (
"enaklo-pos-be/internal/entity" "enaklo-pos-be/internal/entity"
"enaklo-pos-be/internal/repository/auth" "enaklo-pos-be/internal/repository/auth"
"enaklo-pos-be/internal/repository/crypto" "enaklo-pos-be/internal/repository/crypto"
event "enaklo-pos-be/internal/repository/events"
) )
type RepoManagerImpl struct { type RepoManagerImpl struct {
Crypto Crypto Crypto Crypto
Auth Auth Auth Auth
Event Event
User User User User
Studio Studio
Product Product Product Product
Order Order Order Order
OSS OSSRepository OSS OSSRepository
@ -60,15 +56,15 @@ type RepoManagerImpl struct {
MemberRepository MemberRepository MemberRepository MemberRepository
PartnerSetting PartnerSettingsRepository PartnerSetting PartnerSettingsRepository
UndianRepository UndianRepo UndianRepository UndianRepo
CashierSeasionRepo CashierSessionRepository
CategoryRepository CategoryRepository
} }
func NewRepoManagerImpl(db *gorm.DB, cfg *config.Config) *RepoManagerImpl { func NewRepoManagerImpl(db *gorm.DB, cfg *config.Config) *RepoManagerImpl {
return &RepoManagerImpl{ return &RepoManagerImpl{
Crypto: crypto.NewCrypto(cfg.Auth()), Crypto: crypto.NewCrypto(cfg.Auth()),
Auth: auth.NewAuthRepository(db), Auth: auth.NewAuthRepository(db),
Event: event.NewEventRepo(db),
User: users.NewUserRepository(db), User: users.NewUserRepository(db),
Studio: studios.NewStudioRepository(db),
Product: products.NewProductRepository(db), Product: products.NewProductRepository(db),
Order: orders.NewOrderRepository(db), Order: orders.NewOrderRepository(db),
OSS: oss.NewOssRepositoryImpl(cfg.OSSConfig), OSS: oss.NewOssRepositoryImpl(cfg.OSSConfig),
@ -92,6 +88,8 @@ func NewRepoManagerImpl(db *gorm.DB, cfg *config.Config) *RepoManagerImpl {
InProgressOrderRepo: NewInProgressOrderRepository(db), InProgressOrderRepo: NewInProgressOrderRepository(db),
PartnerSetting: NewPartnerSettingsRepository(db), PartnerSetting: NewPartnerSettingsRepository(db),
UndianRepository: NewUndianRepository(db), UndianRepository: NewUndianRepository(db),
CashierSeasionRepo: NewCashierSessionRepository(db),
CategoryRepository: NewCategoryRepository(db),
} }
} }
@ -101,14 +99,6 @@ type Auth interface {
UpdatePassword(ctx context.Context, trx *gorm.DB, newHashedPassword string, userID int64, resetPassword bool) error UpdatePassword(ctx context.Context, trx *gorm.DB, newHashedPassword string, userID int64, resetPassword bool) error
} }
type Event interface {
CreateEvent(ctx context.Context, event *entity.EventDB) (*entity.EventDB, error)
UpdateEvent(ctx context.Context, event *entity.EventDB) (*entity.EventDB, error)
GetEventByID(ctx context.Context, id int64) (*entity.EventDB, error)
GetAllEvents(ctx context.Context, nameFilter string, limit, offset int) (entity.EventList, int, error)
DeleteEvent(ctx context.Context, id int64) error
}
type Crypto interface { type Crypto interface {
CompareHashAndPassword(hash string, password string) bool CompareHashAndPassword(hash string, password string) bool
ValidateWT(tokenString string) (*jwt.Token, error) ValidateWT(tokenString string) (*jwt.Token, error)

View File

@ -1,89 +0,0 @@
package studios
import (
"context"
"enaklo-pos-be/internal/common/logger"
"enaklo-pos-be/internal/entity"
"go.uber.org/zap"
"gorm.io/gorm"
)
type StudioRepository struct {
db *gorm.DB
}
func NewStudioRepository(db *gorm.DB) *StudioRepository {
return &StudioRepository{
db: db,
}
}
func (s *StudioRepository) CreateStudio(ctx context.Context, studio *entity.StudioDB) (*entity.StudioDB, error) {
err := s.db.Omit("ID").Create(studio).Error
if err != nil {
logger.ContextLogger(ctx).Error("error when creating studio", zap.Error(err))
return nil, err
}
return studio, nil
}
func (s *StudioRepository) UpdateStudio(ctx context.Context, studio *entity.StudioDB) (*entity.StudioDB, error) {
if err := s.db.Save(studio).Error; err != nil {
logger.ContextLogger(ctx).Error("error when updating studio", zap.Error(err))
return nil, err
}
return studio, nil
}
func (s *StudioRepository) GetStudioByID(ctx context.Context, id int64) (*entity.StudioDB, error) {
studio := new(entity.StudioDB)
if err := s.db.First(studio, id).Error; err != nil {
logger.ContextLogger(ctx).Error("error when getting studio by ID", zap.Error(err))
return nil, err
}
return studio, nil
}
func (s *StudioRepository) SearchStudios(ctx context.Context, req entity.StudioSearch) (entity.StudioList, int, error) {
var studios []*entity.StudioDB
var total int64
query := s.db
if req.Id > 0 {
query = query.Where("id = ?", req.Id)
}
if req.Name != "" {
query = query.Where("name ILIKE ?", "%"+req.Name+"%")
}
if req.Status != "" {
query = query.Where("status = ?", req.Status)
}
if req.BranchId > 0 {
query = query.Where("branch_id = ?", req.BranchId)
}
if req.Limit > 0 {
query = query.Limit(req.Limit)
}
if req.Offset > 0 {
query = query.Offset(req.Offset)
}
if err := query.Find(&studios).Error; err != nil {
logger.ContextLogger(ctx).Error("error when getting all studios", zap.Error(err))
return nil, 0, err
}
if err := s.db.Model(&entity.StudioDB{}).Where(query).Count(&total).Error; err != nil {
logger.ContextLogger(ctx).Error("error when counting studios", zap.Error(err))
return nil, 0, err
}
return studios, int(total), nil
}

View File

@ -3,7 +3,6 @@ package routes
import ( import (
"enaklo-pos-be/internal/handlers/http" "enaklo-pos-be/internal/handlers/http"
"enaklo-pos-be/internal/handlers/http/customerauth" "enaklo-pos-be/internal/handlers/http/customerauth"
"enaklo-pos-be/internal/handlers/http/discovery"
"enaklo-pos-be/internal/middlewares" "enaklo-pos-be/internal/middlewares"
"enaklo-pos-be/internal/app" "enaklo-pos-be/internal/app"
@ -19,7 +18,6 @@ func RegisterCustomerRoutes(app *app.Server, serviceManager *services.ServiceMan
optionlMiddleWare := middlewares.OptionalCustomerAuthorizationMiddleware(repoManager.Crypto) optionlMiddleWare := middlewares.OptionalCustomerAuthorizationMiddleware(repoManager.Crypto)
serverRoutes := []HTTPHandlerRoutes{ serverRoutes := []HTTPHandlerRoutes{
discovery.NewHandler(serviceManager.DiscoverService),
customerauth.NewAuthHandler(serviceManager.AuthV2Svc, serviceManager.MemberRegistrationSvc), customerauth.NewAuthHandler(serviceManager.AuthV2Svc, serviceManager.MemberRegistrationSvc),
http.NewMenuHandler(serviceManager.ProductV2Svc, serviceManager.InProgressSvc), http.NewMenuHandler(serviceManager.ProductV2Svc, serviceManager.InProgressSvc),
http.NewCustomerOrderHandler(serviceManager.OrderV2Svc), http.NewCustomerOrderHandler(serviceManager.OrderV2Svc),

View File

@ -8,14 +8,12 @@ import (
"enaklo-pos-be/internal/handlers/http/partner" "enaklo-pos-be/internal/handlers/http/partner"
"enaklo-pos-be/internal/handlers/http/product" "enaklo-pos-be/internal/handlers/http/product"
site "enaklo-pos-be/internal/handlers/http/sites" site "enaklo-pos-be/internal/handlers/http/sites"
"enaklo-pos-be/internal/handlers/http/studio"
"enaklo-pos-be/internal/handlers/http/transaction" "enaklo-pos-be/internal/handlers/http/transaction"
"enaklo-pos-be/internal/handlers/http/user" "enaklo-pos-be/internal/handlers/http/user"
swaggerFiles "github.com/swaggo/files" swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger" ginSwagger "github.com/swaggo/gin-swagger"
"net/http" "net/http"
"enaklo-pos-be/internal/handlers/http/event"
"enaklo-pos-be/internal/middlewares" "enaklo-pos-be/internal/middlewares"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
@ -46,9 +44,7 @@ func RegisterPrivateRoutes(app *app.Server, serviceManager *services.ServiceMana
authMiddleware := middlewares.AuthorizationMiddleware(repoManager.Crypto) authMiddleware := middlewares.AuthorizationMiddleware(repoManager.Crypto)
serverRoutes := []HTTPHandlerRoutes{ serverRoutes := []HTTPHandlerRoutes{
auth.NewAuthHandler(serviceManager.AuthSvc), auth.NewAuthHandler(serviceManager.AuthSvc),
event.NewHandler(serviceManager.EventSvc),
user.NewHandler(serviceManager.UserSvc), user.NewHandler(serviceManager.UserSvc),
studio.NewStudioHandler(serviceManager.StudioSvc),
product.NewHandler(serviceManager.ProductSvc), product.NewHandler(serviceManager.ProductSvc),
oss.NewOssHandler(serviceManager.OSSSvc), oss.NewOssHandler(serviceManager.OSSSvc),
partner.NewHandler(serviceManager.PartnerSvc), partner.NewHandler(serviceManager.PartnerSvc),
@ -74,6 +70,8 @@ func RegisterPrivateRoutesV2(app *app.Server, serviceManager *services.ServiceMa
http2.NewMemberRegistrationHandler(serviceManager.MemberRegistrationSvc), http2.NewMemberRegistrationHandler(serviceManager.MemberRegistrationSvc),
http2.NewCustomerHandler(serviceManager.CustomerV2Svc), http2.NewCustomerHandler(serviceManager.CustomerV2Svc),
http2.NewInProgressOrderHandler(serviceManager.InProgressSvc), http2.NewInProgressOrderHandler(serviceManager.InProgressSvc),
http2.NewCashierSession(serviceManager.CashierSvc),
http2.NewCategoryHandler(serviceManager.CategorySvc),
} }
for _, handler := range serverRoutes { for _, handler := range serverRoutes {

View File

@ -1,168 +0,0 @@
package discovery
import (
"context"
"enaklo-pos-be/config"
"errors"
"gorm.io/gorm"
"enaklo-pos-be/internal/entity"
"enaklo-pos-be/internal/repository"
)
const (
defaultLatitude = -6.2088
defaultLongitude = 106.8456
radius = 10000
)
type DiscoveryService struct {
repo repository.SiteRepository
cfg config.Discovery
product repository.Product
}
func NewDiscoveryService(repo repository.SiteRepository, cfg config.Discovery, product repository.Product) *DiscoveryService {
return &DiscoveryService{
repo: repo,
cfg: cfg,
product: product,
}
}
func (s *DiscoveryService) Home(ctx context.Context, search *entity.DiscoverySearch) (*entity.DiscoverySearchResp, error) {
if search.Lat == 0 || search.Long == 0 {
search.Lat = defaultLatitude
search.Long = defaultLongitude
}
siteProducts, err := s.repo.GetNearestSites(ctx, search.Lat, search.Long, radius)
if err != nil {
return nil, err
}
exploreDestinations := []entity.ExploreDestination{}
for _, exploreDestination := range s.cfg.ExploreDestinations {
exploreDestinations = append(exploreDestinations, entity.ExploreDestination{
Name: exploreDestination.Name,
ImageURL: exploreDestination.ImageURL,
})
}
exploreRegions := []entity.ExploreRegion{}
for _, exploreRegion := range s.cfg.ExploreRegions {
exploreRegions = append(exploreRegions, entity.ExploreRegion{
Name: exploreRegion.Name,
})
}
mustVisits := []entity.MustVisit{}
for _, siteProduct := range siteProducts {
if siteProduct.Status == "Active" {
mustVisits = append(mustVisits, entity.MustVisit{
Name: siteProduct.SiteName,
Price: siteProduct.ProductPrice,
Region: siteProduct.Region,
SiteID: siteProduct.SiteID,
ImageURL: siteProduct.Image,
})
}
}
response := &entity.DiscoverySearchResp{
ExploreRegions: exploreRegions,
ExploreDestinations: exploreDestinations,
MustVisit: mustVisits,
}
return response, nil
}
func (s *DiscoveryService) Search(ctx context.Context, search *entity.DiscoverySearch) (*entity.DiscoverySearchResp, int64, error) {
if search.Lat == 0 || search.Long == 0 {
search.Lat = defaultLatitude
search.Long = defaultLongitude
search.Radius = radius
}
search.Status = "Active"
siteProducts, total, err := s.repo.SearchSites(ctx, search)
if err != nil {
return nil, 0, err
}
exploreDestinations := []entity.ExploreDestination{}
for _, exploreDestination := range s.cfg.ExploreDestinations {
exploreDestinations = append(exploreDestinations, entity.ExploreDestination{
Name: exploreDestination.Name,
ImageURL: exploreDestination.ImageURL,
})
}
exploreRegions := []entity.ExploreRegion{}
for _, exploreRegion := range s.cfg.ExploreRegions {
exploreRegions = append(exploreRegions, entity.ExploreRegion{
Name: exploreRegion.Name,
})
}
mustVisits := []entity.MustVisit{}
for _, siteProduct := range siteProducts {
mustVisits = append(mustVisits, entity.MustVisit{
Name: siteProduct.SiteName,
Price: siteProduct.ProductPrice,
Region: siteProduct.Region,
SiteID: siteProduct.SiteID,
ImageURL: siteProduct.Image,
Regency: siteProduct.Regency,
})
}
response := &entity.DiscoverySearchResp{
ExploreRegions: exploreRegions,
ExploreDestinations: exploreDestinations,
MustVisit: mustVisits,
}
return response, total, nil
}
func (s *DiscoveryService) GetByID(ctx context.Context, id int64) (*entity.Site, error) {
site, err := s.repo.GetByID(ctx, id)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, err
}
if site.Status != "Active" {
return nil, nil
}
return site.ToSite(), nil
}
func (s *DiscoveryService) GetProductsByID(ctx context.Context, id int64) ([]*entity.Product, error) {
site, err := s.repo.GetByID(ctx, id)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, err
}
if site.Status == "Inactive" {
return nil, nil
}
product, err := s.product.GetProductsBySiteID(ctx, site.ID)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, err
}
return product.ToProductList(), nil
}

View File

@ -1,88 +0,0 @@
package event
import (
"context"
"go.uber.org/zap"
"enaklo-pos-be/internal/common/logger"
"enaklo-pos-be/internal/entity"
"enaklo-pos-be/internal/repository"
)
type EventService struct {
repo repository.Event
}
func NewEventService(repo repository.Event) *EventService {
return &EventService{
repo: repo,
}
}
func (s *EventService) Create(ctx context.Context, eventReq *entity.Event) (*entity.Event, error) {
eventDB := eventReq.ToEventDB()
eventDB, err := s.repo.CreateEvent(ctx, eventDB)
if err != nil {
logger.ContextLogger(ctx).Error("error when create event", zap.Error(err))
return nil, err
}
return eventDB.ToEvent(), nil
}
func (s *EventService) Update(ctx context.Context, id int64, eventReq *entity.Event) (*entity.Event, error) {
existingEvent, err := s.repo.GetEventByID(ctx, id)
if err != nil {
return nil, err
}
existingEvent.ToUpdatedEvent(*eventReq)
updatedEventDB, err := s.repo.UpdateEvent(ctx, existingEvent.ToEventDB())
if err != nil {
logger.ContextLogger(ctx).Error("error when update event", zap.Error(err))
return nil, err
}
return updatedEventDB.ToEvent(), nil
}
func (s *EventService) GetByID(ctx context.Context, id int64) (*entity.Event, error) {
eventDB, err := s.repo.GetEventByID(ctx, id)
if err != nil {
logger.ContextLogger(ctx).Error("error when get event by id", zap.Error(err))
return nil, err
}
return eventDB.ToEvent(), nil
}
func (s *EventService) GetAll(ctx context.Context, search entity.EventSearch) ([]*entity.Event, int, error) {
events, total, err := s.repo.GetAllEvents(ctx, search.Name, search.Limit, search.Offset)
if err != nil {
logger.ContextLogger(ctx).Error("error when get all events", zap.Error(err))
return nil, 0, err
}
return events.ToEventList(), total, nil
}
func (s *EventService) Delete(ctx context.Context, id int64) error {
eventDB, err := s.repo.GetEventByID(ctx, id)
if err != nil {
logger.ContextLogger(ctx).Error("error when get event by id", zap.Error(err))
return err
}
eventDB.SetDeleted()
_, err = s.repo.UpdateEvent(ctx, eventDB)
if err != nil {
logger.ContextLogger(ctx).Error("error when update event", zap.Error(err))
return err
}
return nil
}

View File

@ -4,17 +4,17 @@ import (
"context" "context"
"enaklo-pos-be/internal/common/mycontext" "enaklo-pos-be/internal/common/mycontext"
"enaklo-pos-be/internal/services/balance" "enaklo-pos-be/internal/services/balance"
"enaklo-pos-be/internal/services/discovery"
service "enaklo-pos-be/internal/services/license" service "enaklo-pos-be/internal/services/license"
"enaklo-pos-be/internal/services/member" "enaklo-pos-be/internal/services/member"
"enaklo-pos-be/internal/services/oss" "enaklo-pos-be/internal/services/oss"
"enaklo-pos-be/internal/services/partner" "enaklo-pos-be/internal/services/partner"
"enaklo-pos-be/internal/services/product" "enaklo-pos-be/internal/services/product"
site "enaklo-pos-be/internal/services/sites" site "enaklo-pos-be/internal/services/sites"
"enaklo-pos-be/internal/services/studio"
"enaklo-pos-be/internal/services/transaction" "enaklo-pos-be/internal/services/transaction"
"enaklo-pos-be/internal/services/users" "enaklo-pos-be/internal/services/users"
authSvc "enaklo-pos-be/internal/services/v2/auth" authSvc "enaklo-pos-be/internal/services/v2/auth"
"enaklo-pos-be/internal/services/v2/cashier_session"
category "enaklo-pos-be/internal/services/v2/categories"
customerSvc "enaklo-pos-be/internal/services/v2/customer" customerSvc "enaklo-pos-be/internal/services/v2/customer"
"enaklo-pos-be/internal/services/v2/inprogress_order" "enaklo-pos-be/internal/services/v2/inprogress_order"
orderSvc "enaklo-pos-be/internal/services/v2/order" orderSvc "enaklo-pos-be/internal/services/v2/order"
@ -29,22 +29,18 @@ import (
"enaklo-pos-be/internal/entity" "enaklo-pos-be/internal/entity"
"enaklo-pos-be/internal/repository" "enaklo-pos-be/internal/repository"
"enaklo-pos-be/internal/services/auth" "enaklo-pos-be/internal/services/auth"
"enaklo-pos-be/internal/services/event"
) )
type ServiceManagerImpl struct { type ServiceManagerImpl struct {
AuthSvc Auth AuthSvc Auth
EventSvc Event UserSvc User
UserSvc User ProductSvc Product
StudioSvc Studio OSSSvc OSSService
ProductSvc Product PartnerSvc Partner
OSSSvc OSSService SiteSvc Site
PartnerSvc Partner LicenseSvc License
SiteSvc Site Transaction Transaction
LicenseSvc License Balance Balance
Transaction Transaction
Balance Balance
DiscoverService DiscoverService
OrderV2Svc orderSvc.Service OrderV2Svc orderSvc.Service
CustomerV2Svc customerSvc.Service CustomerV2Svc customerSvc.Service
@ -53,6 +49,8 @@ type ServiceManagerImpl struct {
InProgressSvc inprogress_order.InProgressOrderService InProgressSvc inprogress_order.InProgressOrderService
AuthV2Svc authSvc.Service AuthV2Svc authSvc.Service
UndianSvc undian.Service UndianSvc undian.Service
CashierSvc cashier_session.Service
CategorySvc category.Service
} }
func NewServiceManagerImpl(cfg *config.Config, repo *repository.RepoManagerImpl) *ServiceManagerImpl { func NewServiceManagerImpl(cfg *config.Config, repo *repository.RepoManagerImpl) *ServiceManagerImpl {
@ -60,14 +58,16 @@ func NewServiceManagerImpl(cfg *config.Config, repo *repository.RepoManagerImpl)
custSvcV2 := customerSvc.New(repo.CustomerRepo, repo.EmailService) custSvcV2 := customerSvc.New(repo.CustomerRepo, repo.EmailService)
productSvcV2 := productSvc.New(repo.ProductRepo) productSvcV2 := productSvc.New(repo.ProductRepo)
partnerSettings := partner_settings.NewPartnerSettingsService(repo.PartnerSetting) partnerSettings := partner_settings.NewPartnerSettingsService(repo.PartnerSetting)
cashierSvc := cashier_session.New(repo.CashierSeasionRepo)
orderService := orderSvc.New(repo.OrderRepo, productSvcV2, custSvcV2, repo.TransactionRepo, repo.Crypto, &cfg.Order, repo.EmailService, partnerSettings, repo.UndianRepository) orderService := orderSvc.New(repo.OrderRepo,
productSvcV2, custSvcV2, repo.TransactionRepo,
repo.Crypto, &cfg.Order, repo.EmailService, partnerSettings,
repo.UndianRepository, cashierSvc)
inprogressOrder := inprogress_order.NewInProgressOrderService(repo.OrderRepo, orderService, productSvcV2) inprogressOrder := inprogress_order.NewInProgressOrderService(repo.OrderRepo, orderService, productSvcV2)
categorySvc := category.New(repo.CategoryRepository)
return &ServiceManagerImpl{ return &ServiceManagerImpl{
AuthSvc: auth.New(repo.Auth, repo.Crypto, repo.User, repo.EmailService, cfg.Email, repo.Trx, repo.License), AuthSvc: auth.New(repo.Auth, repo.Crypto, repo.User, repo.EmailService, cfg.Email, repo.Trx, repo.License),
EventSvc: event.NewEventService(repo.Event),
UserSvc: users.NewUserService(repo.User), UserSvc: users.NewUserService(repo.User),
StudioSvc: studio.NewStudioService(repo.Studio),
ProductSvc: product.NewProductService(repo.Product), ProductSvc: product.NewProductService(repo.Product),
OSSSvc: oss.NewOSSService(repo.OSS), OSSSvc: oss.NewOSSService(repo.OSS),
PartnerSvc: partner.NewPartnerService( PartnerSvc: partner.NewPartnerService(
@ -76,14 +76,15 @@ func NewServiceManagerImpl(cfg *config.Config, repo *repository.RepoManagerImpl)
LicenseSvc: service.NewLicenseService(repo.License), LicenseSvc: service.NewLicenseService(repo.License),
Transaction: transaction.New(repo.Transaction, repo.Wallet, repo.Trx), Transaction: transaction.New(repo.Transaction, repo.Wallet, repo.Trx),
Balance: balance.NewBalanceService(repo.Wallet, repo.Trx, repo.Crypto, &cfg.Withdraw, repo.Transaction), Balance: balance.NewBalanceService(repo.Wallet, repo.Trx, repo.Crypto, &cfg.Withdraw, repo.Transaction),
DiscoverService: discovery.NewDiscoveryService(repo.Site, cfg.Discovery, repo.Product), OrderV2Svc: orderSvc.New(repo.OrderRepo, productSvcV2, custSvcV2, repo.TransactionRepo, repo.Crypto, &cfg.Order, repo.EmailService, partnerSettings, repo.UndianRepository, cashierSvc),
OrderV2Svc: orderSvc.New(repo.OrderRepo, productSvcV2, custSvcV2, repo.TransactionRepo, repo.Crypto, &cfg.Order, repo.EmailService, partnerSettings, repo.UndianRepository),
MemberRegistrationSvc: member.NewMemberRegistrationService(repo.MemberRepository, repo.EmailService, custSvcV2, repo.Crypto), MemberRegistrationSvc: member.NewMemberRegistrationService(repo.MemberRepository, repo.EmailService, custSvcV2, repo.Crypto),
CustomerV2Svc: custSvcV2, CustomerV2Svc: custSvcV2,
InProgressSvc: inprogressOrder, InProgressSvc: inprogressOrder,
ProductV2Svc: productSvcV2, ProductV2Svc: productSvcV2,
AuthV2Svc: authSvc.New(repo.CustomerRepo, repo.Crypto), AuthV2Svc: authSvc.New(repo.CustomerRepo, repo.Crypto),
UndianSvc: undian.New(repo.UndianRepository), UndianSvc: undian.New(repo.UndianRepository),
CashierSvc: cashierSvc,
CategorySvc: categorySvc,
} }
} }
@ -93,14 +94,6 @@ type Auth interface {
ResetPassword(ctx mycontext.Context, oldPassword, newPassword string) error ResetPassword(ctx mycontext.Context, oldPassword, newPassword string) error
} }
type Event interface {
Create(ctx context.Context, eventReq *entity.Event) (*entity.Event, error)
Update(ctx context.Context, id int64, eventReq *entity.Event) (*entity.Event, error)
GetByID(ctx context.Context, id int64) (*entity.Event, error)
GetAll(ctx context.Context, search entity.EventSearch) ([]*entity.Event, int, error)
Delete(ctx context.Context, id int64) error
}
type User interface { type User interface {
Create(ctx mycontext.Context, userReq *entity.User) (*entity.User, error) Create(ctx mycontext.Context, userReq *entity.User) (*entity.User, error)
CreateWithTx(ctx mycontext.Context, tx *gorm.DB, userReq *entity.User) (*entity.User, error) CreateWithTx(ctx mycontext.Context, tx *gorm.DB, userReq *entity.User) (*entity.User, error)
@ -112,13 +105,6 @@ type User interface {
Delete(ctx mycontext.Context, id int64) error Delete(ctx mycontext.Context, id int64) error
} }
type Studio interface {
Create(ctx mycontext.Context, studioReq *entity.Studio) (*entity.Studio, error)
Update(ctx mycontext.Context, id int64, studioReq *entity.Studio) (*entity.Studio, error)
GetByID(ctx context.Context, id int64) (*entity.Studio, error)
Search(ctx context.Context, search entity.StudioSearch) ([]*entity.Studio, int, error)
}
type Product interface { type Product interface {
Create(ctx mycontext.Context, productReq *entity.Product) (*entity.Product, error) Create(ctx mycontext.Context, productReq *entity.Product) (*entity.Product, error)
Update(ctx mycontext.Context, id int64, productReq *entity.Product) (*entity.Product, error) Update(ctx mycontext.Context, id int64, productReq *entity.Product) (*entity.Product, error)
@ -128,23 +114,6 @@ type Product interface {
Delete(ctx mycontext.Context, id int64) error Delete(ctx mycontext.Context, id int64) error
} }
type Order interface {
CreateOrder(ctx mycontext.Context, req *entity.OrderRequest) (*entity.OrderResponse, error)
CheckInInquiry(ctx mycontext.Context, qrCode string, partnerID *int64) (*entity.CheckinResponse, error)
CheckInExecute(ctx mycontext.Context,
token string, partnerID *int64) (*entity.CheckinExecute, error)
Execute(ctx mycontext.Context, req *entity.OrderExecuteRequest) (*entity.ExecuteOrderResponse, error)
ProcessCallback(ctx context.Context, req *entity.CallbackRequest) error
GetAllHistoryOrders(ctx mycontext.Context, req entity.OrderSearch) ([]*entity.HistoryOrder, int, error)
CountSoldOfTicket(ctx mycontext.Context, req entity.OrderSearch) (*entity.TicketSold, error)
SumAmount(ctx mycontext.Context, req entity.OrderSearch) (*entity.Order, error)
GetDailySales(ctx mycontext.Context, req entity.OrderSearch) ([]entity.ProductDailySales, error)
GetPaymentDistribution(ctx mycontext.Context, req entity.OrderSearch) ([]entity.PaymentTypeDistribution, error)
GetByID(ctx mycontext.Context, id int64, referenceID string) (*entity.Order, error)
GetPrintDetail(ctx mycontext.Context, id int64) (*entity.OrderPrintDetail, error)
ProcessLinkQuCallback(ctx context.Context, req *entity.LinkQuCallback) error
}
type OSSService interface { type OSSService interface {
UploadFile(ctx context.Context, req *entity.UploadFileRequest) (*entity.UploadFileResponse, error) UploadFile(ctx context.Context, req *entity.UploadFileRequest) (*entity.UploadFileResponse, error)
} }
@ -183,10 +152,3 @@ type Balance interface {
WithdrawInquiry(ctx context.Context, req *entity.BalanceWithdrawInquiry) (*entity.BalanceWithdrawInquiryResponse, error) WithdrawInquiry(ctx context.Context, req *entity.BalanceWithdrawInquiry) (*entity.BalanceWithdrawInquiryResponse, error)
WithdrawExecute(ctx mycontext.Context, req *entity.WalletWithdrawRequest) (*entity.WalletWithdrawResponse, error) WithdrawExecute(ctx mycontext.Context, req *entity.WalletWithdrawRequest) (*entity.WalletWithdrawResponse, error)
} }
type DiscoverService interface {
Home(ctx context.Context, search *entity.DiscoverySearch) (*entity.DiscoverySearchResp, error)
Search(ctx context.Context, search *entity.DiscoverySearch) (*entity.DiscoverySearchResp, int64, error)
GetByID(ctx context.Context, id int64) (*entity.Site, error)
GetProductsByID(ctx context.Context, id int64) ([]*entity.Product, error)
}

View File

@ -1,70 +0,0 @@
package studio
import (
"context"
"enaklo-pos-be/internal/common/logger"
"enaklo-pos-be/internal/common/mycontext"
"enaklo-pos-be/internal/entity"
"enaklo-pos-be/internal/repository"
"go.uber.org/zap"
)
type StudioService struct {
repo repository.Studio
}
func NewStudioService(repo repository.Studio) *StudioService {
return &StudioService{
repo: repo,
}
}
func (s *StudioService) Create(ctx mycontext.Context, studioReq *entity.Studio) (*entity.Studio, error) {
newStudioDB := studioReq.NewStudiosDB()
newStudioDB.CreatedBy = ctx.RequestedBy()
newStudioDB, err := s.repo.CreateStudio(ctx, newStudioDB)
if err != nil {
logger.ContextLogger(ctx).Error("error when creating studio", zap.Error(err))
return nil, err
}
return newStudioDB.ToStudio(), nil
}
func (s *StudioService) Update(ctx mycontext.Context, id int64, studioReq *entity.Studio) (*entity.Studio, error) {
existingStudio, err := s.repo.GetStudioByID(ctx, id)
if err != nil {
return nil, err
}
existingStudio.ToUpdatedStudio(ctx.RequestedBy(), *studioReq)
updatedStudioDB, err := s.repo.UpdateStudio(ctx, existingStudio.ToStudioDB())
if err != nil {
logger.ContextLogger(ctx).Error("error when updating studio", zap.Error(err))
return nil, err
}
return updatedStudioDB.ToStudio(), nil
}
func (s *StudioService) GetByID(ctx context.Context, id int64) (*entity.Studio, error) {
studioDB, err := s.repo.GetStudioByID(ctx, id)
if err != nil {
logger.ContextLogger(ctx).Error("error when getting studio by id", zap.Error(err))
return nil, err
}
return studioDB.ToStudio(), nil
}
func (s *StudioService) Search(ctx context.Context, search entity.StudioSearch) ([]*entity.Studio, int, error) {
studios, total, err := s.repo.SearchStudios(ctx, search)
if err != nil {
logger.ContextLogger(ctx).Error("error when getting all studios", zap.Error(err))
return nil, 0, err
}
return studios.ToStudioList(), total, nil
}

View File

@ -0,0 +1,99 @@
package cashier_session
import (
"enaklo-pos-be/internal/common/logger"
"enaklo-pos-be/internal/common/mycontext"
"enaklo-pos-be/internal/entity"
"github.com/pkg/errors"
"go.uber.org/zap"
)
type Service interface {
OpenSession(ctx mycontext.Context, session *entity.CashierSession) (*entity.CashierSession, error)
CloseSession(ctx mycontext.Context, sessionID int64, closingAmount float64) (*entity.CashierSessionReport, error)
GetOpenSession(ctx mycontext.Context, cashierID int64) (*entity.CashierSession, error)
GetSessionReport(ctx mycontext.Context, sessionID int64) (*entity.CashierSessionReport, error)
}
type Repository interface {
CreateSession(ctx mycontext.Context, session *entity.CashierSession) (*entity.CashierSession, error)
CloseSession(ctx mycontext.Context, sessionID int64, closingAmount, expectedAmount float64) error
GetOpenSessionByCashierID(ctx mycontext.Context, cashierID int64) (*entity.CashierSession, error)
GetSessionByID(ctx mycontext.Context, sessionID int64) (*entity.CashierSession, error)
GetPaymentSummaryBySessionID(ctx mycontext.Context, sessionID int64) ([]entity.PaymentSummary, error)
}
type cashierSessionSvc struct {
repo Repository
}
func New(repo Repository) Service {
return &cashierSessionSvc{repo: repo}
}
func (s *cashierSessionSvc) OpenSession(ctx mycontext.Context, session *entity.CashierSession) (*entity.CashierSession, error) {
openSession, err := s.repo.GetOpenSessionByCashierID(ctx, session.CashierID)
if err != nil {
return nil, errors.Wrap(err, "failed to check existing open session")
}
if openSession != nil {
return nil, errors.New("cashier already has an open session")
}
newSession, err := s.repo.CreateSession(ctx, session)
if err != nil {
logger.ContextLogger(ctx).Error("failed to create cashier session", zap.Error(err))
return nil, errors.Wrap(err, "failed to create cashier session")
}
return newSession, nil
}
func (s *cashierSessionSvc) CloseSession(ctx mycontext.Context, sessionID int64, closingAmount float64) (*entity.CashierSessionReport, error) {
report, err := s.repo.GetPaymentSummaryBySessionID(ctx, sessionID)
if err != nil {
return nil, errors.Wrap(err, "failed to get payment summary")
}
var expectedAmount float64
for _, r := range report {
expectedAmount += r.TotalAmount
}
if err := s.repo.CloseSession(ctx, sessionID, closingAmount, expectedAmount); err != nil {
return nil, errors.Wrap(err, "failed to close session")
}
return &entity.CashierSessionReport{
SessionID: sessionID,
ClosingAmount: closingAmount,
ExpectedAmount: expectedAmount,
Payments: report,
}, nil
}
func (s *cashierSessionSvc) GetOpenSession(ctx mycontext.Context, cashierID int64) (*entity.CashierSession, error) {
session, err := s.repo.GetOpenSessionByCashierID(ctx, cashierID)
if err != nil {
return nil, errors.Wrap(err, "failed to get open session")
}
return session, nil
}
func (s *cashierSessionSvc) GetSessionReport(ctx mycontext.Context, sessionID int64) (*entity.CashierSessionReport, error) {
report, err := s.repo.GetPaymentSummaryBySessionID(ctx, sessionID)
if err != nil {
return nil, errors.Wrap(err, "failed to get payment summary")
}
var expectedAmount float64
for _, r := range report {
expectedAmount += r.TotalAmount
}
return &entity.CashierSessionReport{
SessionID: sessionID,
ExpectedAmount: expectedAmount,
Payments: report,
}, nil
}

View File

@ -0,0 +1,88 @@
package category
import (
"enaklo-pos-be/internal/common/logger"
"enaklo-pos-be/internal/common/mycontext"
"enaklo-pos-be/internal/entity"
"github.com/pkg/errors"
"go.uber.org/zap"
)
type Service interface {
Create(ctx mycontext.Context, category *entity.Category) (*entity.Category, error)
GetByPartnerID(ctx mycontext.Context, partnerID int64) ([]*entity.Category, error)
Update(ctx mycontext.Context, category *entity.Category) error
Delete(ctx mycontext.Context, id int64) error
GetByID(ctx mycontext.Context, id int64) (*entity.Category, error)
}
type Repository interface {
Create(ctx mycontext.Context, category *entity.Category) (*entity.Category, error)
GetByPartnerID(ctx mycontext.Context, partnerID int64) ([]*entity.Category, error)
Update(ctx mycontext.Context, category *entity.Category) error
Delete(ctx mycontext.Context, id int64) error
GetByID(ctx mycontext.Context, id int64) (*entity.Category, error)
}
type categorySvc struct {
repo Repository
}
func New(repo Repository) Service {
return &categorySvc{repo: repo}
}
func (s *categorySvc) Create(ctx mycontext.Context, category *entity.Category) (*entity.Category, error) {
existing, err := s.repo.GetByPartnerID(ctx, category.PartnerID)
if err != nil {
return nil, errors.Wrap(err, "failed to fetch categories")
}
for _, cat := range existing {
if cat.Name == category.Name {
return nil, errors.New("category name already exists for this partner")
}
}
newCategory, err := s.repo.Create(ctx, category)
if err != nil {
logger.ContextLogger(ctx).Error("failed to create category", zap.Error(err))
return nil, errors.Wrap(err, "failed to create category")
}
return newCategory, nil
}
func (s *categorySvc) GetByPartnerID(ctx mycontext.Context, partnerID int64) ([]*entity.Category, error) {
categories, err := s.repo.GetByPartnerID(ctx, partnerID)
if err != nil {
return nil, errors.Wrap(err, "failed to get categories by partner")
}
return categories, nil
}
func (s *categorySvc) Update(ctx mycontext.Context, category *entity.Category) error {
err := s.repo.Update(ctx, category)
if err != nil {
logger.ContextLogger(ctx).Error("failed to update category", zap.Error(err))
return errors.Wrap(err, "failed to update category")
}
return nil
}
func (s *categorySvc) Delete(ctx mycontext.Context, id int64) error {
err := s.repo.Delete(ctx, id)
if err != nil {
logger.ContextLogger(ctx).Error("failed to delete category", zap.Error(err))
return errors.Wrap(err, "failed to delete category")
}
return nil
}
func (s *categorySvc) GetByID(ctx mycontext.Context, id int64) (*entity.Category, error) {
category, err := s.repo.GetByID(ctx, id)
if err != nil {
return nil, errors.Wrap(err, "failed to get category by ID")
}
return category, nil
}

View File

@ -12,6 +12,13 @@ import (
func (s *orderSvc) CreateOrderInquiry(ctx mycontext.Context, func (s *orderSvc) CreateOrderInquiry(ctx mycontext.Context,
req *entity.OrderRequest) (*entity.OrderInquiryResponse, error) { req *entity.OrderRequest) (*entity.OrderInquiryResponse, error) {
cashierSession, err := s.cashierSvc.GetOpenSession(ctx, ctx.RequestedBy())
if err != nil {
logger.ContextLogger(ctx).Error("no open session found for cashier", zap.Error(err))
return nil, err
}
productIDs, filteredItems, err := s.ValidateOrderItems(ctx, req.OrderItems) productIDs, filteredItems, err := s.ValidateOrderItems(ctx, req.OrderItems)
if err != nil { if err != nil {
return nil, err return nil, err
@ -60,6 +67,7 @@ func (s *orderSvc) CreateOrderInquiry(ctx mycontext.Context,
req.PaymentProvider, req.PaymentProvider,
req.TableNumber, req.TableNumber,
req.OrderType, req.OrderType,
cashierSession.ID,
) )
for _, item := range req.OrderItems { for _, item := range req.OrderItems {

View File

@ -6,6 +6,7 @@ import (
"enaklo-pos-be/internal/constants" "enaklo-pos-be/internal/constants"
"enaklo-pos-be/internal/entity" "enaklo-pos-be/internal/entity"
"fmt" "fmt"
"github.com/pkg/errors"
"go.uber.org/zap" "go.uber.org/zap"
"time" "time"
) )
@ -36,6 +37,20 @@ func (s *orderSvc) ExecuteOrderInquiry(ctx mycontext.Context,
}, nil }, nil
} }
func (s *orderSvc) RefundRequest(ctx mycontext.Context, partnerID, orderID int64, reason string) error {
order, err := s.repo.FindByIDAndPartnerID(ctx, partnerID, orderID)
if err != nil {
logger.ContextLogger(ctx).Error("failed to create order", zap.Error(err))
return err
}
if order.Status != "PAID" {
return errors.New("only paid order can be refund")
}
return s.repo.UpdateOrder(ctx, order.ID, "REFUNDED", reason)
}
func (s *orderSvc) processPostOrderActions( func (s *orderSvc) processPostOrderActions(
ctx mycontext.Context, ctx mycontext.Context,
order *entity.Order, order *entity.Order,

View File

@ -12,6 +12,7 @@ type Repository interface {
CreateInquiry(ctx mycontext.Context, inquiry *entity.OrderInquiry) (*entity.OrderInquiry, error) CreateInquiry(ctx mycontext.Context, inquiry *entity.OrderInquiry) (*entity.OrderInquiry, error)
FindInquiryByID(ctx mycontext.Context, id string) (*entity.OrderInquiry, error) FindInquiryByID(ctx mycontext.Context, id string) (*entity.OrderInquiry, error)
UpdateInquiryStatus(ctx mycontext.Context, id string, status string) error UpdateInquiryStatus(ctx mycontext.Context, id string, status string) error
UpdateOrder(ctx mycontext.Context, id int64, status string, description string) error
GetOrderHistoryByPartnerID(ctx mycontext.Context, partnerID int64, req entity.SearchRequest) ([]*entity.Order, int64, error) GetOrderHistoryByPartnerID(ctx mycontext.Context, partnerID int64, req entity.SearchRequest) ([]*entity.Order, int64, error)
GetOrderPaymentMethodBreakdown( GetOrderPaymentMethodBreakdown(
ctx mycontext.Context, ctx mycontext.Context,
@ -65,6 +66,7 @@ type Service interface {
req *entity.OrderRequest) (*entity.OrderInquiryResponse, error) req *entity.OrderRequest) (*entity.OrderInquiryResponse, error)
ExecuteOrderInquiry(ctx mycontext.Context, ExecuteOrderInquiry(ctx mycontext.Context,
token string, paymentMethod, paymentProvider string, inProgressOrderID int64) (*entity.OrderResponse, error) token string, paymentMethod, paymentProvider string, inProgressOrderID int64) (*entity.OrderResponse, error)
RefundRequest(ctx mycontext.Context, partnerID, orderID int64, reason string) error
GetOrderHistory(ctx mycontext.Context, partnerID int64, request entity.SearchRequest) ([]*entity.Order, int64, error) GetOrderHistory(ctx mycontext.Context, partnerID int64, request entity.SearchRequest) ([]*entity.Order, int64, error)
CalculateOrderTotals( CalculateOrderTotals(
ctx mycontext.Context, ctx mycontext.Context,
@ -122,6 +124,10 @@ type VoucherUndianRepo interface {
CreateUndianVouchers(ctx mycontext.Context, vouchers []*entity.UndianVoucherDB) error CreateUndianVouchers(ctx mycontext.Context, vouchers []*entity.UndianVoucherDB) error
} }
type CashierSvc interface {
GetOpenSession(ctx mycontext.Context, cashierID int64) (*entity.CashierSession, error)
}
type orderSvc struct { type orderSvc struct {
repo Repository repo Repository
product ProductService product ProductService
@ -133,6 +139,7 @@ type orderSvc struct {
partnerSetting PartnerSettings partnerSetting PartnerSettings
inprogressOrder InProgressOrderRepository inprogressOrder InProgressOrderRepository
voucherUndianRepo VoucherUndianRepo voucherUndianRepo VoucherUndianRepo
cashierSvc CashierSvc
} }
func New( func New(
@ -145,6 +152,7 @@ func New(
notification NotificationService, notification NotificationService,
partnerSetting PartnerSettings, partnerSetting PartnerSettings,
voucherUndianRepo VoucherUndianRepo, voucherUndianRepo VoucherUndianRepo,
cashierSvc CashierSvc,
) Service { ) Service {
return &orderSvc{ return &orderSvc{
repo: repo, repo: repo,
@ -156,5 +164,6 @@ func New(
notification: notification, notification: notification,
partnerSetting: partnerSetting, partnerSetting: partnerSetting,
voucherUndianRepo: voucherUndianRepo, voucherUndianRepo: voucherUndianRepo,
cashierSvc: cashierSvc,
} }
} }