package services import ( "context" "fmt" "time" "github.com/google/uuid" "github.com/jmoiron/sqlx" "mgit.msbls.de/m/KanzlAI-mGMT/internal/models" ) type AppointmentService struct { db *sqlx.DB audit *AuditService } func NewAppointmentService(db *sqlx.DB, audit *AuditService) *AppointmentService { return &AppointmentService{db: db, audit: audit} } type AppointmentFilter struct { CaseID *uuid.UUID Type *string StartFrom *time.Time StartTo *time.Time } func (s *AppointmentService) List(ctx context.Context, tenantID uuid.UUID, filter AppointmentFilter) ([]models.Appointment, error) { query := "SELECT * FROM appointments WHERE tenant_id = $1" args := []any{tenantID} argN := 2 if filter.CaseID != nil { query += fmt.Sprintf(" AND case_id = $%d", argN) args = append(args, *filter.CaseID) argN++ } if filter.Type != nil { query += fmt.Sprintf(" AND appointment_type = $%d", argN) args = append(args, *filter.Type) argN++ } if filter.StartFrom != nil { query += fmt.Sprintf(" AND start_at >= $%d", argN) args = append(args, *filter.StartFrom) argN++ } if filter.StartTo != nil { query += fmt.Sprintf(" AND start_at <= $%d", argN) args = append(args, *filter.StartTo) argN++ } query += " ORDER BY start_at ASC" var appointments []models.Appointment if err := s.db.SelectContext(ctx, &appointments, query, args...); err != nil { return nil, fmt.Errorf("listing appointments: %w", err) } if appointments == nil { appointments = []models.Appointment{} } return appointments, nil } func (s *AppointmentService) GetByID(ctx context.Context, tenantID, id uuid.UUID) (*models.Appointment, error) { var a models.Appointment err := s.db.GetContext(ctx, &a, "SELECT * FROM appointments WHERE id = $1 AND tenant_id = $2", id, tenantID) if err != nil { return nil, fmt.Errorf("getting appointment: %w", err) } return &a, nil } func (s *AppointmentService) Create(ctx context.Context, a *models.Appointment) error { a.ID = uuid.New() now := time.Now().UTC() a.CreatedAt = now a.UpdatedAt = now _, err := s.db.NamedExecContext(ctx, ` INSERT INTO appointments (id, tenant_id, case_id, title, description, start_at, end_at, location, appointment_type, caldav_uid, caldav_etag, created_at, updated_at) VALUES (:id, :tenant_id, :case_id, :title, :description, :start_at, :end_at, :location, :appointment_type, :caldav_uid, :caldav_etag, :created_at, :updated_at) `, a) if err != nil { return fmt.Errorf("creating appointment: %w", err) } s.audit.Log(ctx, "create", "appointment", &a.ID, nil, a) return nil } func (s *AppointmentService) Update(ctx context.Context, a *models.Appointment) error { a.UpdatedAt = time.Now().UTC() result, err := s.db.NamedExecContext(ctx, ` UPDATE appointments SET case_id = :case_id, title = :title, description = :description, start_at = :start_at, end_at = :end_at, location = :location, appointment_type = :appointment_type, caldav_uid = :caldav_uid, caldav_etag = :caldav_etag, updated_at = :updated_at WHERE id = :id AND tenant_id = :tenant_id `, a) if err != nil { return fmt.Errorf("updating appointment: %w", err) } rows, err := result.RowsAffected() if err != nil { return fmt.Errorf("checking rows affected: %w", err) } if rows == 0 { return fmt.Errorf("appointment not found") } s.audit.Log(ctx, "update", "appointment", &a.ID, nil, a) return nil } func (s *AppointmentService) Delete(ctx context.Context, tenantID, id uuid.UUID) error { result, err := s.db.ExecContext(ctx, "DELETE FROM appointments WHERE id = $1 AND tenant_id = $2", id, tenantID) if err != nil { return fmt.Errorf("deleting appointment: %w", err) } rows, err := result.RowsAffected() if err != nil { return fmt.Errorf("checking rows affected: %w", err) } if rows == 0 { return fmt.Errorf("appointment not found") } s.audit.Log(ctx, "delete", "appointment", &id, nil, nil) return nil }