package repository import ( "enaklo-pos-be/internal/common/mycontext" "enaklo-pos-be/internal/constants" "enaklo-pos-be/internal/entity" "enaklo-pos-be/internal/repository/models" "github.com/pkg/errors" "gorm.io/gorm" time2 "time" ) type InProgressOrderRepository interface { CreateOrUpdate(ctx mycontext.Context, order *entity.InProgressOrder) (*entity.InProgressOrder, error) GetListByPartnerID(ctx mycontext.Context, partnerID int64, limit, offset int) ([]*entity.InProgressOrder, error) } type inprogressOrderRepository struct { db *gorm.DB } func NewInProgressOrderRepository(db *gorm.DB) *inprogressOrderRepository { return &inprogressOrderRepository{db: db} } func (r *inprogressOrderRepository) CreateOrUpdate(ctx mycontext.Context, order *entity.InProgressOrder) (*entity.InProgressOrder, error) { isUpdate := order.ID != "" 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() } }() orderDB := r.toInProgressOrderDBModel(order) if isUpdate { var existingOrder models.InProgressOrderDB if err := tx.First(&existingOrder, order.ID).Error; err != nil { tx.Rollback() return nil, errors.Wrap(err, "order not found for update") } if err := tx.Model(&orderDB).Updates(orderDB).Error; err != nil { tx.Rollback() return nil, errors.Wrap(err, "failed to update order") } if err := tx.Where("in_progress_order_id = ?", order.ID).Delete(&models.InProgressOrderItemDB{}).Error; err != nil { tx.Rollback() return nil, errors.Wrap(err, "failed to delete existing order items") } } else { if err := tx.Create(&orderDB).Error; err != nil { tx.Rollback() return nil, errors.Wrap(err, "failed to insert order") } order.ID = orderDB.ID } var itemIDs []int64 for i := range order.OrderItems { itemIDs = append(itemIDs, order.OrderItems[i].ItemID) } var products []models.ProductDB if len(itemIDs) > 0 { if err := tx.Where("id IN ?", itemIDs).Find(&products).Error; err != nil { tx.Rollback() return nil, errors.Wrap(err, "failed to fetch products") } } productMap := make(map[int64]models.ProductDB) for _, product := range products { productMap[product.ID] = product } for i := range order.OrderItems { item := &order.OrderItems[i] itemDB := r.toOrderItemDBModel(item, orderDB.ID) 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 product, exists := productMap[item.ItemID]; exists { item.Product = r.toDomainProductModel(&product) } } if err := tx.Commit().Error; err != nil { return nil, errors.Wrap(err, "failed to commit transaction") } return order, nil } func (r *inprogressOrderRepository) GetListByPartnerID(ctx mycontext.Context, partnerID int64, limit, offset int) ([]*entity.InProgressOrder, error) { var ordersDB []models.InProgressOrderDB query := r.db.Where("partner_id = ?", partnerID).Order("created_at DESC") if limit > 0 { query = query.Limit(limit) } if offset > 0 { query = query.Offset(offset) } if err := query.Preload("OrderItems.Product").Find(&ordersDB).Error; err != nil { return nil, errors.Wrap(err, "failed to find orders by partner ID") } orders := make([]*entity.InProgressOrder, 0, len(ordersDB)) for _, orderDB := range ordersDB { order := r.toDomainOrderModel(&orderDB) order.OrderItems = make([]entity.InProgressOrderItem, 0, len(orderDB.OrderItems)) for _, itemDB := range orderDB.OrderItems { item := r.toDomainOrderItemModel(&itemDB) orderItem := entity.InProgressOrderItem{ ID: item.ID, ItemID: item.ItemID, Quantity: item.Quantity, } if itemDB.Product.ID > 0 { productDomain := r.toDomainProductModel(&itemDB.Product) orderItem.Product = productDomain } order.OrderItems = append(order.OrderItems, orderItem) } orders = append(orders, order) } return orders, nil } func (r *inprogressOrderRepository) toInProgressOrderDBModel(order *entity.InProgressOrder) models.InProgressOrderDB { now := time2.Now() return models.InProgressOrderDB{ ID: constants.GenerateUUID(), PartnerID: order.PartnerID, CustomerID: order.CustomerID, CustomerName: order.CustomerName, PaymentType: order.PaymentType, CreatedBy: order.CreatedBy, CreatedAt: now, UpdatedAt: now, TableNumber: order.TableNumber, OrderType: order.OrderType, } } func (r *inprogressOrderRepository) toDomainOrderModel(dbModel *models.InProgressOrderDB) *entity.InProgressOrder { return &entity.InProgressOrder{ ID: dbModel.ID, PartnerID: dbModel.PartnerID, CustomerID: dbModel.CustomerID, CustomerName: dbModel.CustomerName, PaymentType: dbModel.PaymentType, CreatedBy: dbModel.CreatedBy, OrderItems: []entity.InProgressOrderItem{}, TableNumber: dbModel.TableNumber, OrderType: dbModel.OrderType, CreatedAt: dbModel.CreatedAt, UpdatedAt: dbModel.UpdatedAt, } } func (r *inprogressOrderRepository) toOrderItemDBModel(item *entity.InProgressOrderItem, inprogressOrderID string) models.InProgressOrderItemDB { return models.InProgressOrderItemDB{ ID: item.ID, InProgressOrderIO: inprogressOrderID, ItemID: item.ItemID, Quantity: item.Quantity, } } func (r *inprogressOrderRepository) toDomainOrderItemModel(dbModel *models.InProgressOrderItemDB) *entity.OrderItem { return &entity.OrderItem{ ID: dbModel.ID, ItemID: dbModel.ItemID, Quantity: dbModel.Quantity, CreatedBy: dbModel.CreatedBy, CreatedAt: dbModel.CreatedAt, } } func (r *inprogressOrderRepository) 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, PaymentProvider: inquiry.PaymentProvider, OrderType: inquiry.OrderType, TableNumber: inquiry.TableNumber, } } func (r *inprogressOrderRepository) 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 } func (r *inprogressOrderRepository) toDomainProductModel(productDB *models.ProductDB) *entity.Product { if productDB == nil { return nil } return &entity.Product{ ID: productDB.ID, Name: productDB.Name, Description: productDB.Description, Price: productDB.Price, CreatedAt: productDB.CreatedAt, UpdatedAt: productDB.UpdatedAt, Type: productDB.Type, Image: productDB.Image, } }