2025-07-18 20:10:29 +07:00
|
|
|
package service
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
"fmt"
|
2025-08-08 00:22:28 +07:00
|
|
|
"time"
|
2025-07-18 20:10:29 +07:00
|
|
|
|
|
|
|
|
"apskel-pos-be/internal/models"
|
|
|
|
|
"apskel-pos-be/internal/processor"
|
2025-08-08 00:22:28 +07:00
|
|
|
"apskel-pos-be/internal/repository"
|
2025-07-18 20:10:29 +07:00
|
|
|
|
|
|
|
|
"github.com/google/uuid"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type OrderService interface {
|
|
|
|
|
CreateOrder(ctx context.Context, req *models.CreateOrderRequest, organizationID uuid.UUID) (*models.OrderResponse, error)
|
|
|
|
|
AddToOrder(ctx context.Context, orderID uuid.UUID, req *models.AddToOrderRequest) (*models.AddToOrderResponse, error)
|
|
|
|
|
UpdateOrder(ctx context.Context, id uuid.UUID, req *models.UpdateOrderRequest) (*models.OrderResponse, error)
|
|
|
|
|
GetOrderByID(ctx context.Context, id uuid.UUID) (*models.OrderResponse, error)
|
|
|
|
|
ListOrders(ctx context.Context, req *models.ListOrdersRequest) (*models.ListOrdersResponse, error)
|
|
|
|
|
VoidOrder(ctx context.Context, req *models.VoidOrderRequest, voidedBy uuid.UUID) error
|
|
|
|
|
RefundOrder(ctx context.Context, id uuid.UUID, req *models.RefundOrderRequest, refundedBy uuid.UUID) error
|
|
|
|
|
CreatePayment(ctx context.Context, req *models.CreatePaymentRequest) (*models.PaymentResponse, error)
|
|
|
|
|
RefundPayment(ctx context.Context, paymentID uuid.UUID, refundAmount float64, reason string, refundedBy uuid.UUID) error
|
|
|
|
|
SetOrderCustomer(ctx context.Context, orderID uuid.UUID, req *models.SetOrderCustomerRequest, organizationID uuid.UUID) (*models.SetOrderCustomerResponse, error)
|
2025-08-07 22:45:02 +07:00
|
|
|
SplitBill(ctx context.Context, req *models.SplitBillRequest) (*models.SplitBillResponse, error)
|
2025-07-18 20:10:29 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type OrderServiceImpl struct {
|
|
|
|
|
orderProcessor processor.OrderProcessor
|
2025-08-08 00:22:28 +07:00
|
|
|
tableRepo repository.TableRepositoryInterface
|
2025-07-18 20:10:29 +07:00
|
|
|
}
|
|
|
|
|
|
2025-08-08 00:22:28 +07:00
|
|
|
func NewOrderServiceImpl(orderProcessor processor.OrderProcessor, tableRepo repository.TableRepositoryInterface) *OrderServiceImpl {
|
2025-07-18 20:10:29 +07:00
|
|
|
return &OrderServiceImpl{
|
|
|
|
|
orderProcessor: orderProcessor,
|
2025-08-08 00:22:28 +07:00
|
|
|
tableRepo: tableRepo,
|
2025-07-18 20:10:29 +07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *OrderServiceImpl) CreateOrder(ctx context.Context, req *models.CreateOrderRequest, organizationID uuid.UUID) (*models.OrderResponse, error) {
|
|
|
|
|
if err := s.validateCreateOrderRequest(req, organizationID); err != nil {
|
|
|
|
|
return nil, fmt.Errorf("validation error: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-08 00:22:28 +07:00
|
|
|
if req.TableID != nil {
|
|
|
|
|
if err := s.validateTable(ctx, req); err != nil {
|
|
|
|
|
return nil, fmt.Errorf("table validation failed: %w", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-18 20:10:29 +07:00
|
|
|
response, err := s.orderProcessor.CreateOrder(ctx, req, organizationID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("failed to create order: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-08 00:22:28 +07:00
|
|
|
if req.TableID != nil {
|
|
|
|
|
if err := s.occupyTableWithOrder(ctx, *req.TableID, response.ID); err != nil {
|
|
|
|
|
fmt.Printf("Warning: failed to occupy table %s with order %s: %v\n", *req.TableID, response.ID, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-18 20:10:29 +07:00
|
|
|
return response, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *OrderServiceImpl) AddToOrder(ctx context.Context, orderID uuid.UUID, req *models.AddToOrderRequest) (*models.AddToOrderResponse, error) {
|
|
|
|
|
// Validate inputs
|
|
|
|
|
if orderID == uuid.Nil {
|
|
|
|
|
return nil, fmt.Errorf("invalid order ID")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Validate request
|
|
|
|
|
if err := s.validateAddToOrderRequest(req); err != nil {
|
|
|
|
|
return nil, fmt.Errorf("validation error: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Process adding items to order
|
|
|
|
|
response, err := s.orderProcessor.AddToOrder(ctx, orderID, req)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("failed to add items to order: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return response, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *OrderServiceImpl) UpdateOrder(ctx context.Context, id uuid.UUID, req *models.UpdateOrderRequest) (*models.OrderResponse, error) {
|
|
|
|
|
// Validate request
|
|
|
|
|
if err := s.validateUpdateOrderRequest(req); err != nil {
|
|
|
|
|
return nil, fmt.Errorf("validation error: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Process order update
|
|
|
|
|
response, err := s.orderProcessor.UpdateOrder(ctx, id, req)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("failed to update order: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return response, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *OrderServiceImpl) GetOrderByID(ctx context.Context, id uuid.UUID) (*models.OrderResponse, error) {
|
|
|
|
|
if id == uuid.Nil {
|
|
|
|
|
return nil, fmt.Errorf("invalid order ID")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
response, err := s.orderProcessor.GetOrderByID(ctx, id)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("failed to get order: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return response, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *OrderServiceImpl) ListOrders(ctx context.Context, req *models.ListOrdersRequest) (*models.ListOrdersResponse, error) {
|
|
|
|
|
if err := s.validateListOrdersRequest(req); err != nil {
|
|
|
|
|
return nil, fmt.Errorf("validation error: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
response, err := s.orderProcessor.ListOrders(ctx, req)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("failed to list orders: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return response, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *OrderServiceImpl) VoidOrder(ctx context.Context, req *models.VoidOrderRequest, voidedBy uuid.UUID) error {
|
|
|
|
|
if req.OrderID == uuid.Nil {
|
|
|
|
|
return fmt.Errorf("invalid order ID")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if voidedBy == uuid.Nil {
|
|
|
|
|
return fmt.Errorf("invalid user ID")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err := s.orderProcessor.VoidOrder(ctx, req, voidedBy); err != nil {
|
|
|
|
|
return fmt.Errorf("failed to void order: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-08 00:22:28 +07:00
|
|
|
// Release table if order is voided
|
|
|
|
|
if err := s.handleTableReleaseOnVoid(ctx, req.OrderID); err != nil {
|
|
|
|
|
// Log the error but don't fail the void operation
|
|
|
|
|
fmt.Printf("Warning: failed to handle table release for voided order %s: %v\n", req.OrderID, err)
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-18 20:10:29 +07:00
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *OrderServiceImpl) RefundOrder(ctx context.Context, id uuid.UUID, req *models.RefundOrderRequest, refundedBy uuid.UUID) error {
|
|
|
|
|
// Validate inputs
|
|
|
|
|
if id == uuid.Nil {
|
|
|
|
|
return fmt.Errorf("invalid order ID")
|
|
|
|
|
}
|
|
|
|
|
if refundedBy == uuid.Nil {
|
|
|
|
|
return fmt.Errorf("invalid user ID")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Validate refund request
|
|
|
|
|
if err := s.validateRefundOrderRequest(req); err != nil {
|
|
|
|
|
return fmt.Errorf("validation error: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Process order refund
|
|
|
|
|
if err := s.orderProcessor.RefundOrder(ctx, id, req, refundedBy); err != nil {
|
|
|
|
|
return fmt.Errorf("failed to refund order: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *OrderServiceImpl) CreatePayment(ctx context.Context, req *models.CreatePaymentRequest) (*models.PaymentResponse, error) {
|
|
|
|
|
if err := s.validateCreatePaymentRequest(req); err != nil {
|
|
|
|
|
return nil, fmt.Errorf("validation error: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
response, err := s.orderProcessor.CreatePayment(ctx, req)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("failed to create payment: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-08 00:22:28 +07:00
|
|
|
if err := s.handleTableReleaseOnPayment(ctx, req.OrderID); err != nil {
|
|
|
|
|
fmt.Printf("Warning: failed to handle table release for order %s: %v\n", req.OrderID, err)
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-18 20:10:29 +07:00
|
|
|
return response, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *OrderServiceImpl) RefundPayment(ctx context.Context, paymentID uuid.UUID, refundAmount float64, reason string, refundedBy uuid.UUID) error {
|
|
|
|
|
// Validate inputs
|
|
|
|
|
if paymentID == uuid.Nil {
|
|
|
|
|
return fmt.Errorf("invalid payment ID")
|
|
|
|
|
}
|
|
|
|
|
if refundAmount <= 0 {
|
|
|
|
|
return fmt.Errorf("refund amount must be greater than zero")
|
|
|
|
|
}
|
|
|
|
|
if refundedBy == uuid.Nil {
|
|
|
|
|
return fmt.Errorf("invalid user ID")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Process payment refund
|
|
|
|
|
if err := s.orderProcessor.RefundPayment(ctx, paymentID, refundAmount, reason, refundedBy); err != nil {
|
|
|
|
|
return fmt.Errorf("failed to refund payment: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *OrderServiceImpl) SetOrderCustomer(ctx context.Context, orderID uuid.UUID, req *models.SetOrderCustomerRequest, organizationID uuid.UUID) (*models.SetOrderCustomerResponse, error) {
|
|
|
|
|
// Validate inputs
|
|
|
|
|
if orderID == uuid.Nil {
|
|
|
|
|
return nil, fmt.Errorf("invalid order ID")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if organizationID == uuid.Nil {
|
|
|
|
|
return nil, fmt.Errorf("invalid organization ID")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Validate request
|
|
|
|
|
if err := s.validateSetOrderCustomerRequest(req); err != nil {
|
|
|
|
|
return nil, fmt.Errorf("validation error: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Process setting customer for order
|
|
|
|
|
response, err := s.orderProcessor.SetOrderCustomer(ctx, orderID, req, organizationID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("failed to set customer for order: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return response, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *OrderServiceImpl) validateAddToOrderRequest(req *models.AddToOrderRequest) error {
|
|
|
|
|
if req == nil {
|
|
|
|
|
return fmt.Errorf("request cannot be nil")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(req.OrderItems) == 0 {
|
|
|
|
|
return fmt.Errorf("must add at least one item")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for i, item := range req.OrderItems {
|
|
|
|
|
if item.ProductID == uuid.Nil {
|
|
|
|
|
return fmt.Errorf("product ID is required for item %d", i+1)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if item.Quantity <= 0 {
|
|
|
|
|
return fmt.Errorf("quantity must be greater than zero for item %d", i+1)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if item.UnitPrice != nil && *item.UnitPrice < 0 {
|
|
|
|
|
return fmt.Errorf("unit price cannot be negative for item %d", i+1)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *OrderServiceImpl) validateCreateOrderRequest(req *models.CreateOrderRequest, organizationID uuid.UUID) error {
|
|
|
|
|
if req == nil {
|
|
|
|
|
return fmt.Errorf("request cannot be nil")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if organizationID == uuid.Nil {
|
|
|
|
|
return fmt.Errorf("organization ID is required")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if req.OutletID == uuid.Nil {
|
|
|
|
|
return fmt.Errorf("outlet ID is required")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if req.UserID == uuid.Nil {
|
|
|
|
|
return fmt.Errorf("user ID is required")
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-08 00:22:28 +07:00
|
|
|
// Validate table ID if provided
|
|
|
|
|
if req.TableID != nil && *req.TableID == uuid.Nil {
|
|
|
|
|
return fmt.Errorf("table ID cannot be nil if provided")
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-18 20:10:29 +07:00
|
|
|
if len(req.OrderItems) == 0 {
|
|
|
|
|
return fmt.Errorf("order must have at least one item")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for i, item := range req.OrderItems {
|
|
|
|
|
if item.ProductID == uuid.Nil {
|
|
|
|
|
return fmt.Errorf("product ID is required for item %d", i+1)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if item.Quantity <= 0 {
|
|
|
|
|
return fmt.Errorf("quantity must be greater than zero for item %d", i+1)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if item.UnitPrice != nil && *item.UnitPrice < 0 {
|
|
|
|
|
return fmt.Errorf("unit price cannot be negative for item %d", i+1)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *OrderServiceImpl) validateUpdateOrderRequest(req *models.UpdateOrderRequest) error {
|
|
|
|
|
if req == nil {
|
|
|
|
|
return fmt.Errorf("request cannot be nil")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if req.DiscountAmount != nil && *req.DiscountAmount < 0 {
|
|
|
|
|
return fmt.Errorf("discount amount cannot be negative")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *OrderServiceImpl) validateListOrdersRequest(req *models.ListOrdersRequest) error {
|
|
|
|
|
if req == nil {
|
|
|
|
|
return fmt.Errorf("request cannot be nil")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if req.Page < 1 {
|
|
|
|
|
return fmt.Errorf("page must be greater than zero")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if req.Limit < 1 || req.Limit > 100 {
|
|
|
|
|
return fmt.Errorf("limit must be between 1 and 100")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *OrderServiceImpl) validateRefundOrderRequest(req *models.RefundOrderRequest) error {
|
|
|
|
|
if req == nil {
|
|
|
|
|
return fmt.Errorf("request cannot be nil")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Must have either refund amount or order items
|
|
|
|
|
if req.RefundAmount == nil && len(req.OrderItems) == 0 {
|
|
|
|
|
return fmt.Errorf("must specify either refund amount or order items to refund")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Cannot have both refund amount and order items
|
|
|
|
|
if req.RefundAmount != nil && len(req.OrderItems) > 0 {
|
|
|
|
|
return fmt.Errorf("cannot specify both refund amount and order items")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if req.RefundAmount != nil && *req.RefundAmount <= 0 {
|
|
|
|
|
return fmt.Errorf("refund amount must be greater than zero")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Validate order items if provided
|
|
|
|
|
for i, item := range req.OrderItems {
|
|
|
|
|
if item.OrderItemID == uuid.Nil {
|
|
|
|
|
return fmt.Errorf("order item ID is required for item %d", i+1)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if item.RefundQuantity < 0 {
|
|
|
|
|
return fmt.Errorf("refund quantity cannot be negative for item %d", i+1)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if item.RefundAmount != nil && *item.RefundAmount < 0 {
|
|
|
|
|
return fmt.Errorf("refund amount cannot be negative for item %d", i+1)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *OrderServiceImpl) validateCreatePaymentRequest(req *models.CreatePaymentRequest) error {
|
|
|
|
|
if req == nil {
|
|
|
|
|
return fmt.Errorf("request cannot be nil")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if req.OrderID == uuid.Nil {
|
|
|
|
|
return fmt.Errorf("order ID is required")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if req.PaymentMethodID == uuid.Nil {
|
|
|
|
|
return fmt.Errorf("payment method ID is required")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if req.Amount <= 0 {
|
|
|
|
|
return fmt.Errorf("payment amount must be greater than zero")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(req.PaymentOrderItems) > 0 {
|
|
|
|
|
totalItemAmount := float64(0)
|
|
|
|
|
for i, item := range req.PaymentOrderItems {
|
|
|
|
|
if item.OrderItemID == uuid.Nil {
|
|
|
|
|
return fmt.Errorf("order item ID is required for payment item %d", i+1)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if item.Amount <= 0 {
|
|
|
|
|
return fmt.Errorf("payment item amount must be greater than zero for item %d", i+1)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
totalItemAmount += item.Amount
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if totalItemAmount != req.Amount {
|
|
|
|
|
return fmt.Errorf("sum of payment item amounts must equal total payment amount")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *OrderServiceImpl) validateSetOrderCustomerRequest(req *models.SetOrderCustomerRequest) error {
|
|
|
|
|
if req == nil {
|
|
|
|
|
return fmt.Errorf("request cannot be nil")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if req.CustomerID == uuid.Nil {
|
|
|
|
|
return fmt.Errorf("customer ID is required")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2025-08-07 22:45:02 +07:00
|
|
|
|
|
|
|
|
func (s *OrderServiceImpl) SplitBill(ctx context.Context, req *models.SplitBillRequest) (*models.SplitBillResponse, error) {
|
|
|
|
|
if err := s.validateSplitBillRequest(req); err != nil {
|
|
|
|
|
return nil, fmt.Errorf("validation error: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
response, err := s.orderProcessor.SplitBill(ctx, req)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("failed to split bill: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-08 22:46:55 +07:00
|
|
|
if err := s.handleTableReleaseOnPayment(ctx, req.OrderID); err != nil {
|
|
|
|
|
fmt.Printf("Warning: failed to handle table release for order %s after split bill: %v\n", req.OrderID, err)
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-07 22:45:02 +07:00
|
|
|
return response, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *OrderServiceImpl) validateSplitBillRequest(req *models.SplitBillRequest) error {
|
|
|
|
|
if req == nil {
|
|
|
|
|
return fmt.Errorf("request cannot be nil")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if req.OrderID == uuid.Nil {
|
|
|
|
|
return fmt.Errorf("order ID is required")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if req.PaymentMethodID == uuid.Nil {
|
|
|
|
|
return fmt.Errorf("payment ID is required")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if req.Type != "ITEM" && req.Type != "AMOUNT" {
|
|
|
|
|
return fmt.Errorf("split type must be either ITEM or AMOUNT")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if req.Type == "ITEM" {
|
|
|
|
|
if len(req.Items) == 0 {
|
|
|
|
|
return fmt.Errorf("items are required when splitting by ITEM")
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-08 22:33:08 +07:00
|
|
|
totalItemAmount := 0
|
2025-08-07 22:45:02 +07:00
|
|
|
for i, item := range req.Items {
|
|
|
|
|
if item.OrderItemID == uuid.Nil {
|
|
|
|
|
return fmt.Errorf("order item ID is required for item %d", i+1)
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-08 22:33:08 +07:00
|
|
|
if item.Quantity <= 0 {
|
|
|
|
|
return fmt.Errorf("quantity must be greater than zero for item %d", i+1)
|
2025-08-07 22:45:02 +07:00
|
|
|
}
|
|
|
|
|
|
2025-08-08 22:33:08 +07:00
|
|
|
totalItemAmount += item.Quantity
|
2025-08-07 22:45:02 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if totalItemAmount <= 0 {
|
|
|
|
|
return fmt.Errorf("total item amount must be greater than zero")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if req.Type == "AMOUNT" {
|
|
|
|
|
if req.Amount <= 0 {
|
|
|
|
|
return fmt.Errorf("amount must be greater than zero when splitting by AMOUNT")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2025-08-08 00:22:28 +07:00
|
|
|
|
|
|
|
|
// validateTable validates that the table exists and is available for occupation
|
|
|
|
|
func (s *OrderServiceImpl) validateTable(ctx context.Context, req *models.CreateOrderRequest) error {
|
|
|
|
|
// Validate table exists and is available
|
|
|
|
|
table, err := s.tableRepo.GetByID(ctx, *req.TableID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("table not found: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if table belongs to the same outlet
|
|
|
|
|
if table.OutletID != req.OutletID {
|
|
|
|
|
return fmt.Errorf("table does not belong to the specified outlet")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if table is available for occupation
|
|
|
|
|
if !table.CanBeOccupied() {
|
|
|
|
|
return fmt.Errorf("table is not available for occupation (current status: %s)", table.Status)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *OrderServiceImpl) occupyTableWithOrder(ctx context.Context, tableID, orderID uuid.UUID) error {
|
|
|
|
|
startTime := time.Now()
|
|
|
|
|
if err := s.tableRepo.OccupyTable(ctx, tableID, orderID, &startTime); err != nil {
|
|
|
|
|
return fmt.Errorf("failed to occupy table: %w", err)
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *OrderServiceImpl) handleTableReleaseOnPayment(ctx context.Context, orderID uuid.UUID) error {
|
|
|
|
|
order, err := s.orderProcessor.GetOrderByID(ctx, orderID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("failed to get order: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if order.PaymentStatus == "completed" {
|
|
|
|
|
table, err := s.tableRepo.GetByOrderID(ctx, orderID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if table != nil {
|
|
|
|
|
if err := s.tableRepo.ReleaseTable(ctx, table.ID, order.TotalAmount); err != nil {
|
|
|
|
|
return fmt.Errorf("failed to release table: %w", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// handleTableReleaseOnVoid releases the table when an order is voided
|
|
|
|
|
func (s *OrderServiceImpl) handleTableReleaseOnVoid(ctx context.Context, orderID uuid.UUID) error {
|
|
|
|
|
table, err := s.tableRepo.GetByOrderID(ctx, orderID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
// Table might not exist or not be occupied, which is fine
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if table != nil {
|
|
|
|
|
if err := s.tableRepo.ReleaseTable(ctx, table.ID, 0); err != nil {
|
|
|
|
|
return fmt.Errorf("failed to release table: %w", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|