apskel-pos-backend/internal/services/v2/order/create_order_inquiry.go
aditya.siregar 06d7b4764f update
2025-04-26 12:23:12 +07:00

236 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, err := s.customer.ResolveCustomer(ctx, &entity.CustomerResolutionRequest{
ID: req.CustomerID,
Name: req.CustomerName,
Email: req.CustomerEmail,
PhoneNumber: req.CustomerPhoneNumber,
})
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)
}