numerus/pkg/dashboard.go

169 lines
5.5 KiB
Go
Raw Normal View History

package pkg
import (
"fmt"
"github.com/julienschmidt/httprouter"
"net/http"
)
const (
MonthPeriod = "month"
YestermonthPeriod = "yestermonth"
QuarterPeriod = "quarter"
YesterquarterPeriod = "yesterquarter"
YearPeriod = "year"
YesteryearPeriod = "yesteryear"
)
type DashboardPage struct {
Sales string
Income string
Expenses string
VAT string
IRPF string
NetIncome string
Filters *dashboardFilterForm
}
func ServeDashboard(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
company := mustGetCompany(r)
locale := getLocale(r)
filters := newDashboardFilterForm(locale, company)
if err := filters.Parse(r); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
periodStart := "date_trunc('month', current_date)::date"
periodEnd := "current_date"
switch filters.Period.Selected {
case YestermonthPeriod:
periodStart = "date_trunc('month', current_date - interval '1 month')::date"
periodEnd = "(date_trunc('month', current_date) - interval '1 day')::date"
case QuarterPeriod:
periodStart = "date_trunc('quarter', current_date)::date"
case YesterquarterPeriod:
periodStart = "date_trunc('quarter', current_date - interval '3 months')::date"
periodEnd = "(date_trunc('quarter', current_date) - interval '1 day')::date"
case YearPeriod:
periodStart = "date_trunc('year', current_date)::date"
case YesteryearPeriod:
periodStart = "date_trunc('year', current_date - interval '1 year')::date"
periodEnd = "(date_trunc('year', current_date) - interval '1 day')::date"
case "":
filters.Period.Selected = MonthPeriod
}
conn := getConn(r)
rows := conn.MustQuery(r.Context(), fmt.Sprintf(`
select to_price(0, decimal_digits) as sales
2023-05-16 13:14:20 +00:00
, to_price(coalesce(invoice.total, 0), decimal_digits) as income
, to_price(coalesce(expense.total, 0), decimal_digits) as expenses
, to_price(coalesce(invoice_tax.vat, 0) - coalesce(expense_tax.vat, 0), decimal_digits) as vat
, to_price(coalesce(invoice_tax.irpf, 0) + coalesce(expense_tax.irpf, 0), decimal_digits) as irpf
, to_price(coalesce(invoice.total, 0) - coalesce(expense.total, 0) - (coalesce(invoice_tax.vat, 0) - coalesce(expense_tax.vat, 0)) + coalesce(expense_tax.irpf, 0), decimal_digits) as net_income
from company
left join (
select company_id, sum(total)::integer as total
from invoice
join invoice_amount using (invoice_id)
where invoice_date between %[1]s and %[2]s
group by company_id
) as invoice using (company_id)
left join (
select company_id, sum(amount)::integer as total
from expense
where invoice_date between %[1]s and %[2]s
group by company_id
) as expense using (company_id)
left join (
select invoice.company_id
, sum(case when tax_class.name = 'IVA' then invoice_tax_amount.amount else 0 end)::integer as vat
, sum(case when tax_class.name = 'IRPF' then invoice_tax_amount.amount else 0 end)::integer as irpf
from invoice
join invoice_tax_amount using (invoice_id)
join tax using (tax_id)
join tax_class using (tax_class_id)
where invoice_date between %[1]s and %[2]s
group by invoice.company_id
) as invoice_tax using (company_id)
left join (
select expense.company_id
, sum(case when tax_class.name = 'IVA' then expense_tax_amount.amount else 0 end)::integer as vat
, sum(case when tax_class.name = 'IRPF' then expense_tax_amount.amount else 0 end)::integer as irpf
from expense
join expense_tax_amount using (expense_id)
join tax using (tax_id)
join tax_class using (tax_class_id)
where invoice_date between %[1]s and %[2]s
group by expense.company_id
) as expense_tax using (company_id)
join currency using (currency_code)
where company_id = $1
`, periodStart, periodEnd), company.Id)
defer rows.Close()
dashboard := &DashboardPage{
Filters: filters,
}
for rows.Next() {
if err := rows.Scan(&dashboard.Sales, &dashboard.Income, &dashboard.Expenses, &dashboard.VAT, &dashboard.IRPF, &dashboard.NetIncome); err != nil {
panic(err)
}
}
if rows.Err() != nil {
panic(rows.Err())
}
mustRenderMainTemplate(w, r, "dashboard.gohtml", dashboard)
}
type dashboardFilterForm struct {
locale *Locale
company *Company
Period *RadioField
}
func newDashboardFilterForm(locale *Locale, company *Company) *dashboardFilterForm {
return &dashboardFilterForm{
locale: locale,
company: company,
Period: &RadioField{
Name: "period",
Selected: MonthPeriod,
Label: pgettext("input", "Period", locale),
Options: []*RadioOption{
{
Label: pgettext("period option", "Month", locale),
Value: MonthPeriod,
},
{
Label: pgettext("period option", "Previous month", locale),
Value: YestermonthPeriod,
},
{
Label: pgettext("period option", "Quarter", locale),
Value: QuarterPeriod,
},
{
Label: pgettext("period option", "Previous quarter", locale),
Value: YesterquarterPeriod,
},
{
Label: pgettext("period option", "Year", locale),
Value: YearPeriod,
},
{
Label: pgettext("period option", "Previous year", locale),
Value: YesteryearPeriod,
},
},
},
}
}
func (form *dashboardFilterForm) Parse(r *http.Request) error {
if err := r.ParseForm(); err != nil {
return err
}
form.Period.FillValue(r)
return nil
}