package order import ( "enaklo-pos-be/internal/common/logger" "enaklo-pos-be/internal/common/mycontext" "enaklo-pos-be/internal/constants" "enaklo-pos-be/internal/entity" "fmt" "github.com/pkg/errors" "go.uber.org/zap" "time" ) func (s *orderSvc) ExecuteOrderInquiry(ctx mycontext.Context, token string, paymentMethod, paymentProvider string, inprogressOrderID int64) (*entity.OrderResponse, error) { inquiry, err := s.validateInquiry(ctx, token) if err != nil { return nil, err } order := inquiry.ToOrder(paymentMethod, paymentProvider) order.InProgressOrderID = inprogressOrderID savedOrder, err := s.repo.Create(ctx, order) if err != nil { logger.ContextLogger(ctx).Error("failed to create order", zap.Error(err)) return nil, err } err = s.processPostOrderActions(ctx, savedOrder, inquiry.ID, paymentMethod) if err != nil { logger.ContextLogger(ctx).Warn("some post-order actions failed", zap.Error(err)) } return &entity.OrderResponse{ Order: savedOrder, }, nil } func (s *orderSvc) RefundRequest(ctx mycontext.Context, partnerID, orderID int64, reason string) error { order, err := s.repo.FindByIDAndPartnerID(ctx, partnerID, orderID) if err != nil { logger.ContextLogger(ctx).Error("failed to create order", zap.Error(err)) return err } if order.Status != "PAID" { return errors.New("only paid order can be refund") } return s.repo.UpdateOrder(ctx, order.ID, "REFUNDED", reason) } func (s *orderSvc) processPostOrderActions( ctx mycontext.Context, order *entity.Order, inquiryID string, paymentMethod string, ) error { err := s.repo.UpdateInquiryStatus(ctx, inquiryID, constants.StatusExecuted) if err != nil { logger.ContextLogger(ctx).Error("error when updating inquiry status", zap.Error(err)) } trx, err := s.createTransaction(ctx, order, paymentMethod) if err != nil { logger.ContextLogger(ctx).Error("error when creating transaction", zap.Error(err)) } if order.CustomerID != nil && *order.CustomerID > 0 { err = s.addCustomerVouchers(ctx, *order.CustomerID, int64(order.Total), trx.OrderID) if err != nil { logger.ContextLogger(ctx).Error("error when adding points", zap.Error(err)) } } return nil } func (s *orderSvc) createTransaction(ctx mycontext.Context, order *entity.Order, paymentMethod string) (*entity.Transaction, error) { transaction := &entity.Transaction{ ID: constants.GenerateUUID(), OrderID: order.ID, Amount: order.Total, PaymentMethod: paymentMethod, Status: "SUCCESS", CreatedAt: constants.TimeNow(), PartnerID: order.PartnerID, TransactionType: "TRANSACTION", } _, err := s.transaction.Create(ctx, transaction) return transaction, err } func (s *orderSvc) addCustomerVouchers(ctx mycontext.Context, customerID int64, total int64, reference int64) error { undians, err := s.voucherUndianRepo.GetActiveUndianEvents(ctx) if err != nil { return err } eligibleVoucher := []*entity.UndianVoucherDB{} totalVouchersNeeded := 0 for _, v := range undians { if total >= int64(v.MinimumPurchase) { voucherCount := int(total / int64(v.MinimumPurchase)) totalVouchersNeeded += voucherCount } } if totalVouchersNeeded == 0 { return nil } startSequence, err := s.voucherUndianRepo.GetNextVoucherSequenceBatch(ctx, totalVouchersNeeded) if err != nil { return err } currentSequence := startSequence for _, v := range undians { if total >= int64(v.MinimumPurchase) { voucherCount := int(total / int64(v.MinimumPurchase)) for i := 0; i < voucherCount; i++ { voucherCode := s.generateVoucherCode(v.ID, reference, currentSequence) voucher := &entity.UndianVoucherDB{ UndianEventID: v.ID, CustomerID: customerID, VoucherCode: voucherCode, VoucherNumber: &i, IsWinner: false, CreatedAt: time.Now(), } eligibleVoucher = append(eligibleVoucher, voucher) currentSequence++ } } } return s.voucherUndianRepo.CreateUndianVouchers(ctx, eligibleVoucher) } func (s *orderSvc) generateVoucherCode(eventID int64, reference int64, sequence int64) string { eventPart := eventID % 100 // Last 2 digits of event ID sequencePart := sequence % 100000 // Last 5 digits of sequence orderPart := reference % 1000 // Last 3 digits of order ID return fmt.Sprintf("%02d%05d%03d", eventPart, sequencePart, orderPart) } func (s *orderSvc) sendTransactionReceipt(ctx mycontext.Context, order *entity.Order, transaction *entity.Transaction, paymentMethod string) error { newPoint := int(order.Total / 50000) if newPoint <= 0 { return nil } if order.CustomerID == nil || *order.CustomerID == 0 { return nil } customerPoint, err := s.customer.GetCustomerPoints(ctx, *order.CustomerID) if err != nil { return nil } customer, err := s.customer.GetCustomer(ctx, *order.CustomerID) if err != nil { logger.ContextLogger(ctx).Error("error getting customer details", zap.Error(err)) return err } branchName := "Bakso 343 Rawamangun" var productIDs []int64 productIDMap := make(map[int64]bool) for _, item := range order.OrderItems { if item.ItemID > 0 && !productIDMap[item.ItemID] { productIDs = append(productIDs, item.ItemID) productIDMap[item.ItemID] = true } } productMap := make(map[int64]*entity.Product) if len(productIDs) > 0 { products, err := s.product.GetProductsByIDs(ctx, productIDs, order.PartnerID) if err != nil { logger.ContextLogger(ctx).Error("error fetching products", zap.Error(err)) } else { for _, product := range products { productMap[product.ID] = product } } } var itemsData []map[string]string for _, item := range order.OrderItems { itemName := "Item" if product, exists := productMap[item.ItemID]; exists { itemName = product.Name } itemsData = append(itemsData, map[string]string{ "ItemName": itemName, "Quantity": fmt.Sprintf("%d", item.Quantity), "Price": fmt.Sprintf("Rp %s", formatCurrency(item.Price)), }) } transactionDate := transaction.CreatedAt.Format("02 January 2006 15:04") viewTransactionLink := "https://web.enaklo.co.id" emailData := map[string]interface{}{ "UserName": customer.Name, "PointsName": "Point", "PointsBalance": newPoint, "NewPoints": newPoint, "TotalPoints": customerPoint.TotalPoints, "RedeemLink": "web.enaklo.co.id", "BranchName": branchName, "TransactionNumber": order.ID, "TransactionDate": transactionDate, "PaymentMethod": formatPaymentMethod(paymentMethod), "Items": itemsData, "TotalPayment": fmt.Sprintf("Rp %s", formatCurrency(order.Total)), "ViewTransactionLink": viewTransactionLink, "ExpiryDate": order.CreatedAt.Format("02 January 2006"), "UndianDate": "17 Mei 2025", "WebURL": "https://web.enaklo.co.id/undian", } return s.notification.SendEmailTransactional(ctx, entity.SendEmailNotificationParam{ Sender: "noreply@enaklo.co.id", Recipient: customer.Email, Subject: "Hore, kamu dapat poin!", TemplateName: "monthly_points", TemplatePath: "/templates/monthly_points.html", Data: emailData, }) } func formatCurrency(amount float64) string { return fmt.Sprintf("%.2f", amount) } func formatPaymentMethod(method string) string { methodMap := map[string]string{ "CASH": "Tunai", "QRIS": "QRIS", "CARD": "Kartu Kredit/Debit", } if displayName, exists := methodMap[method]; exists { return displayName } return method }