Update template email

This commit is contained in:
aditya.siregar
2025-03-08 00:35:23 +07:00
parent 3c80b710af
commit 18003313dd
54 changed files with 2309 additions and 199 deletions
-1
View File
@@ -112,7 +112,6 @@ func (s *BalanceService) WithdrawExecute(ctx mycontext.Context, req *entity.Wall
transaction := &entity.Transaction{
PartnerID: wallet.PartnerID,
TransactionType: "WITHDRAW",
ReferenceID: "",
Status: "WAITING_APPROVAL",
CreatedBy: ctx.RequestedBy(),
Amount: totalAmount,
+10 -74
View File
@@ -76,11 +76,9 @@ func (s *OrderService) CreateOrder(ctx mycontext.Context, req *entity.OrderReque
return nil, err
}
var siteID int64
productMap := make(map[int64]*entity.ProductDB)
for _, product := range products {
productMap[product.ID] = product
siteID = product.SiteID
}
totalAmount := 0.0
@@ -93,32 +91,16 @@ func (s *OrderService) CreateOrder(ctx mycontext.Context, req *entity.OrderReque
totalAmount += product.Price * float64(item.Quantity)
}
parsedTime, err := time.Parse("2006-01-02", req.VisitDate)
if err != nil {
fmt.Println("Error parsing date:", err)
return nil, errors.New("visit date not defined")
}
metadata, err := json.Marshal(map[string]string{
"bank_code": req.BankCode,
"bank_name": req.BankName,
})
order := &entity.Order{
PartnerID: req.PartnerID,
RefID: generator.GenerateUUID(),
Status: order2.New.String(),
Amount: totalAmount,
Total: totalAmount + s.cfg.GetOrderFee(req.Source),
Fee: s.cfg.GetOrderFee(req.Source),
PaymentType: req.PaymentMethod,
SiteID: &siteID,
CreatedBy: req.CreatedBy,
OrderItems: []entity.OrderItem{},
Source: req.Source,
VisitDate: parsedTime,
TicketStatus: "UNUSED",
Metadata: metadata,
PartnerID: req.PartnerID,
Status: order2.New.String(),
Amount: totalAmount,
Total: totalAmount + s.cfg.GetOrderFee(req.Source),
Fee: s.cfg.GetOrderFee(req.Source),
PaymentType: req.PaymentMethod,
CreatedBy: req.CreatedBy,
OrderItems: []entity.OrderItem{},
Source: req.Source,
}
for _, item := range req.OrderItems {
@@ -126,7 +108,7 @@ func (s *OrderService) CreateOrder(ctx mycontext.Context, req *entity.OrderReque
ItemID: item.ProductID,
ItemType: productMap[item.ProductID].Type,
Price: productMap[item.ProductID].Price,
Quantity: item.Quantity,
Quantity: int(item.Quantity),
CreatedBy: req.CreatedBy,
Product: productMap[item.ProductID].ToProduct(),
})
@@ -138,12 +120,6 @@ func (s *OrderService) CreateOrder(ctx mycontext.Context, req *entity.OrderReque
return nil, err
}
token, err := s.crypt.GenerateJWTOrder(order)
if err != nil {
logger.ContextLogger(ctx).Error("error when create token", zap.Error(err))
return nil, err
}
order, err = s.repo.FindByID(ctx, order.ID)
if err != nil {
logger.ContextLogger(ctx).Error("error when creating order", zap.Error(err))
@@ -152,7 +128,6 @@ func (s *OrderService) CreateOrder(ctx mycontext.Context, req *entity.OrderReque
return &entity.OrderResponse{
Order: order,
Token: token,
}, nil
}
@@ -187,26 +162,6 @@ func (s *OrderService) CheckInInquiry(ctx mycontext.Context, qrCode string, part
return nil, errors2.ErrorInvalidRequest
}
location, _ := time.LoadLocation("Asia/Jakarta")
today := time.Now().In(location).Format("2006-01-02")
visitDate := time.Date(
order.VisitDate.Year(),
order.VisitDate.Month(),
order.VisitDate.Day(),
0, 0, 0, 0,
location,
).Format("2006-01-02")
if order.TicketStatus == "USED" || visitDate < today {
return nil, errors2.NewErrorMessage(errors2.ErrorTicketInvalidOrAlreadyUsed,
"Maaf! Tiket ini tidak valid karena sudah terpakai atau sudah kadaluwarsa")
}
if visitDate != today {
return nil, errors2.NewErrorMessage(errors2.ErrorTicketInvalidOrAlreadyUsed,
"Maaf Tiket ini tidak valid karena tidak sesuai dengan tanggal tiket")
}
token, err := s.crypt.GenerateJWTOrder(order)
if err != nil {
logger.ContextLogger(ctx).Error("error when generate checkin token", zap.Error(err))
@@ -242,17 +197,6 @@ func (s *OrderService) CheckInExecute(ctx mycontext.Context,
Order: order,
}
if order.TicketStatus != "UNUSED" {
return resp, nil
}
order.TicketStatus = "USED"
order, err = s.repo.Update(ctx, order)
if err != nil {
logger.ContextLogger(ctx).Error("error when updating order status", zap.Error(err))
return nil, err
}
return resp, nil
}
@@ -446,11 +390,6 @@ func (s *OrderService) processQRPayment(ctx mycontext.Context, order *entity.Ord
}
func (s *OrderService) processVAPayment(ctx mycontext.Context, order *entity.Order, partnerID, createdBy int64) (*entity.PaymentResponse, error) {
metadata := map[string]string{}
if err := json.Unmarshal(order.Metadata, &metadata); err != nil {
return nil, err
}
paymentRequest := entity.PaymentRequest{
PaymentReferenceID: generator.GenerateUUIDV4(),
TotalAmount: int64(order.Total),
@@ -458,7 +397,6 @@ func (s *OrderService) processVAPayment(ctx mycontext.Context, order *entity.Ord
CustomerID: strconv.FormatInt(order.User.ID, 10),
CustomerName: order.User.Name,
CustomerEmail: order.User.Email,
BankCode: metadata["bank_code"],
}
paymentResponse, err := s.pg.CreatePaymentVA(paymentRequest)
@@ -544,13 +482,11 @@ func (s *OrderService) processPayment(ctx context.Context, tx *gorm.DB, req *ent
transaction := &entity.Transaction{
PartnerID: existingPayment.PartnerID,
TransactionType: "PAYMENT_RECEIVED",
ReferenceID: existingPayment.ReferenceID,
Status: "SUCCESS",
CreatedBy: 0,
Amount: existingPayment.Amount,
Fee: order.Fee,
Total: order.Total,
SiteID: order.SiteID,
}
if _, err = s.transaction.Create(ctx, tx, transaction); err != nil {
return fmt.Errorf("failed to update transaction: %w", err)
+12
View File
@@ -14,6 +14,9 @@ import (
"enaklo-pos-be/internal/services/studio"
"enaklo-pos-be/internal/services/transaction"
"enaklo-pos-be/internal/services/users"
customerSvc "enaklo-pos-be/internal/services/v2/customer"
orderSvc "enaklo-pos-be/internal/services/v2/order"
productSvc "enaklo-pos-be/internal/services/v2/product"
"gorm.io/gorm"
@@ -38,9 +41,17 @@ type ServiceManagerImpl struct {
Transaction Transaction
Balance Balance
DiscoverService DiscoverService
OrderV2Svc orderSvc.Service
CustomerV2Svc customerSvc.Service
ProductV2Svc productSvc.Service
}
func NewServiceManagerImpl(cfg *config.Config, repo *repository.RepoManagerImpl) *ServiceManagerImpl {
custSvcV2 := customerSvc.New(repo.CustomerRepo)
productSvcV2 := productSvc.New(repo.ProductRepo)
return &ServiceManagerImpl{
AuthSvc: auth.New(repo.Auth, repo.Crypto, repo.User, repo.EmailService, cfg.Email, repo.Trx, repo.License),
EventSvc: event.NewEventService(repo.Event),
@@ -56,6 +67,7 @@ func NewServiceManagerImpl(cfg *config.Config, repo *repository.RepoManagerImpl)
Transaction: transaction.New(repo.Transaction, repo.Wallet, repo.Trx),
Balance: balance.NewBalanceService(repo.Wallet, repo.Trx, repo.Crypto, &cfg.Withdraw, repo.Transaction),
DiscoverService: discovery.NewDiscoveryService(repo.Site, cfg.Discovery, repo.Product),
OrderV2Svc: orderSvc.New(repo.OrderRepo, productSvcV2, custSvcV2, repo.TransactionRepo, repo.Crypto, &cfg.Order, repo.EmailService),
}
}
+117
View File
@@ -0,0 +1,117 @@
package customer
import (
"enaklo-pos-be/internal/common/logger"
"enaklo-pos-be/internal/common/mycontext"
"enaklo-pos-be/internal/constants"
"enaklo-pos-be/internal/entity"
"github.com/pkg/errors"
"go.uber.org/zap"
"strings"
)
type Repository interface {
Create(ctx mycontext.Context, customer *entity.Customer) (*entity.Customer, error)
FindByID(ctx mycontext.Context, id int64) (*entity.Customer, error)
FindByPhone(ctx mycontext.Context, phone string) (*entity.Customer, error)
FindByEmail(ctx mycontext.Context, email string) (*entity.Customer, error)
AddPoints(ctx mycontext.Context, id int64, points int) error
}
type Service interface {
ResolveCustomer(ctx mycontext.Context, req *entity.CustomerResolutionRequest) (int64, error)
AddPoints(ctx mycontext.Context, customerID int64, points int) error
GetCustomer(ctx mycontext.Context, id int64) (*entity.Customer, error)
}
type customerSvc struct {
repo Repository
}
func New(repo Repository) Service {
return &customerSvc{
repo: repo,
}
}
func (s *customerSvc) ResolveCustomer(ctx mycontext.Context, req *entity.CustomerResolutionRequest) (int64, error) {
if req.Email == "" && req.PhoneNumber == "" {
return 0, nil
}
if req.ID != nil && *req.ID > 0 {
customer, err := s.repo.FindByID(ctx, *req.ID)
if err != nil {
if !strings.Contains(err.Error(), "not found") {
return 0, errors.Wrap(err, "failed to find customer by ID")
}
} else {
return customer.ID, nil
}
}
if req.PhoneNumber != "" {
customer, err := s.repo.FindByPhone(ctx, req.PhoneNumber)
if err != nil {
if !strings.Contains(err.Error(), "not found") {
return 0, errors.Wrap(err, "failed to find customer by phone")
}
} else {
return customer.ID, nil
}
}
if req.Email != "" {
customer, err := s.repo.FindByEmail(ctx, req.Email)
if err != nil {
if !strings.Contains(err.Error(), "not found") {
return 0, errors.Wrap(err, "failed to find customer by email")
}
} else {
return customer.ID, nil
}
}
if req.Name == "" {
return 0, errors.New("customer name is required to create a new customer")
}
newCustomer := &entity.Customer{
Name: req.Name,
Email: req.Email,
Phone: req.PhoneNumber,
Points: 0,
CreatedAt: constants.TimeNow(),
UpdatedAt: constants.TimeNow(),
}
customer, err := s.repo.Create(ctx, newCustomer)
if err != nil {
logger.ContextLogger(ctx).Error("failed to create customer", zap.Error(err))
return 0, errors.Wrap(err, "failed to create customer")
}
return customer.ID, nil
}
func (s *customerSvc) AddPoints(ctx mycontext.Context, customerID int64, points int) error {
if points <= 0 {
return nil
}
err := s.repo.AddPoints(ctx, customerID, points)
if err != nil {
return errors.Wrap(err, "failed to add points to customer")
}
return nil
}
func (s *customerSvc) GetCustomer(ctx mycontext.Context, id int64) (*entity.Customer, error) {
customer, err := s.repo.FindByID(ctx, id)
if err != nil {
return nil, errors.Wrap(err, "failed to get customer")
}
return customer, nil
}
@@ -0,0 +1,144 @@
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"
)
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.Fee,
orderCalculation.Total,
req.PaymentMethod,
req.Source,
req.CreatedBy,
req.CustomerName,
req.CustomerPhoneNumber,
req.CustomerEmail,
)
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)
}
fee := s.cfg.GetOrderFee(source)
return &entity.OrderCalculation{
Subtotal: subtotal,
Fee: fee,
Total: subtotal + fee,
}, 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
}
+173
View File
@@ -0,0 +1,173 @@
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"
"go.uber.org/zap"
)
func (s *orderSvc) ExecuteOrderInquiry(ctx mycontext.Context,
token string, paymentMethod string) (*entity.OrderResponse, error) {
inquiry, err := s.validateInquiry(ctx, token)
if err != nil {
return nil, err
}
order := inquiry.ToOrder(paymentMethod)
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) 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.addCustomerPoints(ctx, *order.CustomerID, int(order.Total))
if err != nil {
logger.ContextLogger(ctx).Error("error when adding points", zap.Error(err))
}
}
s.sendTransactionReceipt(ctx, order, trx, "CASH")
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) addCustomerPoints(ctx mycontext.Context, customerID int64, points int) error {
return s.customer.AddPoints(ctx, customerID, points)
}
func (s *orderSvc) sendTransactionReceipt(ctx mycontext.Context, order *entity.Order, transaction *entity.Transaction, paymentMethod string) error {
if order.CustomerID == nil || *order.CustomerID == 0 {
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 := fmt.Sprintf("https://enaklo.co.id/transaction/%s", transaction.ID)
emailData := map[string]interface{}{
"UserName": customer.Name,
"BranchName": branchName,
"TransactionNumber": order.ID,
"TransactionDate": transactionDate,
"PaymentMethod": formatPaymentMethod(paymentMethod),
"Items": itemsData,
"TotalPayment": fmt.Sprintf("Rp %s", formatCurrency(order.Total)),
"ViewTransactionLink": viewTransactionLink,
}
return s.notification.SendEmailTransactional(ctx, entity.SendEmailNotificationParam{
Sender: "noreply@enaklo.co.id",
Recipient: customer.Email,
Subject: "Enaklo - Resi Pembelian",
TemplateName: "transaction_receipt",
TemplatePath: "templates/transaction_receipt.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
}
+80
View File
@@ -0,0 +1,80 @@
package order
import (
"context"
"enaklo-pos-be/internal/common/mycontext"
"enaklo-pos-be/internal/entity"
)
type Repository interface {
Create(ctx mycontext.Context, order *entity.Order) (*entity.Order, error)
FindByID(ctx mycontext.Context, id int64) (*entity.Order, error)
CreateInquiry(ctx mycontext.Context, inquiry *entity.OrderInquiry) (*entity.OrderInquiry, error)
FindInquiryByID(ctx mycontext.Context, id string) (*entity.OrderInquiry, error)
UpdateInquiryStatus(ctx mycontext.Context, id string, status string) error
}
type ProductService interface {
GetProductDetails(ctx mycontext.Context, productIDs []int64, partnerID int64) (*entity.ProductDetails, error)
GetProductsByIDs(ctx mycontext.Context, ids []int64, partnerID int64) ([]*entity.Product, error)
}
type CustomerService interface {
ResolveCustomer(ctx mycontext.Context, req *entity.CustomerResolutionRequest) (int64, error)
AddPoints(ctx mycontext.Context, customerID int64, points int) error
GetCustomer(ctx mycontext.Context, id int64) (*entity.Customer, error)
}
type TransactionService interface {
Create(ctx mycontext.Context, transaction *entity.Transaction) (*entity.Transaction, error)
}
type CryptService interface {
GenerateJWTOrderInquiry(inquiry *entity.OrderInquiry) (string, error)
ValidateJWTOrderInquiry(tokenString string) (int64, string, error)
}
type NotificationService interface {
SendEmailTransactional(ctx context.Context, param entity.SendEmailNotificationParam) error
}
type Service interface {
CreateOrderInquiry(ctx mycontext.Context,
req *entity.OrderRequest) (*entity.OrderInquiryResponse, error)
ExecuteOrderInquiry(ctx mycontext.Context,
token string, paymentMethod string) (*entity.OrderResponse, error)
}
type Config interface {
GetOrderFee(source string) float64
}
type orderSvc struct {
repo Repository
product ProductService
customer CustomerService
transaction TransactionService
crypt CryptService
cfg Config
notification NotificationService
}
func New(
repo Repository,
product ProductService,
customer CustomerService,
transaction TransactionService,
crypt CryptService,
cfg Config,
notification NotificationService,
) Service {
return &orderSvc{
repo: repo,
product: product,
customer: customer,
transaction: transaction,
crypt: crypt,
cfg: cfg,
notification: notification,
}
}
@@ -0,0 +1,33 @@
package product
import (
"enaklo-pos-be/internal/common/logger"
"enaklo-pos-be/internal/common/mycontext"
"enaklo-pos-be/internal/entity"
"github.com/pkg/errors"
"go.uber.org/zap"
)
func (s *productSvc) GetProductsByIDs(ctx mycontext.Context, ids []int64, partnerID int64) ([]*entity.Product, error) {
if len(ids) == 0 {
return []*entity.Product{}, nil
}
products, err := s.repo.GetProductsByIDs(ctx, ids, partnerID)
if err != nil {
logger.ContextLogger(ctx).Error("failed to get products by IDs",
zap.Int64s("productIDs", ids),
zap.Int64("partnerID", partnerID),
zap.Error(err))
return nil, errors.Wrap(err, "failed to get products by IDs")
}
// Validate that we found all requested products
if len(products) != len(ids) {
logger.ContextLogger(ctx).Warn("some products not found",
zap.Int("requestedCount", len(ids)),
zap.Int("foundCount", len(products)))
}
return products, nil
}
@@ -0,0 +1,56 @@
package product
import (
"enaklo-pos-be/internal/common/logger"
"enaklo-pos-be/internal/common/mycontext"
"enaklo-pos-be/internal/entity"
"github.com/pkg/errors"
"go.uber.org/zap"
)
func (s *productSvc) GetProductDetails(ctx mycontext.Context, productIDs []int64, partnerID int64) (*entity.ProductDetails, error) {
if len(productIDs) == 0 {
return &entity.ProductDetails{
Products: make(map[int64]*entity.Product),
}, nil
}
productDetails, err := s.repo.GetProductDetails(ctx, productIDs, partnerID)
if err != nil {
logger.ContextLogger(ctx).Error("failed to get product details",
zap.Int64s("productIDs", productIDs),
zap.Int64("partnerID", partnerID),
zap.Error(err))
return nil, errors.Wrap(err, "failed to get product details")
}
if len(productDetails.Products) != len(productIDs) {
missingIDs := findMissingProductIDs(productIDs, productDetails.Products)
logger.ContextLogger(ctx).Warn("some products not found",
zap.Int("requestedCount", len(productIDs)),
zap.Int("foundCount", len(productDetails.Products)),
zap.Int64s("missingIDs", missingIDs))
if len(productDetails.Products) == 0 {
return nil, errors.New("no products found")
}
}
return productDetails, nil
}
func findMissingProductIDs(requestedIDs []int64, foundProducts map[int64]*entity.Product) []int64 {
var missingIDs []int64
for _, id := range requestedIDs {
if _, exists := foundProducts[id]; !exists {
missingIDs = append(missingIDs, id)
}
}
return missingIDs
}
func (s *productSvc) IsProductAvailable(product *entity.Product) bool {
return product.Status == "ACTIVE"
}
+26
View File
@@ -0,0 +1,26 @@
package product
import (
"enaklo-pos-be/internal/common/mycontext"
"enaklo-pos-be/internal/entity"
)
type Repository interface {
GetProductsByIDs(ctx mycontext.Context, ids []int64, partnerID int64) ([]*entity.Product, error)
GetProductDetails(ctx mycontext.Context, productIDs []int64, partnerID int64) (*entity.ProductDetails, error)
}
type Service interface {
GetProductsByIDs(ctx mycontext.Context, ids []int64, partnerID int64) ([]*entity.Product, error)
GetProductDetails(ctx mycontext.Context, productIDs []int64, partnerID int64) (*entity.ProductDetails, error)
}
type productSvc struct {
repo Repository
}
func New(repo Repository) Service {
return &productSvc{
repo: repo,
}
}