Add order items
This commit is contained in:
parent
c18e915d1e
commit
8e9c14b860
@ -28,6 +28,7 @@ const (
|
|||||||
OrderItemStatusServed OrderItemStatus = "served"
|
OrderItemStatusServed OrderItemStatus = "served"
|
||||||
OrderItemStatusCancelled OrderItemStatus = "cancelled"
|
OrderItemStatusCancelled OrderItemStatus = "cancelled"
|
||||||
OrderItemStatusCompleted OrderItemStatus = "completed"
|
OrderItemStatusCompleted OrderItemStatus = "completed"
|
||||||
|
OrderItemStatusPaid OrderItemStatus = "paid"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetAllOrderTypes() []OrderType {
|
func GetAllOrderTypes() []OrderType {
|
||||||
|
|||||||
@ -108,6 +108,7 @@ type OrderItemResponse struct {
|
|||||||
CreatedAt time.Time `json:"created_at"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
UpdatedAt time.Time `json:"updated_at"`
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
PrinterType string `json:"printer_type"`
|
PrinterType string `json:"printer_type"`
|
||||||
|
PaidQuantity int `json:"paid_quantity"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ListOrdersQuery struct {
|
type ListOrdersQuery struct {
|
||||||
@ -249,7 +250,7 @@ type SplitBillRequest struct {
|
|||||||
|
|
||||||
type SplitBillItemRequest struct {
|
type SplitBillItemRequest struct {
|
||||||
OrderItemID uuid.UUID `json:"order_item_id" validate:"required"`
|
OrderItemID uuid.UUID `json:"order_item_id" validate:"required"`
|
||||||
Amount float64 `json:"amount" validate:"required,min=0"`
|
Quantity int `json:"quantity" validate:"required,min=0"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SplitBillResponse struct {
|
type SplitBillResponse struct {
|
||||||
|
|||||||
@ -38,6 +38,7 @@ const (
|
|||||||
OrderItemStatusReady OrderItemStatus = "ready"
|
OrderItemStatusReady OrderItemStatus = "ready"
|
||||||
OrderItemStatusServed OrderItemStatus = "served"
|
OrderItemStatusServed OrderItemStatus = "served"
|
||||||
OrderItemStatusCancelled OrderItemStatus = "cancelled"
|
OrderItemStatusCancelled OrderItemStatus = "cancelled"
|
||||||
|
OrderItemStatusPaid OrderItemStatus = "paid"
|
||||||
)
|
)
|
||||||
|
|
||||||
type OrderItem struct {
|
type OrderItem struct {
|
||||||
|
|||||||
@ -11,6 +11,7 @@ type PaymentOrderItem struct {
|
|||||||
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
|
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
|
||||||
PaymentID uuid.UUID `gorm:"type:uuid;not null;index" json:"payment_id"`
|
PaymentID uuid.UUID `gorm:"type:uuid;not null;index" json:"payment_id"`
|
||||||
OrderItemID uuid.UUID `gorm:"type:uuid;not null;index" json:"order_item_id"`
|
OrderItemID uuid.UUID `gorm:"type:uuid;not null;index" json:"order_item_id"`
|
||||||
|
Quantity int `gorm:"not null;default:0" json:"quantity"` // Quantity paid for this specific payment
|
||||||
Amount float64 `gorm:"type:decimal(10,2);not null" json:"amount"`
|
Amount float64 `gorm:"type:decimal(10,2);not null" json:"amount"`
|
||||||
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
|
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
|
||||||
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
|
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
|
||||||
|
|||||||
@ -70,8 +70,23 @@ func OrderEntityToResponse(order *entities.Order) *models.OrderResponse {
|
|||||||
// Map order items
|
// Map order items
|
||||||
if order.OrderItems != nil {
|
if order.OrderItems != nil {
|
||||||
response.OrderItems = make([]models.OrderItemResponse, len(order.OrderItems))
|
response.OrderItems = make([]models.OrderItemResponse, len(order.OrderItems))
|
||||||
|
|
||||||
|
// Build map of paid quantities per order item from payments
|
||||||
|
paidQtyByOrderItem := map[uuid.UUID]int{}
|
||||||
|
if order.Payments != nil {
|
||||||
|
for _, p := range order.Payments {
|
||||||
|
for _, poi := range p.PaymentOrderItems {
|
||||||
|
paidQtyByOrderItem[poi.OrderItemID] += poi.Quantity
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for i, item := range order.OrderItems {
|
for i, item := range order.OrderItems {
|
||||||
response.OrderItems[i] = *OrderItemEntityToResponse(&item)
|
resp := OrderItemEntityToResponse(&item)
|
||||||
|
if resp != nil {
|
||||||
|
resp.PaidQuantity = paidQtyByOrderItem[item.ID]
|
||||||
|
response.OrderItems[i] = *resp
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -65,6 +65,7 @@ type PaymentOrderItem struct {
|
|||||||
ID uuid.UUID
|
ID uuid.UUID
|
||||||
PaymentID uuid.UUID
|
PaymentID uuid.UUID
|
||||||
OrderItemID uuid.UUID
|
OrderItemID uuid.UUID
|
||||||
|
Quantity int // Quantity paid for this specific payment
|
||||||
Amount float64
|
Amount float64
|
||||||
CreatedAt time.Time
|
CreatedAt time.Time
|
||||||
UpdatedAt time.Time
|
UpdatedAt time.Time
|
||||||
@ -114,6 +115,7 @@ type UpdateOrderRequest struct {
|
|||||||
|
|
||||||
type CreatePaymentOrderItemRequest struct {
|
type CreatePaymentOrderItemRequest struct {
|
||||||
OrderItemID uuid.UUID `validate:"required"`
|
OrderItemID uuid.UUID `validate:"required"`
|
||||||
|
Quantity int `validate:"required,min=1"` // Quantity being paid for
|
||||||
Amount float64 `validate:"required,min=0"`
|
Amount float64 `validate:"required,min=0"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,12 +207,14 @@ type OrderItemResponse struct {
|
|||||||
CreatedAt time.Time
|
CreatedAt time.Time
|
||||||
UpdatedAt time.Time
|
UpdatedAt time.Time
|
||||||
PrinterType string
|
PrinterType string
|
||||||
|
PaidQuantity int
|
||||||
}
|
}
|
||||||
|
|
||||||
type PaymentOrderItemResponse struct {
|
type PaymentOrderItemResponse struct {
|
||||||
ID uuid.UUID
|
ID uuid.UUID
|
||||||
PaymentID uuid.UUID
|
PaymentID uuid.UUID
|
||||||
OrderItemID uuid.UUID
|
OrderItemID uuid.UUID
|
||||||
|
Quantity int // Quantity paid for this specific payment
|
||||||
Amount float64
|
Amount float64
|
||||||
CreatedAt time.Time
|
CreatedAt time.Time
|
||||||
UpdatedAt time.Time
|
UpdatedAt time.Time
|
||||||
|
|||||||
@ -27,7 +27,7 @@ func (s *SplitBillRequest) IsAmount() bool {
|
|||||||
|
|
||||||
type SplitBillItemRequest struct {
|
type SplitBillItemRequest struct {
|
||||||
OrderItemID uuid.UUID `validate:"required"`
|
OrderItemID uuid.UUID `validate:"required"`
|
||||||
Amount float64 `validate:"required,min=0"`
|
Quantity int `validate:"required,min=0"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SplitBillResponse struct {
|
type SplitBillResponse struct {
|
||||||
|
|||||||
@ -74,6 +74,7 @@ type PaymentOrderItemRepository interface {
|
|||||||
Create(ctx context.Context, paymentOrderItem *entities.PaymentOrderItem) error
|
Create(ctx context.Context, paymentOrderItem *entities.PaymentOrderItem) error
|
||||||
GetByID(ctx context.Context, id uuid.UUID) (*entities.PaymentOrderItem, error)
|
GetByID(ctx context.Context, id uuid.UUID) (*entities.PaymentOrderItem, error)
|
||||||
GetByPaymentID(ctx context.Context, paymentID uuid.UUID) ([]*entities.PaymentOrderItem, error)
|
GetByPaymentID(ctx context.Context, paymentID uuid.UUID) ([]*entities.PaymentOrderItem, error)
|
||||||
|
GetPaidQuantitiesByOrderID(ctx context.Context, orderID uuid.UUID) (map[uuid.UUID]int, error)
|
||||||
Update(ctx context.Context, paymentOrderItem *entities.PaymentOrderItem) error
|
Update(ctx context.Context, paymentOrderItem *entities.PaymentOrderItem) error
|
||||||
Delete(ctx context.Context, id uuid.UUID) error
|
Delete(ctx context.Context, id uuid.UUID) error
|
||||||
}
|
}
|
||||||
@ -110,6 +111,7 @@ type OrderProcessorImpl struct {
|
|||||||
productVariantRepo repository.ProductVariantRepository
|
productVariantRepo repository.ProductVariantRepository
|
||||||
outletRepo OutletRepository
|
outletRepo OutletRepository
|
||||||
customerRepo CustomerRepository
|
customerRepo CustomerRepository
|
||||||
|
splitBillProcessor SplitBillProcessor
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewOrderProcessorImpl(
|
func NewOrderProcessorImpl(
|
||||||
@ -137,6 +139,7 @@ func NewOrderProcessorImpl(
|
|||||||
productVariantRepo: productVariantRepo,
|
productVariantRepo: productVariantRepo,
|
||||||
outletRepo: outletRepo,
|
outletRepo: outletRepo,
|
||||||
customerRepo: customerRepo,
|
customerRepo: customerRepo,
|
||||||
|
splitBillProcessor: NewSplitBillProcessorImpl(orderRepo, orderItemRepo, paymentRepo, paymentOrderItemRepo, outletRepo),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -800,10 +803,6 @@ func (p *OrderProcessorImpl) CreatePayment(ctx context.Context, req *models.Crea
|
|||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func stringPtr(s string) *string {
|
|
||||||
return &s
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *OrderProcessorImpl) RefundPayment(ctx context.Context, paymentID uuid.UUID, refundAmount float64, reason string, refundedBy uuid.UUID) error {
|
func (p *OrderProcessorImpl) RefundPayment(ctx context.Context, paymentID uuid.UUID, refundAmount float64, reason string, refundedBy uuid.UUID) error {
|
||||||
payment, err := p.paymentRepo.GetByID(ctx, paymentID)
|
payment, err := p.paymentRepo.GetByID(ctx, paymentID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -910,200 +909,14 @@ func (p *OrderProcessorImpl) SplitBill(ctx context.Context, req *models.SplitBil
|
|||||||
|
|
||||||
var response *models.SplitBillResponse
|
var response *models.SplitBillResponse
|
||||||
if req.IsAmount() {
|
if req.IsAmount() {
|
||||||
response, err = p.splitBillByAmount(ctx, req, order, payment, customer)
|
response, err = p.splitBillProcessor.SplitByAmount(ctx, req, order, payment, customer)
|
||||||
} else if req.IsItem() {
|
} else if req.IsItem() {
|
||||||
response, err = p.splitBillByItem(ctx, req, order, payment, customer)
|
response, err = p.splitBillProcessor.SplitByItem(ctx, req, order, payment, customer)
|
||||||
} else {
|
} else {
|
||||||
return nil, fmt.Errorf("invalid split type: must be AMOUNT or ITEM")
|
return nil, fmt.Errorf("invalid split type: must be AMOUNT or ITEM")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *OrderProcessorImpl) splitBillByAmount(ctx context.Context, req *models.SplitBillRequest, order *entities.Order, payment *entities.PaymentMethod, customer *entities.Customer) (*models.SplitBillResponse, error) {
|
|
||||||
totalPaid, err := p.paymentRepo.GetTotalPaidByOrderID(ctx, req.OrderID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to get total paid amount: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
remainingBalance := order.TotalAmount - totalPaid
|
|
||||||
if req.Amount > remainingBalance {
|
|
||||||
return nil, fmt.Errorf("split amount %.2f cannot exceed remaining balance %.2f", req.Amount, remainingBalance)
|
|
||||||
}
|
|
||||||
|
|
||||||
existingPayments, err := p.paymentRepo.GetByOrderID(ctx, req.OrderID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to get existing payments: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
splitNumber := len(existingPayments) + 1
|
|
||||||
splitTotal := splitNumber + 1
|
|
||||||
|
|
||||||
splitType := entities.SplitTypeAmount
|
|
||||||
splitPayment := &entities.Payment{
|
|
||||||
OrderID: req.OrderID,
|
|
||||||
PaymentMethodID: payment.ID,
|
|
||||||
Amount: req.Amount,
|
|
||||||
Status: entities.PaymentTransactionStatusCompleted,
|
|
||||||
SplitNumber: splitNumber,
|
|
||||||
SplitTotal: splitTotal,
|
|
||||||
SplitType: &splitType,
|
|
||||||
SplitDescription: stringPtr(fmt.Sprint("Split payment for customer")),
|
|
||||||
Metadata: entities.Metadata{
|
|
||||||
"split_type": "AMOUNT",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := p.paymentRepo.Create(ctx, splitPayment); err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to create split payment: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if order.Metadata == nil {
|
|
||||||
order.Metadata = make(entities.Metadata)
|
|
||||||
}
|
|
||||||
|
|
||||||
order.Metadata["last_split_payment_id"] = splitPayment.ID.String()
|
|
||||||
order.Metadata["last_split_customer_id"] = req.CustomerID.String()
|
|
||||||
order.Metadata["last_split_amount"] = req.Amount
|
|
||||||
order.Metadata["last_split_type"] = "AMOUNT"
|
|
||||||
|
|
||||||
newTotalPaid := totalPaid + req.Amount
|
|
||||||
order.RemainingAmount = order.TotalAmount - newTotalPaid
|
|
||||||
|
|
||||||
if newTotalPaid >= order.TotalAmount {
|
|
||||||
order.PaymentStatus = entities.PaymentStatusCompleted
|
|
||||||
order.Status = entities.OrderStatusCompleted
|
|
||||||
order.RemainingAmount = 0
|
|
||||||
} else {
|
|
||||||
order.PaymentStatus = entities.PaymentStatusPartial
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := p.orderRepo.Update(ctx, order); err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to update order: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &models.SplitBillResponse{
|
|
||||||
PaymentID: splitPayment.ID,
|
|
||||||
OrderID: req.OrderID,
|
|
||||||
CustomerID: req.CustomerID,
|
|
||||||
Type: "AMOUNT",
|
|
||||||
Amount: req.Amount,
|
|
||||||
Message: fmt.Sprintf("Successfully split payment by amount %.2f for customer %s. Remaining balance: %.2f", req.Amount, customer.Name, order.RemainingAmount),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *OrderProcessorImpl) splitBillByItem(ctx context.Context, req *models.SplitBillRequest, order *entities.Order, payment *entities.PaymentMethod, customer *entities.Customer) (*models.SplitBillResponse, error) {
|
|
||||||
totalSplitAmount := float64(0)
|
|
||||||
for _, item := range req.Items {
|
|
||||||
totalSplitAmount += item.Amount
|
|
||||||
}
|
|
||||||
|
|
||||||
totalPaid, err := p.paymentRepo.GetTotalPaidByOrderID(ctx, req.OrderID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to get total paid amount: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
remainingBalance := order.TotalAmount - totalPaid
|
|
||||||
|
|
||||||
if totalSplitAmount > remainingBalance {
|
|
||||||
return nil, fmt.Errorf("split amount %.2f cannot exceed remaining balance %.2f", totalSplitAmount, remainingBalance)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, item := range req.Items {
|
|
||||||
orderItem, err := p.orderItemRepo.GetByID(ctx, item.OrderItemID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("order item not found: %w", err)
|
|
||||||
}
|
|
||||||
if orderItem.OrderID != req.OrderID {
|
|
||||||
return nil, fmt.Errorf("order item does not belong to this order")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
existingPayments, err := p.paymentRepo.GetByOrderID(ctx, req.OrderID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to get existing payments: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
splitNumber := len(existingPayments) + 1
|
|
||||||
splitTotal := splitNumber + 1
|
|
||||||
|
|
||||||
splitType := entities.SplitTypeItem
|
|
||||||
splitPayment := &entities.Payment{
|
|
||||||
OrderID: req.OrderID,
|
|
||||||
PaymentMethodID: payment.ID,
|
|
||||||
Amount: totalSplitAmount,
|
|
||||||
Status: entities.PaymentTransactionStatusCompleted,
|
|
||||||
SplitNumber: splitNumber,
|
|
||||||
SplitTotal: splitTotal,
|
|
||||||
SplitType: &splitType,
|
|
||||||
SplitDescription: stringPtr(fmt.Sprintf("Split payment by items for customer: %s", customer.Name)),
|
|
||||||
Metadata: entities.Metadata{
|
|
||||||
"split_type": "ITEM",
|
|
||||||
"customer_id": req.CustomerID.String(),
|
|
||||||
"customer_name": customer.Name,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := p.paymentRepo.Create(ctx, splitPayment); err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to create split payment: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, item := range req.Items {
|
|
||||||
paymentOrderItem := &entities.PaymentOrderItem{
|
|
||||||
PaymentID: splitPayment.ID,
|
|
||||||
OrderItemID: item.OrderItemID,
|
|
||||||
Amount: item.Amount,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := p.paymentOrderItemRepo.Create(ctx, paymentOrderItem); err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to create payment order item: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if order.Metadata == nil {
|
|
||||||
order.Metadata = make(entities.Metadata)
|
|
||||||
}
|
|
||||||
|
|
||||||
order.Metadata["last_split_payment_id"] = splitPayment.ID.String()
|
|
||||||
order.Metadata["last_split_customer_id"] = req.CustomerID.String()
|
|
||||||
order.Metadata["last_split_customer_name"] = customer.Name
|
|
||||||
order.Metadata["last_split_amount"] = totalSplitAmount
|
|
||||||
order.Metadata["last_split_type"] = "ITEM"
|
|
||||||
|
|
||||||
newTotalPaid := totalPaid + totalSplitAmount
|
|
||||||
order.RemainingAmount = order.TotalAmount - newTotalPaid
|
|
||||||
|
|
||||||
if newTotalPaid >= order.TotalAmount {
|
|
||||||
order.PaymentStatus = entities.PaymentStatusCompleted
|
|
||||||
order.Status = entities.OrderStatusCompleted
|
|
||||||
order.RemainingAmount = 0
|
|
||||||
} else {
|
|
||||||
order.PaymentStatus = entities.PaymentStatusPartial
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := p.orderRepo.Update(ctx, order); err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to update order: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
responseItems := make([]models.SplitBillItemResponse, len(req.Items))
|
|
||||||
for i, item := range req.Items {
|
|
||||||
responseItems[i] = models.SplitBillItemResponse{
|
|
||||||
OrderItemID: item.OrderItemID,
|
|
||||||
Amount: item.Amount,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &models.SplitBillResponse{
|
|
||||||
PaymentID: splitPayment.ID,
|
|
||||||
OrderID: req.OrderID,
|
|
||||||
CustomerID: req.CustomerID,
|
|
||||||
Type: "ITEM",
|
|
||||||
Amount: totalSplitAmount,
|
|
||||||
Items: responseItems,
|
|
||||||
Message: fmt.Sprintf("Successfully split payment by items (%.2f) for customer %s. Remaining balance: %.2f", totalSplitAmount, customer.Name, order.RemainingAmount),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|||||||
349
internal/processor/split_bill_processor.go
Normal file
349
internal/processor/split_bill_processor.go
Normal file
@ -0,0 +1,349 @@
|
|||||||
|
package processor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"apskel-pos-be/internal/entities"
|
||||||
|
"apskel-pos-be/internal/models"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
SplitTypeItem = "ITEM"
|
||||||
|
SplitTypeAmount = "AMOUNT"
|
||||||
|
|
||||||
|
MetadataKeyLastSplitPaymentID = "last_split_payment_id"
|
||||||
|
MetadataKeyLastSplitCustomerID = "last_split_customer_id"
|
||||||
|
MetadataKeyLastSplitCustomerName = "last_split_customer_name"
|
||||||
|
MetadataKeyLastSplitAmount = "last_split_amount"
|
||||||
|
MetadataKeyLastSplitType = "last_split_type"
|
||||||
|
MetadataKeyLastSplitQuantities = "last_split_quantities"
|
||||||
|
)
|
||||||
|
|
||||||
|
func stringPtr(s string) *string {
|
||||||
|
return &s
|
||||||
|
}
|
||||||
|
|
||||||
|
type SplitBillValidation struct {
|
||||||
|
OrderItems map[uuid.UUID]*entities.OrderItem
|
||||||
|
PaidQuantities map[uuid.UUID]int
|
||||||
|
Outlet *entities.Outlet
|
||||||
|
TotalPaid float64
|
||||||
|
RemainingBalance float64
|
||||||
|
}
|
||||||
|
|
||||||
|
type SplitBillProcessor interface {
|
||||||
|
SplitByAmount(ctx context.Context, req *models.SplitBillRequest, order *entities.Order, payment *entities.PaymentMethod, customer *entities.Customer) (*models.SplitBillResponse, error)
|
||||||
|
SplitByItem(ctx context.Context, req *models.SplitBillRequest, order *entities.Order, payment *entities.PaymentMethod, customer *entities.Customer) (*models.SplitBillResponse, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type SplitBillProcessorImpl struct {
|
||||||
|
orderRepo OrderRepository
|
||||||
|
orderItemRepo OrderItemRepository
|
||||||
|
paymentRepo PaymentRepository
|
||||||
|
paymentOrderItemRepo PaymentOrderItemRepository
|
||||||
|
outletRepo OutletRepository
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSplitBillProcessorImpl(
|
||||||
|
orderRepo OrderRepository,
|
||||||
|
orderItemRepo OrderItemRepository,
|
||||||
|
paymentRepo PaymentRepository,
|
||||||
|
paymentOrderItemRepo PaymentOrderItemRepository,
|
||||||
|
outletRepo OutletRepository,
|
||||||
|
) *SplitBillProcessorImpl {
|
||||||
|
return &SplitBillProcessorImpl{
|
||||||
|
orderRepo: orderRepo,
|
||||||
|
orderItemRepo: orderItemRepo,
|
||||||
|
paymentRepo: paymentRepo,
|
||||||
|
paymentOrderItemRepo: paymentOrderItemRepo,
|
||||||
|
outletRepo: outletRepo,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateSplitBillRequest validates the split bill request and returns validation data
|
||||||
|
func (p *SplitBillProcessorImpl) validateSplitBillRequest(ctx context.Context, req *models.SplitBillRequest, order *entities.Order) (*SplitBillValidation, error) {
|
||||||
|
if req == nil {
|
||||||
|
return nil, fmt.Errorf("split bill request cannot be nil")
|
||||||
|
}
|
||||||
|
if order == nil {
|
||||||
|
return nil, fmt.Errorf("order cannot be nil")
|
||||||
|
}
|
||||||
|
if len(req.Items) == 0 && req.IsItem() {
|
||||||
|
return nil, fmt.Errorf("split bill request must contain at least one item")
|
||||||
|
}
|
||||||
|
|
||||||
|
outlet, err := p.outletRepo.GetByID(ctx, order.OutletID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("outlet not found for order %s: %w", req.OrderID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
totalPaid, err := p.paymentRepo.GetTotalPaidByOrderID(ctx, req.OrderID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get total paid amount for order %s: %w", req.OrderID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
remainingBalance := order.TotalAmount - totalPaid
|
||||||
|
|
||||||
|
paidQuantities, err := p.getPaidQuantitiesForOrder(ctx, req.OrderID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get paid quantities for order %s: %w", req.OrderID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
orderItems := make(map[uuid.UUID]*entities.OrderItem)
|
||||||
|
for _, item := range req.Items {
|
||||||
|
orderItem, err := p.orderItemRepo.GetByID(ctx, item.OrderItemID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("order item %s not found: %w", item.OrderItemID, err)
|
||||||
|
}
|
||||||
|
if orderItem.OrderID != req.OrderID {
|
||||||
|
return nil, fmt.Errorf("order item %s does not belong to order %s", item.OrderItemID, req.OrderID)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := validateSplitItemQuantity(item, orderItem, paidQuantities); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
orderItems[item.OrderItemID] = orderItem
|
||||||
|
}
|
||||||
|
|
||||||
|
return &SplitBillValidation{
|
||||||
|
OrderItems: orderItems,
|
||||||
|
PaidQuantities: paidQuantities,
|
||||||
|
Outlet: outlet,
|
||||||
|
TotalPaid: totalPaid,
|
||||||
|
RemainingBalance: remainingBalance,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *SplitBillProcessorImpl) getPaidQuantitiesForOrder(ctx context.Context, orderID uuid.UUID) (map[uuid.UUID]int, error) {
|
||||||
|
paidQuantities, err := p.paymentOrderItemRepo.GetPaidQuantitiesByOrderID(ctx, orderID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get paid quantities: %w", err)
|
||||||
|
}
|
||||||
|
return paidQuantities, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateSplitItemQuantity(item models.SplitBillItemRequest, orderItem *entities.OrderItem, paidQuantities map[uuid.UUID]int) error {
|
||||||
|
if item.Quantity <= 0 {
|
||||||
|
return fmt.Errorf("requested quantity must be greater than 0 for order item %s", item.OrderItemID)
|
||||||
|
}
|
||||||
|
alreadyPaid := paidQuantities[item.OrderItemID]
|
||||||
|
availableQuantity := orderItem.Quantity - alreadyPaid
|
||||||
|
if item.Quantity > availableQuantity {
|
||||||
|
return fmt.Errorf("requested quantity %d for order item %s exceeds available quantity %d (total: %d, already paid: %d)", item.Quantity, item.OrderItemID, availableQuantity, orderItem.Quantity, alreadyPaid)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func calculateSplitAmounts(req *models.SplitBillRequest, validation *SplitBillValidation) (float64, float64, []models.SplitBillItemResponse) {
|
||||||
|
var totalSplitAmount float64
|
||||||
|
var responseItems []models.SplitBillItemResponse
|
||||||
|
|
||||||
|
for _, item := range req.Items {
|
||||||
|
orderItem := validation.OrderItems[item.OrderItemID]
|
||||||
|
itemAmount := float64(item.Quantity) * orderItem.UnitPrice
|
||||||
|
itemTaxAmount := itemAmount * validation.Outlet.TaxRate
|
||||||
|
totalItemAmount := itemAmount + itemTaxAmount
|
||||||
|
|
||||||
|
totalSplitAmount += itemAmount
|
||||||
|
|
||||||
|
responseItems = append(responseItems, models.SplitBillItemResponse{
|
||||||
|
OrderItemID: item.OrderItemID,
|
||||||
|
Amount: totalItemAmount,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
splitTaxAmount := totalSplitAmount * validation.Outlet.TaxRate
|
||||||
|
totalSplitAmountWithTax := totalSplitAmount + splitTaxAmount
|
||||||
|
return totalSplitAmount, totalSplitAmountWithTax, responseItems
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *SplitBillProcessorImpl) createSplitPayment(ctx context.Context, req *models.SplitBillRequest, order *entities.Order, payment *entities.PaymentMethod, customer *entities.Customer, totalSplitAmountWithTax float64, existingPayments []*entities.Payment) (*entities.Payment, error) {
|
||||||
|
splitNumber := len(existingPayments) + 1
|
||||||
|
splitTotal := splitNumber + 1
|
||||||
|
splitType := entities.SplitTypeItem
|
||||||
|
|
||||||
|
splitPayment := &entities.Payment{
|
||||||
|
OrderID: req.OrderID,
|
||||||
|
PaymentMethodID: payment.ID,
|
||||||
|
Amount: totalSplitAmountWithTax,
|
||||||
|
Status: entities.PaymentTransactionStatusCompleted,
|
||||||
|
SplitNumber: splitNumber,
|
||||||
|
SplitTotal: splitTotal,
|
||||||
|
SplitType: &splitType,
|
||||||
|
SplitDescription: stringPtr(fmt.Sprintf("Split payment by items for customer: %s", customer.Name)),
|
||||||
|
Metadata: entities.Metadata{
|
||||||
|
"split_type": SplitTypeItem,
|
||||||
|
"customer_id": req.CustomerID.String(),
|
||||||
|
"customer_name": customer.Name,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if err := p.paymentRepo.Create(ctx, splitPayment); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create split payment: %w", err)
|
||||||
|
}
|
||||||
|
return splitPayment, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *SplitBillProcessorImpl) createPaymentOrderItems(ctx context.Context, splitPayment *entities.Payment, req *models.SplitBillRequest, validation *SplitBillValidation) error {
|
||||||
|
for _, item := range req.Items {
|
||||||
|
orderItem := validation.OrderItems[item.OrderItemID]
|
||||||
|
itemAmount := float64(item.Quantity) * orderItem.UnitPrice
|
||||||
|
itemTaxAmount := itemAmount * validation.Outlet.TaxRate
|
||||||
|
totalItemAmount := itemAmount + itemTaxAmount
|
||||||
|
|
||||||
|
paymentOrderItem := &entities.PaymentOrderItem{
|
||||||
|
PaymentID: splitPayment.ID,
|
||||||
|
OrderItemID: item.OrderItemID,
|
||||||
|
Quantity: item.Quantity,
|
||||||
|
Amount: totalItemAmount,
|
||||||
|
}
|
||||||
|
if err := p.paymentOrderItemRepo.Create(ctx, paymentOrderItem); err != nil {
|
||||||
|
return fmt.Errorf("failed to create payment order item: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update order item status to paid if fully covered
|
||||||
|
alreadyPaid := validation.PaidQuantities[item.OrderItemID]
|
||||||
|
newPaid := alreadyPaid + item.Quantity
|
||||||
|
if newPaid >= orderItem.Quantity {
|
||||||
|
if err := p.orderItemRepo.UpdateStatus(ctx, item.OrderItemID, entities.OrderItemStatusPaid); err != nil {
|
||||||
|
return fmt.Errorf("failed to update order item status to paid: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *SplitBillProcessorImpl) updateOrderAfterSplit(ctx context.Context, order *entities.Order, req *models.SplitBillRequest, customer *entities.Customer, totalSplitAmountWithTax float64, validation *SplitBillValidation) error {
|
||||||
|
if order.Metadata == nil {
|
||||||
|
order.Metadata = make(entities.Metadata)
|
||||||
|
}
|
||||||
|
order.Metadata[MetadataKeyLastSplitPaymentID] = req.OrderID.String()
|
||||||
|
order.Metadata[MetadataKeyLastSplitCustomerID] = req.CustomerID.String()
|
||||||
|
order.Metadata[MetadataKeyLastSplitCustomerName] = customer.Name
|
||||||
|
order.Metadata[MetadataKeyLastSplitAmount] = totalSplitAmountWithTax
|
||||||
|
order.Metadata[MetadataKeyLastSplitType] = SplitTypeItem
|
||||||
|
|
||||||
|
quantityInfo := make(map[string]interface{})
|
||||||
|
for _, item := range req.Items {
|
||||||
|
orderItem := validation.OrderItems[item.OrderItemID]
|
||||||
|
quantityInfo[item.OrderItemID.String()] = map[string]interface{}{
|
||||||
|
"quantity": item.Quantity,
|
||||||
|
"unit_price": orderItem.UnitPrice,
|
||||||
|
"total_amount": float64(item.Quantity) * orderItem.UnitPrice,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
order.Metadata[MetadataKeyLastSplitQuantities] = quantityInfo
|
||||||
|
|
||||||
|
newTotalPaid := validation.TotalPaid + totalSplitAmountWithTax
|
||||||
|
order.RemainingAmount = order.TotalAmount - newTotalPaid
|
||||||
|
if newTotalPaid >= order.TotalAmount {
|
||||||
|
order.PaymentStatus = entities.PaymentStatusCompleted
|
||||||
|
order.Status = entities.OrderStatusCompleted
|
||||||
|
order.RemainingAmount = 0
|
||||||
|
} else {
|
||||||
|
order.PaymentStatus = entities.PaymentStatusPartial
|
||||||
|
}
|
||||||
|
if err := p.orderRepo.Update(ctx, order); err != nil {
|
||||||
|
return fmt.Errorf("failed to update order: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *SplitBillProcessorImpl) SplitByItem(ctx context.Context, req *models.SplitBillRequest, order *entities.Order, payment *entities.PaymentMethod, customer *entities.Customer) (*models.SplitBillResponse, error) {
|
||||||
|
validation, err := p.validateSplitBillRequest(ctx, req, order)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, totalSplitAmountWithTax, responseItems := calculateSplitAmounts(req, validation)
|
||||||
|
if totalSplitAmountWithTax > validation.RemainingBalance {
|
||||||
|
return nil, fmt.Errorf("split amount %.2f cannot exceed remaining balance %.2f", totalSplitAmountWithTax, validation.RemainingBalance)
|
||||||
|
}
|
||||||
|
existingPayments, err := p.paymentRepo.GetByOrderID(ctx, req.OrderID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get existing payments: %w", err)
|
||||||
|
}
|
||||||
|
splitPayment, err := p.createSplitPayment(ctx, req, order, payment, customer, totalSplitAmountWithTax, existingPayments)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := p.createPaymentOrderItems(ctx, splitPayment, req, validation); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := p.updateOrderAfterSplit(ctx, order, req, customer, totalSplitAmountWithTax, validation); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &models.SplitBillResponse{
|
||||||
|
PaymentID: splitPayment.ID,
|
||||||
|
OrderID: req.OrderID,
|
||||||
|
CustomerID: req.CustomerID,
|
||||||
|
Type: SplitTypeItem,
|
||||||
|
Amount: totalSplitAmountWithTax,
|
||||||
|
Items: responseItems,
|
||||||
|
Message: fmt.Sprintf("Successfully split payment by items (%.2f) for customer %s. Remaining balance: %.2f", totalSplitAmountWithTax, customer.Name, order.RemainingAmount),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *SplitBillProcessorImpl) SplitByAmount(ctx context.Context, req *models.SplitBillRequest, order *entities.Order, payment *entities.PaymentMethod, customer *entities.Customer) (*models.SplitBillResponse, error) {
|
||||||
|
totalPaid, err := p.paymentRepo.GetTotalPaidByOrderID(ctx, req.OrderID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get total paid amount: %w", err)
|
||||||
|
}
|
||||||
|
remainingBalance := order.TotalAmount - totalPaid
|
||||||
|
if req.Amount > remainingBalance {
|
||||||
|
return nil, fmt.Errorf("split amount %.2f cannot exceed remaining balance %.2f", req.Amount, remainingBalance)
|
||||||
|
}
|
||||||
|
existingPayments, err := p.paymentRepo.GetByOrderID(ctx, req.OrderID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get existing payments: %w", err)
|
||||||
|
}
|
||||||
|
splitNumber := len(existingPayments) + 1
|
||||||
|
splitTotal := splitNumber + 1
|
||||||
|
splitType := entities.SplitTypeAmount
|
||||||
|
splitPayment := &entities.Payment{
|
||||||
|
OrderID: req.OrderID,
|
||||||
|
PaymentMethodID: payment.ID,
|
||||||
|
Amount: req.Amount,
|
||||||
|
Status: entities.PaymentTransactionStatusCompleted,
|
||||||
|
SplitNumber: splitNumber,
|
||||||
|
SplitTotal: splitTotal,
|
||||||
|
SplitType: &splitType,
|
||||||
|
SplitDescription: stringPtr(fmt.Sprint("Split payment for customer")),
|
||||||
|
Metadata: entities.Metadata{
|
||||||
|
"split_type": SplitTypeAmount,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if err := p.paymentRepo.Create(ctx, splitPayment); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create split payment: %w", err)
|
||||||
|
}
|
||||||
|
if order.Metadata == nil {
|
||||||
|
order.Metadata = make(entities.Metadata)
|
||||||
|
}
|
||||||
|
order.Metadata[MetadataKeyLastSplitPaymentID] = splitPayment.ID.String()
|
||||||
|
order.Metadata[MetadataKeyLastSplitCustomerID] = req.CustomerID.String()
|
||||||
|
order.Metadata[MetadataKeyLastSplitAmount] = req.Amount
|
||||||
|
order.Metadata[MetadataKeyLastSplitType] = SplitTypeAmount
|
||||||
|
|
||||||
|
newTotalPaid := totalPaid + req.Amount
|
||||||
|
order.RemainingAmount = order.TotalAmount - newTotalPaid
|
||||||
|
if newTotalPaid >= order.TotalAmount {
|
||||||
|
order.PaymentStatus = entities.PaymentStatusCompleted
|
||||||
|
order.Status = entities.OrderStatusCompleted
|
||||||
|
order.RemainingAmount = 0
|
||||||
|
} else {
|
||||||
|
order.PaymentStatus = entities.PaymentStatusPartial
|
||||||
|
}
|
||||||
|
if err := p.orderRepo.Update(ctx, order); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to update order: %w", err)
|
||||||
|
}
|
||||||
|
return &models.SplitBillResponse{
|
||||||
|
PaymentID: splitPayment.ID,
|
||||||
|
OrderID: req.OrderID,
|
||||||
|
CustomerID: req.CustomerID,
|
||||||
|
Type: SplitTypeAmount,
|
||||||
|
Amount: req.Amount,
|
||||||
|
Message: fmt.Sprintf("Successfully split payment by amount %.2f for customer %s. Remaining balance: %.2f", req.Amount, customer.Name, order.RemainingAmount),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
@ -13,6 +13,7 @@ type PaymentOrderItemRepository interface {
|
|||||||
Create(ctx context.Context, paymentOrderItem *entities.PaymentOrderItem) error
|
Create(ctx context.Context, paymentOrderItem *entities.PaymentOrderItem) error
|
||||||
GetByID(ctx context.Context, id uuid.UUID) (*entities.PaymentOrderItem, error)
|
GetByID(ctx context.Context, id uuid.UUID) (*entities.PaymentOrderItem, error)
|
||||||
GetByPaymentID(ctx context.Context, paymentID uuid.UUID) ([]*entities.PaymentOrderItem, error)
|
GetByPaymentID(ctx context.Context, paymentID uuid.UUID) ([]*entities.PaymentOrderItem, error)
|
||||||
|
GetPaidQuantitiesByOrderID(ctx context.Context, orderID uuid.UUID) (map[uuid.UUID]int, error)
|
||||||
Update(ctx context.Context, paymentOrderItem *entities.PaymentOrderItem) error
|
Update(ctx context.Context, paymentOrderItem *entities.PaymentOrderItem) error
|
||||||
Delete(ctx context.Context, id uuid.UUID) error
|
Delete(ctx context.Context, id uuid.UUID) error
|
||||||
}
|
}
|
||||||
@ -60,3 +61,34 @@ func (r *PaymentOrderItemRepositoryImpl) Update(ctx context.Context, paymentOrde
|
|||||||
func (r *PaymentOrderItemRepositoryImpl) Delete(ctx context.Context, id uuid.UUID) error {
|
func (r *PaymentOrderItemRepositoryImpl) Delete(ctx context.Context, id uuid.UUID) error {
|
||||||
return r.db.WithContext(ctx).Delete(&entities.PaymentOrderItem{}, "id = ?", id).Error
|
return r.db.WithContext(ctx).Delete(&entities.PaymentOrderItem{}, "id = ?", id).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetPaidQuantitiesByOrderID efficiently aggregates paid quantities for an order using SQL
|
||||||
|
func (r *PaymentOrderItemRepositoryImpl) GetPaidQuantitiesByOrderID(ctx context.Context, orderID uuid.UUID) (map[uuid.UUID]int, error) {
|
||||||
|
type Result struct {
|
||||||
|
OrderItemID uuid.UUID `json:"order_item_id"`
|
||||||
|
TotalQuantity int `json:"total_quantity"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var results []Result
|
||||||
|
|
||||||
|
// Efficient SQL query that aggregates quantities in the database
|
||||||
|
err := r.db.WithContext(ctx).
|
||||||
|
Table("payment_order_items poi").
|
||||||
|
Select("poi.order_item_id, SUM(poi.quantity) as total_quantity").
|
||||||
|
Joins("JOIN payments p ON poi.payment_id = p.id").
|
||||||
|
Where("p.order_id = ?", orderID).
|
||||||
|
Group("poi.order_item_id").
|
||||||
|
Scan(&results).Error
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert results to map
|
||||||
|
paidQuantities := make(map[uuid.UUID]int)
|
||||||
|
for _, result := range results {
|
||||||
|
paidQuantities[result.OrderItemID] = result.TotalQuantity
|
||||||
|
}
|
||||||
|
|
||||||
|
return paidQuantities, nil
|
||||||
|
}
|
||||||
|
|||||||
@ -449,17 +449,17 @@ func (s *OrderServiceImpl) validateSplitBillRequest(req *models.SplitBillRequest
|
|||||||
return fmt.Errorf("items are required when splitting by ITEM")
|
return fmt.Errorf("items are required when splitting by ITEM")
|
||||||
}
|
}
|
||||||
|
|
||||||
totalItemAmount := float64(0)
|
totalItemAmount := 0
|
||||||
for i, item := range req.Items {
|
for i, item := range req.Items {
|
||||||
if item.OrderItemID == uuid.Nil {
|
if item.OrderItemID == uuid.Nil {
|
||||||
return fmt.Errorf("order item ID is required for item %d", i+1)
|
return fmt.Errorf("order item ID is required for item %d", i+1)
|
||||||
}
|
}
|
||||||
|
|
||||||
if item.Amount <= 0 {
|
if item.Quantity <= 0 {
|
||||||
return fmt.Errorf("amount must be greater than zero for item %d", i+1)
|
return fmt.Errorf("quantity must be greater than zero for item %d", i+1)
|
||||||
}
|
}
|
||||||
|
|
||||||
totalItemAmount += item.Amount
|
totalItemAmount += item.Quantity
|
||||||
}
|
}
|
||||||
|
|
||||||
if totalItemAmount <= 0 {
|
if totalItemAmount <= 0 {
|
||||||
|
|||||||
@ -110,6 +110,7 @@ func OrderModelToContract(resp *models.OrderResponse) *contract.OrderResponse {
|
|||||||
CreatedAt: item.CreatedAt,
|
CreatedAt: item.CreatedAt,
|
||||||
UpdatedAt: item.UpdatedAt,
|
UpdatedAt: item.UpdatedAt,
|
||||||
PrinterType: item.PrinterType,
|
PrinterType: item.PrinterType,
|
||||||
|
PaidQuantity: item.PaidQuantity,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Map payments
|
// Map payments
|
||||||
@ -379,7 +380,7 @@ func SplitBillContractToModel(req *contract.SplitBillRequest) *models.SplitBillR
|
|||||||
for i, item := range req.Items {
|
for i, item := range req.Items {
|
||||||
items[i] = models.SplitBillItemRequest{
|
items[i] = models.SplitBillItemRequest{
|
||||||
OrderItemID: item.OrderItemID,
|
OrderItemID: item.OrderItemID,
|
||||||
Amount: item.Amount,
|
Quantity: item.Quantity,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return &models.SplitBillRequest{
|
return &models.SplitBillRequest{
|
||||||
|
|||||||
@ -0,0 +1,3 @@
|
|||||||
|
-- Remove quantity field from payment_order_items table
|
||||||
|
ALTER TABLE payment_order_items
|
||||||
|
DROP COLUMN quantity;
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
-- Add quantity field to payment_order_items table
|
||||||
|
ALTER TABLE payment_order_items
|
||||||
|
ADD COLUMN quantity INTEGER NOT NULL DEFAULT 0;
|
||||||
|
|
||||||
|
-- Add comment to explain the field
|
||||||
|
COMMENT ON COLUMN payment_order_items.quantity IS 'Quantity paid for this specific payment split';
|
||||||
Loading…
x
Reference in New Issue
Block a user