package repositories import ( "context" "fmt" "strings" entity "gitlab.com/mbugroup/lti-api.git/internal/entities" validation "gitlab.com/mbugroup/lti-api.git/internal/modules/repports/validations" "gitlab.com/mbugroup/lti-api.git/internal/utils" "gorm.io/gorm" ) type PurchaseSupplierRepository interface { GetSuppliersWithPurchases(ctx context.Context, offset, limit int, filters *validation.PurchaseSupplierQuery) ([]entity.Supplier, int64, error) GetItemsBySuppliers(ctx context.Context, supplierIDs []uint, filters *validation.PurchaseSupplierQuery) ([]entity.PurchaseItem, error) } type purchaseSupplierRepositoryImpl struct { db *gorm.DB } func NewPurchaseSupplierRepository(db *gorm.DB) PurchaseSupplierRepository { return &purchaseSupplierRepositoryImpl{db: db} } func (r *purchaseSupplierRepositoryImpl) latestPurchaseApproval(ctx context.Context) *gorm.DB { return r.db.WithContext(ctx). Table("approvals AS a"). Select("a.approvable_id, a.step_number, a.action"). Joins(` JOIN ( SELECT approvable_id, MAX(action_at) AS latest_action_at FROM approvals WHERE approvable_type = ? GROUP BY approvable_id ) AS la ON la.approvable_id = a.approvable_id AND la.latest_action_at = a.action_at`, string(utils.ApprovalWorkflowPurchase), ) } func (r *purchaseSupplierRepositoryImpl) baseSupplierQuery(ctx context.Context, filters *validation.PurchaseSupplierQuery) *gorm.DB { dateColumn := "purchase_items.received_date" switch strings.ToLower(strings.TrimSpace(filters.FilterBy)) { case "po_date": dateColumn = "purchases.po_date" case "receive_date", "": dateColumn = "purchase_items.received_date" } latestApproval := r.latestPurchaseApproval(ctx) db := r.db.WithContext(ctx). Model(&entity.Supplier{}). Joins("JOIN purchases ON purchases.supplier_id = suppliers.id"). Joins("JOIN purchase_items ON purchase_items.purchase_id = purchases.id"). Joins("JOIN (?) AS la ON la.approvable_id = purchases.id", latestApproval). Where("la.step_number >= ?", uint16(utils.PurchaseStepReceiving)). Where("(la.action IS NULL OR la.action != ?)", string(entity.ApprovalActionRejected)). Where("purchase_items.received_date IS NOT NULL") if filters.SupplierId > 0 { db = db.Where("suppliers.id = ?", filters.SupplierId) } if filters.ProductId > 0 { db = db.Where("purchase_items.product_id = ?", filters.ProductId) } if filters.ProductCategoryId > 0 { db = db. Joins("JOIN products ON products.id = purchase_items.product_id"). Where("products.product_category_id = ?", filters.ProductCategoryId) } if filters.AreaId > 0 || filters.AllowedAreaIDs != nil { db = db.Joins("JOIN warehouses ON warehouses.id = purchase_items.warehouse_id") if filters.AreaId > 0 { db = db.Where("warehouses.area_id = ?", filters.AreaId) } if filters.AllowedAreaIDs != nil { if len(filters.AllowedAreaIDs) == 0 { db = db.Where("1 = 0") } else { db = db.Where("warehouses.area_id IN ?", filters.AllowedAreaIDs) } } } if filters.StartDate != "" { if dateFrom, err := utils.ParseDateString(filters.StartDate); err == nil { db = db.Where(fmt.Sprintf("DATE(%s) >= ?", dateColumn), dateFrom) } } if filters.EndDate != "" { if dateTo, err := utils.ParseDateString(filters.EndDate); err == nil { db = db.Where(fmt.Sprintf("DATE(%s) <= ?", dateColumn), dateTo) } } return db } func (r *purchaseSupplierRepositoryImpl) GetSuppliersWithPurchases(ctx context.Context, offset, limit int, filters *validation.PurchaseSupplierQuery) ([]entity.Supplier, int64, error) { query := r.baseSupplierQuery(ctx, filters) var totalSuppliers int64 if err := query. Distinct("suppliers.id"). Count(&totalSuppliers).Error; err != nil { return nil, 0, err } if totalSuppliers == 0 { return []entity.Supplier{}, 0, nil } if offset < 0 { offset = 0 } var supplierIDs []uint if err := query. Select("suppliers.id"). Order("suppliers.id ASC"). Offset(offset). Limit(limit). Pluck("suppliers.id", &supplierIDs).Error; err != nil { return nil, 0, err } if len(supplierIDs) == 0 { return []entity.Supplier{}, totalSuppliers, nil } var suppliers []entity.Supplier if err := r.db.WithContext(ctx). Where("id IN ?", supplierIDs). Find(&suppliers).Error; err != nil { return nil, 0, err } return suppliers, totalSuppliers, nil } func (r *purchaseSupplierRepositoryImpl) GetItemsBySuppliers(ctx context.Context, supplierIDs []uint, filters *validation.PurchaseSupplierQuery) ([]entity.PurchaseItem, error) { if len(supplierIDs) == 0 { return []entity.PurchaseItem{}, nil } // Tentukan kolom tanggal yang akan dipakai untuk filter & sort dateColumn := "purchase_items.received_date" switch strings.ToLower(strings.TrimSpace(filters.FilterBy)) { case "po_date": dateColumn = "purchases.po_date" case "receive_date", "": dateColumn = "purchase_items.received_date" } orderDirection := "ASC" switch strings.ToUpper(strings.TrimSpace(filters.SortBy)) { case "DESC": orderDirection = "DESC" case "ASC", "": orderDirection = "ASC" } db := r.db.WithContext(ctx). Model(&entity.PurchaseItem{}). Preload("Purchase"). Preload("Purchase.Supplier"). Preload("Product"). Preload("Product.ProductCategory"). Preload("Warehouse"). Preload("Warehouse.Area"). Preload("Warehouse.Location"). Preload("Warehouse.Kandang"). Preload("ExpenseNonstock"). Preload("ExpenseNonstock.Expense"). Preload("ExpenseNonstock.Expense.Supplier"). Joins("JOIN purchases ON purchases.id = purchase_items.purchase_id"). Joins("JOIN (?) AS la ON la.approvable_id = purchases.id", r.latestPurchaseApproval(ctx)). Where("purchases.supplier_id IN ?", supplierIDs). Where("la.step_number >= ?", uint16(utils.PurchaseStepReceiving)). Where("(la.action IS NULL OR la.action != ?)", string(entity.ApprovalActionRejected)). Where("purchase_items.received_date IS NOT NULL") if filters.ProductId > 0 { db = db.Where("purchase_items.product_id = ?", filters.ProductId) } if filters.ProductCategoryId > 0 { db = db. Joins("JOIN products ON products.id = purchase_items.product_id"). Where("products.product_category_id = ?", filters.ProductCategoryId) } if filters.AreaId > 0 || filters.AllowedAreaIDs != nil { db = db.Joins("JOIN warehouses ON warehouses.id = purchase_items.warehouse_id") if filters.AreaId > 0 { db = db.Where("warehouses.area_id = ?", filters.AreaId) } if filters.AllowedAreaIDs != nil { if len(filters.AllowedAreaIDs) == 0 { db = db.Where("1 = 0") } else { db = db.Where("warehouses.area_id IN ?", filters.AllowedAreaIDs) } } } if filters.StartDate != "" { if dateFrom, err := utils.ParseDateString(filters.StartDate); err == nil { db = db.Where(fmt.Sprintf("DATE(%s) >= ?", dateColumn), dateFrom) } } if filters.EndDate != "" { if dateTo, err := utils.ParseDateString(filters.EndDate); err == nil { db = db.Where(fmt.Sprintf("DATE(%s) <= ?", dateColumn), dateTo) } } // Urutkan berdasarkan kolom tanggal yang dipilih dan arah sort db = db.Order(fmt.Sprintf("%s %s", dateColumn, orderDirection)). Order("purchase_items.id ASC") var items []entity.PurchaseItem if err := db.Find(&items).Error; err != nil { return nil, err } return items, nil }