diff --git a/internal/repository/analytics_repository.go b/internal/repository/analytics_repository.go index 5f90e0f..c03ee0f 100644 --- a/internal/repository/analytics_repository.go +++ b/internal/repository/analytics_repository.go @@ -144,6 +144,9 @@ func (r *AnalyticsRepositoryImpl) GetPurchasingAnalytics(ctx context.Context, or outletName = &outlet.Name } } + if outletID == nil { + return r.getPurchaseOrderPurchasingAnalytics(ctx, organizationID, dateFrom, dateTo, groupBy) + } summaryQuery := r.db.WithContext(ctx). Table("inventory_movements im"). @@ -276,6 +279,123 @@ func (r *AnalyticsRepositoryImpl) GetPurchasingAnalytics(ctx context.Context, or }, nil } +func (r *AnalyticsRepositoryImpl) getPurchaseOrderPurchasingAnalytics(ctx context.Context, organizationID uuid.UUID, dateFrom, dateTo time.Time, groupBy string) (*entities.PurchasingAnalytics, error) { + var summary entities.PurchasingSummary + summaryQuery := r.db.WithContext(ctx). + Table("purchase_orders po"). + Select(` + COALESCE(SUM(poi.amount), 0) as total_purchases, + COUNT(DISTINCT po.id) as total_purchase_orders, + COALESCE(SUM(poi.quantity), 0) as total_quantity, + CASE + WHEN COUNT(DISTINCT po.id) > 0 + THEN COALESCE(SUM(poi.amount), 0) / COUNT(DISTINCT po.id) + ELSE 0 + END as average_purchase_order_value, + COUNT(DISTINCT poi.ingredient_id) as total_ingredients, + COUNT(DISTINCT po.vendor_id) as total_vendors + `). + Joins("LEFT JOIN purchase_order_items poi ON poi.purchase_order_id = po.id"). + Where("po.organization_id = ?", organizationID). + Where("po.status != ?", "cancelled"). + Where("po.transaction_date >= ? AND po.transaction_date <= ?", dateFrom, dateTo) + + if err := summaryQuery.Scan(&summary).Error; err != nil { + return nil, err + } + + var dateFormat string + switch groupBy { + case "hour": + dateFormat = "DATE_TRUNC('hour', po.created_at)" + case "week": + dateFormat = "DATE_TRUNC('week', po.transaction_date::timestamp)" + case "month": + dateFormat = "DATE_TRUNC('month', po.transaction_date::timestamp)" + default: + dateFormat = "DATE_TRUNC('day', po.transaction_date::timestamp)" + } + + var data []entities.PurchasingAnalyticsData + dataQuery := r.db.WithContext(ctx). + Table("purchase_orders po"). + Select(` + `+dateFormat+` as date, + COALESCE(SUM(poi.amount), 0) as purchases, + COUNT(DISTINCT po.id) as purchase_orders, + COALESCE(SUM(poi.quantity), 0) as quantity, + COUNT(DISTINCT poi.ingredient_id) as ingredients, + COUNT(DISTINCT po.vendor_id) as vendors + `). + Joins("LEFT JOIN purchase_order_items poi ON poi.purchase_order_id = po.id"). + Where("po.organization_id = ?", organizationID). + Where("po.status != ?", "cancelled"). + Where("po.transaction_date >= ? AND po.transaction_date <= ?", dateFrom, dateTo). + Group(dateFormat). + Order(dateFormat) + + if err := dataQuery.Scan(&data).Error; err != nil { + return nil, err + } + + var ingredientData []entities.PurchasingIngredientData + ingredientQuery := r.db.WithContext(ctx). + Table("purchase_order_items poi"). + Select(` + i.id as ingredient_id, + i.name as ingredient_name, + COALESCE(SUM(poi.quantity), 0) as quantity, + COALESCE(SUM(poi.amount), 0) as total_cost, + CASE + WHEN SUM(poi.quantity) > 0 + THEN COALESCE(SUM(poi.amount), 0) / SUM(poi.quantity) + ELSE 0 + END as average_unit_cost, + COUNT(DISTINCT po.id) as purchase_order_count + `). + Joins("JOIN purchase_orders po ON poi.purchase_order_id = po.id"). + Joins("JOIN ingredients i ON poi.ingredient_id = i.id"). + Where("po.organization_id = ?", organizationID). + Where("po.status != ?", "cancelled"). + Where("po.transaction_date >= ? AND po.transaction_date <= ?", dateFrom, dateTo). + Group("i.id, i.name"). + Order("total_cost DESC") + + if err := ingredientQuery.Scan(&ingredientData).Error; err != nil { + return nil, err + } + + var vendorData []entities.PurchasingVendorData + vendorQuery := r.db.WithContext(ctx). + Table("purchase_orders po"). + Select(` + v.id as vendor_id, + v.name as vendor_name, + COALESCE(SUM(poi.amount), 0) as total_cost, + COUNT(DISTINCT po.id) as purchase_order_count, + COUNT(DISTINCT poi.ingredient_id) as ingredient_count, + COALESCE(SUM(poi.quantity), 0) as quantity + `). + Joins("JOIN vendors v ON po.vendor_id = v.id"). + Joins("LEFT JOIN purchase_order_items poi ON poi.purchase_order_id = po.id"). + Where("po.organization_id = ?", organizationID). + Where("po.status != ?", "cancelled"). + Where("po.transaction_date >= ? AND po.transaction_date <= ?", dateFrom, dateTo). + Group("v.id, v.name"). + Order("total_cost DESC") + + if err := vendorQuery.Scan(&vendorData).Error; err != nil { + return nil, err + } + + return &entities.PurchasingAnalytics{ + Summary: summary, + Data: data, + IngredientData: ingredientData, + VendorData: vendorData, + }, nil +} + func (r *AnalyticsRepositoryImpl) GetProductAnalytics(ctx context.Context, organizationID uuid.UUID, outletID *uuid.UUID, dateFrom, dateTo time.Time, limit int) ([]*entities.ProductAnalytics, error) { var results []*entities.ProductAnalytics