2025-07-18 20:10:29 +07:00
|
|
|
package repository
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
"fmt"
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
"apskel-pos-be/internal/entities"
|
|
|
|
|
|
|
|
|
|
"github.com/google/uuid"
|
|
|
|
|
"gorm.io/gorm"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type OrderRepository interface {
|
|
|
|
|
Create(ctx context.Context, order *entities.Order) error
|
|
|
|
|
GetByID(ctx context.Context, id uuid.UUID) (*entities.Order, error)
|
|
|
|
|
GetWithRelations(ctx context.Context, id uuid.UUID) (*entities.Order, error)
|
|
|
|
|
Update(ctx context.Context, order *entities.Order) error
|
|
|
|
|
Delete(ctx context.Context, id uuid.UUID) error
|
|
|
|
|
List(ctx context.Context, filters map[string]interface{}, limit, offset int) ([]*entities.Order, int64, error)
|
|
|
|
|
GetByOrderNumber(ctx context.Context, orderNumber string) (*entities.Order, error)
|
|
|
|
|
ExistsByOrderNumber(ctx context.Context, orderNumber string) (bool, error)
|
|
|
|
|
VoidOrder(ctx context.Context, id uuid.UUID, reason string, voidedBy uuid.UUID) error
|
|
|
|
|
VoidOrderWithStatus(ctx context.Context, id uuid.UUID, status entities.OrderStatus, reason string, voidedBy uuid.UUID) error
|
|
|
|
|
RefundOrder(ctx context.Context, id uuid.UUID, reason string, refundedBy uuid.UUID) error
|
|
|
|
|
UpdatePaymentStatus(ctx context.Context, id uuid.UUID, status entities.PaymentStatus) error
|
|
|
|
|
UpdateStatus(ctx context.Context, id uuid.UUID, status entities.OrderStatus) error
|
|
|
|
|
GetNextOrderNumber(ctx context.Context, organizationID, outletID uuid.UUID) (string, error)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type OrderRepositoryImpl struct {
|
|
|
|
|
db *gorm.DB
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewOrderRepositoryImpl(db *gorm.DB) *OrderRepositoryImpl {
|
|
|
|
|
return &OrderRepositoryImpl{
|
|
|
|
|
db: db,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *OrderRepositoryImpl) Create(ctx context.Context, order *entities.Order) error {
|
|
|
|
|
return r.db.WithContext(ctx).Create(order).Error
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *OrderRepositoryImpl) GetByID(ctx context.Context, id uuid.UUID) (*entities.Order, error) {
|
|
|
|
|
var order entities.Order
|
|
|
|
|
err := r.db.WithContext(ctx).First(&order, "id = ?", id).Error
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
return &order, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *OrderRepositoryImpl) GetWithRelations(ctx context.Context, id uuid.UUID) (*entities.Order, error) {
|
|
|
|
|
var order entities.Order
|
|
|
|
|
err := r.db.WithContext(ctx).
|
|
|
|
|
Preload("Organization").
|
|
|
|
|
Preload("Outlet").
|
|
|
|
|
Preload("User").
|
|
|
|
|
Preload("OrderItems").
|
|
|
|
|
Preload("OrderItems.Product").
|
|
|
|
|
Preload("OrderItems.ProductVariant").
|
|
|
|
|
Preload("Payments").
|
|
|
|
|
Preload("Payments.PaymentMethod").
|
|
|
|
|
Preload("Payments.PaymentOrderItems").
|
|
|
|
|
First(&order, "id = ?", id).Error
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
return &order, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *OrderRepositoryImpl) Update(ctx context.Context, order *entities.Order) error {
|
|
|
|
|
return r.db.WithContext(ctx).Save(order).Error
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-13 23:36:31 +07:00
|
|
|
func (r *OrderRepositoryImpl) UpdateStatusSuccess(
|
|
|
|
|
ctx context.Context,
|
|
|
|
|
id uuid.UUID,
|
|
|
|
|
orderStatus entities.OrderStatus,
|
|
|
|
|
paymentStatus entities.PaymentStatus,
|
|
|
|
|
) error {
|
|
|
|
|
return r.db.WithContext(ctx).
|
|
|
|
|
Model(&entities.Order{}).
|
|
|
|
|
Where("id = ?", id).
|
|
|
|
|
Updates(map[string]interface{}{
|
|
|
|
|
"order_status": orderStatus,
|
|
|
|
|
"payment_status": paymentStatus,
|
|
|
|
|
}).Error
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-18 20:10:29 +07:00
|
|
|
func (r *OrderRepositoryImpl) Delete(ctx context.Context, id uuid.UUID) error {
|
|
|
|
|
return r.db.WithContext(ctx).Delete(&entities.Order{}, "id = ?", id).Error
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *OrderRepositoryImpl) List(ctx context.Context, filters map[string]interface{}, limit, offset int) ([]*entities.Order, int64, error) {
|
|
|
|
|
var orders []*entities.Order
|
|
|
|
|
var total int64
|
|
|
|
|
|
|
|
|
|
query := r.db.WithContext(ctx).Model(&entities.Order{}).
|
|
|
|
|
Preload("Organization").
|
|
|
|
|
Preload("Outlet").
|
|
|
|
|
Preload("User").
|
|
|
|
|
Preload("OrderItems").
|
|
|
|
|
Preload("OrderItems.Product").
|
|
|
|
|
Preload("OrderItems.ProductVariant").
|
|
|
|
|
Preload("Payments").
|
2025-08-07 22:45:02 +07:00
|
|
|
Preload("Payments.PaymentMethod").
|
|
|
|
|
Preload("Payments.PaymentOrderItems")
|
2025-07-18 20:10:29 +07:00
|
|
|
|
|
|
|
|
for key, value := range filters {
|
|
|
|
|
switch key {
|
|
|
|
|
case "search":
|
|
|
|
|
searchValue := "%" + value.(string) + "%"
|
|
|
|
|
query = query.Where("order_number ILIKE ?", searchValue)
|
|
|
|
|
case "date_from":
|
|
|
|
|
query = query.Where("created_at >= ?", value)
|
|
|
|
|
case "date_to":
|
|
|
|
|
query = query.Where("created_at <= ?", value)
|
|
|
|
|
default:
|
|
|
|
|
query = query.Where(key+" = ?", value)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err := query.Count(&total).Error; err != nil {
|
|
|
|
|
return nil, 0, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err := query.Limit(limit).Offset(offset).Order("created_at DESC").Find(&orders).Error
|
|
|
|
|
return orders, total, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *OrderRepositoryImpl) GetByOrderNumber(ctx context.Context, orderNumber string) (*entities.Order, error) {
|
|
|
|
|
var order entities.Order
|
|
|
|
|
err := r.db.WithContext(ctx).First(&order, "order_number = ?", orderNumber).Error
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
return &order, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *OrderRepositoryImpl) ExistsByOrderNumber(ctx context.Context, orderNumber string) (bool, error) {
|
|
|
|
|
var count int64
|
|
|
|
|
err := r.db.WithContext(ctx).Model(&entities.Order{}).Where("order_number = ?", orderNumber).Count(&count).Error
|
|
|
|
|
return count > 0, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *OrderRepositoryImpl) VoidOrder(ctx context.Context, id uuid.UUID, reason string, voidedBy uuid.UUID) error {
|
|
|
|
|
now := time.Now()
|
|
|
|
|
return r.db.WithContext(ctx).Model(&entities.Order{}).
|
|
|
|
|
Where("id = ?", id).
|
|
|
|
|
Updates(map[string]interface{}{
|
|
|
|
|
"is_void": true,
|
|
|
|
|
"void_reason": reason,
|
|
|
|
|
"voided_at": now,
|
|
|
|
|
"voided_by": voidedBy,
|
|
|
|
|
}).Error
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *OrderRepositoryImpl) VoidOrderWithStatus(ctx context.Context, id uuid.UUID, status entities.OrderStatus, reason string, voidedBy uuid.UUID) error {
|
|
|
|
|
now := time.Now()
|
|
|
|
|
return r.db.WithContext(ctx).Model(&entities.Order{}).
|
|
|
|
|
Where("id = ?", id).
|
|
|
|
|
Updates(map[string]interface{}{
|
|
|
|
|
"status": status,
|
|
|
|
|
"is_void": true,
|
|
|
|
|
"void_reason": reason,
|
|
|
|
|
"voided_at": now,
|
|
|
|
|
"voided_by": voidedBy,
|
|
|
|
|
}).Error
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *OrderRepositoryImpl) RefundOrder(ctx context.Context, id uuid.UUID, reason string, refundedBy uuid.UUID) error {
|
|
|
|
|
now := time.Now()
|
|
|
|
|
return r.db.WithContext(ctx).Model(&entities.Order{}).
|
|
|
|
|
Where("id = ?", id).
|
|
|
|
|
Updates(map[string]interface{}{
|
|
|
|
|
"is_refund": true,
|
|
|
|
|
"refund_reason": reason,
|
|
|
|
|
"refunded_at": now,
|
|
|
|
|
"refunded_by": refundedBy,
|
|
|
|
|
}).Error
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *OrderRepositoryImpl) UpdatePaymentStatus(ctx context.Context, id uuid.UUID, status entities.PaymentStatus) error {
|
|
|
|
|
return r.db.WithContext(ctx).Model(&entities.Order{}).
|
|
|
|
|
Where("id = ?", id).
|
|
|
|
|
Update("payment_status", status).Error
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *OrderRepositoryImpl) UpdateStatus(ctx context.Context, id uuid.UUID, status entities.OrderStatus) error {
|
|
|
|
|
return r.db.WithContext(ctx).Model(&entities.Order{}).
|
|
|
|
|
Where("id = ?", id).
|
|
|
|
|
Update("status", status).Error
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *OrderRepositoryImpl) GetNextOrderNumber(ctx context.Context, organizationID, outletID uuid.UUID) (string, error) {
|
|
|
|
|
now := time.Now()
|
|
|
|
|
year := now.Year()
|
|
|
|
|
month := int(now.Month())
|
|
|
|
|
|
|
|
|
|
// Use a transaction to ensure atomic sequence increment
|
|
|
|
|
tx := r.db.WithContext(ctx).Begin()
|
|
|
|
|
if tx.Error != nil {
|
|
|
|
|
return "", tx.Error
|
|
|
|
|
}
|
|
|
|
|
defer func() {
|
|
|
|
|
if r := recover(); r != nil {
|
|
|
|
|
tx.Rollback()
|
|
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
// Get or create sequence record
|
|
|
|
|
var sequence entities.OrderSequence
|
|
|
|
|
err := tx.Where("organization_id = ? AND outlet_id = ? AND year = ? AND month = ?",
|
|
|
|
|
organizationID, outletID, year, month).
|
|
|
|
|
First(&sequence).Error
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
if err == gorm.ErrRecordNotFound {
|
|
|
|
|
// Create new sequence record
|
|
|
|
|
sequence = entities.OrderSequence{
|
|
|
|
|
OrganizationID: organizationID,
|
|
|
|
|
OutletID: outletID,
|
|
|
|
|
Year: year,
|
|
|
|
|
Month: month,
|
|
|
|
|
SequenceNumber: 0,
|
|
|
|
|
}
|
|
|
|
|
if err := tx.Create(&sequence).Error; err != nil {
|
|
|
|
|
tx.Rollback()
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
tx.Rollback()
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Increment sequence number
|
|
|
|
|
sequence.SequenceNumber++
|
|
|
|
|
if err := tx.Save(&sequence).Error; err != nil {
|
|
|
|
|
tx.Rollback()
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Commit transaction
|
|
|
|
|
if err := tx.Commit().Error; err != nil {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
orderNumber := fmt.Sprintf("ORD/%04d%02d/%06d", year, month, sequence.SequenceNumber)
|
|
|
|
|
return orderNumber, nil
|
|
|
|
|
}
|