2025-07-18 20:10:29 +07:00
|
|
|
package repository
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
2025-08-06 00:02:49 +07:00
|
|
|
"fmt"
|
2025-07-18 20:10:29 +07:00
|
|
|
"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
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-06 00:02:49 +07:00
|
|
|
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
|
|
|
|
|
}
|
2025-07-18 20:10:29 +07:00
|
|
|
|
2025-08-06 00:02:49 +07:00
|
|
|
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,
|
|
|
|
|
}
|
2025-07-18 20:10:29 +07:00
|
|
|
|
2025-08-06 00:02:49 +07:00
|
|
|
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
|
2025-07-18 20:10:29 +07:00
|
|
|
|
|
|
|
|
updates := map[string]interface{}{
|
2025-08-06 00:02:49 +07:00
|
|
|
"quantity": remainingQuantity,
|
|
|
|
|
"total_price": remainingTotalPrice,
|
|
|
|
|
"total_cost": remainingTotalCost,
|
|
|
|
|
"updated_at": now,
|
2025-07-18 20:10:29 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return r.db.WithContext(ctx).Model(&entities.OrderItem{}).
|
|
|
|
|
Where("id = ?", id).
|
|
|
|
|
Updates(updates).Error
|
|
|
|
|
}
|