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" "enaklo-pos-be/internal/services/v2/inprogress_order" time2 "time" "github.com/pkg/errors" "gorm.io/gorm" ) type InProgressOrderRepository interface { FindByID(ctx mycontext.Context, id int64) (*entity.Order, error) CreateOrder(ctx mycontext.Context, order *entity.Order, tx *gorm.DB) (*entity.Order, error) CreateOrderItems(ctx mycontext.Context, orderID int64, items []entity.OrderItem, tx *gorm.DB) error GetListByPartnerID(ctx mycontext.Context, partnerID int64, limit, offset int, status string) ([]*entity.Order, error) FindByIDAndPartnerID(ctx mycontext.Context, id int64, partnerID int64) (*entity.Order, error) FindByIDWithTx(ctx mycontext.Context, id int64, tx *gorm.DB) (*entity.Order, error) } type inprogressOrderRepository struct { db *gorm.DB } func NewInProgressOrderRepository(db *gorm.DB) inprogress_order.OrderRepository { return &inprogressOrderRepository{db: db} } func (r *inprogressOrderRepository) 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) return order, nil } func (r *inprogressOrderRepository) FindByIDWithTx(ctx mycontext.Context, id int64, tx *gorm.DB) (*entity.Order, error) { var orderDB models.OrderDB if err := tx.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) return order, nil } func (r *inprogressOrderRepository) CreateOrder(ctx mycontext.Context, order *entity.Order, tx *gorm.DB) (*entity.Order, error) { orderDB := r.toOrderDBModel(order) // Use provided transaction or create new one var dbTx *gorm.DB if tx != nil { dbTx = tx } else { dbTx = r.db.Begin() if dbTx.Error != nil { return nil, errors.Wrap(dbTx.Error, "failed to begin transaction") } defer func() { if r := recover(); r != nil { dbTx.Rollback() } }() } if err := dbTx.Create(&orderDB).Error; err != nil { if tx == nil { dbTx.Rollback() } return nil, errors.Wrap(err, "failed to insert order") } order.ID = orderDB.ID // Only commit if we created the transaction if tx == nil { if err := dbTx.Commit().Error; err != nil { return nil, errors.Wrap(err, "failed to commit transaction") } } return order, nil } func (r *inprogressOrderRepository) CreateOrderItems(ctx mycontext.Context, orderID int64, items []entity.OrderItem, tx *gorm.DB) error { if len(items) == 0 { return nil } itemsDB := make([]models.OrderItemDB, len(items)) for i, item := range items { itemDB := r.toOrderItemDBModel(&item) itemDB.OrderID = orderID itemsDB[i] = itemDB } if err := tx.Create(&itemsDB).Error; err != nil { return errors.Wrap(err, "failed to bulk insert order items") } for i := range items { items[i].ID = itemsDB[i].ID } return nil } func (r *inprogressOrderRepository) GetListByPartnerID(ctx mycontext.Context, partnerID int64, limit, offset int, status string) ([]*entity.Order, error) { var ordersDB []models.OrderDB query := r.db.Where("partner_id = ?", partnerID) if status != "" { query = query.Where("status = ?", status) } query = query.Order("created_at DESC") if limit > 0 { query = query.Limit(limit) } if offset > 0 { query = query.Offset(offset) } if err := query.Find(&ordersDB).Error; err != nil { return nil, errors.Wrap(err, "failed to find orders by partner ID") } orders := make([]*entity.Order, 0, len(ordersDB)) for _, orderDB := range ordersDB { order := r.toDomainOrderModel(&orderDB) var orderItems []models.OrderItemDB if err := r.db.Where("order_id = ?", orderDB.ID).Find(&orderItems).Error; err != nil { return nil, errors.Wrap(err, "failed to find order items") } order.OrderItems = make([]entity.OrderItem, 0, len(orderItems)) for _, itemDB := range orderItems { item := r.toDomainOrderItemModel(&itemDB) orderItem := entity.OrderItem{ ID: item.ID, ItemID: item.ItemID, Quantity: item.Quantity, ItemName: item.ItemName, } if itemDB.ItemID > 0 { var product models.ProductDB err := r.db.First(&product, itemDB.ItemID).Error if err == nil { productDomain := r.toDomainProductModel(&product) orderItem.Product = productDomain } } order.OrderItems = append(order.OrderItems, orderItem) } orders = append(orders, order) } return orders, nil } func (r *inprogressOrderRepository) FindByIDAndPartnerID(ctx mycontext.Context, id int64, partnerID int64) (*entity.Order, error) { var orderDB models.OrderDB if err := r.db.Preload("OrderItems").Where("id = ? AND partner_id = ?", id, partnerID).First(&orderDB).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 *inprogressOrderRepository) toOrderDBModel(order *entity.Order) models.OrderDB { now := time2.Now() return models.OrderDB{ ID: order.ID, PartnerID: order.PartnerID, CustomerID: order.CustomerID, CustomerName: order.CustomerName, PaymentType: order.PaymentType, PaymentProvider: order.PaymentProvider, CreatedBy: order.CreatedBy, CreatedAt: now, UpdatedAt: now, TableNumber: order.TableNumber, OrderType: order.OrderType, Status: order.Status, Amount: order.Amount, Total: order.Total, Tax: order.Tax, Source: order.Source, } } func (r *inprogressOrderRepository) toDomainOrderModel(dbModel *models.OrderDB) *entity.Order { orderItems := make([]entity.OrderItem, 0, len(dbModel.OrderItems)) for _, itemDB := range dbModel.OrderItems { orderItems = append(orderItems, entity.OrderItem{ ItemID: itemDB.ItemID, ItemType: itemDB.ItemType, ItemName: itemDB.ItemName, Price: itemDB.Price, Quantity: itemDB.Quantity, Status: itemDB.Status, CreatedBy: itemDB.CreatedBy, CreatedAt: itemDB.CreatedAt, Notes: itemDB.Notes, }) } return &entity.Order{ ID: dbModel.ID, PartnerID: dbModel.PartnerID, CustomerID: dbModel.CustomerID, InquiryID: dbModel.InquiryID, Status: dbModel.Status, Amount: dbModel.Amount, Tax: dbModel.Tax, Total: dbModel.Total, PaymentType: dbModel.PaymentType, Source: dbModel.Source, CreatedBy: dbModel.CreatedBy, CreatedAt: dbModel.CreatedAt, UpdatedAt: dbModel.UpdatedAt, OrderItems: orderItems, CustomerName: dbModel.CustomerName, TableNumber: dbModel.TableNumber, OrderType: dbModel.OrderType, PaymentProvider: dbModel.PaymentProvider, } } func (r *inprogressOrderRepository) toOrderItemDBModel(item *entity.OrderItem) models.OrderItemDB { return models.OrderItemDB{ ID: item.ID, OrderID: item.OrderID, ItemID: item.ItemID, ItemType: item.ItemType, ItemName: item.ItemName, Price: item.Price, Quantity: item.Quantity, Status: item.Status, CreatedBy: item.CreatedBy, CreatedAt: item.CreatedAt, Notes: item.Notes, } } func (r *inprogressOrderRepository) 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, Status: dbModel.Status, CreatedBy: dbModel.CreatedBy, CreatedAt: dbModel.CreatedAt, ItemName: dbModel.ItemName, Notes: dbModel.Notes, Product: &entity.Product{ ID: dbModel.ItemID, Name: dbModel.ItemName, }, } } 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, } } func (r *inprogressOrderRepository) UpdateOrderTotalsWithTx(ctx mycontext.Context, trx *gorm.DB, orderID int64, amount, tax, total float64) error { now := time2.Now() result := trx.Model(&models.OrderDB{}). Where("id = ?", orderID). Updates(map[string]interface{}{ "amount": amount, "tax": tax, "total": total, "updated_at": now, }) if result.Error != nil { return errors.Wrap(result.Error, "failed to update order totals") } if result.RowsAffected == 0 { logger.ContextLogger(ctx).Warn("no order updated") } return nil }