241 lines
5.9 KiB
Go
241 lines
5.9 KiB
Go
package order
|
|
|
|
import (
|
|
"enaklo-pos-be/internal/common/errors"
|
|
"enaklo-pos-be/internal/common/logger"
|
|
"enaklo-pos-be/internal/common/mycontext"
|
|
"enaklo-pos-be/internal/constants"
|
|
"enaklo-pos-be/internal/entity"
|
|
"go.uber.org/zap"
|
|
"math"
|
|
)
|
|
|
|
func (s *orderSvc) CreateOrderInquiry(ctx mycontext.Context,
|
|
req *entity.OrderRequest) (*entity.OrderInquiryResponse, error) {
|
|
productIDs, filteredItems, err := s.ValidateOrderItems(ctx, req.OrderItems)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
req.OrderItems = filteredItems
|
|
|
|
productDetails, err := s.product.GetProductDetails(ctx, productIDs, req.PartnerID)
|
|
if err != nil {
|
|
logger.ContextLogger(ctx).Error("failed to get product details", zap.Error(err))
|
|
return nil, err
|
|
}
|
|
|
|
orderCalculation, err := s.CalculateOrderTotals(ctx, req.OrderItems, productDetails, req.Source, req.PartnerID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
customerID := int64(0)
|
|
|
|
if req.CustomerID != nil {
|
|
customer, err := s.customer.GetCustomer(ctx, *req.CustomerID)
|
|
if err != nil {
|
|
logger.ContextLogger(ctx).Error("customer is not found", zap.Error(err))
|
|
return nil, err
|
|
}
|
|
customerID = customer.ID
|
|
}
|
|
|
|
if err != nil {
|
|
logger.ContextLogger(ctx).Error("failed to resolve customer", zap.Error(err))
|
|
return nil, err
|
|
}
|
|
|
|
inquiry := entity.NewOrderInquiry(
|
|
req.PartnerID,
|
|
customerID,
|
|
orderCalculation.Subtotal,
|
|
orderCalculation.Tax,
|
|
orderCalculation.Total,
|
|
req.PaymentMethod,
|
|
req.Source,
|
|
req.CreatedBy,
|
|
req.CustomerName,
|
|
req.CustomerPhoneNumber,
|
|
req.CustomerEmail,
|
|
req.PaymentProvider,
|
|
req.TableNumber,
|
|
req.OrderType,
|
|
)
|
|
|
|
for _, item := range req.OrderItems {
|
|
product := productDetails.Products[item.ProductID]
|
|
inquiry.AddOrderItem(item, product)
|
|
}
|
|
|
|
savedInquiry, err := s.repo.CreateInquiry(ctx, inquiry)
|
|
if err != nil {
|
|
logger.ContextLogger(ctx).Error("failed to create order inquiry", zap.Error(err))
|
|
return nil, err
|
|
}
|
|
|
|
token, err := s.crypt.GenerateJWTOrderInquiry(savedInquiry)
|
|
if err != nil {
|
|
logger.ContextLogger(ctx).Error("failed to generate token", zap.Error(err))
|
|
return nil, err
|
|
}
|
|
|
|
return &entity.OrderInquiryResponse{
|
|
OrderInquiry: savedInquiry,
|
|
Token: token,
|
|
}, nil
|
|
}
|
|
|
|
func (s *orderSvc) ValidateOrderItems(ctx mycontext.Context, items []entity.OrderItemRequest) ([]int64, []entity.OrderItemRequest, error) {
|
|
var productIDs []int64
|
|
var filteredItems []entity.OrderItemRequest
|
|
|
|
for _, item := range items {
|
|
if item.Quantity <= 0 {
|
|
continue
|
|
}
|
|
productIDs = append(productIDs, item.ProductID)
|
|
filteredItems = append(filteredItems, item)
|
|
}
|
|
|
|
if len(productIDs) == 0 {
|
|
return nil, nil, errors.ErrorBadRequest
|
|
}
|
|
|
|
return productIDs, filteredItems, nil
|
|
}
|
|
|
|
func (s *orderSvc) CalculateOrderTotals(
|
|
ctx mycontext.Context,
|
|
items []entity.OrderItemRequest,
|
|
productDetails *entity.ProductDetails,
|
|
source string,
|
|
partnerID int64,
|
|
) (*entity.OrderCalculation, error) {
|
|
subtotal := 0.0
|
|
|
|
for _, item := range items {
|
|
product, ok := productDetails.Products[item.ProductID]
|
|
if !ok {
|
|
return nil, errors.NewError(errors.ErrorInvalidRequest.ErrorType(), "product not found")
|
|
}
|
|
subtotal += product.Price * float64(item.Quantity)
|
|
}
|
|
|
|
setting, err := s.partnerSetting.GetSettings(ctx, partnerID)
|
|
|
|
if err != nil {
|
|
return nil, errors.NewError(errors.ErrorInvalidRequest.ErrorType(), "failed to get partner settings")
|
|
}
|
|
|
|
tax := 0.0
|
|
if setting.TaxEnabled {
|
|
tax = (setting.TaxPercentage / 100) * subtotal
|
|
tax = math.Round(tax/100) * 100
|
|
}
|
|
|
|
return &entity.OrderCalculation{
|
|
Subtotal: subtotal,
|
|
Tax: tax,
|
|
Total: subtotal + tax,
|
|
}, nil
|
|
}
|
|
|
|
func (s *orderSvc) validateInquiry(ctx mycontext.Context, token string) (*entity.OrderInquiry, error) {
|
|
partnerID, inquiryID, err := s.crypt.ValidateJWTOrderInquiry(token)
|
|
if err != nil {
|
|
return nil, errors.NewError(errors.ErrorInvalidRequest.ErrorType(), "inquiry is not valid or expired")
|
|
}
|
|
|
|
if partnerID != *ctx.GetPartnerID() {
|
|
return nil, errors.NewError(errors.ErrorInvalidRequest.ErrorType(), "invalid request")
|
|
}
|
|
|
|
inquiry, err := s.repo.FindInquiryByID(ctx, inquiryID)
|
|
if err != nil {
|
|
logger.ContextLogger(ctx).Error("error when finding inquiry", zap.Error(err))
|
|
return nil, err
|
|
}
|
|
|
|
if inquiry.Status != constants.StatusPending {
|
|
return nil, errors.NewError(errors.ErrorInvalidRequest.ErrorType(), "inquiry is no longer pending")
|
|
}
|
|
|
|
return inquiry, nil
|
|
}
|
|
|
|
func (s *orderSvc) GetOrderPaymentAnalysis(
|
|
ctx mycontext.Context,
|
|
partnerID int64,
|
|
req entity.SearchRequest,
|
|
) (*entity.OrderPaymentAnalysis, error) {
|
|
paymentBreakdown, err := s.repo.GetOrderPaymentMethodBreakdown(ctx, partnerID, req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var totalAmount float64
|
|
var totalTransactions int64
|
|
|
|
for _, breakdown := range paymentBreakdown {
|
|
totalAmount += breakdown.TotalAmount
|
|
totalTransactions += breakdown.TotalTransactions
|
|
}
|
|
|
|
return &entity.OrderPaymentAnalysis{
|
|
TotalAmount: totalAmount,
|
|
TotalTransactions: totalTransactions,
|
|
PaymentMethodBreakdown: paymentBreakdown,
|
|
}, nil
|
|
}
|
|
|
|
func (s *orderSvc) GetRevenueOverview(
|
|
ctx mycontext.Context,
|
|
partnerID int64,
|
|
year int,
|
|
granularity string,
|
|
status string,
|
|
) ([]entity.RevenueOverviewItem, error) {
|
|
req := entity.RevenueOverviewRequest{
|
|
PartnerID: partnerID,
|
|
Year: year,
|
|
Granularity: granularity,
|
|
Status: status,
|
|
}
|
|
|
|
return s.repo.GetRevenueOverview(ctx, req)
|
|
}
|
|
|
|
func (s *orderSvc) GetSalesByCategory(
|
|
ctx mycontext.Context,
|
|
partnerID int64,
|
|
period string,
|
|
status string,
|
|
) ([]entity.SalesByCategoryItem, error) {
|
|
req := entity.SalesByCategoryRequest{
|
|
PartnerID: partnerID,
|
|
Period: period,
|
|
Status: status,
|
|
}
|
|
|
|
return s.repo.GetSalesByCategory(ctx, req)
|
|
}
|
|
|
|
func (s *orderSvc) GetPopularProducts(
|
|
ctx mycontext.Context,
|
|
partnerID int64,
|
|
period string,
|
|
status string,
|
|
limit int,
|
|
sortBy string,
|
|
) ([]entity.PopularProductItem, error) {
|
|
req := entity.PopularProductsRequest{
|
|
PartnerID: partnerID,
|
|
Period: period,
|
|
Status: status,
|
|
Limit: limit,
|
|
SortBy: sortBy,
|
|
}
|
|
|
|
return s.repo.GetPopularProducts(ctx, req)
|
|
}
|