249 lines
5.3 KiB
Go
249 lines
5.3 KiB
Go
package calendar
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"time"
|
|
|
|
"github.com/jackc/pgx/v5"
|
|
)
|
|
|
|
var ErrCustomDateNotFound = errors.New("custom date not found")
|
|
|
|
type CustomDate struct {
|
|
ID string `json:"id"`
|
|
ClientID *string `json:"client_id"`
|
|
Name string `json:"name"`
|
|
Description string `json:"description"`
|
|
Date string `json:"date"`
|
|
RecursAnnually bool `json:"recurs_annually"`
|
|
Visibility string `json:"visibility"`
|
|
Category string `json:"category"`
|
|
CreatedBy *string `json:"created_by"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
}
|
|
|
|
func (s Store) ListCustomDatesByYear(ctx context.Context, year int, clientID string, viewerUserID string) ([]CustomDate, error) {
|
|
rows, err := s.db.Query(ctx, `
|
|
SELECT
|
|
id::text,
|
|
client_id::text,
|
|
name,
|
|
description,
|
|
CASE
|
|
WHEN recurs_annually THEN make_date(
|
|
$1,
|
|
EXTRACT(MONTH FROM date)::int,
|
|
LEAST(
|
|
EXTRACT(DAY FROM date)::int,
|
|
EXTRACT(DAY FROM (date_trunc('month', make_date($1, EXTRACT(MONTH FROM date)::int, 1)) + interval '1 month - 1 day'))::int
|
|
)
|
|
)::text
|
|
ELSE date::text
|
|
END,
|
|
recurs_annually,
|
|
visibility,
|
|
category,
|
|
created_by::text,
|
|
created_at,
|
|
updated_at
|
|
FROM commemorative_dates
|
|
WHERE (
|
|
(date >= make_date($1, 1, 1) AND date < make_date($1 + 1, 1, 1))
|
|
OR (recurs_annually AND date < make_date($1 + 1, 1, 1))
|
|
)
|
|
AND ($2 = '' OR client_id IS NULL OR client_id::text = $2)
|
|
AND (
|
|
$3 = ''
|
|
OR (
|
|
visibility = 'client'
|
|
AND (
|
|
client_id IS NULL
|
|
OR EXISTS (
|
|
SELECT 1
|
|
FROM client_users
|
|
WHERE client_users.client_id = commemorative_dates.client_id
|
|
AND client_users.user_id = $3::uuid
|
|
)
|
|
)
|
|
)
|
|
)
|
|
ORDER BY date ASC, name ASC
|
|
`, year, clientID, viewerUserID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
dates := []CustomDate{}
|
|
for rows.Next() {
|
|
date, err := scanCustomDate(rows)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
dates = append(dates, date)
|
|
}
|
|
|
|
return dates, rows.Err()
|
|
}
|
|
|
|
func (s Store) CreateCustomDate(ctx context.Context, input CreateCustomDateInput) (CustomDate, error) {
|
|
row := s.db.QueryRow(ctx, `
|
|
INSERT INTO commemorative_dates (
|
|
client_id,
|
|
name,
|
|
description,
|
|
date,
|
|
recurs_annually,
|
|
visibility,
|
|
category,
|
|
created_by
|
|
)
|
|
VALUES (NULLIF($1, '')::uuid, $2, $3, $4, $5, $6, $7, NULLIF($8, '')::uuid)
|
|
RETURNING
|
|
id::text,
|
|
client_id::text,
|
|
name,
|
|
description,
|
|
date::text,
|
|
recurs_annually,
|
|
visibility,
|
|
category,
|
|
created_by::text,
|
|
created_at,
|
|
updated_at
|
|
`,
|
|
input.ClientID,
|
|
input.Name,
|
|
input.Description,
|
|
input.Date,
|
|
input.RecursAnnually,
|
|
input.Visibility,
|
|
input.Category,
|
|
input.CreatedBy,
|
|
)
|
|
|
|
return scanCustomDate(row)
|
|
}
|
|
|
|
type UpdateCustomDateInput struct {
|
|
ID string
|
|
ClientID string
|
|
Name string
|
|
Description string
|
|
Date string
|
|
RecursAnnually bool
|
|
Visibility string
|
|
}
|
|
|
|
func (s Store) UpdateCustomDate(ctx context.Context, input UpdateCustomDateInput) (CustomDate, error) {
|
|
row := s.db.QueryRow(ctx, `
|
|
UPDATE commemorative_dates
|
|
SET
|
|
client_id = NULLIF($2, '')::uuid,
|
|
name = $3,
|
|
description = $4,
|
|
date = $5::date,
|
|
recurs_annually = $6,
|
|
visibility = $7,
|
|
updated_at = now()
|
|
WHERE id = $1::uuid
|
|
AND category != 'feriados-brasil'
|
|
RETURNING
|
|
id::text,
|
|
client_id::text,
|
|
name,
|
|
description,
|
|
date::text,
|
|
recurs_annually,
|
|
visibility,
|
|
category,
|
|
created_by::text,
|
|
created_at,
|
|
updated_at
|
|
`, input.ID, input.ClientID, input.Name, input.Description, input.Date, input.RecursAnnually, input.Visibility)
|
|
|
|
return scanCustomDate(row)
|
|
}
|
|
|
|
func (s Store) DeleteCustomDate(ctx context.Context, id string) error {
|
|
command, err := s.db.Exec(ctx, `
|
|
DELETE FROM commemorative_dates
|
|
WHERE id = $1::uuid
|
|
AND category != 'feriados-brasil'
|
|
`, id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if command.RowsAffected() == 0 {
|
|
return ErrCustomDateNotFound
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s Store) UpsertExternalCommemorativeDates(ctx context.Context, dates []ExternalCommemorativeDate) error {
|
|
for _, date := range dates {
|
|
if date.Name == "" || date.Date == "" {
|
|
continue
|
|
}
|
|
|
|
if _, err := s.db.Exec(ctx, `
|
|
INSERT INTO commemorative_dates (
|
|
client_id,
|
|
name,
|
|
description,
|
|
date,
|
|
recurs_annually,
|
|
visibility,
|
|
category
|
|
)
|
|
VALUES (NULL, $1, $2, $3::date, false, 'client', 'feriados-brasil')
|
|
ON CONFLICT DO NOTHING
|
|
`, date.Name, date.Description, date.Date); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
type CreateCustomDateInput struct {
|
|
ClientID string
|
|
Name string
|
|
Description string
|
|
Date string
|
|
RecursAnnually bool
|
|
Visibility string
|
|
Category string
|
|
CreatedBy string
|
|
}
|
|
|
|
type customDateScanner interface {
|
|
Scan(dest ...any) error
|
|
}
|
|
|
|
func scanCustomDate(row customDateScanner) (CustomDate, error) {
|
|
var date CustomDate
|
|
err := row.Scan(
|
|
&date.ID,
|
|
&date.ClientID,
|
|
&date.Name,
|
|
&date.Description,
|
|
&date.Date,
|
|
&date.RecursAnnually,
|
|
&date.Visibility,
|
|
&date.Category,
|
|
&date.CreatedBy,
|
|
&date.CreatedAt,
|
|
&date.UpdatedAt,
|
|
)
|
|
if errors.Is(err, pgx.ErrNoRows) {
|
|
return CustomDate{}, ErrCustomDateNotFound
|
|
}
|
|
if err != nil {
|
|
return CustomDate{}, err
|
|
}
|
|
return date, nil
|
|
}
|