package repository import ( "context" "fmt" "time" "apskel-pos-be/internal/entities" "github.com/google/uuid" "gorm.io/gorm" ) type OrderItemRepository interface { Create(ctx context.Context, orderItem *entities.OrderItem) error GetByID(ctx context.Context, id uuid.UUID) (*entities.OrderItem, error) GetByOrderID(ctx context.Context, orderID uuid.UUID) ([]*entities.OrderItem, error) Update(ctx context.Context, orderItem *entities.OrderItem) error Delete(ctx context.Context, id uuid.UUID) error RefundOrderItem(ctx context.Context, id uuid.UUID, refundQuantity int, refundAmount float64, reason string, refundedBy uuid.UUID) error VoidOrderItem(ctx context.Context, id uuid.UUID, voidQuantity int, reason string, voidedBy uuid.UUID) error UpdateStatus(ctx context.Context, id uuid.UUID, status entities.OrderItemStatus) error } type OrderItemRepositoryImpl struct { db *gorm.DB } func NewOrderItemRepositoryImpl(db *gorm.DB) *OrderItemRepositoryImpl { return &OrderItemRepositoryImpl{ db: db, } } func (r *OrderItemRepositoryImpl) Create(ctx context.Context, orderItem *entities.OrderItem) error { return r.db.WithContext(ctx).Create(orderItem).Error } func (r *OrderItemRepositoryImpl) GetByID(ctx context.Context, id uuid.UUID) (*entities.OrderItem, error) { var orderItem entities.OrderItem err := r.db.WithContext(ctx).First(&orderItem, "id = ?", id).Error if err != nil { return nil, err } return &orderItem, nil } func (r *OrderItemRepositoryImpl) GetByOrderID(ctx context.Context, orderID uuid.UUID) ([]*entities.OrderItem, error) { var orderItems []*entities.OrderItem err := r.db.WithContext(ctx). Preload("Product"). Preload("ProductVariant"). Where("order_id = ?", orderID). Find(&orderItems).Error return orderItems, err } func (r *OrderItemRepositoryImpl) Update(ctx context.Context, orderItem *entities.OrderItem) error { return r.db.WithContext(ctx).Save(orderItem).Error } func (r *OrderItemRepositoryImpl) Delete(ctx context.Context, id uuid.UUID) error { return r.db.WithContext(ctx).Delete(&entities.OrderItem{}, "id = ?", id).Error } func (r *OrderItemRepositoryImpl) RefundOrderItem(ctx context.Context, id uuid.UUID, refundQuantity int, refundAmount float64, reason string, refundedBy uuid.UUID) error { now := time.Now() // Get current order item var orderItem entities.OrderItem if err := r.db.WithContext(ctx).First(&orderItem, "id = ?", id).Error; err != nil { return err } // Calculate new refund quantities and amounts newRefundQuantity := orderItem.RefundQuantity + refundQuantity newRefundAmount := orderItem.RefundAmount + refundAmount // Determine if fully or partially refunded isFullyRefunded := newRefundQuantity >= orderItem.Quantity isPartiallyRefunded := newRefundQuantity > 0 && newRefundQuantity < orderItem.Quantity updates := map[string]interface{}{ "refund_quantity": newRefundQuantity, "refund_amount": newRefundAmount, "is_partially_refunded": isPartiallyRefunded, "is_fully_refunded": isFullyRefunded, "refund_reason": reason, "refunded_at": now, "refunded_by": refundedBy, } return r.db.WithContext(ctx).Model(&entities.OrderItem{}). Where("id = ?", id). Updates(updates).Error } func (r *OrderItemRepositoryImpl) UpdateStatus(ctx context.Context, id uuid.UUID, status entities.OrderItemStatus) error { return r.db.WithContext(ctx).Model(&entities.OrderItem{}). Where("id = ?", id). Update("status", status).Error } func (r *OrderItemRepositoryImpl) VoidOrderItem(ctx context.Context, id uuid.UUID, voidQuantity int, reason string, voidedBy uuid.UUID) error { now := time.Now() var orderItem entities.OrderItem if err := r.db.WithContext(ctx).First(&orderItem, "id = ?", id).Error; err != nil { return err } if voidQuantity >= orderItem.Quantity { voidedAmount := float64(voidQuantity) * orderItem.UnitPrice updates := map[string]interface{}{ "refund_quantity": voidQuantity, "refund_amount": voidedAmount, "is_partially_refunded": false, "is_fully_refunded": true, "refund_reason": reason, "refunded_at": now, "refunded_by": voidedBy, "status": entities.OrderItemStatusCancelled, } return r.db.WithContext(ctx).Model(&entities.OrderItem{}). Where("id = ?", id). Updates(updates).Error } voidedOrderItem := entities.OrderItem{ OrderID: orderItem.OrderID, ProductID: orderItem.ProductID, ProductVariantID: orderItem.ProductVariantID, Quantity: voidQuantity, UnitPrice: orderItem.UnitPrice, TotalPrice: float64(voidQuantity) * orderItem.UnitPrice, UnitCost: orderItem.UnitCost, TotalCost: float64(voidQuantity) * orderItem.UnitCost, RefundAmount: float64(voidQuantity) * orderItem.UnitPrice, RefundQuantity: voidQuantity, IsPartiallyRefunded: false, IsFullyRefunded: true, RefundReason: &reason, RefundedAt: &now, RefundedBy: &voidedBy, Modifiers: orderItem.Modifiers, Notes: orderItem.Notes, Metadata: orderItem.Metadata, Status: entities.OrderItemStatusCancelled, } if err := r.db.WithContext(ctx).Create(&voidedOrderItem).Error; err != nil { return fmt.Errorf("failed to create voided order item: %w", err) } remainingQuantity := orderItem.Quantity - voidQuantity remainingTotalPrice := float64(remainingQuantity) * orderItem.UnitPrice remainingTotalCost := float64(remainingQuantity) * orderItem.UnitCost updates := map[string]interface{}{ "quantity": remainingQuantity, "total_price": remainingTotalPrice, "total_cost": remainingTotalCost, "updated_at": now, } return r.db.WithContext(ctx).Model(&entities.OrderItem{}). Where("id = ?", id). Updates(updates).Error }