update
This commit is contained in:
@@ -1,623 +0,0 @@
|
||||
package order
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
errors2 "enaklo-pos-be/internal/common/errors"
|
||||
"enaklo-pos-be/internal/common/logger"
|
||||
"enaklo-pos-be/internal/common/mycontext"
|
||||
order2 "enaklo-pos-be/internal/constants/order"
|
||||
"enaklo-pos-be/internal/entity"
|
||||
"enaklo-pos-be/internal/repository"
|
||||
"enaklo-pos-be/internal/utils/generator"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go.uber.org/zap"
|
||||
"golang.org/x/net/context"
|
||||
"gorm.io/gorm"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Config interface {
|
||||
GetOrderFee(source string) float64
|
||||
}
|
||||
|
||||
type OrderService struct {
|
||||
repo repository.Order
|
||||
crypt repository.Crypto
|
||||
product repository.Product
|
||||
pg repository.PaymentGateway
|
||||
payment repository.Payment
|
||||
transaction repository.TransactionRepository
|
||||
txmanager repository.TransactionManager
|
||||
wallet repository.WalletRepository
|
||||
linkquRepo repository.LinkQu
|
||||
cfg Config
|
||||
}
|
||||
|
||||
func NewOrderService(
|
||||
repo repository.Order,
|
||||
product repository.Product, crypt repository.Crypto,
|
||||
pg repository.PaymentGateway, payment repository.Payment,
|
||||
txmanager repository.TransactionManager,
|
||||
wallet repository.WalletRepository, cfg Config,
|
||||
transaction repository.TransactionRepository,
|
||||
linkquRepo repository.LinkQu,
|
||||
) *OrderService {
|
||||
return &OrderService{
|
||||
repo: repo,
|
||||
product: product,
|
||||
crypt: crypt,
|
||||
pg: pg,
|
||||
payment: payment,
|
||||
txmanager: txmanager,
|
||||
wallet: wallet,
|
||||
cfg: cfg,
|
||||
transaction: transaction,
|
||||
linkquRepo: linkquRepo,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *OrderService) CreateOrder(ctx mycontext.Context, req *entity.OrderRequest) (*entity.OrderResponse, error) {
|
||||
productIDs, filteredItems := s.filterOrderItems(req.OrderItems)
|
||||
if len(productIDs) == 0 {
|
||||
return nil, errors2.ErrorBadRequest
|
||||
}
|
||||
req.OrderItems = filteredItems
|
||||
|
||||
if len(productIDs) < 1 {
|
||||
return nil, errors2.ErrorBadRequest
|
||||
}
|
||||
|
||||
products, err := s.product.GetProductsByIDs(ctx, productIDs, req.PartnerID)
|
||||
if err != nil {
|
||||
logger.ContextLogger(ctx).Error("error when getting products by IDs", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
productMap := make(map[int64]*entity.ProductDB)
|
||||
for _, product := range products {
|
||||
productMap[product.ID] = product
|
||||
}
|
||||
|
||||
totalAmount := 0.0
|
||||
for _, item := range req.OrderItems {
|
||||
product, ok := productMap[item.ProductID]
|
||||
if !ok {
|
||||
logger.ContextLogger(ctx).Error("product not found", zap.Int64("productID", item.ProductID))
|
||||
return nil, errors.New("product not found")
|
||||
}
|
||||
totalAmount += product.Price * float64(item.Quantity)
|
||||
}
|
||||
|
||||
order := &entity.Order{
|
||||
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 {
|
||||
order.OrderItems = append(order.OrderItems, entity.OrderItem{
|
||||
ItemID: item.ProductID,
|
||||
ItemType: productMap[item.ProductID].Type,
|
||||
Price: productMap[item.ProductID].Price,
|
||||
Quantity: int(item.Quantity),
|
||||
CreatedBy: req.CreatedBy,
|
||||
Product: productMap[item.ProductID].ToProduct(),
|
||||
})
|
||||
}
|
||||
|
||||
order, err = s.repo.Create(ctx, order)
|
||||
if err != nil {
|
||||
logger.ContextLogger(ctx).Error("error when creating order", 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))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &entity.OrderResponse{
|
||||
Order: order,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *OrderService) filterOrderItems(items []entity.OrderItemRequest) ([]int64, []entity.OrderItemRequest) {
|
||||
var productIDs []int64
|
||||
var filteredItems []entity.OrderItemRequest
|
||||
for _, item := range items {
|
||||
if item.Quantity != 0 {
|
||||
productIDs = append(productIDs, item.ProductID)
|
||||
filteredItems = append(filteredItems, item)
|
||||
}
|
||||
}
|
||||
return productIDs, filteredItems
|
||||
}
|
||||
|
||||
func (s *OrderService) CheckInInquiry(ctx mycontext.Context, qrCode string, partnerID *int64) (*entity.CheckinResponse, error) {
|
||||
order, err := s.repo.FindByQRCode(ctx, qrCode)
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, errors2.NewErrorMessage(errors2.ErrorInvalidRequest, "Not Valid QR Code")
|
||||
}
|
||||
|
||||
logger.ContextLogger(ctx).Error("error when getting order by QR code", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if order.PartnerID != *partnerID {
|
||||
return nil, errors2.ErrorBadRequest
|
||||
}
|
||||
|
||||
if order.Status != "PAID" {
|
||||
return nil, errors2.ErrorInvalidRequest
|
||||
}
|
||||
|
||||
token, err := s.crypt.GenerateJWTOrder(order)
|
||||
if err != nil {
|
||||
logger.ContextLogger(ctx).Error("error when generate checkin token", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
orderResponse := &entity.CheckinResponse{
|
||||
Token: token,
|
||||
}
|
||||
|
||||
return orderResponse, nil
|
||||
}
|
||||
|
||||
func (s *OrderService) CheckInExecute(ctx mycontext.Context,
|
||||
token string, partnerID *int64) (*entity.CheckinExecute, error) {
|
||||
pID, orderID, err := s.crypt.ValidateJWTOrder(token)
|
||||
if err != nil {
|
||||
logger.ContextLogger(ctx).Error("error when validating JWT order", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if pID != *partnerID {
|
||||
return nil, errors2.ErrorBadRequest
|
||||
}
|
||||
|
||||
order, err := s.repo.FindByID(ctx, orderID)
|
||||
if err != nil {
|
||||
logger.ContextLogger(ctx).Error("error when getting order by ID", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &entity.CheckinExecute{
|
||||
Order: order,
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (s *OrderService) Execute(ctx mycontext.Context, req *entity.OrderExecuteRequest) (*entity.ExecuteOrderResponse, error) {
|
||||
partnerID, orderID, err := s.crypt.ValidateJWTOrder(req.Token)
|
||||
if err != nil {
|
||||
logger.ContextLogger(ctx).Error("error when validating JWT order", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
order, err := s.repo.FindByID(ctx, orderID)
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
logger.ContextLogger(ctx).Error("order not found", zap.Int64("orderID", orderID))
|
||||
return nil, errors.New("order not found")
|
||||
}
|
||||
logger.ContextLogger(ctx).Error("error when finding order by ID", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
payment, err := s.payment.FindByOrderAndPartnerID(ctx, orderID, partnerID)
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
logger.ContextLogger(ctx).Error("error getting payment data from db", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if payment != nil {
|
||||
return s.createExecuteOrderResponse(order, payment), nil
|
||||
}
|
||||
|
||||
if order.PartnerID != partnerID {
|
||||
logger.ContextLogger(ctx).Error("partner ID mismatch", zap.Int64("orderID", orderID), zap.Int64("tokenPartnerID", partnerID), zap.Int64("orderPartnerID", order.PartnerID))
|
||||
return nil, errors.New("partner ID mismatch")
|
||||
}
|
||||
|
||||
if order.Status != "NEW" {
|
||||
return nil, errors.New("invalid state")
|
||||
}
|
||||
|
||||
resp := &entity.ExecuteOrderResponse{
|
||||
Order: order,
|
||||
}
|
||||
|
||||
order.SetExecutePaymentStatus()
|
||||
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
|
||||
}
|
||||
|
||||
func (s *OrderService) createExecuteOrderResponse(order *entity.Order, payment *entity.Payment) *entity.ExecuteOrderResponse {
|
||||
var metadata map[string]string
|
||||
if err := json.Unmarshal(payment.RequestMetadata, &metadata); err != nil {
|
||||
logger.ContextLogger(context.Background()).Error("error unmarshaling request metadata", zap.Error(err))
|
||||
return &entity.ExecuteOrderResponse{
|
||||
Order: order,
|
||||
}
|
||||
}
|
||||
return &entity.ExecuteOrderResponse{
|
||||
Order: order,
|
||||
PaymentToken: metadata["payment_token"],
|
||||
RedirectURL: metadata["payment_redirect_url"],
|
||||
}
|
||||
}
|
||||
|
||||
func (s *OrderService) processNonCashPayment(ctx context.Context, order *entity.Order, partnerID, createdBy int64) (*entity.MidtransResponse, error) {
|
||||
paymentRequest := entity.PaymentRequest{
|
||||
PaymentReferenceID: generator.GenerateUUIDV4(),
|
||||
TotalAmount: int64(order.Total),
|
||||
//OrderItems: order.OrderItems,
|
||||
Provider: order.PaymentType,
|
||||
}
|
||||
|
||||
paymentResponse, err := s.pg.CreatePayment(paymentRequest)
|
||||
if err != nil {
|
||||
logger.ContextLogger(ctx).Error("error when creating payment", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
requestMetadata, err := json.Marshal(map[string]string{
|
||||
"partner_id": strconv.FormatInt(partnerID, 10),
|
||||
"created_by": strconv.FormatInt(createdBy, 10),
|
||||
"payment_token": paymentResponse.Token,
|
||||
"payment_redirect_url": paymentResponse.RedirectURL,
|
||||
})
|
||||
if err != nil {
|
||||
logger.ContextLogger(ctx).Error("error when marshaling request metadata", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
payment := &entity.Payment{
|
||||
PartnerID: partnerID,
|
||||
OrderID: order.ID,
|
||||
ReferenceID: paymentRequest.PaymentReferenceID,
|
||||
Channel: "MIDTRANS",
|
||||
PaymentType: order.PaymentType,
|
||||
Amount: order.Amount,
|
||||
State: "PENDING",
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
RequestMetadata: requestMetadata,
|
||||
}
|
||||
|
||||
_, err = s.payment.Create(ctx, payment)
|
||||
if err != nil {
|
||||
logger.ContextLogger(ctx).Error("error when creating payment record", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &entity.MidtransResponse{
|
||||
Token: paymentResponse.Token,
|
||||
RedirectURL: paymentResponse.RedirectURL,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *OrderService) processQRPayment(ctx mycontext.Context, order *entity.Order, partnerID, createdBy int64) (*entity.PaymentResponse, error) {
|
||||
paymentRequest := entity.PaymentRequest{
|
||||
PaymentReferenceID: generator.GenerateUUIDV4(),
|
||||
TotalAmount: int64(order.Total),
|
||||
Provider: "LINKQU",
|
||||
CustomerID: fmt.Sprintf("POS-%d", ctx.RequestedBy()),
|
||||
CustomerName: fmt.Sprintf("POS-%s", ctx.GetName()),
|
||||
}
|
||||
|
||||
paymentResponse, err := s.pg.CreateQRISPayment(paymentRequest)
|
||||
if err != nil {
|
||||
logger.ContextLogger(ctx).Error("error when creating payment", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
requestMetadata, err := json.Marshal(map[string]string{
|
||||
"partner_id": strconv.FormatInt(partnerID, 10),
|
||||
"created_by": strconv.FormatInt(createdBy, 10),
|
||||
"qr_code": paymentResponse.QRCodeURL,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
logger.ContextLogger(ctx).Error("error when marshaling request metadata", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
payment := &entity.Payment{
|
||||
PartnerID: partnerID,
|
||||
OrderID: order.ID,
|
||||
ReferenceID: paymentRequest.PaymentReferenceID,
|
||||
Channel: "LINKQU",
|
||||
PaymentType: order.PaymentType,
|
||||
Amount: order.Amount,
|
||||
State: "PENDING",
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
RequestMetadata: requestMetadata,
|
||||
}
|
||||
|
||||
_, err = s.payment.Create(ctx, payment)
|
||||
if err != nil {
|
||||
logger.ContextLogger(ctx).Error("error when creating payment record", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return paymentResponse, nil
|
||||
}
|
||||
|
||||
func (s *OrderService) processVAPayment(ctx mycontext.Context, order *entity.Order, partnerID, createdBy int64) (*entity.PaymentResponse, error) {
|
||||
paymentRequest := entity.PaymentRequest{
|
||||
PaymentReferenceID: generator.GenerateUUIDV4(),
|
||||
TotalAmount: int64(order.Total),
|
||||
Provider: "LINKQU",
|
||||
CustomerID: strconv.FormatInt(order.User.ID, 10),
|
||||
CustomerName: order.User.Name,
|
||||
CustomerEmail: order.User.Email,
|
||||
}
|
||||
|
||||
paymentResponse, err := s.pg.CreatePaymentVA(paymentRequest)
|
||||
if err != nil {
|
||||
logger.ContextLogger(ctx).Error("error when creating payment", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
requestMetadata, err := json.Marshal(map[string]string{
|
||||
"virtual_account": paymentResponse.VirtualAccountNumber,
|
||||
"bank_name": paymentResponse.BankName,
|
||||
"bank_code": paymentResponse.BankCode,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
logger.ContextLogger(ctx).Error("error when marshaling request metadata", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
payment := &entity.Payment{
|
||||
PartnerID: partnerID,
|
||||
OrderID: order.ID,
|
||||
ReferenceID: paymentRequest.PaymentReferenceID,
|
||||
Channel: "LINKQU",
|
||||
PaymentType: order.PaymentType,
|
||||
Amount: order.Amount,
|
||||
State: "PENDING",
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
RequestMetadata: requestMetadata,
|
||||
}
|
||||
|
||||
_, err = s.payment.Create(ctx, payment)
|
||||
if err != nil {
|
||||
logger.ContextLogger(ctx).Error("error when creating payment record", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return paymentResponse, nil
|
||||
}
|
||||
|
||||
func (s *OrderService) ProcessCallback(ctx context.Context, req *entity.CallbackRequest) error {
|
||||
tx, err := s.txmanager.Begin(ctx, &sql.TxOptions{Isolation: sql.LevelSerializable})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to begin transaction: %w", err)
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
err = s.processPayment(ctx, tx, req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to process payment: %w", err)
|
||||
}
|
||||
|
||||
return tx.Commit().Error
|
||||
}
|
||||
|
||||
func (s *OrderService) processPayment(ctx context.Context, tx *gorm.DB, req *entity.CallbackRequest) error {
|
||||
existingPayment, err := s.payment.FindByReferenceID(ctx, tx, req.TransactionID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to retrieve payment: %w", err)
|
||||
}
|
||||
|
||||
existingPayment.State = updatePaymentState(req.TransactionStatus)
|
||||
_, err = s.payment.UpdateWithTx(ctx, tx, existingPayment)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to update payment: %w", err)
|
||||
}
|
||||
|
||||
order, err := s.repo.FindByID(ctx, existingPayment.OrderID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get order: %w", err)
|
||||
}
|
||||
|
||||
if err := s.updateOrderStatus(ctx, tx, existingPayment.State, existingPayment.OrderID); err != nil {
|
||||
return fmt.Errorf("failed to update order status: %w", err)
|
||||
}
|
||||
|
||||
if existingPayment.State == "PAID" {
|
||||
if err := s.updateWalletBalance(ctx, tx, existingPayment.PartnerID, existingPayment.Amount); err != nil {
|
||||
return fmt.Errorf("failed to update wallet balance: %w", err)
|
||||
}
|
||||
|
||||
transaction := &entity.Transaction{
|
||||
PartnerID: existingPayment.PartnerID,
|
||||
TransactionType: "PAYMENT_RECEIVED",
|
||||
Status: "SUCCESS",
|
||||
CreatedBy: 0,
|
||||
Amount: existingPayment.Amount,
|
||||
Fee: order.Fee,
|
||||
Total: order.Total,
|
||||
}
|
||||
if _, err = s.transaction.Create(ctx, tx, transaction); err != nil {
|
||||
return fmt.Errorf("failed to update transaction: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func updatePaymentState(status string) string {
|
||||
switch status {
|
||||
case "settlement", "capture", "paid", "settle":
|
||||
return "PAID"
|
||||
case "expire", "deny", "cancel", "failure", "EXPIRED":
|
||||
return "EXPIRED"
|
||||
default:
|
||||
return status
|
||||
}
|
||||
}
|
||||
|
||||
func (s *OrderService) updateOrderStatus(ctx context.Context, tx *gorm.DB, status string, orderID int64) error {
|
||||
if status != "PENDING" {
|
||||
return s.repo.SetOrderStatus(ctx, tx, orderID, status)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *OrderService) updateWalletBalance(ctx context.Context, tx *gorm.DB, partnerID int64, amount float64) error {
|
||||
wallet, err := s.wallet.GetByPartnerID(ctx, tx, partnerID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get wallet: %w", err)
|
||||
}
|
||||
wallet.Balance += amount
|
||||
_, err = s.wallet.Update(ctx, tx, wallet)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *OrderService) GetAllHistoryOrders(ctx mycontext.Context, req entity.OrderSearch) ([]*entity.HistoryOrder, int, error) {
|
||||
historyOrders, total, err := s.repo.GetAllHystoryOrders(ctx, req)
|
||||
if err != nil {
|
||||
logger.ContextLogger(ctx).Error("error when get all history orders", zap.Error(err))
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
data := historyOrders.ToHistoryOrderList()
|
||||
|
||||
return data, total, nil
|
||||
}
|
||||
|
||||
func (s *OrderService) CountSoldOfTicket(ctx mycontext.Context, req entity.OrderSearch) (*entity.TicketSold, error) {
|
||||
ticket, err := s.repo.CountSoldOfTicket(ctx, req)
|
||||
|
||||
if err != nil {
|
||||
logger.ContextLogger(ctx).Error("error when get all history orders", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data := ticket.ToTicketSold()
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (s *OrderService) GetDailySales(ctx mycontext.Context, req entity.OrderSearch) ([]entity.ProductDailySales, error) {
|
||||
dailySales, err := s.repo.GetDailySalesMetrics(ctx, req)
|
||||
|
||||
if err != nil {
|
||||
logger.ContextLogger(ctx).Error("error when get all history orders", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return dailySales, nil
|
||||
}
|
||||
|
||||
func (s *OrderService) GetPaymentDistribution(ctx mycontext.Context, req entity.OrderSearch) ([]entity.PaymentTypeDistribution, error) {
|
||||
paymentDistribution, err := s.repo.GetPaymentTypeDistribution(ctx, req)
|
||||
|
||||
if err != nil {
|
||||
logger.ContextLogger(ctx).Error("error when get all history orders", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return paymentDistribution, nil
|
||||
}
|
||||
|
||||
func (s *OrderService) SumAmount(ctx mycontext.Context, req entity.OrderSearch) (*entity.Order, error) {
|
||||
amount, err := s.repo.SumAmount(ctx, req)
|
||||
|
||||
if err != nil {
|
||||
logger.ContextLogger(ctx).Error("error when get amount cash orders", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data := amount.ToSumAmount()
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (s *OrderService) GetByID(ctx mycontext.Context, id int64, referenceID string) (*entity.Order, error) {
|
||||
if referenceID != "" {
|
||||
payment, err := s.payment.FindByReferenceID(ctx, nil, referenceID)
|
||||
if err != nil {
|
||||
logger.ContextLogger(ctx).Error("error when getting payment by IDs", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
id = payment.OrderID
|
||||
}
|
||||
|
||||
order, err := s.repo.FindByID(ctx, id)
|
||||
if err != nil {
|
||||
logger.ContextLogger(ctx).Error("error when getting products by IDs", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if ctx.IsCasheer() {
|
||||
return order, nil
|
||||
}
|
||||
|
||||
//if order.CreatedBy != ctx.RequestedBy() {
|
||||
// return nil, errors2.NewError(errors2.ErrorBadRequest.ErrorType(), "order not found")
|
||||
//}
|
||||
|
||||
return order, nil
|
||||
}
|
||||
|
||||
func (s *OrderService) GetPrintDetail(ctx mycontext.Context, id int64) (*entity.OrderPrintDetail, error) {
|
||||
order, err := s.repo.FindPrintDetailByID(ctx, id)
|
||||
if err != nil {
|
||||
logger.ContextLogger(ctx).Error("error when getting products by IDs", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return order, nil
|
||||
}
|
||||
|
||||
func (s *OrderService) ProcessLinkQuCallback(ctx context.Context, req *entity.LinkQuCallback) error {
|
||||
tx, err := s.txmanager.Begin(ctx, &sql.TxOptions{Isolation: sql.LevelSerializable})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to begin transaction: %w", err)
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
pay, err := s.linkquRepo.CheckPaymentStatus(req.PaymentReff)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to begin transaction: %w", err)
|
||||
}
|
||||
|
||||
if pay.ResponseCode != "00" {
|
||||
return nil
|
||||
}
|
||||
|
||||
err = s.processPayment(ctx, tx, &entity.CallbackRequest{
|
||||
TransactionID: req.PartnerReff,
|
||||
TransactionStatus: pay.Data.StatusPaid,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to process payment: %w", err)
|
||||
}
|
||||
|
||||
return tx.Commit().Error
|
||||
}
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"enaklo-pos-be/internal/services/discovery"
|
||||
service "enaklo-pos-be/internal/services/license"
|
||||
"enaklo-pos-be/internal/services/member"
|
||||
"enaklo-pos-be/internal/services/order"
|
||||
"enaklo-pos-be/internal/services/oss"
|
||||
"enaklo-pos-be/internal/services/partner"
|
||||
"enaklo-pos-be/internal/services/product"
|
||||
@@ -18,6 +17,7 @@ import (
|
||||
customerSvc "enaklo-pos-be/internal/services/v2/customer"
|
||||
"enaklo-pos-be/internal/services/v2/inprogress_order"
|
||||
orderSvc "enaklo-pos-be/internal/services/v2/order"
|
||||
"enaklo-pos-be/internal/services/v2/partner_settings"
|
||||
productSvc "enaklo-pos-be/internal/services/v2/product"
|
||||
|
||||
"gorm.io/gorm"
|
||||
@@ -35,7 +35,6 @@ type ServiceManagerImpl struct {
|
||||
UserSvc User
|
||||
StudioSvc Studio
|
||||
ProductSvc Product
|
||||
OrderSvc Order
|
||||
OSSSvc OSSService
|
||||
PartnerSvc Partner
|
||||
SiteSvc Site
|
||||
@@ -55,15 +54,16 @@ func NewServiceManagerImpl(cfg *config.Config, repo *repository.RepoManagerImpl)
|
||||
|
||||
custSvcV2 := customerSvc.New(repo.CustomerRepo, repo.EmailService)
|
||||
productSvcV2 := productSvc.New(repo.ProductRepo)
|
||||
inprogressOrder := inprogress_order.NewInProgressOrderService(repo.InProgressOrderRepo)
|
||||
partnerSettings := partner_settings.NewPartnerSettingsService(repo.PartnerSetting)
|
||||
|
||||
orderService := orderSvc.New(repo.OrderRepo, productSvcV2, custSvcV2, repo.TransactionRepo, repo.Crypto, &cfg.Order, repo.EmailService, partnerSettings)
|
||||
inprogressOrder := inprogress_order.NewInProgressOrderService(repo.OrderRepo, orderService, productSvcV2)
|
||||
return &ServiceManagerImpl{
|
||||
AuthSvc: auth.New(repo.Auth, repo.Crypto, repo.User, repo.EmailService, cfg.Email, repo.Trx, repo.License),
|
||||
EventSvc: event.NewEventService(repo.Event),
|
||||
UserSvc: users.NewUserService(repo.User),
|
||||
StudioSvc: studio.NewStudioService(repo.Studio),
|
||||
ProductSvc: product.NewProductService(repo.Product),
|
||||
OrderSvc: order.NewOrderService(repo.Order, repo.Product, repo.Crypto, repo.PG, repo.Payment, repo.Trx, repo.Wallet, &cfg.Order, repo.Transaction, repo.LinkQu),
|
||||
OSSSvc: oss.NewOSSService(repo.OSS),
|
||||
PartnerSvc: partner.NewPartnerService(
|
||||
repo.Partner, users.NewUserService(repo.User), repo.Trx, repo.Wallet, repo.User),
|
||||
@@ -72,7 +72,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),
|
||||
OrderV2Svc: orderSvc.New(repo.OrderRepo, productSvcV2, custSvcV2, repo.TransactionRepo, repo.Crypto, &cfg.Order, repo.EmailService, partnerSettings),
|
||||
MemberRegistrationSvc: member.NewMemberRegistrationService(repo.MemberRepository, repo.EmailService, custSvcV2),
|
||||
CustomerV2Svc: custSvcV2,
|
||||
InProgressSvc: inprogressOrder,
|
||||
|
||||
@@ -3,28 +3,98 @@ package inprogress_order
|
||||
import (
|
||||
"enaklo-pos-be/internal/common/logger"
|
||||
"enaklo-pos-be/internal/common/mycontext"
|
||||
order2 "enaklo-pos-be/internal/constants/order"
|
||||
"enaklo-pos-be/internal/entity"
|
||||
"enaklo-pos-be/internal/repository"
|
||||
"enaklo-pos-be/internal/services/v2/order"
|
||||
"github.com/pkg/errors"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type InProgressOrderService interface {
|
||||
Save(ctx mycontext.Context, order *entity.InProgressOrder) (*entity.InProgressOrder, error)
|
||||
GetOrdersByPartnerID(ctx mycontext.Context, partnerID int64, limit, offset int) ([]*entity.InProgressOrder, error)
|
||||
Save(ctx mycontext.Context, order *entity.OrderRequest) (*entity.Order, error)
|
||||
GetOrdersByPartnerID(ctx mycontext.Context, partnerID int64, limit, offset int) ([]*entity.Order, error)
|
||||
}
|
||||
|
||||
type OrderRepository interface {
|
||||
CreateOrUpdate(ctx mycontext.Context, order *entity.Order) (*entity.Order, error)
|
||||
GetListByPartnerID(ctx mycontext.Context, partnerID int64, limit, offset int, status string) ([]*entity.Order, error)
|
||||
}
|
||||
|
||||
type OrderCalculator interface {
|
||||
CalculateOrderTotals(
|
||||
ctx mycontext.Context,
|
||||
items []entity.OrderItemRequest,
|
||||
productDetails *entity.ProductDetails,
|
||||
source string,
|
||||
) (*entity.OrderCalculation, error)
|
||||
ValidateOrderItems(ctx mycontext.Context, items []entity.OrderItemRequest) ([]int64, []entity.OrderItemRequest, error)
|
||||
}
|
||||
|
||||
type inProgressOrderSvc struct {
|
||||
repo repository.InProgressOrderRepository
|
||||
repo OrderRepository
|
||||
orderCalculator OrderCalculator
|
||||
product order.ProductService
|
||||
}
|
||||
|
||||
func NewInProgressOrderService(repo repository.InProgressOrderRepository) InProgressOrderService {
|
||||
func NewInProgressOrderService(repo OrderRepository, calculator OrderCalculator, product order.ProductService) InProgressOrderService {
|
||||
return &inProgressOrderSvc{
|
||||
repo: repo,
|
||||
repo: repo,
|
||||
orderCalculator: calculator,
|
||||
product: product,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *inProgressOrderSvc) Save(ctx mycontext.Context, order *entity.InProgressOrder) (*entity.InProgressOrder, error) {
|
||||
func (s *inProgressOrderSvc) Save(ctx mycontext.Context, req *entity.OrderRequest) (*entity.Order, error) {
|
||||
productIDs, filteredItems, err := s.orderCalculator.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.orderCalculator.CalculateOrderTotals(ctx, req.OrderItems, productDetails, req.Source)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
orderItems := make([]entity.OrderItem, len(req.OrderItems))
|
||||
for i, item := range req.OrderItems {
|
||||
product, exists := productDetails.Products[item.ProductID]
|
||||
productName := ""
|
||||
if exists {
|
||||
productName = product.Name
|
||||
}
|
||||
|
||||
orderItems[i] = entity.OrderItem{
|
||||
ItemID: item.ProductID,
|
||||
ItemName: productName,
|
||||
Quantity: item.Quantity,
|
||||
Price: product.Price,
|
||||
ItemType: product.Type,
|
||||
}
|
||||
}
|
||||
|
||||
order := &entity.Order{
|
||||
ID: req.ID,
|
||||
PartnerID: req.PartnerID,
|
||||
CustomerID: req.CustomerID,
|
||||
CustomerName: req.CustomerName,
|
||||
CreatedBy: req.CreatedBy,
|
||||
OrderItems: orderItems,
|
||||
TableNumber: req.TableNumber,
|
||||
OrderType: req.OrderType,
|
||||
Total: orderCalculation.Total,
|
||||
Tax: orderCalculation.Tax,
|
||||
Amount: orderCalculation.Subtotal,
|
||||
Status: order2.Pending.String(),
|
||||
Source: req.Source,
|
||||
}
|
||||
|
||||
createdOrder, err := s.repo.CreateOrUpdate(ctx, order)
|
||||
if err != nil {
|
||||
logger.ContextLogger(ctx).Error("failed to create in-progress order",
|
||||
@@ -36,8 +106,8 @@ func (s *inProgressOrderSvc) Save(ctx mycontext.Context, order *entity.InProgres
|
||||
return createdOrder, nil
|
||||
}
|
||||
|
||||
func (s *inProgressOrderSvc) GetOrdersByPartnerID(ctx mycontext.Context, partnerID int64, limit, offset int) ([]*entity.InProgressOrder, error) {
|
||||
orders, err := s.repo.GetListByPartnerID(ctx, partnerID, limit, offset)
|
||||
func (s *inProgressOrderSvc) GetOrdersByPartnerID(ctx mycontext.Context, partnerID int64, limit, offset int) ([]*entity.Order, error) {
|
||||
orders, err := s.repo.GetListByPartnerID(ctx, partnerID, limit, offset, order2.Pending.String())
|
||||
if err != nil {
|
||||
logger.ContextLogger(ctx).Error("failed to get in-progress orders by partner ID",
|
||||
zap.Error(err),
|
||||
|
||||
@@ -7,11 +7,12 @@ import (
|
||||
"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)
|
||||
productIDs, filteredItems, err := s.ValidateOrderItems(ctx, req.OrderItems)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -23,7 +24,7 @@ func (s *orderSvc) CreateOrderInquiry(ctx mycontext.Context,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
orderCalculation, err := s.calculateOrderTotals(ctx, req.OrderItems, productDetails, req.Source)
|
||||
orderCalculation, err := s.CalculateOrderTotals(ctx, req.OrderItems, productDetails, req.Source)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -43,7 +44,7 @@ func (s *orderSvc) CreateOrderInquiry(ctx mycontext.Context,
|
||||
req.PartnerID,
|
||||
customerID,
|
||||
orderCalculation.Subtotal,
|
||||
orderCalculation.Fee,
|
||||
orderCalculation.Tax,
|
||||
orderCalculation.Total,
|
||||
req.PaymentMethod,
|
||||
req.Source,
|
||||
@@ -79,7 +80,7 @@ func (s *orderSvc) CreateOrderInquiry(ctx mycontext.Context,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *orderSvc) validateOrderItems(ctx mycontext.Context, items []entity.OrderItemRequest) ([]int64, []entity.OrderItemRequest, error) {
|
||||
func (s *orderSvc) ValidateOrderItems(ctx mycontext.Context, items []entity.OrderItemRequest) ([]int64, []entity.OrderItemRequest, error) {
|
||||
var productIDs []int64
|
||||
var filteredItems []entity.OrderItemRequest
|
||||
|
||||
@@ -98,7 +99,7 @@ func (s *orderSvc) validateOrderItems(ctx mycontext.Context, items []entity.Orde
|
||||
return productIDs, filteredItems, nil
|
||||
}
|
||||
|
||||
func (s *orderSvc) calculateOrderTotals(
|
||||
func (s *orderSvc) CalculateOrderTotals(
|
||||
ctx mycontext.Context,
|
||||
items []entity.OrderItemRequest,
|
||||
productDetails *entity.ProductDetails,
|
||||
@@ -114,12 +115,23 @@ func (s *orderSvc) calculateOrderTotals(
|
||||
subtotal += product.Price * float64(item.Quantity)
|
||||
}
|
||||
|
||||
fee := s.cfg.GetOrderFee(source)
|
||||
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,
|
||||
Fee: fee,
|
||||
Total: subtotal + fee,
|
||||
Tax: tax,
|
||||
Total: subtotal + tax,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -145,3 +157,79 @@ func (s *orderSvc) validateInquiry(ctx mycontext.Context, token string) (*entity
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
)
|
||||
|
||||
func (s *orderSvc) ExecuteOrderInquiry(ctx mycontext.Context,
|
||||
token string, paymentMethod, paymentProvider, inprogressOrderID string) (*entity.OrderResponse, error) {
|
||||
token string, paymentMethod, paymentProvider string, inprogressOrderID int64) (*entity.OrderResponse, error) {
|
||||
inquiry, err := s.validateInquiry(ctx, token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -12,6 +12,24 @@ type Repository interface {
|
||||
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
|
||||
GetOrderHistoryByPartnerID(ctx mycontext.Context, partnerID int64, req entity.SearchRequest) ([]*entity.Order, int64, error)
|
||||
GetOrderPaymentMethodBreakdown(
|
||||
ctx mycontext.Context,
|
||||
partnerID int64,
|
||||
req entity.SearchRequest,
|
||||
) ([]entity.PaymentMethodBreakdown, error)
|
||||
GetRevenueOverview(
|
||||
ctx mycontext.Context,
|
||||
req entity.RevenueOverviewRequest,
|
||||
) ([]entity.RevenueOverviewItem, error)
|
||||
GetSalesByCategory(
|
||||
ctx mycontext.Context,
|
||||
req entity.SalesByCategoryRequest,
|
||||
) ([]entity.SalesByCategoryItem, error)
|
||||
GetPopularProducts(
|
||||
ctx mycontext.Context,
|
||||
req entity.PopularProductsRequest,
|
||||
) ([]entity.PopularProductItem, error)
|
||||
}
|
||||
|
||||
type ProductService interface {
|
||||
@@ -42,21 +60,65 @@ type Service interface {
|
||||
CreateOrderInquiry(ctx mycontext.Context,
|
||||
req *entity.OrderRequest) (*entity.OrderInquiryResponse, error)
|
||||
ExecuteOrderInquiry(ctx mycontext.Context,
|
||||
token string, paymentMethod, paymentProvider, inProgressOrderID string) (*entity.OrderResponse, error)
|
||||
token string, paymentMethod, paymentProvider string, inProgressOrderID int64) (*entity.OrderResponse, error)
|
||||
GetOrderHistory(ctx mycontext.Context, partnerID int64, request entity.SearchRequest) ([]*entity.Order, int64, error)
|
||||
CalculateOrderTotals(
|
||||
ctx mycontext.Context,
|
||||
items []entity.OrderItemRequest,
|
||||
productDetails *entity.ProductDetails,
|
||||
source string,
|
||||
) (*entity.OrderCalculation, error)
|
||||
ValidateOrderItems(ctx mycontext.Context, items []entity.OrderItemRequest) ([]int64, []entity.OrderItemRequest, error)
|
||||
GetOrderPaymentAnalysis(
|
||||
ctx mycontext.Context,
|
||||
partnerID int64,
|
||||
req entity.SearchRequest,
|
||||
) (*entity.OrderPaymentAnalysis, error)
|
||||
GetRevenueOverview(
|
||||
ctx mycontext.Context,
|
||||
partnerID int64,
|
||||
year int,
|
||||
granularity string,
|
||||
status string,
|
||||
) ([]entity.RevenueOverviewItem, error)
|
||||
GetSalesByCategory(
|
||||
ctx mycontext.Context,
|
||||
partnerID int64,
|
||||
period string,
|
||||
status string,
|
||||
) ([]entity.SalesByCategoryItem, error)
|
||||
GetPopularProducts(
|
||||
ctx mycontext.Context,
|
||||
partnerID int64,
|
||||
period string,
|
||||
status string,
|
||||
limit int,
|
||||
sortBy string,
|
||||
) ([]entity.PopularProductItem, error)
|
||||
}
|
||||
|
||||
type Config interface {
|
||||
GetOrderFee(source string) float64
|
||||
}
|
||||
|
||||
type PartnerSettings interface {
|
||||
GetSettings(ctx mycontext.Context, partnerID int64) (*entity.PartnerSettings, error)
|
||||
}
|
||||
|
||||
type InProgressOrderRepository interface {
|
||||
GetListByPartnerID(ctx mycontext.Context, partnerID int64, limit, offset int) ([]*entity.InProgressOrder, error)
|
||||
}
|
||||
|
||||
type orderSvc struct {
|
||||
repo Repository
|
||||
product ProductService
|
||||
customer CustomerService
|
||||
transaction TransactionService
|
||||
crypt CryptService
|
||||
cfg Config
|
||||
notification NotificationService
|
||||
repo Repository
|
||||
product ProductService
|
||||
customer CustomerService
|
||||
transaction TransactionService
|
||||
crypt CryptService
|
||||
cfg Config
|
||||
notification NotificationService
|
||||
partnerSetting PartnerSettings
|
||||
inprogressOrder InProgressOrderRepository
|
||||
}
|
||||
|
||||
func New(
|
||||
@@ -67,14 +129,16 @@ func New(
|
||||
crypt CryptService,
|
||||
cfg Config,
|
||||
notification NotificationService,
|
||||
partnerSetting PartnerSettings,
|
||||
) Service {
|
||||
return &orderSvc{
|
||||
repo: repo,
|
||||
product: product,
|
||||
customer: customer,
|
||||
transaction: transaction,
|
||||
crypt: crypt,
|
||||
cfg: cfg,
|
||||
notification: notification,
|
||||
repo: repo,
|
||||
product: product,
|
||||
customer: customer,
|
||||
transaction: transaction,
|
||||
crypt: crypt,
|
||||
cfg: cfg,
|
||||
notification: notification,
|
||||
partnerSetting: partnerSetting,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package order
|
||||
|
||||
import (
|
||||
"enaklo-pos-be/internal/common/mycontext"
|
||||
"enaklo-pos-be/internal/entity"
|
||||
)
|
||||
|
||||
func (s *orderSvc) GetOrderHistory(ctx mycontext.Context, partnerID int64, request entity.SearchRequest) ([]*entity.Order, int64, error) {
|
||||
return s.repo.GetOrderHistoryByPartnerID(ctx, partnerID, request)
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
package partner_settings
|
||||
|
||||
import (
|
||||
"enaklo-pos-be/internal/common/mycontext"
|
||||
"enaklo-pos-be/internal/entity"
|
||||
"enaklo-pos-be/internal/repository"
|
||||
"encoding/json"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type PartnerSettingsService interface {
|
||||
GetSettings(ctx mycontext.Context, partnerID int64) (*entity.PartnerSettings, error)
|
||||
UpdateSettings(ctx mycontext.Context, settings *entity.PartnerSettings) error
|
||||
GetPaymentMethods(ctx mycontext.Context, partnerID int64) ([]entity.PartnerPaymentMethod, error)
|
||||
AddPaymentMethod(ctx mycontext.Context, method *entity.PartnerPaymentMethod) error
|
||||
UpdatePaymentMethod(ctx mycontext.Context, method *entity.PartnerPaymentMethod) error
|
||||
DeletePaymentMethod(ctx mycontext.Context, id int64, partnerID int64) error
|
||||
ReorderPaymentMethods(ctx mycontext.Context, partnerID int64, methodIDs []int64) error
|
||||
GetBusinessHours(ctx mycontext.Context, partnerID int64) (*entity.BusinessHoursSetting, error)
|
||||
UpdateBusinessHours(ctx mycontext.Context, partnerID int64, hours *entity.BusinessHoursSetting) error
|
||||
}
|
||||
|
||||
type partnerSettingsService struct {
|
||||
settingsRepo repository.PartnerSettingsRepository
|
||||
}
|
||||
|
||||
func NewPartnerSettingsService(settingsRepo repository.PartnerSettingsRepository) PartnerSettingsService {
|
||||
return &partnerSettingsService{
|
||||
settingsRepo: settingsRepo,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *partnerSettingsService) GetSettings(ctx mycontext.Context, partnerID int64) (*entity.PartnerSettings, error) {
|
||||
return s.settingsRepo.GetByPartnerID(ctx, partnerID)
|
||||
}
|
||||
|
||||
func (s *partnerSettingsService) UpdateSettings(ctx mycontext.Context, settings *entity.PartnerSettings) error {
|
||||
if settings == nil {
|
||||
return errors.New("settings cannot be nil")
|
||||
}
|
||||
|
||||
// Validate tax percentage
|
||||
if settings.TaxEnabled && (settings.TaxPercentage < 0 || settings.TaxPercentage > 100) {
|
||||
return errors.New("tax percentage must be between 0 and 100")
|
||||
}
|
||||
|
||||
return s.settingsRepo.Upsert(ctx, settings)
|
||||
}
|
||||
|
||||
func (s *partnerSettingsService) GetPaymentMethods(ctx mycontext.Context, partnerID int64) ([]entity.PartnerPaymentMethod, error) {
|
||||
return s.settingsRepo.GetPaymentMethods(ctx, partnerID)
|
||||
}
|
||||
|
||||
func (s *partnerSettingsService) AddPaymentMethod(ctx mycontext.Context, method *entity.PartnerPaymentMethod) error {
|
||||
if method == nil {
|
||||
return errors.New("payment method cannot be nil")
|
||||
}
|
||||
|
||||
method.ID = 0
|
||||
|
||||
return s.settingsRepo.UpsertPaymentMethod(ctx, method)
|
||||
}
|
||||
|
||||
func (s *partnerSettingsService) UpdatePaymentMethod(ctx mycontext.Context, method *entity.PartnerPaymentMethod) error {
|
||||
if method == nil {
|
||||
return errors.New("payment method cannot be nil")
|
||||
}
|
||||
|
||||
if method.ID <= 0 {
|
||||
return errors.New("invalid payment method ID")
|
||||
}
|
||||
|
||||
return s.settingsRepo.UpsertPaymentMethod(ctx, method)
|
||||
}
|
||||
|
||||
func (s *partnerSettingsService) DeletePaymentMethod(ctx mycontext.Context, id int64, partnerID int64) error {
|
||||
if id <= 0 {
|
||||
return errors.New("invalid payment method ID")
|
||||
}
|
||||
|
||||
return s.settingsRepo.DeletePaymentMethod(ctx, id, partnerID)
|
||||
}
|
||||
|
||||
func (s *partnerSettingsService) ReorderPaymentMethods(ctx mycontext.Context, partnerID int64, methodIDs []int64) error {
|
||||
if len(methodIDs) == 0 {
|
||||
return errors.New("method IDs cannot be empty")
|
||||
}
|
||||
|
||||
return s.settingsRepo.UpdatePaymentMethodOrder(ctx, partnerID, methodIDs)
|
||||
}
|
||||
|
||||
// GetBusinessHours retrieves parsed business hours for a partner
|
||||
func (s *partnerSettingsService) GetBusinessHours(ctx mycontext.Context, partnerID int64) (*entity.BusinessHoursSetting, error) {
|
||||
settings, err := s.settingsRepo.GetByPartnerID(ctx, partnerID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create default hours if not set
|
||||
if settings.BusinessHours == "" {
|
||||
defaultHours := createDefaultBusinessHours()
|
||||
return defaultHours, nil
|
||||
}
|
||||
|
||||
var hours entity.BusinessHoursSetting
|
||||
if err := json.Unmarshal([]byte(settings.BusinessHours), &hours); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to parse business hours")
|
||||
}
|
||||
|
||||
return &hours, nil
|
||||
}
|
||||
|
||||
func (s *partnerSettingsService) UpdateBusinessHours(ctx mycontext.Context, partnerID int64, hours *entity.BusinessHoursSetting) error {
|
||||
if hours == nil {
|
||||
return errors.New("business hours cannot be nil")
|
||||
}
|
||||
|
||||
settings, err := s.settingsRepo.GetByPartnerID(ctx, partnerID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Serialize hours to JSON
|
||||
hoursJSON, err := json.Marshal(hours)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to serialize business hours")
|
||||
}
|
||||
|
||||
settings.BusinessHours = string(hoursJSON)
|
||||
|
||||
return s.settingsRepo.Upsert(ctx, settings)
|
||||
}
|
||||
|
||||
func createDefaultBusinessHours() *entity.BusinessHoursSetting {
|
||||
defaultDay := entity.DayHours{
|
||||
Open: "08:00",
|
||||
Close: "22:00",
|
||||
}
|
||||
|
||||
return &entity.BusinessHoursSetting{
|
||||
Monday: defaultDay,
|
||||
Tuesday: defaultDay,
|
||||
Wednesday: defaultDay,
|
||||
Thursday: defaultDay,
|
||||
Friday: defaultDay,
|
||||
Saturday: defaultDay,
|
||||
Sunday: defaultDay,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user