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) 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, ) (*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) } partnerID := ctx.GetPartnerID() 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) }