This commit is contained in:
Efril 2026-06-11 16:43:53 +07:00
commit d0a548f44e

View File

@ -145,179 +145,68 @@ func (r *AnalyticsRepositoryImpl) GetPurchasingAnalytics(ctx context.Context, or
} }
} }
rawMaterialOutletFilter := "" summaryQuery := r.db.WithContext(ctx).
expenseOutletFilter := "" Table("inventory_movements im").
rawMaterialSummaryArgs := []interface{}{ Select(`
organizationID, COALESCE(SUM(im.total_cost), 0) as total_purchases,
entities.InventoryMovementTypePurchase, COUNT(DISTINCT im.reference_id) as total_purchase_orders,
"INGREDIENT", COALESCE(SUM(im.quantity), 0) as total_quantity,
entities.InventoryMovementReferenceTypePurchaseOrder,
dateFrom,
dateTo,
}
expenseSummaryArgs := []interface{}{
organizationID,
entities.PurchaseCategoryTypeExpense,
"approved",
dateFrom,
dateTo,
}
if outletID != nil {
rawMaterialOutletFilter = "AND im.outlet_id = ?"
expenseOutletFilter = "AND e.outlet_id = ?"
rawMaterialSummaryArgs = append(rawMaterialSummaryArgs, *outletID)
expenseSummaryArgs = append(expenseSummaryArgs, *outletID)
}
summaryArgs := append(rawMaterialSummaryArgs, expenseSummaryArgs...)
summaryQuery := `
WITH raw_material AS (
SELECT
COALESCE(SUM(im.total_cost), 0) as raw_material_purchases,
COUNT(DISTINCT im.reference_id) as raw_material_purchase_orders,
COALESCE(SUM(im.quantity), 0) as total_quantity,
COUNT(DISTINCT im.item_id) as total_ingredients,
COUNT(DISTINCT po.vendor_id) as total_vendors
FROM inventory_movements im
LEFT JOIN purchase_orders po ON im.reference_id = po.id
WHERE im.organization_id = ?
AND im.movement_type = ?
AND im.item_type = ?
AND im.reference_type = ?
AND im.created_at >= ? AND im.created_at <= ?
` + rawMaterialOutletFilter + `
),
expense AS (
SELECT
COALESCE(SUM(ei.amount), 0) as expense_purchases,
COUNT(DISTINCT e.id) as expense_count
FROM expense_items ei
JOIN expenses e ON ei.expense_id = e.id
JOIN purchase_categories pc ON ei.purchase_category_id = pc.id
WHERE e.organization_id = ?
AND pc.type = ?
AND e.status = ?
AND e.transaction_date >= ? AND e.transaction_date <= ?
` + expenseOutletFilter + `
)
SELECT
rm.raw_material_purchases + ex.expense_purchases as total_purchases,
rm.raw_material_purchases,
ex.expense_purchases,
rm.raw_material_purchase_orders + ex.expense_count as total_purchase_orders,
rm.raw_material_purchase_orders,
ex.expense_count,
rm.total_quantity,
CASE CASE
WHEN rm.raw_material_purchase_orders + ex.expense_count > 0 WHEN COUNT(DISTINCT im.reference_id) > 0
THEN (rm.raw_material_purchases + ex.expense_purchases) / (rm.raw_material_purchase_orders + ex.expense_count) THEN COALESCE(SUM(im.total_cost), 0) / COUNT(DISTINCT im.reference_id)
ELSE 0 ELSE 0
END as average_purchase_order_value, END as average_purchase_order_value,
rm.total_ingredients, COUNT(DISTINCT im.item_id) as total_ingredients,
rm.total_vendors COUNT(DISTINCT po.vendor_id) as total_vendors
FROM raw_material rm `).
CROSS JOIN expense ex Joins("LEFT JOIN purchase_orders po ON im.reference_id = po.id").
` Where("im.organization_id = ?", organizationID).
Where("im.movement_type = ?", entities.InventoryMovementTypePurchase).
Where("im.item_type = ?", "INGREDIENT").
Where("im.reference_type = ?", entities.InventoryMovementReferenceTypePurchaseOrder).
Where("im.created_at >= ? AND im.created_at <= ?", dateFrom, dateTo)
if err := r.db.WithContext(ctx).Raw(summaryQuery, summaryArgs...).Scan(&summary).Error; err != nil { summaryQuery = r.resolveOutletID(summaryQuery, outletID, "im.outlet_id")
if err := summaryQuery.Scan(&summary).Error; err != nil {
return nil, err return nil, err
} }
var dateFormat string var dateFormat string
switch groupBy { switch groupBy {
case "hour": case "hour":
dateFormat = "DATE_TRUNC('hour', im.created_at)::timestamp" dateFormat = "DATE_TRUNC('hour', im.created_at)"
case "week": case "week":
dateFormat = "DATE_TRUNC('week', im.created_at)::timestamp" dateFormat = "DATE_TRUNC('week', im.created_at)"
case "month": case "month":
dateFormat = "DATE_TRUNC('month', im.created_at)::timestamp" dateFormat = "DATE_TRUNC('month', im.created_at)"
default: default:
dateFormat = "DATE_TRUNC('day', im.created_at)::timestamp" dateFormat = "DATE_TRUNC('day', im.created_at)"
} }
expenseDateFormat := "DATE_TRUNC('day', e.transaction_date)::timestamp"
switch groupBy {
case "hour":
expenseDateFormat = "DATE_TRUNC('hour', e.transaction_date)::timestamp"
case "week":
expenseDateFormat = "DATE_TRUNC('week', e.transaction_date)::timestamp"
case "month":
expenseDateFormat = "DATE_TRUNC('month', e.transaction_date)::timestamp"
}
rawMaterialDataArgs := []interface{}{
organizationID,
entities.InventoryMovementTypePurchase,
"INGREDIENT",
entities.InventoryMovementReferenceTypePurchaseOrder,
dateFrom,
dateTo,
}
expenseDataArgs := []interface{}{
organizationID,
entities.PurchaseCategoryTypeExpense,
"approved",
dateFrom,
dateTo,
}
if outletID != nil {
rawMaterialDataArgs = append(rawMaterialDataArgs, *outletID)
expenseDataArgs = append(expenseDataArgs, *outletID)
}
dataArgs := append(rawMaterialDataArgs, expenseDataArgs...)
var data []entities.PurchasingAnalyticsData var data []entities.PurchasingAnalyticsData
dataQuery := ` dataQuery := r.db.WithContext(ctx).
WITH raw_material AS ( Table("inventory_movements im").
SELECT Select(`
` + dateFormat + ` as date, `+dateFormat+` as date,
COALESCE(SUM(im.total_cost), 0) as raw_material_purchases, COALESCE(SUM(im.total_cost), 0) as purchases,
COUNT(DISTINCT im.reference_id) as raw_material_purchase_orders, COUNT(DISTINCT im.reference_id) as purchase_orders,
COALESCE(SUM(im.quantity), 0) as quantity, COALESCE(SUM(im.quantity), 0) as quantity,
COUNT(DISTINCT im.item_id) as ingredients, COUNT(DISTINCT im.item_id) as ingredients,
COUNT(DISTINCT po.vendor_id) as vendors COUNT(DISTINCT po.vendor_id) as vendors
FROM inventory_movements im `).
LEFT JOIN purchase_orders po ON im.reference_id = po.id Joins("LEFT JOIN purchase_orders po ON im.reference_id = po.id").
WHERE im.organization_id = ? Where("im.organization_id = ?", organizationID).
AND im.movement_type = ? Where("im.movement_type = ?", entities.InventoryMovementTypePurchase).
AND im.item_type = ? Where("im.item_type = ?", "INGREDIENT").
AND im.reference_type = ? Where("im.reference_type = ?", entities.InventoryMovementReferenceTypePurchaseOrder).
AND im.created_at >= ? AND im.created_at <= ? Where("im.created_at >= ? AND im.created_at <= ?", dateFrom, dateTo).
` + rawMaterialOutletFilter + ` Group(dateFormat).
GROUP BY 1 Order(dateFormat)
),
expense AS (
SELECT
` + expenseDateFormat + ` as date,
COALESCE(SUM(ei.amount), 0) as expense_purchases,
COUNT(DISTINCT e.id) as expense_count
FROM expense_items ei
JOIN expenses e ON ei.expense_id = e.id
JOIN purchase_categories pc ON ei.purchase_category_id = pc.id
WHERE e.organization_id = ?
AND pc.type = ?
AND e.status = ?
AND e.transaction_date >= ? AND e.transaction_date <= ?
` + expenseOutletFilter + `
GROUP BY 1
)
SELECT
COALESCE(rm.date, ex.date) as date,
COALESCE(rm.raw_material_purchases, 0) + COALESCE(ex.expense_purchases, 0) as purchases,
COALESCE(rm.raw_material_purchases, 0) as raw_material_purchases,
COALESCE(ex.expense_purchases, 0) as expense_purchases,
COALESCE(rm.raw_material_purchase_orders, 0) + COALESCE(ex.expense_count, 0) as purchase_orders,
COALESCE(rm.raw_material_purchase_orders, 0) as raw_material_purchase_orders,
COALESCE(ex.expense_count, 0) as expense_count,
COALESCE(rm.quantity, 0) as quantity,
COALESCE(rm.ingredients, 0) as ingredients,
COALESCE(rm.vendors, 0) as vendors
FROM raw_material rm
FULL OUTER JOIN expense ex ON rm.date = ex.date
ORDER BY date
`
if err := r.db.WithContext(ctx).Raw(dataQuery, dataArgs...).Scan(&data).Error; err != nil { dataQuery = r.resolveOutletID(dataQuery, outletID, "im.outlet_id")
if err := dataQuery.Scan(&data).Error; err != nil {
return nil, err return nil, err
} }
@ -749,7 +638,7 @@ func (r *AnalyticsRepositoryImpl) getExpenseByCategory(ctx context.Context, orga
query := r.db.WithContext(ctx). query := r.db.WithContext(ctx).
Table("expense_items ei"). Table("expense_items ei").
Select(`COALESCE(parent_coa.name, coa.name, 'Lain-lain') as category_name, COALESCE(SUM(ei.amount), 0) as amount`). Select(`COALESCE(parent_coa.name, 'Lain-lain') as category_name, COALESCE(SUM(ei.amount), 0) as amount`).
Joins("JOIN expenses e ON ei.expense_id = e.id"). Joins("JOIN expenses e ON ei.expense_id = e.id").
Joins("JOIN chart_of_accounts coa ON ei.chart_of_account_id = coa.id"). Joins("JOIN chart_of_accounts coa ON ei.chart_of_account_id = coa.id").
Joins("LEFT JOIN chart_of_accounts parent_coa ON coa.parent_id = parent_coa.id"). Joins("LEFT JOIN chart_of_accounts parent_coa ON coa.parent_id = parent_coa.id").
@ -762,8 +651,8 @@ func (r *AnalyticsRepositoryImpl) getExpenseByCategory(ctx context.Context, orga
} }
err := query. err := query.
Group("COALESCE(parent_coa.name, coa.name, 'Lain-lain')"). Group("parent_coa.name").
Order("COALESCE(parent_coa.name, coa.name, 'Lain-lain')"). Order("parent_coa.name").
Scan(&results).Error Scan(&results).Error
return results, err return results, err