update
This commit is contained in:
parent
118ec58521
commit
c642c5c61b
@ -23,6 +23,8 @@ const (
|
|||||||
errTicketAlreadyUsed ErrType = "Ticket Already Used."
|
errTicketAlreadyUsed ErrType = "Ticket Already Used."
|
||||||
errProductIsRequired ErrType = "Product"
|
errProductIsRequired ErrType = "Product"
|
||||||
errEmailAndPhoneNumberRequired ErrType = "Email or Phone is required"
|
errEmailAndPhoneNumberRequired ErrType = "Email or Phone is required"
|
||||||
|
errEmailAlreadyRegistered ErrType = "Email is already registered"
|
||||||
|
errPhoneNumberAlreadyRegistered ErrType = "Phone is already registered"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -43,6 +45,8 @@ var (
|
|||||||
ErrorInvalidLicense = NewServiceException(errInactivePartner)
|
ErrorInvalidLicense = NewServiceException(errInactivePartner)
|
||||||
ErrorTicketInvalidOrAlreadyUsed = NewServiceException(errTicketAlreadyUsed)
|
ErrorTicketInvalidOrAlreadyUsed = NewServiceException(errTicketAlreadyUsed)
|
||||||
ErrorPhoneNumberEmailIsRequired = NewServiceException(errEmailAndPhoneNumberRequired)
|
ErrorPhoneNumberEmailIsRequired = NewServiceException(errEmailAndPhoneNumberRequired)
|
||||||
|
ErrorPhoneNumberIsAlreadyRegistered = NewServiceException(errPhoneNumberAlreadyRegistered)
|
||||||
|
ErrorEmailIsAlreadyRegistered = NewServiceException(errEmailAlreadyRegistered)
|
||||||
)
|
)
|
||||||
|
|
||||||
type Error interface {
|
type Error interface {
|
||||||
|
|||||||
2
internal/entity/customer.go
Normal file
2
internal/entity/customer.go
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
package entity
|
||||||
|
|
||||||
29
internal/entity/in_progress_order.go
Normal file
29
internal/entity/in_progress_order.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package entity
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type InProgressOrder struct {
|
||||||
|
ID string
|
||||||
|
PartnerID int64
|
||||||
|
CustomerID *int64
|
||||||
|
CustomerName string
|
||||||
|
CreatedBy int64
|
||||||
|
PaymentType string
|
||||||
|
PaymentProvider string
|
||||||
|
OrderItems []InProgressOrderItem
|
||||||
|
Payment Payment
|
||||||
|
User User
|
||||||
|
Source string
|
||||||
|
OrderType string
|
||||||
|
TableNumber string
|
||||||
|
CreatedAt time.Time
|
||||||
|
UpdatedAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
type InProgressOrderItem struct {
|
||||||
|
ID int64
|
||||||
|
InProgressOrderID int64
|
||||||
|
ItemID int64
|
||||||
|
Quantity int
|
||||||
|
Product *Product
|
||||||
|
}
|
||||||
@ -12,17 +12,22 @@ type Order struct {
|
|||||||
Total float64 `gorm:"type:numeric;not null;column:total"`
|
Total float64 `gorm:"type:numeric;not null;column:total"`
|
||||||
Fee float64 `gorm:"type:numeric;not null;column:fee"`
|
Fee float64 `gorm:"type:numeric;not null;column:fee"`
|
||||||
CustomerID *int64
|
CustomerID *int64
|
||||||
|
CustomerName string
|
||||||
InquiryID *string
|
InquiryID *string
|
||||||
Site *Site `gorm:"foreignKey:SiteID;constraint:OnDelete:CASCADE;"`
|
Site *Site `gorm:"foreignKey:SiteID;constraint:OnDelete:CASCADE;"`
|
||||||
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"`
|
||||||
CreatedBy int64 `gorm:"type:int;column:created_by"`
|
CreatedBy int64 `gorm:"type:int;column:created_by"`
|
||||||
PaymentType string `gorm:"type:varchar;column:payment_type"`
|
PaymentType string `gorm:"type:varchar;column:payment_type"`
|
||||||
|
PaymentProvider string `gorm:"type:varchar;column:payment_provider"`
|
||||||
UpdatedBy int64 `gorm:"type:int;column:updated_by"`
|
UpdatedBy int64 `gorm:"type:int;column:updated_by"`
|
||||||
OrderItems []OrderItem `gorm:"foreignKey:OrderID;constraint:OnDelete:CASCADE;"`
|
OrderItems []OrderItem `gorm:"foreignKey:OrderID;constraint:OnDelete:CASCADE;"`
|
||||||
Payment Payment `gorm:"foreignKey:OrderID;constraint:OnDelete:CASCADE;"`
|
Payment Payment `gorm:"foreignKey:OrderID;constraint:OnDelete:CASCADE;"`
|
||||||
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"`
|
||||||
|
TableNumber string
|
||||||
|
InProgressOrderID string
|
||||||
}
|
}
|
||||||
|
|
||||||
type OrderDB struct {
|
type OrderDB struct {
|
||||||
@ -97,6 +102,9 @@ type OrderRequest struct {
|
|||||||
CustomerName string
|
CustomerName string
|
||||||
CustomerEmail string
|
CustomerEmail string
|
||||||
CustomerPhoneNumber string
|
CustomerPhoneNumber string
|
||||||
|
TableNumber string
|
||||||
|
PaymentProvider string
|
||||||
|
OrderType string
|
||||||
}
|
}
|
||||||
|
|
||||||
type OrderItemRequest struct {
|
type OrderItemRequest struct {
|
||||||
@ -111,11 +119,7 @@ type OrderExecuteRequest struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (o *Order) SetExecutePaymentStatus() {
|
func (o *Order) SetExecutePaymentStatus() {
|
||||||
if o.PaymentType == "CASH" {
|
|
||||||
o.Status = "PAID"
|
o.Status = "PAID"
|
||||||
return
|
|
||||||
}
|
|
||||||
o.Status = "PENDING"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type CallbackRequest struct {
|
type CallbackRequest struct {
|
||||||
|
|||||||
@ -23,6 +23,9 @@ type OrderInquiry struct {
|
|||||||
UpdatedAt time.Time `json:"updated_at"`
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
ExpiresAt time.Time `json:"expires_at"`
|
ExpiresAt time.Time `json:"expires_at"`
|
||||||
OrderItems []OrderItem `json:"order_items"`
|
OrderItems []OrderItem `json:"order_items"`
|
||||||
|
PaymentProvider string `json:"payment_provider"`
|
||||||
|
TableNumber string `json:"table_number"`
|
||||||
|
OrderType string `json:"order_type"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type OrderCalculation struct {
|
type OrderCalculation struct {
|
||||||
@ -48,6 +51,9 @@ func NewOrderInquiry(
|
|||||||
customerName string,
|
customerName string,
|
||||||
customerPhoneNumber string,
|
customerPhoneNumber string,
|
||||||
customerEmail string,
|
customerEmail string,
|
||||||
|
paymentProvider string,
|
||||||
|
tableNumber string,
|
||||||
|
orderType string,
|
||||||
) *OrderInquiry {
|
) *OrderInquiry {
|
||||||
return &OrderInquiry{
|
return &OrderInquiry{
|
||||||
ID: constants.GenerateUUID(),
|
ID: constants.GenerateUUID(),
|
||||||
@ -66,6 +72,9 @@ func NewOrderInquiry(
|
|||||||
CustomerName: customerName,
|
CustomerName: customerName,
|
||||||
CustomerEmail: customerEmail,
|
CustomerEmail: customerEmail,
|
||||||
CustomerPhoneNumber: customerPhoneNumber,
|
CustomerPhoneNumber: customerPhoneNumber,
|
||||||
|
PaymentProvider: paymentProvider,
|
||||||
|
TableNumber: tableNumber,
|
||||||
|
OrderType: orderType,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,7 +89,7 @@ func (oi *OrderInquiry) AddOrderItem(item OrderItemRequest, product *Product) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *OrderInquiry) ToOrder(paymentMethod string) *Order {
|
func (i *OrderInquiry) ToOrder(paymentMethod, paymentProvider string) *Order {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|
||||||
order := &Order{
|
order := &Order{
|
||||||
@ -92,10 +101,12 @@ func (i *OrderInquiry) ToOrder(paymentMethod string) *Order {
|
|||||||
Fee: i.Fee,
|
Fee: i.Fee,
|
||||||
Total: i.Total,
|
Total: i.Total,
|
||||||
PaymentType: paymentMethod,
|
PaymentType: paymentMethod,
|
||||||
|
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,
|
||||||
}
|
}
|
||||||
|
|
||||||
for idx, item := range i.OrderItems {
|
for idx, item := range i.OrderItems {
|
||||||
|
|||||||
@ -51,6 +51,8 @@ type Customer struct {
|
|||||||
ResetPassword bool
|
ResetPassword bool
|
||||||
CustomerID string
|
CustomerID string
|
||||||
BirthDate time.Time
|
BirthDate time.Time
|
||||||
|
VerificationID string
|
||||||
|
OTP string
|
||||||
}
|
}
|
||||||
|
|
||||||
type AuthenticateUser struct {
|
type AuthenticateUser struct {
|
||||||
@ -117,3 +119,9 @@ func (u User) HashedPassword(password string) (string, error) {
|
|||||||
|
|
||||||
return string(hashedPassword), nil
|
return string(hashedPassword), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c Customer) HashedPassword() string {
|
||||||
|
hashedPassword, _ := bcrypt.GenerateFromPassword([]byte(c.Password), bcrypt.DefaultCost)
|
||||||
|
|
||||||
|
return string(hashedPassword)
|
||||||
|
}
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
package customerauth
|
package customerauth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
auth2 "enaklo-pos-be/internal/handlers/request"
|
||||||
|
"enaklo-pos-be/internal/services/v2/customer"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
@ -8,7 +10,6 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
"enaklo-pos-be/internal/common/errors"
|
"enaklo-pos-be/internal/common/errors"
|
||||||
auth2 "enaklo-pos-be/internal/handlers/request"
|
|
||||||
"enaklo-pos-be/internal/handlers/response"
|
"enaklo-pos-be/internal/handlers/response"
|
||||||
"enaklo-pos-be/internal/services"
|
"enaklo-pos-be/internal/services"
|
||||||
)
|
)
|
||||||
@ -16,6 +17,7 @@ import (
|
|||||||
type AuthHandler struct {
|
type AuthHandler struct {
|
||||||
service services.Auth
|
service services.Auth
|
||||||
userService services.User
|
userService services.User
|
||||||
|
customerSvc customer.Service
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *AuthHandler) Route(group *gin.RouterGroup, jwt gin.HandlerFunc) {
|
func (a *AuthHandler) Route(group *gin.RouterGroup, jwt gin.HandlerFunc) {
|
||||||
@ -24,12 +26,14 @@ func (a *AuthHandler) Route(group *gin.RouterGroup, jwt gin.HandlerFunc) {
|
|||||||
authRoute.POST("/forgot-password", a.ForgotPassword)
|
authRoute.POST("/forgot-password", a.ForgotPassword)
|
||||||
authRoute.POST("/reset-password", jwt, a.ResetPassword)
|
authRoute.POST("/reset-password", jwt, a.ResetPassword)
|
||||||
authRoute.POST("/register", a.Register)
|
authRoute.POST("/register", a.Register)
|
||||||
|
authRoute.POST("/verify", a.VerifyRegistration)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAuthHandler(service services.Auth, userService services.User) *AuthHandler {
|
func NewAuthHandler(service services.Auth, userService services.User, customerSvc customer.Service) *AuthHandler {
|
||||||
return &AuthHandler{
|
return &AuthHandler{
|
||||||
service: service,
|
service: service,
|
||||||
userService: userService,
|
userService: userService,
|
||||||
|
customerSvc: customerSvc,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,31 +151,47 @@ func (h *AuthHandler) ResetPassword(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *AuthHandler) Register(c *gin.Context) {
|
func (h *AuthHandler) Register(c *gin.Context) {
|
||||||
var req auth2.UserRegister
|
var req auth2.CustomerRegister
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
response.ErrorWrapper(c, errors.ErrorBadRequest)
|
response.ErrorWrapper(c, errors.ErrorBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := auth2.GetMyContext(c)
|
ctx := auth2.GetMyContext(c)
|
||||||
res, err := h.userService.Create(ctx, req.ToEntity())
|
customer, err := h.customerSvc.RegistrationMember(ctx, req.ToEntity())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.ErrorWrapper(c, err)
|
response.ErrorWrapper(c, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
resp := response.UserRegister{
|
c.JSON(http.StatusOK, response.BaseResponse{
|
||||||
ID: res.ID,
|
Success: true,
|
||||||
Name: res.Name,
|
Status: http.StatusOK,
|
||||||
Email: res.Email,
|
Data: response.CustomerRegistrationResp{
|
||||||
Status: string(res.Status),
|
EmailVerificationRequired: true,
|
||||||
CreatedAt: res.CreatedAt,
|
PhoneVerificationRequired: false,
|
||||||
UpdatedAt: res.UpdatedAt,
|
VerificationID: customer.VerificationID,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *AuthHandler) VerifyRegistration(c *gin.Context) {
|
||||||
|
var req auth2.VerifyEmailRequest
|
||||||
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
|
response.ErrorWrapper(c, errors.ErrorBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := auth2.GetMyContext(c)
|
||||||
|
err := h.customerSvc.VerifyOTP(ctx, req.VerificationID, req.OTPCode)
|
||||||
|
if err != nil {
|
||||||
|
response.ErrorWrapper(c, err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.JSON(http.StatusOK, response.BaseResponse{
|
c.JSON(http.StatusOK, response.BaseResponse{
|
||||||
Success: true,
|
Success: true,
|
||||||
Status: http.StatusOK,
|
Status: http.StatusOK,
|
||||||
Data: resp,
|
Message: "Email verification successful",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
173
internal/handlers/http/inprogress_order.go
Normal file
173
internal/handlers/http/inprogress_order.go
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
package http
|
||||||
|
|
||||||
|
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/v2/inprogress_order"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/go-playground/validator/v10"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
type InProgressOrderHandler struct {
|
||||||
|
service inprogress_order.InProgressOrderService
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewInProgressOrderHandler(service inprogress_order.InProgressOrderService) *InProgressOrderHandler {
|
||||||
|
return &InProgressOrderHandler{
|
||||||
|
service: service,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *InProgressOrderHandler) Route(group *gin.RouterGroup, jwt gin.HandlerFunc) {
|
||||||
|
route := group.Group("/inprogress-order")
|
||||||
|
route.POST("/save", jwt, h.Save)
|
||||||
|
route.GET("/list", jwt, h.GetByPartnerID)
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateInProgressOrderRequest struct {
|
||||||
|
CustomerID *int64 `json:"customer_id"`
|
||||||
|
CustomerName string `json:"customer_name" validate:"required_without=CustomerID"`
|
||||||
|
CustomerEmail string `json:"customer_email"`
|
||||||
|
CustomerPhoneNumber string `json:"customer_phone_number"`
|
||||||
|
PaymentMethod string `json:"payment_method"`
|
||||||
|
OrderItems []InProgressOrderItemRequest `json:"order_items" validate:"required,min=1,dive"`
|
||||||
|
OrderType string `json:"order_type"`
|
||||||
|
PaymentProvider string `json:"payment_provider"`
|
||||||
|
TableNumber string `json:"table_number"`
|
||||||
|
InProgressOrderID string `json:"in_progress_order_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type InProgressOrderItemRequest struct {
|
||||||
|
ProductID int64 `json:"product_id" validate:"required"`
|
||||||
|
Quantity int `json:"quantity" validate:"required,min=1"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpdateInProgressOrderRequest struct {
|
||||||
|
Status string `json:"status" validate:"required"`
|
||||||
|
Amount float64 `json:"amount" validate:"required,min=0"`
|
||||||
|
Fee float64 `json:"fee" validate:"min=0"`
|
||||||
|
Total float64 `json:"total" validate:"required,min=0"`
|
||||||
|
PaymentType string `json:"payment_type" validate:"required"`
|
||||||
|
OrderItems []InProgressOrderItemRequest `json:"order_items" validate:"required,min=1,dive"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *InProgressOrderHandler) Save(c *gin.Context) {
|
||||||
|
ctx := request.GetMyContext(c)
|
||||||
|
userID := ctx.RequestedBy()
|
||||||
|
partnerID := ctx.GetPartnerID()
|
||||||
|
|
||||||
|
var req CreateInProgressOrderRequest
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
orderItems := make([]entity.InProgressOrderItem, len(req.OrderItems))
|
||||||
|
for i, item := range req.OrderItems {
|
||||||
|
orderItems[i] = entity.InProgressOrderItem{
|
||||||
|
ItemID: item.ProductID,
|
||||||
|
Quantity: item.Quantity,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
order := &entity.InProgressOrder{
|
||||||
|
PartnerID: *partnerID,
|
||||||
|
CustomerID: req.CustomerID,
|
||||||
|
CustomerName: req.CustomerName,
|
||||||
|
CreatedBy: userID,
|
||||||
|
OrderItems: orderItems,
|
||||||
|
TableNumber: req.TableNumber,
|
||||||
|
OrderType: req.OrderType,
|
||||||
|
ID: req.InProgressOrderID,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := h.service.Save(ctx, order)
|
||||||
|
if err != nil {
|
||||||
|
response.ErrorWrapper(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusCreated, response.BaseResponse{
|
||||||
|
Success: true,
|
||||||
|
Status: http.StatusCreated,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func mapToInProgressOrderResponse(order *entity.InProgressOrder) map[string]interface{} {
|
||||||
|
orderItems := make([]map[string]interface{}, len(order.OrderItems))
|
||||||
|
for i, item := range order.OrderItems {
|
||||||
|
orderItems[i] = map[string]interface{}{
|
||||||
|
"id": item.ID,
|
||||||
|
"item_id": item.ItemID,
|
||||||
|
"quantity": item.Quantity,
|
||||||
|
"name": item.Product.Name,
|
||||||
|
"price": item.Product.Price,
|
||||||
|
"image": item.Product.Image,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return map[string]interface{}{
|
||||||
|
"id": order.ID,
|
||||||
|
"partner_id": order.PartnerID,
|
||||||
|
"customer_id": order.CustomerID,
|
||||||
|
"customer_name": order.CustomerName,
|
||||||
|
"payment_type": order.PaymentType,
|
||||||
|
"source": order.Source,
|
||||||
|
"created_by": order.CreatedBy,
|
||||||
|
"created_at": order.CreatedAt,
|
||||||
|
"updated_at": order.UpdatedAt,
|
||||||
|
"order_items": orderItems,
|
||||||
|
"table_number": order.TableNumber,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *InProgressOrderHandler) GetByPartnerID(c *gin.Context) {
|
||||||
|
ctx := request.GetMyContext(c)
|
||||||
|
|
||||||
|
limitStr := c.DefaultQuery("limit", "10")
|
||||||
|
offsetStr := c.DefaultQuery("offset", "0")
|
||||||
|
|
||||||
|
limit, err := strconv.Atoi(limitStr)
|
||||||
|
if err != nil || limit < 0 {
|
||||||
|
limit = 10
|
||||||
|
}
|
||||||
|
|
||||||
|
offset, err := strconv.Atoi(offsetStr)
|
||||||
|
if err != nil || offset < 0 {
|
||||||
|
offset = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
orders, err := h.service.GetOrdersByPartnerID(ctx, *ctx.GetPartnerID(), limit, offset)
|
||||||
|
if err != nil {
|
||||||
|
response.ErrorWrapper(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
orderResponses := make([]map[string]interface{}, len(orders))
|
||||||
|
for i, order := range orders {
|
||||||
|
orderResponses[i] = mapToInProgressOrderResponse(order)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, response.BaseResponse{
|
||||||
|
Success: true,
|
||||||
|
Status: http.StatusOK,
|
||||||
|
Data: map[string]interface{}{
|
||||||
|
"orders": orderResponses,
|
||||||
|
"pagination": map[string]interface{}{
|
||||||
|
"limit": limit,
|
||||||
|
"offset": offset,
|
||||||
|
"count": len(orders),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
@ -35,6 +35,17 @@ type InquiryRequest struct {
|
|||||||
CustomerPhoneNumber string `json:"customer_phone_number"`
|
CustomerPhoneNumber string `json:"customer_phone_number"`
|
||||||
PaymentMethod string `json:"payment_method" validate:"required"`
|
PaymentMethod string `json:"payment_method" validate:"required"`
|
||||||
OrderItems []OrderItemRequest `json:"order_items" validate:"required,min=1,dive"`
|
OrderItems []OrderItemRequest `json:"order_items" validate:"required,min=1,dive"`
|
||||||
|
OrderType string `json:"order_type"`
|
||||||
|
PaymentProvider string `json:"payment_provider"`
|
||||||
|
TableNumber string `json:"table_number"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *InquiryRequest) GetPaymentProvider() string {
|
||||||
|
if o.PaymentMethod == "CASH" {
|
||||||
|
return "CASH"
|
||||||
|
}
|
||||||
|
|
||||||
|
return o.PaymentProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
type OrderItemRequest struct {
|
type OrderItemRequest struct {
|
||||||
@ -44,6 +55,8 @@ type OrderItemRequest struct {
|
|||||||
|
|
||||||
type ExecuteRequest struct {
|
type ExecuteRequest struct {
|
||||||
PaymentMethod string `json:"payment_method" validate:"required"`
|
PaymentMethod string `json:"payment_method" validate:"required"`
|
||||||
|
PaymentProvider string `json:"payment_provider"`
|
||||||
|
InProgressOrderID string `json:"in_progress_order_id"`
|
||||||
Token string `json:"token"`
|
Token string `json:"token"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,6 +95,9 @@ func (h *Handler) Inquiry(c *gin.Context) {
|
|||||||
CustomerName: req.CustomerName,
|
CustomerName: req.CustomerName,
|
||||||
CustomerEmail: req.CustomerEmail,
|
CustomerEmail: req.CustomerEmail,
|
||||||
CustomerPhoneNumber: req.CustomerPhoneNumber,
|
CustomerPhoneNumber: req.CustomerPhoneNumber,
|
||||||
|
OrderType: req.OrderType,
|
||||||
|
PaymentProvider: req.GetPaymentProvider(),
|
||||||
|
TableNumber: req.TableNumber,
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err := h.service.CreateOrderInquiry(ctx, orderReq)
|
result, err := h.service.CreateOrderInquiry(ctx, orderReq)
|
||||||
@ -112,7 +128,7 @@ func (h *Handler) Execute(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err := h.service.ExecuteOrderInquiry(ctx, req.Token, req.PaymentMethod)
|
result, err := h.service.ExecuteOrderInquiry(ctx, req.Token, req.PaymentMethod, req.PaymentProvider, req.InProgressOrderID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.ErrorWrapper(c, err)
|
response.ErrorWrapper(c, err)
|
||||||
return
|
return
|
||||||
|
|||||||
49
internal/handlers/request/customer.go
Normal file
49
internal/handlers/request/customer.go
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
package request
|
||||||
|
|
||||||
|
import (
|
||||||
|
"enaklo-pos-be/internal/entity"
|
||||||
|
"github.com/go-playground/validator/v10"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CustomerRegister struct {
|
||||||
|
Name string `json:"name" validate:"required" binding:"required"`
|
||||||
|
Email string `json:"email" validate:"required" binding:"required"`
|
||||||
|
PhoneNumber string `json:"phone_number" validate:"required" binding:"required"`
|
||||||
|
BirthDate string `json:"birth_date" validate:"required" binding:"required"`
|
||||||
|
Password string `json:"password" validate:"required" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CustomerRegister) Validate() error {
|
||||||
|
validate := validator.New()
|
||||||
|
if err := validate.Struct(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CustomerRegister) GetBirthdate() (time.Time, error) {
|
||||||
|
parsedDate, err := time.Parse("02-01-2006", c.BirthDate)
|
||||||
|
if err != nil {
|
||||||
|
return time.Time{}, err
|
||||||
|
}
|
||||||
|
return parsedDate, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CustomerRegister) ToEntity() *entity.Customer {
|
||||||
|
birthdate, _ := c.GetBirthdate()
|
||||||
|
return &entity.Customer{
|
||||||
|
Name: c.Name,
|
||||||
|
Email: strings.ToLower(c.Email),
|
||||||
|
PhoneNumber: c.PhoneNumber,
|
||||||
|
Password: c.Password,
|
||||||
|
BirthDate: birthdate,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type VerifyEmailRequest struct {
|
||||||
|
VerificationID string `json:"verification_id" binding:"required"`
|
||||||
|
OTPCode string `json:"otp_code" binding:"required"`
|
||||||
|
}
|
||||||
@ -11,6 +11,8 @@ type Order struct {
|
|||||||
CustomerPhone string `json:"customer_phone"`
|
CustomerPhone string `json:"customer_phone"`
|
||||||
CustomerEmail string `json:"customer_email"`
|
CustomerEmail string `json:"customer_email"`
|
||||||
PaymentMethod string `json:"payment_method"`
|
PaymentMethod string `json:"payment_method"`
|
||||||
|
PaymentProvider string `json:"payment_provider"`
|
||||||
|
TableNumber string `json:"table_number"`
|
||||||
OrderItems []OrderItem `json:"order_items"`
|
OrderItems []OrderItem `json:"order_items"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -174,7 +174,5 @@ func (u *UserRegister) ToEntity() *entity.User {
|
|||||||
Email: strings.ToLower(u.Email),
|
Email: strings.ToLower(u.Email),
|
||||||
PhoneNumber: u.PhoneNumber,
|
PhoneNumber: u.PhoneNumber,
|
||||||
Password: u.Password,
|
Password: u.Password,
|
||||||
RoleID: role.Customer,
|
|
||||||
UserType: "CUSTOMER",
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,3 +33,9 @@ func MapToCustomerListResponse(customers *entity.MemberList) []CustomerResponse
|
|||||||
|
|
||||||
return responseList
|
return responseList
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CustomerRegistrationResp struct {
|
||||||
|
EmailVerificationRequired bool `json:"email_verification_required"`
|
||||||
|
PhoneVerificationRequired bool `json:"phone_verification_required"`
|
||||||
|
VerificationID string `json:"verification_id"`
|
||||||
|
}
|
||||||
|
|||||||
@ -49,7 +49,7 @@ type UserRegister struct {
|
|||||||
ID int64 `json:"id"`
|
ID int64 `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
Status string `json:"status"`
|
PhoneNumber string `json:"phone_number"`
|
||||||
CreatedAt time.Time `json:"created_at,omitempty"`
|
CreatedAt time.Time `json:"created_at,omitempty"`
|
||||||
UpdatedAt time.Time `json:"updated_at,omitempty"`
|
UpdatedAt time.Time `json:"updated_at,omitempty"`
|
||||||
}
|
}
|
||||||
|
|||||||
267
internal/repository/In_progress_orde_repo.go
Normal file
267
internal/repository/In_progress_orde_repo.go
Normal file
@ -0,0 +1,267 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"enaklo-pos-be/internal/common/mycontext"
|
||||||
|
"enaklo-pos-be/internal/constants"
|
||||||
|
"enaklo-pos-be/internal/entity"
|
||||||
|
"enaklo-pos-be/internal/repository/models"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
time2 "time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type InProgressOrderRepository interface {
|
||||||
|
CreateOrUpdate(ctx mycontext.Context, order *entity.InProgressOrder) (*entity.InProgressOrder, error)
|
||||||
|
GetListByPartnerID(ctx mycontext.Context, partnerID int64, limit, offset int) ([]*entity.InProgressOrder, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type inprogressOrderRepository struct {
|
||||||
|
db *gorm.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewInProgressOrderRepository(db *gorm.DB) *inprogressOrderRepository {
|
||||||
|
return &inprogressOrderRepository{db: db}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *inprogressOrderRepository) CreateOrUpdate(ctx mycontext.Context, order *entity.InProgressOrder) (*entity.InProgressOrder, error) {
|
||||||
|
isUpdate := order.ID != ""
|
||||||
|
|
||||||
|
tx := r.db.Begin()
|
||||||
|
if tx.Error != nil {
|
||||||
|
return nil, errors.Wrap(tx.Error, "failed to begin transaction")
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
orderDB := r.toInProgressOrderDBModel(order)
|
||||||
|
|
||||||
|
if isUpdate {
|
||||||
|
var existingOrder models.InProgressOrderDB
|
||||||
|
if err := tx.First(&existingOrder, order.ID).Error; err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return nil, errors.Wrap(err, "order not found for update")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tx.Model(&orderDB).Updates(orderDB).Error; err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return nil, errors.Wrap(err, "failed to update order")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tx.Where("in_progress_order_id = ?", order.ID).Delete(&models.InProgressOrderItemDB{}).Error; err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return nil, errors.Wrap(err, "failed to delete existing order items")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := tx.Create(&orderDB).Error; err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return nil, errors.Wrap(err, "failed to insert order")
|
||||||
|
}
|
||||||
|
|
||||||
|
order.ID = orderDB.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
var itemIDs []int64
|
||||||
|
for i := range order.OrderItems {
|
||||||
|
itemIDs = append(itemIDs, order.OrderItems[i].ItemID)
|
||||||
|
}
|
||||||
|
|
||||||
|
var products []models.ProductDB
|
||||||
|
if len(itemIDs) > 0 {
|
||||||
|
if err := tx.Where("id IN ?", itemIDs).Find(&products).Error; err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return nil, errors.Wrap(err, "failed to fetch products")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
productMap := make(map[int64]models.ProductDB)
|
||||||
|
for _, product := range products {
|
||||||
|
productMap[product.ID] = product
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range order.OrderItems {
|
||||||
|
item := &order.OrderItems[i]
|
||||||
|
|
||||||
|
itemDB := r.toOrderItemDBModel(item, orderDB.ID)
|
||||||
|
|
||||||
|
if err := tx.Create(&itemDB).Error; err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return nil, errors.Wrap(err, "failed to insert order item")
|
||||||
|
}
|
||||||
|
|
||||||
|
item.ID = itemDB.ID
|
||||||
|
|
||||||
|
if product, exists := productMap[item.ItemID]; exists {
|
||||||
|
item.Product = r.toDomainProductModel(&product)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tx.Commit().Error; err != nil {
|
||||||
|
return nil, errors.Wrap(err, "failed to commit transaction")
|
||||||
|
}
|
||||||
|
|
||||||
|
return order, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *inprogressOrderRepository) GetListByPartnerID(ctx mycontext.Context, partnerID int64, limit, offset int) ([]*entity.InProgressOrder, error) {
|
||||||
|
var ordersDB []models.InProgressOrderDB
|
||||||
|
query := r.db.Where("partner_id = ?", partnerID).Order("created_at DESC")
|
||||||
|
|
||||||
|
if limit > 0 {
|
||||||
|
query = query.Limit(limit)
|
||||||
|
}
|
||||||
|
|
||||||
|
if offset > 0 {
|
||||||
|
query = query.Offset(offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := query.Preload("OrderItems.Product").Find(&ordersDB).Error; err != nil {
|
||||||
|
return nil, errors.Wrap(err, "failed to find orders by partner ID")
|
||||||
|
}
|
||||||
|
|
||||||
|
orders := make([]*entity.InProgressOrder, 0, len(ordersDB))
|
||||||
|
for _, orderDB := range ordersDB {
|
||||||
|
order := r.toDomainOrderModel(&orderDB)
|
||||||
|
order.OrderItems = make([]entity.InProgressOrderItem, 0, len(orderDB.OrderItems))
|
||||||
|
|
||||||
|
for _, itemDB := range orderDB.OrderItems {
|
||||||
|
item := r.toDomainOrderItemModel(&itemDB)
|
||||||
|
|
||||||
|
orderItem := entity.InProgressOrderItem{
|
||||||
|
ID: item.ID,
|
||||||
|
ItemID: item.ItemID,
|
||||||
|
Quantity: item.Quantity,
|
||||||
|
}
|
||||||
|
|
||||||
|
if itemDB.Product.ID > 0 {
|
||||||
|
productDomain := r.toDomainProductModel(&itemDB.Product)
|
||||||
|
orderItem.Product = productDomain
|
||||||
|
}
|
||||||
|
|
||||||
|
order.OrderItems = append(order.OrderItems, orderItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
orders = append(orders, order)
|
||||||
|
}
|
||||||
|
|
||||||
|
return orders, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *inprogressOrderRepository) toInProgressOrderDBModel(order *entity.InProgressOrder) models.InProgressOrderDB {
|
||||||
|
now := time2.Now()
|
||||||
|
return models.InProgressOrderDB{
|
||||||
|
ID: constants.GenerateUUID(),
|
||||||
|
PartnerID: order.PartnerID,
|
||||||
|
CustomerID: order.CustomerID,
|
||||||
|
CustomerName: order.CustomerName,
|
||||||
|
PaymentType: order.PaymentType,
|
||||||
|
CreatedBy: order.CreatedBy,
|
||||||
|
CreatedAt: now,
|
||||||
|
UpdatedAt: now,
|
||||||
|
TableNumber: order.TableNumber,
|
||||||
|
OrderType: order.OrderType,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *inprogressOrderRepository) toDomainOrderModel(dbModel *models.InProgressOrderDB) *entity.InProgressOrder {
|
||||||
|
return &entity.InProgressOrder{
|
||||||
|
ID: dbModel.ID,
|
||||||
|
PartnerID: dbModel.PartnerID,
|
||||||
|
CustomerID: dbModel.CustomerID,
|
||||||
|
CustomerName: dbModel.CustomerName,
|
||||||
|
PaymentType: dbModel.PaymentType,
|
||||||
|
CreatedBy: dbModel.CreatedBy,
|
||||||
|
OrderItems: []entity.InProgressOrderItem{},
|
||||||
|
TableNumber: dbModel.TableNumber,
|
||||||
|
OrderType: dbModel.OrderType,
|
||||||
|
CreatedAt: dbModel.CreatedAt,
|
||||||
|
UpdatedAt: dbModel.UpdatedAt,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *inprogressOrderRepository) toOrderItemDBModel(item *entity.InProgressOrderItem, inprogressOrderID string) models.InProgressOrderItemDB {
|
||||||
|
return models.InProgressOrderItemDB{
|
||||||
|
ID: item.ID,
|
||||||
|
InProgressOrderIO: inprogressOrderID,
|
||||||
|
ItemID: item.ItemID,
|
||||||
|
Quantity: item.Quantity,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *inprogressOrderRepository) toDomainOrderItemModel(dbModel *models.InProgressOrderItemDB) *entity.OrderItem {
|
||||||
|
return &entity.OrderItem{
|
||||||
|
ID: dbModel.ID,
|
||||||
|
ItemID: dbModel.ItemID,
|
||||||
|
Quantity: dbModel.Quantity,
|
||||||
|
CreatedBy: dbModel.CreatedBy,
|
||||||
|
CreatedAt: dbModel.CreatedAt,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *inprogressOrderRepository) toOrderInquiryDBModel(inquiry *entity.OrderInquiry) models.OrderInquiryDB {
|
||||||
|
return models.OrderInquiryDB{
|
||||||
|
ID: inquiry.ID,
|
||||||
|
PartnerID: inquiry.PartnerID,
|
||||||
|
CustomerID: &inquiry.CustomerID,
|
||||||
|
Status: inquiry.Status,
|
||||||
|
Amount: inquiry.Amount,
|
||||||
|
Fee: inquiry.Fee,
|
||||||
|
Total: inquiry.Total,
|
||||||
|
PaymentType: inquiry.PaymentType,
|
||||||
|
Source: inquiry.Source,
|
||||||
|
CreatedBy: inquiry.CreatedBy,
|
||||||
|
CreatedAt: inquiry.CreatedAt,
|
||||||
|
UpdatedAt: inquiry.UpdatedAt,
|
||||||
|
ExpiresAt: inquiry.ExpiresAt,
|
||||||
|
CustomerName: inquiry.CustomerName,
|
||||||
|
CustomerPhoneNumber: inquiry.CustomerPhoneNumber,
|
||||||
|
CustomerEmail: inquiry.CustomerEmail,
|
||||||
|
PaymentProvider: inquiry.PaymentProvider,
|
||||||
|
OrderType: inquiry.OrderType,
|
||||||
|
TableNumber: inquiry.TableNumber,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *inprogressOrderRepository) toDomainOrderInquiryModel(dbModel *models.OrderInquiryDB) *entity.OrderInquiry {
|
||||||
|
inquiry := &entity.OrderInquiry{
|
||||||
|
ID: dbModel.ID,
|
||||||
|
PartnerID: dbModel.PartnerID,
|
||||||
|
Status: dbModel.Status,
|
||||||
|
Amount: dbModel.Amount,
|
||||||
|
Fee: dbModel.Fee,
|
||||||
|
Total: dbModel.Total,
|
||||||
|
PaymentType: dbModel.PaymentType,
|
||||||
|
Source: dbModel.Source,
|
||||||
|
CreatedBy: dbModel.CreatedBy,
|
||||||
|
CreatedAt: dbModel.CreatedAt,
|
||||||
|
ExpiresAt: dbModel.ExpiresAt,
|
||||||
|
OrderItems: []entity.OrderItem{},
|
||||||
|
}
|
||||||
|
|
||||||
|
if dbModel.CustomerID != nil {
|
||||||
|
inquiry.CustomerID = *dbModel.CustomerID
|
||||||
|
}
|
||||||
|
|
||||||
|
inquiry.UpdatedAt = dbModel.UpdatedAt
|
||||||
|
|
||||||
|
return inquiry
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *inprogressOrderRepository) toDomainProductModel(productDB *models.ProductDB) *entity.Product {
|
||||||
|
if productDB == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &entity.Product{
|
||||||
|
ID: productDB.ID,
|
||||||
|
Name: productDB.Name,
|
||||||
|
Description: productDB.Description,
|
||||||
|
Price: productDB.Price,
|
||||||
|
CreatedAt: productDB.CreatedAt,
|
||||||
|
UpdatedAt: productDB.UpdatedAt,
|
||||||
|
Type: productDB.Type,
|
||||||
|
Image: productDB.Image,
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -4,8 +4,11 @@ import (
|
|||||||
"enaklo-pos-be/internal/common/mycontext"
|
"enaklo-pos-be/internal/common/mycontext"
|
||||||
"enaklo-pos-be/internal/entity"
|
"enaklo-pos-be/internal/entity"
|
||||||
"enaklo-pos-be/internal/repository/models"
|
"enaklo-pos-be/internal/repository/models"
|
||||||
|
"fmt"
|
||||||
|
"github.com/google/uuid"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
"math/rand"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -14,9 +17,10 @@ type CustomerRepo interface {
|
|||||||
FindByID(ctx mycontext.Context, id int64) (*entity.Customer, error)
|
FindByID(ctx mycontext.Context, id int64) (*entity.Customer, error)
|
||||||
FindByPhone(ctx mycontext.Context, phone string) (*entity.Customer, error)
|
FindByPhone(ctx mycontext.Context, phone string) (*entity.Customer, error)
|
||||||
FindByEmail(ctx mycontext.Context, email string) (*entity.Customer, error)
|
FindByEmail(ctx mycontext.Context, email string) (*entity.Customer, error)
|
||||||
AddPoints(ctx mycontext.Context, id int64, points int) error
|
AddPoints(ctx mycontext.Context, id int64, points int, reference string) error
|
||||||
FindSequence(ctx mycontext.Context, partnerID int64) (int64, error)
|
FindSequence(ctx mycontext.Context, partnerID int64) (int64, error)
|
||||||
GetAllCustomers(ctx mycontext.Context, req entity.MemberSearch) (entity.MemberList, int, error)
|
GetAllCustomers(ctx mycontext.Context, req entity.MemberSearch) (entity.MemberList, int, error)
|
||||||
|
VerifyOTP(ctx mycontext.Context, verificationHash string, otpCode string) (int64, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type customerRepository struct {
|
type customerRepository struct {
|
||||||
@ -28,13 +32,53 @@ func NewCustomerRepository(db *gorm.DB) *customerRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *customerRepository) Create(ctx mycontext.Context, customer *entity.Customer) (*entity.Customer, error) {
|
func (r *customerRepository) Create(ctx mycontext.Context, customer *entity.Customer) (*entity.Customer, error) {
|
||||||
customerDB := r.toCustomerDBModel(customer)
|
tx := r.db.Begin()
|
||||||
|
if tx.Error != nil {
|
||||||
|
return nil, errors.Wrap(tx.Error, "failed to begin transaction")
|
||||||
|
}
|
||||||
|
|
||||||
if err := r.db.Create(&customerDB).Error; err != nil {
|
customerDB := r.toCustomerDBModel(customer)
|
||||||
|
if err := tx.Omit("CustomerID").Create(&customerDB).Error; err != nil {
|
||||||
|
tx.Rollback()
|
||||||
return nil, errors.Wrap(err, "failed to insert customer")
|
return nil, errors.Wrap(err, "failed to insert customer")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
customerPoints := models.CustomerPointsDB{
|
||||||
|
CustomerID: uint64(customerDB.ID),
|
||||||
|
TotalPoints: 0,
|
||||||
|
AvailablePoints: 0,
|
||||||
|
LastUpdated: time.Now(),
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tx.Create(&customerPoints).Error; err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return nil, errors.Wrap(err, "failed to create initial customer points")
|
||||||
|
}
|
||||||
|
|
||||||
|
otpCode := r.generateOTPCode()
|
||||||
|
expiresAt := time.Now().Add(15 * time.Minute)
|
||||||
|
|
||||||
|
verificationCode := models.CustomerVerificationCodeDB{
|
||||||
|
CustomerID: uint64(customerDB.ID),
|
||||||
|
Code: otpCode,
|
||||||
|
Type: "EMAIL",
|
||||||
|
ExpiresAt: expiresAt,
|
||||||
|
IsUsed: false,
|
||||||
|
VerificationID: uuid.New(),
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tx.Create(&verificationCode).Error; err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return nil, errors.Wrap(err, "failed to create verification code")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tx.Commit().Error; err != nil {
|
||||||
|
return nil, errors.Wrap(err, "failed to commit transaction")
|
||||||
|
}
|
||||||
|
|
||||||
customer.ID = customerDB.ID
|
customer.ID = customerDB.ID
|
||||||
|
customer.VerificationID = verificationCode.VerificationID.String()
|
||||||
|
customer.OTP = otpCode
|
||||||
|
|
||||||
return customer, nil
|
return customer, nil
|
||||||
}
|
}
|
||||||
@ -84,22 +128,45 @@ func (r *customerRepository) FindByEmail(ctx mycontext.Context, email string) (*
|
|||||||
return customer, nil
|
return customer, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *customerRepository) AddPoints(ctx mycontext.Context, id int64, points int) error {
|
func (r *customerRepository) AddPoints(ctx mycontext.Context, customerID int64, points int, reference string) error {
|
||||||
now := time.Now()
|
tx := r.db.Begin()
|
||||||
|
if tx.Error != nil {
|
||||||
|
return errors.Wrap(tx.Error, "failed to begin transaction")
|
||||||
|
}
|
||||||
|
|
||||||
result := r.db.Model(&models.CustomerDB{}).
|
result := tx.Model(&models.CustomerPointsDB{}).
|
||||||
Where("id = ?", id).
|
Where("customer_id = ?", customerID).
|
||||||
Updates(map[string]interface{}{
|
Updates(map[string]interface{}{
|
||||||
"points": gorm.Expr("points + ?", points),
|
"total_points": gorm.Expr("total_points + ?", points),
|
||||||
"updated_at": now,
|
"available_points": gorm.Expr("available_points + ?", points),
|
||||||
|
"last_updated": time.Now(),
|
||||||
})
|
})
|
||||||
|
|
||||||
if result.Error != nil {
|
if result.Error != nil {
|
||||||
return errors.Wrap(result.Error, "failed to add points to customer")
|
tx.Rollback()
|
||||||
|
return errors.Wrap(result.Error, "failed to update customer points")
|
||||||
}
|
}
|
||||||
|
|
||||||
if result.RowsAffected == 0 {
|
if result.RowsAffected == 0 {
|
||||||
return errors.New("customer not found")
|
tx.Rollback()
|
||||||
|
return errors.New("customer points record not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
pointTransaction := models.CustomerPointTransactionDB{
|
||||||
|
CustomerID: customerID,
|
||||||
|
Reference: reference,
|
||||||
|
PointsEarned: points,
|
||||||
|
TransactionDate: time.Now(),
|
||||||
|
Status: "SUCCESS",
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tx.Create(&pointTransaction).Error; err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return errors.Wrap(err, "failed to create point transaction record")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tx.Commit().Error; err != nil {
|
||||||
|
return errors.Wrap(err, "failed to commit transaction")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -114,8 +181,8 @@ func (r *customerRepository) toCustomerDBModel(customer *entity.Customer) models
|
|||||||
Points: customer.Points,
|
Points: customer.Points,
|
||||||
CreatedAt: customer.CreatedAt,
|
CreatedAt: customer.CreatedAt,
|
||||||
UpdatedAt: customer.UpdatedAt,
|
UpdatedAt: customer.UpdatedAt,
|
||||||
CustomerID: customer.CustomerID,
|
|
||||||
BirthDate: customer.BirthDate,
|
BirthDate: customer.BirthDate,
|
||||||
|
Password: customer.Password,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -232,3 +299,59 @@ func (r *customerRepository) GetAllCustomers(ctx mycontext.Context, req entity.M
|
|||||||
|
|
||||||
return customers, int(totalCount), nil
|
return customers, int(totalCount), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *customerRepository) generateOTPCode() string {
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
otpCode := fmt.Sprintf("%06d", rand.Intn(1000000))
|
||||||
|
return otpCode
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *customerRepository) VerifyOTP(ctx mycontext.Context, verificationHash string, otpCode string) (int64, error) {
|
||||||
|
var verificationCode models.CustomerVerificationCodeDB
|
||||||
|
if err := r.db.Where("verification_id = ? AND is_used = false", verificationHash).First(&verificationCode).Error; err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return 0, errors.New("invalid or expired verification code")
|
||||||
|
}
|
||||||
|
return 0, errors.Wrap(err, "failed to find verification code")
|
||||||
|
}
|
||||||
|
|
||||||
|
if time.Now().After(verificationCode.ExpiresAt) {
|
||||||
|
return 0, errors.New("verification code has expired")
|
||||||
|
}
|
||||||
|
|
||||||
|
if verificationCode.Code != otpCode {
|
||||||
|
return 0, errors.New("invalid verification code")
|
||||||
|
}
|
||||||
|
|
||||||
|
tx := r.db.Begin()
|
||||||
|
if tx.Error != nil {
|
||||||
|
return 0, errors.Wrap(tx.Error, "failed to begin transaction")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tx.Model(&verificationCode).Updates(map[string]interface{}{
|
||||||
|
"is_used": true,
|
||||||
|
}).Error; err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return 0, errors.Wrap(err, "failed to mark verification code as used")
|
||||||
|
}
|
||||||
|
|
||||||
|
if verificationCode.Type == "EMAIL" {
|
||||||
|
if err := tx.Model(&models.CustomerDB{}).Where("id = ?", verificationCode.CustomerID).
|
||||||
|
Update("is_email_verified", true).Error; err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return 0, errors.Wrap(err, "failed to update customer verification status")
|
||||||
|
}
|
||||||
|
} else if verificationCode.Type == "PHONE" {
|
||||||
|
if err := tx.Model(&models.CustomerDB{}).Where("id = ?", verificationCode.CustomerID).
|
||||||
|
Update("is_phone_verified", true).Error; err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return 0, errors.Wrap(err, "failed to update customer verification status")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tx.Commit().Error; err != nil {
|
||||||
|
return 0, errors.Wrap(err, "failed to commit transaction")
|
||||||
|
}
|
||||||
|
|
||||||
|
return int64(verificationCode.CustomerID), nil
|
||||||
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/google/uuid"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -14,6 +15,9 @@ type CustomerDB struct {
|
|||||||
UpdatedAt time.Time `gorm:"column:updated_at"`
|
UpdatedAt time.Time `gorm:"column:updated_at"`
|
||||||
CustomerID string `gorm:"column:customer_id"`
|
CustomerID string `gorm:"column:customer_id"`
|
||||||
BirthDate time.Time `gorm:"column:birth_date"`
|
BirthDate time.Time `gorm:"column:birth_date"`
|
||||||
|
Password string `gorm:"column:password"`
|
||||||
|
IsEmailVerified bool `gorm:"column:is_email_verified"`
|
||||||
|
IsPhoneVerified bool `gorm:"column:is_phone_verified"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (CustomerDB) TableName() string {
|
func (CustomerDB) TableName() string {
|
||||||
@ -30,3 +34,45 @@ type PartnerMemberSequence struct {
|
|||||||
func (PartnerMemberSequence) TableName() string {
|
func (PartnerMemberSequence) TableName() string {
|
||||||
return "partner_member_sequences"
|
return "partner_member_sequences"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CustomerPointsDB struct {
|
||||||
|
ID uint64 `gorm:"column:id;primaryKey;autoIncrement"`
|
||||||
|
CustomerID uint64 `gorm:"column:customer_id;not null"`
|
||||||
|
TotalPoints int `gorm:"column:total_points;not null;default:0"`
|
||||||
|
AvailablePoints int `gorm:"column:available_points;not null;default:0"`
|
||||||
|
LastUpdated time.Time `gorm:"column:last_updated;default:CURRENT_TIMESTAMP"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (CustomerPointsDB) TableName() string {
|
||||||
|
return "customer_points"
|
||||||
|
}
|
||||||
|
|
||||||
|
type CustomerPointTransactionDB struct {
|
||||||
|
ID uint64 `gorm:"column:id;primaryKey;autoIncrement"`
|
||||||
|
CustomerID int64 `gorm:"column:customer_id;not null"`
|
||||||
|
Reference string `gorm:"column:transaction_id"`
|
||||||
|
PointsEarned int `gorm:"column:points_earned;not null"`
|
||||||
|
TransactionDate time.Time `gorm:"column:transaction_date;not null"`
|
||||||
|
ExpirationDate *time.Time `gorm:"column:expiration_date"`
|
||||||
|
Status string `gorm:"column:status;default:active"`
|
||||||
|
CreatedAt time.Time `gorm:"column:created_at;default:CURRENT_TIMESTAMP"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (CustomerPointTransactionDB) TableName() string {
|
||||||
|
return "customer_point_transactions"
|
||||||
|
}
|
||||||
|
|
||||||
|
type CustomerVerificationCodeDB struct {
|
||||||
|
ID uint64 `gorm:"column:id;primaryKey;autoIncrement"`
|
||||||
|
CustomerID uint64 `gorm:"column:customer_id;not null"`
|
||||||
|
Code string `gorm:"column:code;not null"`
|
||||||
|
Type string `gorm:"column:type;not null"`
|
||||||
|
ExpiresAt time.Time `gorm:"column:expires_at;not null"`
|
||||||
|
IsUsed bool `gorm:"column:is_used;default:false"`
|
||||||
|
CreatedAt time.Time `gorm:"column:created_at;default:CURRENT_TIMESTAMP"`
|
||||||
|
VerificationID uuid.UUID `gorm:"column:verification_id;type:uuid;default:uuid_generate_v4()"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (CustomerVerificationCodeDB) TableName() string {
|
||||||
|
return "customer_verification_codes"
|
||||||
|
}
|
||||||
|
|||||||
35
internal/repository/models/in_progress_order.go
Normal file
35
internal/repository/models/in_progress_order.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type InProgressOrderDB struct {
|
||||||
|
ID string `gorm:"primaryKey;column:id"`
|
||||||
|
PartnerID int64 `gorm:"column:partner_id"`
|
||||||
|
CustomerID *int64 `gorm:"column:customer_id"`
|
||||||
|
CustomerName string `gorm:"column:customer_name"`
|
||||||
|
PaymentType string `gorm:"column:payment_type"`
|
||||||
|
CreatedBy int64 `gorm:"column:created_by"`
|
||||||
|
CreatedAt time.Time `gorm:"column:created_at"`
|
||||||
|
UpdatedAt time.Time `gorm:"column:updated_at"`
|
||||||
|
TableNumber string `gorm:"column:table_number"`
|
||||||
|
OrderItems []InProgressOrderItemDB `gorm:"foreignKey:InProgressOrderIO"`
|
||||||
|
OrderType string `gorm:"column:order_type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type InProgressOrderItemDB struct {
|
||||||
|
ID int64 `gorm:"primaryKey;column:id"`
|
||||||
|
InProgressOrderIO string `gorm:"column:in_progress_order_id"`
|
||||||
|
ItemID int64 `gorm:"column:item_id"`
|
||||||
|
Quantity int `gorm:"column:quantity"`
|
||||||
|
CreatedBy int64 `gorm:"column:created_by"`
|
||||||
|
CreatedAt time.Time `gorm:"column:created_at"`
|
||||||
|
Product ProductDB `gorm:"foreignKey:ItemID;references:ID"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (InProgressOrderItemDB) TableName() string {
|
||||||
|
return "in_progress_order_items"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (InProgressOrderDB) TableName() string {
|
||||||
|
return "in_progress_order"
|
||||||
|
}
|
||||||
@ -58,6 +58,9 @@ type OrderInquiryDB struct {
|
|||||||
UpdatedAt time.Time `gorm:"column:updated_at"`
|
UpdatedAt time.Time `gorm:"column:updated_at"`
|
||||||
ExpiresAt time.Time `gorm:"column:expires_at"`
|
ExpiresAt time.Time `gorm:"column:expires_at"`
|
||||||
InquiryItems []InquiryItemDB `gorm:"foreignKey:InquiryID"`
|
InquiryItems []InquiryItemDB `gorm:"foreignKey:InquiryID"`
|
||||||
|
PaymentProvider string `gorm:"column:payment_provider"`
|
||||||
|
TableNumber string `gorm:"column:table_number"`
|
||||||
|
OrderType string `gorm:"column:order_type"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (OrderInquiryDB) TableName() string {
|
func (OrderInquiryDB) TableName() string {
|
||||||
|
|||||||
@ -15,6 +15,7 @@ type ProductDB struct {
|
|||||||
Status string `gorm:"column:status"`
|
Status string `gorm:"column:status"`
|
||||||
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"`
|
||||||
|
Image string `gorm:"column:image"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ProductDB) TableName() string {
|
func (ProductDB) TableName() string {
|
||||||
|
|||||||
@ -61,6 +61,18 @@ func (r *orderRepository) Create(ctx mycontext.Context, order *entity.Order) (*e
|
|||||||
item.ID = itemDB.ID
|
item.ID = itemDB.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if order.InProgressOrderID != "" {
|
||||||
|
if err := tx.Where("in_progress_order_id = ?", order.InProgressOrderID).Delete(&models.InProgressOrderItemDB{}).Error; err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return nil, errors.Wrap(err, "failed to delete in-progress order items")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tx.Where("id = ?", order.InProgressOrderID).Delete(&models.InProgressOrderDB{}).Error; err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return nil, errors.Wrap(err, "failed to delete in-progress order")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if err := tx.Commit().Error; err != nil {
|
if err := tx.Commit().Error; err != nil {
|
||||||
return nil, errors.Wrap(err, "failed to commit transaction")
|
return nil, errors.Wrap(err, "failed to commit transaction")
|
||||||
}
|
}
|
||||||
@ -263,6 +275,9 @@ func (r *orderRepository) toOrderInquiryDBModel(inquiry *entity.OrderInquiry) mo
|
|||||||
CustomerName: inquiry.CustomerName,
|
CustomerName: inquiry.CustomerName,
|
||||||
CustomerPhoneNumber: inquiry.CustomerPhoneNumber,
|
CustomerPhoneNumber: inquiry.CustomerPhoneNumber,
|
||||||
CustomerEmail: inquiry.CustomerEmail,
|
CustomerEmail: inquiry.CustomerEmail,
|
||||||
|
PaymentProvider: inquiry.PaymentProvider,
|
||||||
|
OrderType: inquiry.OrderType,
|
||||||
|
TableNumber: inquiry.TableNumber,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -141,9 +141,8 @@ func (b *OrderRepository) GetAllHystoryOrders(ctx context.Context, req entity.Or
|
|||||||
|
|
||||||
query := b.db.Table("orders").
|
query := b.db.Table("orders").
|
||||||
Select("orders.id as id, users.name as employee, sites.name as site, orders.created_at as timestamp, orders.created_at as booking_time, STRING_AGG(ticket_summary.name || ' x' || ticket_summary.total_qty, ', ') AS tickets, orders.payment_type as payment_type, orders.status as status, orders.amount as amount, orders.visit_date as visit_date, orders.ticket_status as ticket_status, orders.source as source").
|
Select("orders.id as id, users.name as employee, sites.name as site, orders.created_at as timestamp, orders.created_at as booking_time, STRING_AGG(ticket_summary.name || ' x' || ticket_summary.total_qty, ', ') AS tickets, orders.payment_type as payment_type, orders.status as status, orders.amount as amount, orders.visit_date as visit_date, orders.ticket_status as ticket_status, orders.source as source").
|
||||||
Joins("left join (SELECT items.order_id, products.name, SUM(items.qty) AS total_qty FROM order_items items LEFT JOIN products ON items.item_id = products.id GROUP BY items.order_id, products.name) AS ticket_summary ON orders.id = ticket_summary.order_id").
|
Joins("left join (SELECT items.order_id, products.name, SUM(items.quantity) AS total_qty FROM order_items items LEFT JOIN products ON items.item_id = products.id GROUP BY items.order_id, products.name) AS ticket_summary ON orders.id = ticket_summary.order_id").
|
||||||
Joins("left join users on orders.created_by = users.id").
|
Joins("left join users on orders.created_by = users.id").
|
||||||
Joins("left join sites on orders.site_id = sites.id").
|
|
||||||
Where("orders.payment_type != ?", "NEW")
|
Where("orders.payment_type != ?", "NEW")
|
||||||
|
|
||||||
if req.PaymentType != "" {
|
if req.PaymentType != "" {
|
||||||
@ -176,7 +175,7 @@ func (b *OrderRepository) GetAllHystoryOrders(ctx context.Context, req entity.Or
|
|||||||
}
|
}
|
||||||
|
|
||||||
if req.SiteID != nil {
|
if req.SiteID != nil {
|
||||||
query = query.Where("orders.site_id = ?", req.SiteID)
|
query = query.Where("orders.partner_id = ?", req.SiteID)
|
||||||
}
|
}
|
||||||
|
|
||||||
if req.Source != "" {
|
if req.Source != "" {
|
||||||
@ -253,10 +252,6 @@ func (r *OrderRepository) SumAmount(ctx mycontext.Context, req entity.OrderSearc
|
|||||||
query = query.Where("orders.partner_id = ?", req.PartnerID)
|
query = query.Where("orders.partner_id = ?", req.PartnerID)
|
||||||
}
|
}
|
||||||
|
|
||||||
if req.SiteID != nil {
|
|
||||||
query = query.Where("orders.site_id = ?", req.SiteID)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := query.Scan(&amount).Error; err != nil {
|
if err := query.Scan(&amount).Error; err != nil {
|
||||||
logger.ContextLogger(ctx).Error("error when get cash amount", zap.Error(err))
|
logger.ContextLogger(ctx).Error("error when get cash amount", zap.Error(err))
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@ -53,6 +53,7 @@ type RepoManagerImpl struct {
|
|||||||
LinkQu LinkQu
|
LinkQu LinkQu
|
||||||
|
|
||||||
OrderRepo OrderRepository
|
OrderRepo OrderRepository
|
||||||
|
InProgressOrderRepo InProgressOrderRepository
|
||||||
CustomerRepo CustomerRepo
|
CustomerRepo CustomerRepo
|
||||||
ProductRepo ProductRepository
|
ProductRepo ProductRepository
|
||||||
TransactionRepo TransactionRepo
|
TransactionRepo TransactionRepo
|
||||||
@ -86,6 +87,7 @@ func NewRepoManagerImpl(db *gorm.DB, cfg *config.Config) *RepoManagerImpl {
|
|||||||
ProductRepo: NewproductRepository(db),
|
ProductRepo: NewproductRepository(db),
|
||||||
TransactionRepo: NewTransactionRepository(db),
|
TransactionRepo: NewTransactionRepository(db),
|
||||||
MemberRepository: NewMemberRepository(db),
|
MemberRepository: NewMemberRepository(db),
|
||||||
|
InProgressOrderRepo: NewInProgressOrderRepository(db),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -19,7 +19,7 @@ func RegisterCustomerRoutes(app *app.Server, serviceManager *services.ServiceMan
|
|||||||
|
|
||||||
serverRoutes := []HTTPHandlerRoutes{
|
serverRoutes := []HTTPHandlerRoutes{
|
||||||
discovery.NewHandler(serviceManager.DiscoverService),
|
discovery.NewHandler(serviceManager.DiscoverService),
|
||||||
customerauth.NewAuthHandler(serviceManager.AuthSvc, serviceManager.UserSvc),
|
customerauth.NewAuthHandler(serviceManager.AuthSvc, serviceManager.UserSvc, serviceManager.CustomerV2Svc),
|
||||||
customerorder.NewHandler(serviceManager.OrderSvc),
|
customerorder.NewHandler(serviceManager.OrderSvc),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -80,6 +80,7 @@ func RegisterPrivateRoutesV2(app *app.Server, serviceManager *services.ServiceMa
|
|||||||
http2.NewOrderHandler(serviceManager.OrderV2Svc),
|
http2.NewOrderHandler(serviceManager.OrderV2Svc),
|
||||||
http2.NewMemberRegistrationHandler(serviceManager.MemberRegistrationSvc),
|
http2.NewMemberRegistrationHandler(serviceManager.MemberRegistrationSvc),
|
||||||
http2.NewCustomerHandler(serviceManager.CustomerV2Svc),
|
http2.NewCustomerHandler(serviceManager.CustomerV2Svc),
|
||||||
|
http2.NewInProgressOrderHandler(serviceManager.InProgressSvc),
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, handler := range serverRoutes {
|
for _, handler := range serverRoutes {
|
||||||
|
|||||||
@ -83,7 +83,6 @@ func (u *AuthServiceImpl) AuthenticateUser(ctx context.Context, email, password
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (u *AuthServiceImpl) SendPasswordResetLink(ctx context.Context, email string) error {
|
func (u *AuthServiceImpl) SendPasswordResetLink(ctx context.Context, email string) error {
|
||||||
// Check if the user exists
|
|
||||||
user, err := u.authRepo.CheckExistsUserAccount(ctx, email)
|
user, err := u.authRepo.CheckExistsUserAccount(ctx, email)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.ContextLogger(ctx).Error("error when getting user", zap.Error(err))
|
logger.ContextLogger(ctx).Error("error when getting user", zap.Error(err))
|
||||||
|
|||||||
@ -162,7 +162,6 @@ func (s *memberSvc) ResendOTP(
|
|||||||
) (*entity.ResendOTPResponse, error) {
|
) (*entity.ResendOTPResponse, error) {
|
||||||
logger.ContextLogger(ctx).Info("resending OTP", zap.String("token", token))
|
logger.ContextLogger(ctx).Info("resending OTP", zap.String("token", token))
|
||||||
|
|
||||||
// Get registration by token
|
|
||||||
registration, err := s.repo.GetRegistrationByToken(ctx, token)
|
registration, err := s.repo.GetRegistrationByToken(ctx, token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.ContextLogger(ctx).Error("failed to get registration", zap.Error(err))
|
logger.ContextLogger(ctx).Error("failed to get registration", zap.Error(err))
|
||||||
@ -211,7 +210,7 @@ func (s *memberSvc) sendRegistrationOTP(
|
|||||||
Recipient: registration.Email,
|
Recipient: registration.Email,
|
||||||
Subject: "Enaklo - Registration Verification Code",
|
Subject: "Enaklo - Registration Verification Code",
|
||||||
TemplateName: "member_registration_otp",
|
TemplateName: "member_registration_otp",
|
||||||
TemplatePath: "/templates/member_registration_otp.html",
|
TemplatePath: "templates/member_registration_otp.html",
|
||||||
Data: emailData,
|
Data: emailData,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -240,32 +240,6 @@ func (s *OrderService) Execute(ctx mycontext.Context, req *entity.OrderExecuteRe
|
|||||||
Order: order,
|
Order: order,
|
||||||
}
|
}
|
||||||
|
|
||||||
if order.PaymentType != "CASH" {
|
|
||||||
if order.PaymentType == "VA" {
|
|
||||||
paymentResponse, err := s.processVAPayment(ctx, order, partnerID, req.CreatedBy)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
resp.VirtualAccount = paymentResponse.VirtualAccountNumber
|
|
||||||
resp.BankName = paymentResponse.BankName
|
|
||||||
resp.BankCode = paymentResponse.BankCode
|
|
||||||
}
|
|
||||||
if order.PaymentType == "QRIS" {
|
|
||||||
paymentResponse, err := s.processQRPayment(ctx, order, partnerID, req.CreatedBy)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
resp.QRCode = paymentResponse.QRCodeURL
|
|
||||||
} else {
|
|
||||||
paymentResponse, err := s.processNonCashPayment(ctx, order, partnerID, req.CreatedBy)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
resp.PaymentToken = paymentResponse.Token
|
|
||||||
resp.RedirectURL = paymentResponse.RedirectURL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
order.SetExecutePaymentStatus()
|
order.SetExecutePaymentStatus()
|
||||||
order, err = s.repo.Update(ctx, order)
|
order, err = s.repo.Update(ctx, order)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import (
|
|||||||
"enaklo-pos-be/internal/services/transaction"
|
"enaklo-pos-be/internal/services/transaction"
|
||||||
"enaklo-pos-be/internal/services/users"
|
"enaklo-pos-be/internal/services/users"
|
||||||
customerSvc "enaklo-pos-be/internal/services/v2/customer"
|
customerSvc "enaklo-pos-be/internal/services/v2/customer"
|
||||||
|
"enaklo-pos-be/internal/services/v2/inprogress_order"
|
||||||
orderSvc "enaklo-pos-be/internal/services/v2/order"
|
orderSvc "enaklo-pos-be/internal/services/v2/order"
|
||||||
productSvc "enaklo-pos-be/internal/services/v2/product"
|
productSvc "enaklo-pos-be/internal/services/v2/product"
|
||||||
|
|
||||||
@ -47,12 +48,14 @@ type ServiceManagerImpl struct {
|
|||||||
CustomerV2Svc customerSvc.Service
|
CustomerV2Svc customerSvc.Service
|
||||||
ProductV2Svc productSvc.Service
|
ProductV2Svc productSvc.Service
|
||||||
MemberRegistrationSvc member.RegistrationService
|
MemberRegistrationSvc member.RegistrationService
|
||||||
|
InProgressSvc inprogress_order.InProgressOrderService
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewServiceManagerImpl(cfg *config.Config, repo *repository.RepoManagerImpl) *ServiceManagerImpl {
|
func NewServiceManagerImpl(cfg *config.Config, repo *repository.RepoManagerImpl) *ServiceManagerImpl {
|
||||||
|
|
||||||
custSvcV2 := customerSvc.New(repo.CustomerRepo)
|
custSvcV2 := customerSvc.New(repo.CustomerRepo, repo.EmailService)
|
||||||
productSvcV2 := productSvc.New(repo.ProductRepo)
|
productSvcV2 := productSvc.New(repo.ProductRepo)
|
||||||
|
inprogressOrder := inprogress_order.NewInProgressOrderService(repo.InProgressOrderRepo)
|
||||||
|
|
||||||
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),
|
||||||
@ -72,6 +75,7 @@ func NewServiceManagerImpl(cfg *config.Config, repo *repository.RepoManagerImpl)
|
|||||||
OrderV2Svc: orderSvc.New(repo.OrderRepo, productSvcV2, custSvcV2, repo.TransactionRepo, repo.Crypto, &cfg.Order, repo.EmailService),
|
OrderV2Svc: orderSvc.New(repo.OrderRepo, productSvcV2, custSvcV2, repo.TransactionRepo, repo.Crypto, &cfg.Order, repo.EmailService),
|
||||||
MemberRegistrationSvc: member.NewMemberRegistrationService(repo.MemberRepository, repo.EmailService, custSvcV2),
|
MemberRegistrationSvc: member.NewMemberRegistrationService(repo.MemberRepository, repo.EmailService, custSvcV2),
|
||||||
CustomerV2Svc: custSvcV2,
|
CustomerV2Svc: custSvcV2,
|
||||||
|
InProgressSvc: inprogressOrder,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
package customer
|
package customer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
errors2 "enaklo-pos-be/internal/common/errors"
|
||||||
"enaklo-pos-be/internal/common/logger"
|
"enaklo-pos-be/internal/common/logger"
|
||||||
"enaklo-pos-be/internal/common/mycontext"
|
"enaklo-pos-be/internal/common/mycontext"
|
||||||
"enaklo-pos-be/internal/constants"
|
"enaklo-pos-be/internal/constants"
|
||||||
@ -8,7 +10,9 @@ import (
|
|||||||
"enaklo-pos-be/internal/utils"
|
"enaklo-pos-be/internal/utils"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
"log"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Repository interface {
|
type Repository interface {
|
||||||
@ -16,26 +20,35 @@ type Repository interface {
|
|||||||
FindByID(ctx mycontext.Context, id int64) (*entity.Customer, error)
|
FindByID(ctx mycontext.Context, id int64) (*entity.Customer, error)
|
||||||
FindByPhone(ctx mycontext.Context, phone string) (*entity.Customer, error)
|
FindByPhone(ctx mycontext.Context, phone string) (*entity.Customer, error)
|
||||||
FindByEmail(ctx mycontext.Context, email string) (*entity.Customer, error)
|
FindByEmail(ctx mycontext.Context, email string) (*entity.Customer, error)
|
||||||
AddPoints(ctx mycontext.Context, id int64, points int) error
|
AddPoints(ctx mycontext.Context, id int64, points int, reference string) error
|
||||||
FindSequence(ctx mycontext.Context, partnerID int64) (int64, error)
|
FindSequence(ctx mycontext.Context, partnerID int64) (int64, error)
|
||||||
GetAllCustomers(ctx mycontext.Context, req entity.MemberSearch) (entity.MemberList, int, error)
|
GetAllCustomers(ctx mycontext.Context, req entity.MemberSearch) (entity.MemberList, int, error)
|
||||||
|
VerifyOTP(ctx mycontext.Context, verificationHash string, otpCode string) (int64, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Service interface {
|
type Service interface {
|
||||||
ResolveCustomer(ctx mycontext.Context, req *entity.CustomerResolutionRequest) (int64, error)
|
ResolveCustomer(ctx mycontext.Context, req *entity.CustomerResolutionRequest) (int64, error)
|
||||||
AddPoints(ctx mycontext.Context, customerID int64, points int) error
|
AddPoints(ctx mycontext.Context, customerID int64, points int, reference string) error
|
||||||
GetCustomer(ctx mycontext.Context, id int64) (*entity.Customer, error)
|
GetCustomer(ctx mycontext.Context, id int64) (*entity.Customer, error)
|
||||||
CustomerCheck(ctx mycontext.Context, req *entity.CustomerResolutionRequest) (*entity.CustomerCheckResponse, error)
|
CustomerCheck(ctx mycontext.Context, req *entity.CustomerResolutionRequest) (*entity.CustomerCheckResponse, error)
|
||||||
GetAllCustomers(ctx mycontext.Context, req *entity.MemberSearch) (*entity.MemberList, int, error)
|
GetAllCustomers(ctx mycontext.Context, req *entity.MemberSearch) (*entity.MemberList, int, error)
|
||||||
|
RegistrationMember(ctx mycontext.Context, req *entity.Customer) (*entity.Customer, error)
|
||||||
|
VerifyOTP(ctx mycontext.Context, verificationID, otpCode string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type EmailService interface {
|
||||||
|
SendEmailTransactional(ctx context.Context, param entity.SendEmailNotificationParam) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type customerSvc struct {
|
type customerSvc struct {
|
||||||
repo Repository
|
repo Repository
|
||||||
|
notification EmailService
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(repo Repository) Service {
|
func New(repo Repository, notification EmailService) Service {
|
||||||
return &customerSvc{
|
return &customerSvc{
|
||||||
repo: repo,
|
repo: repo,
|
||||||
|
notification: notification,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,12 +119,68 @@ func (s *customerSvc) ResolveCustomer(ctx mycontext.Context, req *entity.Custome
|
|||||||
return customer.ID, nil
|
return customer.ID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *customerSvc) AddPoints(ctx mycontext.Context, customerID int64, points int) error {
|
func (s *customerSvc) RegistrationMember(ctx mycontext.Context, req *entity.Customer) (*entity.Customer, error) {
|
||||||
|
if req.Email == "" && req.PhoneNumber == "" {
|
||||||
|
return nil, errors2.ErrorPhoneNumberEmailIsRequired
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.PhoneNumber != "" {
|
||||||
|
customer, err := s.repo.FindByPhone(ctx, req.PhoneNumber)
|
||||||
|
if err != nil && !strings.Contains(err.Error(), "not found") {
|
||||||
|
return nil, errors2.ErrorInternalServer
|
||||||
|
}
|
||||||
|
|
||||||
|
if customer != nil {
|
||||||
|
return nil, errors2.ErrorPhoneNumberIsAlreadyRegistered
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Email != "" {
|
||||||
|
customer, err := s.repo.FindByEmail(ctx, req.Email)
|
||||||
|
if err != nil && !strings.Contains(err.Error(), "not found") {
|
||||||
|
return nil, errors2.ErrorInternalServer
|
||||||
|
}
|
||||||
|
|
||||||
|
if customer != nil {
|
||||||
|
return nil, errors2.ErrorEmailIsAlreadyRegistered
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
newCustomer := &entity.Customer{
|
||||||
|
Name: req.Name,
|
||||||
|
Email: req.Email,
|
||||||
|
Phone: req.PhoneNumber,
|
||||||
|
CreatedAt: constants.TimeNow(),
|
||||||
|
UpdatedAt: constants.TimeNow(),
|
||||||
|
BirthDate: req.BirthDate,
|
||||||
|
Password: req.HashedPassword(),
|
||||||
|
}
|
||||||
|
|
||||||
|
customer, err := s.repo.Create(ctx, newCustomer)
|
||||||
|
if err != nil {
|
||||||
|
logger.ContextLogger(ctx).Error("failed to create customer", zap.Error(err))
|
||||||
|
return nil, errors2.ErrorInternalServer
|
||||||
|
}
|
||||||
|
|
||||||
|
errs := s.sendRegistrationOTP(ctx, &entity.MemberRegistration{
|
||||||
|
Name: customer.Name,
|
||||||
|
Email: customer.Email,
|
||||||
|
OTP: customer.OTP,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
logger.ContextLogger(ctx).Error("failed to send OTP", zap.Error(errs))
|
||||||
|
}
|
||||||
|
|
||||||
|
return customer, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *customerSvc) AddPoints(ctx mycontext.Context, customerID int64, points int, reference string) error {
|
||||||
if points <= 0 {
|
if points <= 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
err := s.repo.AddPoints(ctx, customerID, points)
|
err := s.repo.AddPoints(ctx, customerID, points, reference)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "failed to add points to customer")
|
return errors.Wrap(err, "failed to add points to customer")
|
||||||
}
|
}
|
||||||
@ -202,3 +271,75 @@ func (s *customerSvc) GetAllCustomers(ctx mycontext.Context, req *entity.MemberS
|
|||||||
|
|
||||||
return &customers, totalCount, nil
|
return &customers, totalCount, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *customerSvc) sendRegistrationOTP(
|
||||||
|
ctx mycontext.Context,
|
||||||
|
registration *entity.MemberRegistration,
|
||||||
|
) error {
|
||||||
|
emailData := map[string]interface{}{
|
||||||
|
"UserName": registration.Name,
|
||||||
|
"OTPCode": registration.OTP,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := s.notification.SendEmailTransactional(ctx, entity.SendEmailNotificationParam{
|
||||||
|
Sender: "noreply@enaklo.co.id",
|
||||||
|
Recipient: registration.Email,
|
||||||
|
Subject: "Enaklo - Registration Verification Code",
|
||||||
|
TemplateName: "member_registration_otp",
|
||||||
|
TemplatePath: "templates/member_registration_otp.html",
|
||||||
|
Data: emailData,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *customerSvc) VerifyOTP(ctx mycontext.Context, verificationID, otpCode string) error {
|
||||||
|
customerID, err := s.repo.VerifyOTP(ctx, verificationID, otpCode)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "verification failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
customer, _ := s.repo.FindByID(ctx, customerID)
|
||||||
|
|
||||||
|
go func(customer *entity.Customer) {
|
||||||
|
newCtx := context.Background()
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
log.Printf("Recovered from panic in sendWelcomeEmail: %v", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
s.sendWelcomeEmail(newCtx, customer)
|
||||||
|
}(customer)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *customerSvc) sendWelcomeEmail(
|
||||||
|
ctx context.Context,
|
||||||
|
customer *entity.Customer,
|
||||||
|
) error {
|
||||||
|
|
||||||
|
welcomeData := map[string]interface{}{
|
||||||
|
"UserName": customer.Name,
|
||||||
|
"MemberID": customer.CustomerID,
|
||||||
|
"PointsName": "EnakPoint",
|
||||||
|
"PointsBalance": customer.Points,
|
||||||
|
"RedeemLink": "https://enaklo.co.id/redeem",
|
||||||
|
"CurrentDate": time.Now().Format("01-2006"),
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.notification.SendEmailTransactional(ctx, entity.SendEmailNotificationParam{
|
||||||
|
Sender: "noreply@enaklo.co.id",
|
||||||
|
Recipient: customer.Email,
|
||||||
|
Subject: "Welcome to Enaklo Membership Program",
|
||||||
|
TemplateName: "welcome_member",
|
||||||
|
TemplatePath: "templates/welcome_member.html",
|
||||||
|
Data: welcomeData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
51
internal/services/v2/inprogress_order/in_progress_order.go
Normal file
51
internal/services/v2/inprogress_order/in_progress_order.go
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
package inprogress_order
|
||||||
|
|
||||||
|
import (
|
||||||
|
"enaklo-pos-be/internal/common/logger"
|
||||||
|
"enaklo-pos-be/internal/common/mycontext"
|
||||||
|
"enaklo-pos-be/internal/entity"
|
||||||
|
"enaklo-pos-be/internal/repository"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
type InProgressOrderService interface {
|
||||||
|
Save(ctx mycontext.Context, order *entity.InProgressOrder) (*entity.InProgressOrder, error)
|
||||||
|
GetOrdersByPartnerID(ctx mycontext.Context, partnerID int64, limit, offset int) ([]*entity.InProgressOrder, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type inProgressOrderSvc struct {
|
||||||
|
repo repository.InProgressOrderRepository
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewInProgressOrderService(repo repository.InProgressOrderRepository) InProgressOrderService {
|
||||||
|
return &inProgressOrderSvc{
|
||||||
|
repo: repo,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *inProgressOrderSvc) Save(ctx mycontext.Context, order *entity.InProgressOrder) (*entity.InProgressOrder, error) {
|
||||||
|
createdOrder, err := s.repo.CreateOrUpdate(ctx, order)
|
||||||
|
if err != nil {
|
||||||
|
logger.ContextLogger(ctx).Error("failed to create in-progress order",
|
||||||
|
zap.Error(err),
|
||||||
|
zap.Int64("partnerID", order.PartnerID))
|
||||||
|
return nil, errors.Wrap(err, "failed to create in-progress order")
|
||||||
|
}
|
||||||
|
|
||||||
|
return createdOrder, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *inProgressOrderSvc) GetOrdersByPartnerID(ctx mycontext.Context, partnerID int64, limit, offset int) ([]*entity.InProgressOrder, error) {
|
||||||
|
orders, err := s.repo.GetListByPartnerID(ctx, partnerID, limit, offset)
|
||||||
|
if err != nil {
|
||||||
|
logger.ContextLogger(ctx).Error("failed to get in-progress orders by partner ID",
|
||||||
|
zap.Error(err),
|
||||||
|
zap.Int64("partnerID", partnerID),
|
||||||
|
zap.Int("limit", limit),
|
||||||
|
zap.Int("offset", offset))
|
||||||
|
return nil, errors.Wrap(err, "failed to get in-progress orders")
|
||||||
|
}
|
||||||
|
|
||||||
|
return orders, nil
|
||||||
|
}
|
||||||
@ -51,6 +51,9 @@ func (s *orderSvc) CreateOrderInquiry(ctx mycontext.Context,
|
|||||||
req.CustomerName,
|
req.CustomerName,
|
||||||
req.CustomerPhoneNumber,
|
req.CustomerPhoneNumber,
|
||||||
req.CustomerEmail,
|
req.CustomerEmail,
|
||||||
|
req.PaymentProvider,
|
||||||
|
req.TableNumber,
|
||||||
|
req.OrderType,
|
||||||
)
|
)
|
||||||
|
|
||||||
for _, item := range req.OrderItems {
|
for _, item := range req.OrderItems {
|
||||||
|
|||||||
@ -10,13 +10,14 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (s *orderSvc) ExecuteOrderInquiry(ctx mycontext.Context,
|
func (s *orderSvc) ExecuteOrderInquiry(ctx mycontext.Context,
|
||||||
token string, paymentMethod string) (*entity.OrderResponse, error) {
|
token string, paymentMethod, paymentProvider, inprogressOrderID string) (*entity.OrderResponse, error) {
|
||||||
inquiry, err := s.validateInquiry(ctx, token)
|
inquiry, err := s.validateInquiry(ctx, token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
order := inquiry.ToOrder(paymentMethod)
|
order := inquiry.ToOrder(paymentMethod, paymentProvider)
|
||||||
|
order.InProgressOrderID = inprogressOrderID
|
||||||
|
|
||||||
savedOrder, err := s.repo.Create(ctx, order)
|
savedOrder, err := s.repo.Create(ctx, order)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -51,7 +52,7 @@ func (s *orderSvc) processPostOrderActions(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if order.CustomerID != nil && *order.CustomerID > 0 {
|
if order.CustomerID != nil && *order.CustomerID > 0 {
|
||||||
err = s.addCustomerPoints(ctx, *order.CustomerID, int(order.Total/1000))
|
err = s.addCustomerPoints(ctx, *order.CustomerID, int(order.Total/1000), fmt.Sprintf("TRX #%s", trx.ID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.ContextLogger(ctx).Error("error when adding points", zap.Error(err))
|
logger.ContextLogger(ctx).Error("error when adding points", zap.Error(err))
|
||||||
}
|
}
|
||||||
@ -78,8 +79,8 @@ func (s *orderSvc) createTransaction(ctx mycontext.Context, order *entity.Order,
|
|||||||
return transaction, err
|
return transaction, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *orderSvc) addCustomerPoints(ctx mycontext.Context, customerID int64, points int) error {
|
func (s *orderSvc) addCustomerPoints(ctx mycontext.Context, customerID int64, points int, reference string) error {
|
||||||
return s.customer.AddPoints(ctx, customerID, points)
|
return s.customer.AddPoints(ctx, customerID, points, reference)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *orderSvc) sendTransactionReceipt(ctx mycontext.Context, order *entity.Order, transaction *entity.Transaction, paymentMethod string) error {
|
func (s *orderSvc) sendTransactionReceipt(ctx mycontext.Context, order *entity.Order, transaction *entity.Transaction, paymentMethod string) error {
|
||||||
|
|||||||
@ -21,7 +21,7 @@ type ProductService interface {
|
|||||||
|
|
||||||
type CustomerService interface {
|
type CustomerService interface {
|
||||||
ResolveCustomer(ctx mycontext.Context, req *entity.CustomerResolutionRequest) (int64, error)
|
ResolveCustomer(ctx mycontext.Context, req *entity.CustomerResolutionRequest) (int64, error)
|
||||||
AddPoints(ctx mycontext.Context, customerID int64, points int) error
|
AddPoints(ctx mycontext.Context, customerID int64, points int, reference string) error
|
||||||
GetCustomer(ctx mycontext.Context, id int64) (*entity.Customer, error)
|
GetCustomer(ctx mycontext.Context, id int64) (*entity.Customer, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,7 +42,7 @@ type Service interface {
|
|||||||
CreateOrderInquiry(ctx mycontext.Context,
|
CreateOrderInquiry(ctx mycontext.Context,
|
||||||
req *entity.OrderRequest) (*entity.OrderInquiryResponse, error)
|
req *entity.OrderRequest) (*entity.OrderInquiryResponse, error)
|
||||||
ExecuteOrderInquiry(ctx mycontext.Context,
|
ExecuteOrderInquiry(ctx mycontext.Context,
|
||||||
token string, paymentMethod string) (*entity.OrderResponse, error)
|
token string, paymentMethod, paymentProvider, inProgressOrderID string) (*entity.OrderResponse, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Config interface {
|
type Config interface {
|
||||||
|
|||||||
@ -168,7 +168,7 @@
|
|||||||
<div class="title">Kode Verifikasi Pendaftaran Member</div>
|
<div class="title">Kode Verifikasi Pendaftaran Member</div>
|
||||||
<div class="text">
|
<div class="text">
|
||||||
Hai {{ .UserName }},<br><br>
|
Hai {{ .UserName }},<br><br>
|
||||||
Terima kasih telah mendaftar sebagai member Enaklo. Berikan kode verifikasi berikut kepada staf kasir kami untuk menyelesaikan pendaftaran Anda:
|
Terima kasih telah mendaftar sebagai member Enaklo. Masukan kode verifikasi berikut untuk menyelesaikan pendaftaran Anda:
|
||||||
</div>
|
</div>
|
||||||
<div class="otp-code">{{ .OTPCode }}</div>
|
<div class="otp-code">{{ .OTPCode }}</div>
|
||||||
<div class="expiry">Kode ini berlaku selama 10 menit</div>
|
<div class="expiry">Kode ini berlaku selama 10 menit</div>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user