better end points and better tests
This commit is contained in:
144
internal/database/report.go
Normal file
144
internal/database/report.go
Normal file
@@ -0,0 +1,144 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"Engine/internal/model"
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type ReportRepo struct {
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
func NewReportRepo(db *sql.DB) *ReportRepo {
|
||||
return &ReportRepo{db: db}
|
||||
}
|
||||
|
||||
// getAmountsByGLType is the shared query for both actuals and budget tables,
|
||||
// filtered to a single GL account type, period, and department.
|
||||
func (rr *ReportRepo) getActualAmountsByGLType(
|
||||
ctx context.Context,
|
||||
accountType model.GLAccountType,
|
||||
fiscalYear, fiscalPeriod int,
|
||||
deptCode string,
|
||||
) ([]model.GLAmountRow, error) {
|
||||
const q = `
|
||||
SELECT g.code,
|
||||
g.description,
|
||||
SUM(a.amount),
|
||||
a.currency
|
||||
FROM actuals a
|
||||
JOIN gl_accounts g ON g.id = a.gl_account_id
|
||||
JOIN departments d ON d.id = a.department_id
|
||||
WHERE g.type = ?
|
||||
AND g.active = 1
|
||||
AND a.fiscal_year = ?
|
||||
AND a.fiscal_period = ?
|
||||
AND d.code = ?
|
||||
GROUP BY g.id, a.currency
|
||||
ORDER BY g.code`
|
||||
|
||||
rows, err := rr.db.QueryContext(ctx, q,
|
||||
string(accountType), fiscalYear, fiscalPeriod, deptCode)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getActualAmountsByGLType(%s): %w", accountType, err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var out []model.GLAmountRow
|
||||
for rows.Next() {
|
||||
var r model.GLAmountRow
|
||||
if err := rows.Scan(&r.GLCode, &r.Description, &r.Amount, &r.Currency); err != nil {
|
||||
return nil, fmt.Errorf("getActualAmountsByGLType(%s): scan: %w", accountType, err)
|
||||
}
|
||||
out = append(out, r)
|
||||
}
|
||||
return out, rows.Err()
|
||||
}
|
||||
|
||||
func (rr *ReportRepo) getBudgetAmountsByGLType(
|
||||
ctx context.Context,
|
||||
accountType model.GLAccountType,
|
||||
fiscalYear, fiscalPeriod int,
|
||||
deptCode string,
|
||||
version model.BudgetVersion,
|
||||
) ([]model.RevenueAmounts, error) {
|
||||
const q = `
|
||||
SELECT g.code,
|
||||
g.description,
|
||||
SUM(b.amount),
|
||||
b.currency
|
||||
FROM budgets b
|
||||
JOIN gl_accounts g ON g.id = b.gl_account_id
|
||||
JOIN departments d ON d.id = b.department_id
|
||||
WHERE g.type = ?
|
||||
AND g.active = 1
|
||||
AND b.fiscal_year = ?
|
||||
AND b.fiscal_period = ?
|
||||
AND d.code = ?
|
||||
AND b.version = ?
|
||||
GROUP BY g.id, b.currency
|
||||
ORDER BY g.code`
|
||||
|
||||
rows, err := rr.db.QueryContext(ctx, q,
|
||||
string(accountType), fiscalYear, fiscalPeriod, deptCode, string(version))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getBudgetAmountsByGLType(%s): %w", accountType, err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var out []model.RevenueAmounts
|
||||
for rows.Next() {
|
||||
var r model.RevenueAmounts
|
||||
if err := rows.Scan(&r.GLCode, &r.Description, &r.Amount, &r.Currency); err != nil {
|
||||
return nil, fmt.Errorf("getBudgetAmountsByGLType(%s): scan: %w", accountType, err)
|
||||
}
|
||||
out = append(out, r)
|
||||
}
|
||||
return out, rows.Err()
|
||||
}
|
||||
|
||||
// ── Public surface ────────────────────────────────────────────────────────────
|
||||
|
||||
func (rr *ReportRepo) GetGLRevenueActuals(ctx context.Context, fiscalYear, fiscalPeriod int, deptCode string) ([]model.GLAmountRow, error) {
|
||||
return rr.getActualAmountsByGLType(ctx, model.GLRevenue, fiscalYear, fiscalPeriod, deptCode)
|
||||
}
|
||||
|
||||
func (rr *ReportRepo) GetGLRevenueBudget(ctx context.Context, fiscalYear, fiscalPeriod int, deptCode string, version model.BudgetVersion) ([]model.RevenueAmounts, error) {
|
||||
return rr.getBudgetAmountsByGLType(ctx, model.GLRevenue, fiscalYear, fiscalPeriod, deptCode, version)
|
||||
}
|
||||
|
||||
// Same pair for the other types — all delegate to the shared private methods.
|
||||
|
||||
func (rr *ReportRepo) GetGLCOGSActuals(ctx context.Context, fiscalYear, fiscalPeriod int, deptCode string) ([]model.GLAmountRow, error) {
|
||||
return rr.getActualAmountsByGLType(ctx, model.GLCOGS, fiscalYear, fiscalPeriod, deptCode)
|
||||
}
|
||||
|
||||
func (rr *ReportRepo) GetGLCOGSBudget(ctx context.Context, fiscalYear, fiscalPeriod int, deptCode string, version model.BudgetVersion) ([]model.RevenueAmounts, error) {
|
||||
return rr.getBudgetAmountsByGLType(ctx, model.GLCOGS, fiscalYear, fiscalPeriod, deptCode, version)
|
||||
}
|
||||
|
||||
func (rr *ReportRepo) GetGLOpexActuals(ctx context.Context, fiscalYear, fiscalPeriod int, deptCode string) ([]model.GLAmountRow, error) {
|
||||
return rr.getActualAmountsByGLType(ctx, model.GLOpex, fiscalYear, fiscalPeriod, deptCode)
|
||||
}
|
||||
|
||||
func (rr *ReportRepo) GetGLOpexBudget(ctx context.Context, fiscalYear, fiscalPeriod int, deptCode string, version model.BudgetVersion) ([]model.RevenueAmounts, error) {
|
||||
return rr.getBudgetAmountsByGLType(ctx, model.GLOpex, fiscalYear, fiscalPeriod, deptCode, version)
|
||||
}
|
||||
|
||||
func (rr *ReportRepo) GetGLCapexActuals(ctx context.Context, fiscalYear, fiscalPeriod int, deptCode string) ([]model.GLAmountRow, error) {
|
||||
return rr.getActualAmountsByGLType(ctx, model.GLCapex, fiscalYear, fiscalPeriod, deptCode)
|
||||
}
|
||||
|
||||
func (rr *ReportRepo) GetGLCapexBudget(ctx context.Context, fiscalYear, fiscalPeriod int, deptCode string, version model.BudgetVersion) ([]model.RevenueAmounts, error) {
|
||||
return rr.getBudgetAmountsByGLType(ctx, model.GLCapex, fiscalYear, fiscalPeriod, deptCode, version)
|
||||
}
|
||||
|
||||
func (rr *ReportRepo) GetGLHeadcountActuals(ctx context.Context, fiscalYear, fiscalPeriod int, deptCode string) ([]model.GLAmountRow, error) {
|
||||
return rr.getActualAmountsByGLType(ctx, model.GLHeadcount, fiscalYear, fiscalPeriod, deptCode)
|
||||
}
|
||||
|
||||
func (rr *ReportRepo) GetGLHeadcountBudget(ctx context.Context, fiscalYear, fiscalPeriod int, deptCode string, version model.BudgetVersion) ([]model.RevenueAmounts, error) {
|
||||
return rr.getBudgetAmountsByGLType(ctx, model.GLHeadcount, fiscalYear, fiscalPeriod, deptCode, version)
|
||||
}
|
||||
Reference in New Issue
Block a user