package repository import ( "context" "fmt" "strings" "time" "gitlab.com/mbugroup/lti-api.git/internal/common/repository" entity "gitlab.com/mbugroup/lti-api.git/internal/entities" validation "gitlab.com/mbugroup/lti-api.git/internal/modules/closings/validations" "gitlab.com/mbugroup/lti-api.git/internal/utils" "gorm.io/gorm" ) type ClosingRepository interface { repository.BaseRepository[entity.ProjectFlock] GetSapronak(ctx context.Context, params SapronakQueryParams) ([]SapronakRow, int64, error) GetExpeditionHPP(ctx context.Context, projectFlockID uint, projectFlockKandangID *uint) ([]ExpeditionHPPRow, error) } type ClosingRepositoryImpl struct { *repository.BaseRepositoryImpl[entity.ProjectFlock] } func NewClosingRepository(db *gorm.DB) ClosingRepository { return &ClosingRepositoryImpl{ BaseRepositoryImpl: repository.NewBaseRepository[entity.ProjectFlock](db), } } type SapronakRow struct { Id uint64 `gorm:"column:id"` SortDate time.Time `gorm:"column:sort_date"` DateText string `gorm:"column:date_text"` ReferenceNumber string `gorm:"column:reference_number"` TransactionType string `gorm:"column:transaction_type"` ProductName string `gorm:"column:product_name"` ProductCategory string `gorm:"column:product_category"` ProductSubCategory string `gorm:"column:product_sub_category"` SourceWarehouse string `gorm:"column:source_warehouse"` DestinationWarehouse string `gorm:"column:destination_warehouse"` Destination string `gorm:"column:destination"` Quantity float64 `gorm:"column:quantity"` Unit string `gorm:"column:unit"` Notes string `gorm:"column:notes"` } type ExpeditionHPPRow struct { SupplierID uint64 `gorm:"column:supplier_id"` SupplierName string `gorm:"column:supplier_name"` Qty float64 `gorm:"column:qty"` TotalAmount float64 `gorm:"column:total_amount"` } type SapronakQueryParams struct { Type string WarehouseIDs []uint ProjectFlockKandangIDs []uint Limit int Offset int } func (r *ClosingRepositoryImpl) GetSapronak(ctx context.Context, params SapronakQueryParams) ([]SapronakRow, int64, error) { db := r.DB().WithContext(ctx) var ( unionParts []string args []any ) switch params.Type { case validation.SapronakTypeIncoming: if len(params.WarehouseIDs) == 0 { return []SapronakRow{}, 0, nil } unionParts = append(unionParts, sapronakIncomingPurchasesSQL, sapronakIncomingTransfersSQL) args = append(args, params.WarehouseIDs, params.WarehouseIDs) case validation.SapronakTypeOutgoing: if len(params.WarehouseIDs) > 0 { unionParts = append(unionParts, sapronakOutgoingTransfersSQL) args = append(args, params.WarehouseIDs) } if len(params.ProjectFlockKandangIDs) > 0 { unionParts = append(unionParts, sapronakOutgoingMarketingsSQL) args = append(args, params.ProjectFlockKandangIDs) } if len(unionParts) == 0 { return []SapronakRow{}, 0, nil } default: return nil, 0, fmt.Errorf("invalid sapronak type: %s", params.Type) } unionSQL := strings.Join(unionParts, " UNION ALL ") var totalResults int64 countSQL := fmt.Sprintf("SELECT COUNT(*) FROM (%s) AS combined", unionSQL) if err := db.Raw(countSQL, args...).Scan(&totalResults).Error; err != nil { return nil, 0, err } dataArgs := append(append([]any{}, args...), params.Limit, params.Offset) dataSQL := fmt.Sprintf("SELECT * FROM (%s) AS combined ORDER BY sort_date ASC, id ASC LIMIT ? OFFSET ?", unionSQL) var rows []SapronakRow if err := db.Raw(dataSQL, dataArgs...).Scan(&rows).Error; err != nil { return nil, 0, err } return rows, totalResults, nil } func (r *ClosingRepositoryImpl) GetExpeditionHPP(ctx context.Context, projectFlockID uint, projectFlockKandangID *uint) ([]ExpeditionHPPRow, error) { db := r.DB().WithContext(ctx) if projectFlockID == 0 { return nil, fmt.Errorf("invalid project flock id") } query := db. Table("expense_realizations AS er"). Joins("JOIN expense_nonstocks ens ON ens.id = er.expense_nonstock_id"). Joins("JOIN expenses e ON e.id = ens.expense_id"). Joins("JOIN project_flock_kandangs pfk ON pfk.id = ens.project_flock_kandang_id"). Joins("JOIN nonstocks n ON n.id = ens.nonstock_id"). Joins("JOIN flags f ON f.flagable_id = n.id AND f.flagable_type = ?", entity.FlagableTypeNonstock). Joins("JOIN suppliers s ON s.id = e.supplier_id"). Where("pfk.project_flock_id = ?", projectFlockID). Where("e.category = ?", "BOP"). Where("UPPER(f.name) = ?", strings.ToUpper(string(utils.FlagEkspedisi))) if projectFlockKandangID != nil && *projectFlockKandangID != 0 { query = query.Where("pfk.id = ?", *projectFlockKandangID) } var rows []ExpeditionHPPRow err := query. Select( "e.supplier_id AS supplier_id, " + "s.name AS supplier_name, " + "SUM(er.qty) AS qty, " + "SUM(er.qty * er.price) AS total_amount", ). Group("e.supplier_id, s.name"). Scan(&rows).Error if err != nil { return nil, err } return rows, nil } const ( sapronakIncomingPurchasesSQL = ` SELECT CAST(pi.id AS BIGINT) AS id, COALESCE(pi.received_date, '1970-01-01') AS sort_date, COALESCE(TO_CHAR(pi.received_date, 'DD-Mon-YYYY'), '') AS date_text, COALESCE(p.po_number, '') AS reference_number, 'Purchase' AS transaction_type, prod.name AS product_name, pc.name AS product_category, COALESCE(( SELECT string_agg(f.name, ' ') FROM flags f WHERE f.flagable_type = 'products' AND f.flagable_id = prod.id ), '') AS product_sub_category, 'External Supplier' AS source_warehouse, w.name AS destination_warehouse, '' AS destination, pi.total_qty AS quantity, u.name AS unit, COALESCE(p.notes, '') AS notes FROM purchase_items pi JOIN purchases p ON p.id = pi.purchase_id JOIN products prod ON prod.id = pi.product_id JOIN product_categories pc ON pc.id = prod.product_category_id JOIN uoms u ON u.id = prod.uom_id JOIN warehouses w ON w.id = pi.warehouse_id WHERE pi.warehouse_id IN ? ` sapronakIncomingTransfersSQL = ` SELECT CAST(st.id AS BIGINT) AS id, st.transfer_date AS sort_date, TO_CHAR(st.transfer_date, 'DD-Mon-YYYY') AS date_text, st.movement_number AS reference_number, 'Internal Transfer In' AS transaction_type, prod.name AS product_name, pc.name AS product_category, COALESCE(( SELECT string_agg(f.name, ' ') FROM flags f WHERE f.flagable_type = 'products' AND f.flagable_id = prod.id ), '') AS product_sub_category, COALESCE(fw.name, '') AS source_warehouse, COALESCE(tw.name, '') AS destination_warehouse, '' AS destination, std.quantity AS quantity, u.name AS unit, 'Stock Refill' AS notes FROM stock_transfer_details std JOIN stock_transfers st ON st.id = std.stock_transfer_id LEFT JOIN warehouses fw ON fw.id = st.from_warehouse_id LEFT JOIN warehouses tw ON tw.id = st.to_warehouse_id JOIN products prod ON prod.id = std.product_id JOIN product_categories pc ON pc.id = prod.product_category_id JOIN uoms u ON u.id = prod.uom_id WHERE st.to_warehouse_id IN ? ` sapronakOutgoingTransfersSQL = ` SELECT CAST(st.id AS BIGINT) AS id, st.transfer_date AS sort_date, TO_CHAR(st.transfer_date, 'DD-Mon-YYYY') AS date_text, st.movement_number AS reference_number, 'Internal Transfer Out' AS transaction_type, prod.name AS product_name, pc.name AS product_category, COALESCE(( SELECT string_agg(f.name, ' ') FROM flags f WHERE f.flagable_type = 'products' AND f.flagable_id = prod.id ), '') AS product_sub_category, COALESCE(fw.name, '') AS source_warehouse, '' AS destination_warehouse, COALESCE(tw.name, '') AS destination, std.quantity AS quantity, u.name AS unit, 'Transfer to other unit' AS notes FROM stock_transfer_details std JOIN stock_transfers st ON st.id = std.stock_transfer_id LEFT JOIN warehouses fw ON fw.id = st.from_warehouse_id LEFT JOIN warehouses tw ON tw.id = st.to_warehouse_id JOIN products prod ON prod.id = std.product_id JOIN product_categories pc ON pc.id = prod.product_category_id JOIN uoms u ON u.id = prod.uom_id WHERE st.from_warehouse_id IN ? ` sapronakOutgoingMarketingsSQL = ` SELECT CAST(mp.id AS BIGINT) AS id, m.so_date AS sort_date, TO_CHAR(m.so_date, 'DD-Mon-YYYY') AS date_text, m.so_number AS reference_number, 'Trading Sales' AS transaction_type, prod.name AS product_name, pc.name AS product_category, COALESCE(( SELECT string_agg(f.name, ' ') FROM flags f WHERE f.flagable_type = 'products' AND f.flagable_id = prod.id ), '') AS product_sub_category, w.name AS source_warehouse, '' AS destination_warehouse, 'RETAIL CUSTOMER' AS destination, mp.qty AS quantity, u.name AS unit, m.notes AS notes FROM marketing_products mp JOIN marketings m ON m.id = mp.marketing_id JOIN product_warehouses pw ON pw.id = mp.product_warehouse_id JOIN products prod ON prod.id = pw.product_id JOIN product_categories pc ON pc.id = prod.product_category_id JOIN uoms u ON u.id = prod.uom_id JOIN warehouses w ON w.id = pw.warehouse_id WHERE pw.project_flock_kandang_id IN ? ` )