package repository import ( "enaklo-pos-be/internal/common/logger" "enaklo-pos-be/internal/common/mycontext" "enaklo-pos-be/internal/entity" "enaklo-pos-be/internal/repository/models" "github.com/pkg/errors" "go.uber.org/zap" "gorm.io/gorm" "time" ) type OrderRepository interface { Create(ctx mycontext.Context, order *entity.Order) (*entity.Order, error) FindByID(ctx mycontext.Context, id int64) (*entity.Order, error) CreateInquiry(ctx mycontext.Context, inquiry *entity.OrderInquiry) (*entity.OrderInquiry, error) FindInquiryByID(ctx mycontext.Context, id string) (*entity.OrderInquiry, error) UpdateInquiryStatus(ctx mycontext.Context, id string, status string) error } type orderRepository struct { db *gorm.DB } func NeworderRepository(db *gorm.DB) *orderRepository { return &orderRepository{db: db} } func (r *orderRepository) Create(ctx mycontext.Context, order *entity.Order) (*entity.Order, error) { orderDB := r.toOrderDBModel(order) tx := r.db.Begin() if tx.Error != nil { return nil, errors.Wrap(tx.Error, "failed to begin transaction") } defer func() { if r := recover(); r != nil { tx.Rollback() } }() if err := tx.Create(&orderDB).Error; err != nil { tx.Rollback() return nil, errors.Wrap(err, "failed to insert order") } order.ID = orderDB.ID for i := range order.OrderItems { item := &order.OrderItems[i] item.OrderID = orderDB.ID itemDB := r.toOrderItemDBModel(item) if err := tx.Create(&itemDB).Error; err != nil { tx.Rollback() return nil, errors.Wrap(err, "failed to insert order item") } item.ID = itemDB.ID } if err := tx.Commit().Error; err != nil { return nil, errors.Wrap(err, "failed to commit transaction") } return order, nil } func (r *orderRepository) FindByID(ctx mycontext.Context, id int64) (*entity.Order, error) { var orderDB models.OrderDB if err := r.db.Preload("OrderItems").First(&orderDB, id).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, errors.New("order not found") } return nil, errors.Wrap(err, "failed to find order") } order := r.toDomainOrderModel(&orderDB) for _, itemDB := range orderDB.OrderItems { item := r.toDomainOrderItemModel(&itemDB) order.OrderItems = append(order.OrderItems, *item) } return order, nil } func (r *orderRepository) CreateInquiry(ctx mycontext.Context, inquiry *entity.OrderInquiry) (*entity.OrderInquiry, error) { inquiryDB := r.toOrderInquiryDBModel(inquiry) inquiryItems := make([]models.InquiryItemDB, 0, len(inquiry.OrderItems)) for _, item := range inquiry.OrderItems { inquiryItems = append(inquiryItems, models.InquiryItemDB{ InquiryID: inquiryDB.ID, ItemID: item.ItemID, ItemType: item.ItemType, Price: item.Price, Quantity: item.Quantity, CreatedBy: item.CreatedBy, CreatedAt: time.Now(), }) } tx := r.db.Begin() if tx.Error != nil { return nil, errors.Wrap(tx.Error, "failed to begin transaction") } defer func() { if r := recover(); r != nil { tx.Rollback() } }() if err := tx.Create(&inquiryDB).Error; err != nil { tx.Rollback() return nil, errors.Wrap(err, "failed to insert order inquiry") } if len(inquiryItems) > 0 { if err := tx.CreateInBatches(inquiryItems, 100).Error; err != nil { tx.Rollback() return nil, errors.Wrap(err, "failed to insert inquiry items") } } if err := tx.Commit().Error; err != nil { return nil, errors.Wrap(err, "failed to commit transaction") } return inquiry, nil } func (r *orderRepository) FindInquiryByID(ctx mycontext.Context, id string) (*entity.OrderInquiry, error) { var inquiryDB models.OrderInquiryDB if err := r.db.Preload("InquiryItems").First(&inquiryDB, "id = ?", id).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, errors.New("inquiry not found") } return nil, errors.Wrap(err, "failed to find inquiry") } inquiry := r.toDomainOrderInquiryModel(&inquiryDB) orderItems := make([]entity.OrderItem, 0, len(inquiryDB.InquiryItems)) for _, itemDB := range inquiryDB.InquiryItems { orderItems = append(orderItems, entity.OrderItem{ ItemID: itemDB.ItemID, ItemType: itemDB.ItemType, Price: itemDB.Price, Quantity: itemDB.Quantity, CreatedBy: itemDB.CreatedBy, CreatedAt: itemDB.CreatedAt, }) } inquiry.OrderItems = orderItems return inquiry, nil } func (r *orderRepository) UpdateInquiryStatus(ctx mycontext.Context, id string, status string) error { now := time.Now() result := r.db.Model(&models.OrderInquiryDB{}). Where("id = ?", id). Updates(map[string]interface{}{ "status": status, "updated_at": now, }) if result.Error != nil { return errors.Wrap(result.Error, "failed to update inquiry status") } if result.RowsAffected == 0 { logger.ContextLogger(ctx).Warn("no inquiry updated", zap.String("id", id)) } return nil } func (r *orderRepository) toOrderDBModel(order *entity.Order) models.OrderDB { return models.OrderDB{ ID: order.ID, PartnerID: order.PartnerID, CustomerID: order.CustomerID, InquiryID: order.InquiryID, Status: order.Status, Amount: order.Amount, Fee: order.Fee, Total: order.Total, PaymentType: order.PaymentType, Source: order.Source, CreatedBy: order.CreatedBy, CreatedAt: order.CreatedAt, UpdatedAt: order.UpdatedAt, } } func (r *orderRepository) toDomainOrderModel(dbModel *models.OrderDB) *entity.Order { return &entity.Order{ ID: dbModel.ID, PartnerID: dbModel.PartnerID, CustomerID: dbModel.CustomerID, InquiryID: dbModel.InquiryID, Status: dbModel.Status, Amount: dbModel.Amount, Fee: dbModel.Fee, Total: dbModel.Total, PaymentType: dbModel.PaymentType, Source: dbModel.Source, CreatedBy: dbModel.CreatedBy, CreatedAt: dbModel.CreatedAt, UpdatedAt: dbModel.UpdatedAt, OrderItems: []entity.OrderItem{}, } } func (r *orderRepository) toOrderItemDBModel(item *entity.OrderItem) models.OrderItemDB { return models.OrderItemDB{ ID: item.ID, OrderID: item.OrderID, ItemID: item.ItemID, ItemType: item.ItemType, Price: item.Price, Quantity: item.Quantity, CreatedBy: item.CreatedBy, CreatedAt: item.CreatedAt, } } func (r *orderRepository) toDomainOrderItemModel(dbModel *models.OrderItemDB) *entity.OrderItem { return &entity.OrderItem{ ID: dbModel.ID, OrderID: dbModel.OrderID, ItemID: dbModel.ItemID, ItemType: dbModel.ItemType, Price: dbModel.Price, Quantity: dbModel.Quantity, CreatedBy: dbModel.CreatedBy, CreatedAt: dbModel.CreatedAt, } } func (r *orderRepository) toOrderInquiryDBModel(inquiry *entity.OrderInquiry) models.OrderInquiryDB { return models.OrderInquiryDB{ ID: inquiry.ID, PartnerID: inquiry.PartnerID, CustomerID: &inquiry.CustomerID, Status: inquiry.Status, Amount: inquiry.Amount, Fee: inquiry.Fee, Total: inquiry.Total, PaymentType: inquiry.PaymentType, Source: inquiry.Source, CreatedBy: inquiry.CreatedBy, CreatedAt: inquiry.CreatedAt, UpdatedAt: inquiry.UpdatedAt, ExpiresAt: inquiry.ExpiresAt, CustomerName: inquiry.CustomerName, CustomerPhoneNumber: inquiry.CustomerPhoneNumber, CustomerEmail: inquiry.CustomerEmail, } } func (r *orderRepository) toDomainOrderInquiryModel(dbModel *models.OrderInquiryDB) *entity.OrderInquiry { inquiry := &entity.OrderInquiry{ ID: dbModel.ID, PartnerID: dbModel.PartnerID, Status: dbModel.Status, Amount: dbModel.Amount, Fee: dbModel.Fee, Total: dbModel.Total, PaymentType: dbModel.PaymentType, Source: dbModel.Source, CreatedBy: dbModel.CreatedBy, CreatedAt: dbModel.CreatedAt, ExpiresAt: dbModel.ExpiresAt, OrderItems: []entity.OrderItem{}, } if dbModel.CustomerID != nil { inquiry.CustomerID = *dbModel.CustomerID } inquiry.UpdatedAt = dbModel.UpdatedAt return inquiry }