301 lines
10 KiB
Go
301 lines
10 KiB
Go
package cadastro_fot
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"photum-backend/internal/db/generated"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/jackc/pgx/v5/pgtype"
|
|
)
|
|
|
|
type Service struct {
|
|
queries *generated.Queries
|
|
}
|
|
|
|
func NewService(queries *generated.Queries) *Service {
|
|
return &Service{queries: queries}
|
|
}
|
|
|
|
type CreateInput struct {
|
|
Fot string `json:"fot"`
|
|
EmpresaID string `json:"empresa_id"`
|
|
CursoID string `json:"curso_id"`
|
|
AnoFormaturaID string `json:"ano_formatura_id"`
|
|
Instituicao string `json:"instituicao"`
|
|
Cidade string `json:"cidade"`
|
|
Estado string `json:"estado"`
|
|
Observacoes string `json:"observacoes"`
|
|
GastosCaptacao float64 `json:"gastos_captacao"`
|
|
PreVenda bool `json:"pre_venda"`
|
|
}
|
|
|
|
func (s *Service) Create(ctx context.Context, input CreateInput, regiao string) (*generated.CadastroFot, error) {
|
|
empresaUUID, _ := uuid.Parse(input.EmpresaID)
|
|
cursoUUID, _ := uuid.Parse(input.CursoID)
|
|
anoUUID, _ := uuid.Parse(input.AnoFormaturaID)
|
|
|
|
res, err := s.queries.CreateCadastroFot(ctx, generated.CreateCadastroFotParams{
|
|
Fot: input.Fot,
|
|
EmpresaID: pgtype.UUID{Bytes: empresaUUID, Valid: true},
|
|
CursoID: pgtype.UUID{Bytes: cursoUUID, Valid: true},
|
|
AnoFormaturaID: pgtype.UUID{Bytes: anoUUID, Valid: true},
|
|
Instituicao: pgtype.Text{String: input.Instituicao, Valid: true},
|
|
Cidade: pgtype.Text{String: input.Cidade, Valid: true},
|
|
Estado: pgtype.Text{String: input.Estado, Valid: true},
|
|
Observacoes: pgtype.Text{String: input.Observacoes, Valid: true},
|
|
GastosCaptacao: toPgNumeric(input.GastosCaptacao),
|
|
PreVenda: pgtype.Bool{Bool: input.PreVenda, Valid: true},
|
|
Regiao: pgtype.Text{String: regiao, Valid: true},
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &res, nil
|
|
}
|
|
|
|
func (s *Service) List(ctx context.Context, regiao string) ([]generated.ListCadastroFotRow, error) {
|
|
return s.queries.ListCadastroFot(ctx, pgtype.Text{String: regiao, Valid: true})
|
|
}
|
|
|
|
func (s *Service) ListByEmpresa(ctx context.Context, empresaID string, regiao string) ([]generated.ListCadastroFotByEmpresaRow, error) {
|
|
uuidVal, err := uuid.Parse(empresaID)
|
|
if err != nil {
|
|
return nil, errors.New("invalid empresa_id")
|
|
}
|
|
// Note: ListCadastroFotByEmpresaRow is nearly identical to ListCadastroFotRow but we use the generated type
|
|
return s.queries.ListCadastroFotByEmpresa(ctx, generated.ListCadastroFotByEmpresaParams{
|
|
EmpresaID: pgtype.UUID{Bytes: uuidVal, Valid: true},
|
|
Regiao: pgtype.Text{String: regiao, Valid: true},
|
|
})
|
|
}
|
|
|
|
func (s *Service) GetByID(ctx context.Context, id string, regiao string) (*generated.GetCadastroFotByIDRow, error) {
|
|
uuidVal, err := uuid.Parse(id)
|
|
if err != nil {
|
|
return nil, errors.New("invalid id")
|
|
}
|
|
item, err := s.queries.GetCadastroFotByID(ctx, generated.GetCadastroFotByIDParams{
|
|
ID: pgtype.UUID{Bytes: uuidVal, Valid: true},
|
|
Regiao: pgtype.Text{String: regiao, Valid: true},
|
|
})
|
|
return &item, err
|
|
}
|
|
|
|
func (s *Service) Update(ctx context.Context, id string, input CreateInput, regiao string) (*generated.CadastroFot, error) {
|
|
uuidVal, err := uuid.Parse(id)
|
|
if err != nil {
|
|
return nil, errors.New("invalid id")
|
|
}
|
|
empresaUUID, _ := uuid.Parse(input.EmpresaID)
|
|
cursoUUID, _ := uuid.Parse(input.CursoID)
|
|
anoUUID, _ := uuid.Parse(input.AnoFormaturaID)
|
|
|
|
item, err := s.queries.UpdateCadastroFot(ctx, generated.UpdateCadastroFotParams{
|
|
ID: pgtype.UUID{Bytes: uuidVal, Valid: true},
|
|
Fot: input.Fot,
|
|
EmpresaID: pgtype.UUID{Bytes: empresaUUID, Valid: true},
|
|
CursoID: pgtype.UUID{Bytes: cursoUUID, Valid: true},
|
|
AnoFormaturaID: pgtype.UUID{Bytes: anoUUID, Valid: true},
|
|
Instituicao: pgtype.Text{String: input.Instituicao, Valid: true},
|
|
Cidade: pgtype.Text{String: input.Cidade, Valid: true},
|
|
Estado: pgtype.Text{String: input.Estado, Valid: true},
|
|
Observacoes: pgtype.Text{String: input.Observacoes, Valid: true},
|
|
GastosCaptacao: toPgNumeric(input.GastosCaptacao),
|
|
PreVenda: pgtype.Bool{Bool: input.PreVenda, Valid: true},
|
|
Regiao: pgtype.Text{String: regiao, Valid: true},
|
|
})
|
|
return &item, err
|
|
}
|
|
|
|
func (s *Service) Delete(ctx context.Context, id string, regiao string) error {
|
|
uuidVal, err := uuid.Parse(id)
|
|
if err != nil {
|
|
return errors.New("invalid id")
|
|
}
|
|
return s.queries.DeleteCadastroFot(ctx, generated.DeleteCadastroFotParams{
|
|
ID: pgtype.UUID{Bytes: uuidVal, Valid: true},
|
|
Regiao: pgtype.Text{String: regiao, Valid: true},
|
|
})
|
|
}
|
|
|
|
// Helper to convert float to numeric robustly
|
|
func toPgNumeric(f float64) pgtype.Numeric {
|
|
var n pgtype.Numeric
|
|
s := strconv.FormatFloat(f, 'f', -1, 64)
|
|
if err := n.Scan(s); err != nil {
|
|
return pgtype.Numeric{Valid: false}
|
|
}
|
|
return n
|
|
}
|
|
|
|
// ToggleFinalizada sets the finalized status of a FOT
|
|
func (s *Service) ToggleFinalizada(ctx context.Context, id string, finalizada bool, regiao string) (*generated.CadastroFot, error) {
|
|
uuidVal, err := uuid.Parse(id)
|
|
if err != nil {
|
|
return nil, errors.New("invalid id")
|
|
}
|
|
item, err := s.queries.ToggleFinalizada(ctx, generated.ToggleFinalizadaParams{
|
|
ID: pgtype.UUID{Bytes: uuidVal, Valid: true},
|
|
Finalizada: pgtype.Bool{Bool: finalizada, Valid: true},
|
|
Regiao: pgtype.Text{String: regiao, Valid: true},
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &item, nil
|
|
}
|
|
|
|
// ImportInput for Batch Import
|
|
type ImportInput struct {
|
|
Fot string `json:"fot"`
|
|
EmpresaNome string `json:"empresa_nome"`
|
|
CursoNome string `json:"curso_nome"`
|
|
AnoFormaturaLabel string `json:"ano_formatura_label"`
|
|
Instituicao string `json:"instituicao"`
|
|
Cidade string `json:"cidade"`
|
|
Estado string `json:"estado"`
|
|
Observacoes string `json:"observacoes"`
|
|
GastosCaptacao float64 `json:"gastos_captacao"`
|
|
PreVenda bool `json:"pre_venda"`
|
|
}
|
|
|
|
type ImportResult struct {
|
|
SuccessCount int
|
|
Errors []string
|
|
}
|
|
|
|
func (s *Service) BatchImport(ctx context.Context, items []ImportInput, regiao string) ImportResult {
|
|
result := ImportResult{Errors: []string{}}
|
|
|
|
for i, item := range items {
|
|
// 1. Resolve Empresa
|
|
empresa, err := s.queries.GetEmpresaByNome(ctx, generated.GetEmpresaByNomeParams{
|
|
Nome: item.EmpresaNome,
|
|
Regiao: pgtype.Text{String: regiao, Valid: true},
|
|
})
|
|
var empresaID pgtype.UUID
|
|
if err != nil {
|
|
// Try Create
|
|
newEmp, errCreate := s.queries.CreateEmpresa(ctx, generated.CreateEmpresaParams{
|
|
Nome: item.EmpresaNome,
|
|
Regiao: pgtype.Text{String: regiao, Valid: true},
|
|
})
|
|
if errCreate != nil {
|
|
// Retry Get if duplicate
|
|
if strings.Contains(errCreate.Error(), "duplicate key") || strings.Contains(errCreate.Error(), "23505") {
|
|
empresaRetry, errRetry := s.queries.GetEmpresaByNome(ctx, generated.GetEmpresaByNomeParams{
|
|
Nome: item.EmpresaNome,
|
|
Regiao: pgtype.Text{String: regiao, Valid: true},
|
|
})
|
|
if errRetry != nil {
|
|
result.Errors = append(result.Errors, "Row "+strconv.Itoa(i+1)+": Error resolving empresa (retry): "+errRetry.Error())
|
|
continue
|
|
}
|
|
empresaID = empresaRetry.ID
|
|
} else {
|
|
result.Errors = append(result.Errors, "Row "+strconv.Itoa(i+1)+": Error creating empresa: "+errCreate.Error())
|
|
continue
|
|
}
|
|
} else {
|
|
empresaID = newEmp.ID
|
|
}
|
|
} else {
|
|
empresaID = empresa.ID
|
|
}
|
|
|
|
// 2. Resolve Curso
|
|
curso, err := s.queries.GetCursoByNome(ctx, generated.GetCursoByNomeParams{
|
|
Nome: item.CursoNome,
|
|
Regiao: pgtype.Text{String: regiao, Valid: true},
|
|
})
|
|
var cursoID pgtype.UUID
|
|
if err != nil {
|
|
newCurso, errCreate := s.queries.CreateCurso(ctx, generated.CreateCursoParams{
|
|
Nome: item.CursoNome,
|
|
Regiao: pgtype.Text{String: regiao, Valid: true},
|
|
})
|
|
if errCreate != nil {
|
|
if strings.Contains(errCreate.Error(), "duplicate key") || strings.Contains(errCreate.Error(), "23505") {
|
|
cursoRetry, errRetry := s.queries.GetCursoByNome(ctx, generated.GetCursoByNomeParams{
|
|
Nome: item.CursoNome,
|
|
Regiao: pgtype.Text{String: regiao, Valid: true},
|
|
})
|
|
if errRetry != nil {
|
|
result.Errors = append(result.Errors, "Row "+strconv.Itoa(i+1)+": Error resolving curso (retry): "+errRetry.Error())
|
|
continue
|
|
}
|
|
cursoID = cursoRetry.ID
|
|
} else {
|
|
result.Errors = append(result.Errors, "Row "+strconv.Itoa(i+1)+": Error creating curso: "+errCreate.Error())
|
|
continue
|
|
}
|
|
} else {
|
|
cursoID = newCurso.ID
|
|
}
|
|
} else {
|
|
cursoID = curso.ID
|
|
}
|
|
|
|
// 3. Resolve Ano Formatura
|
|
ano, err := s.queries.GetAnoFormaturaByNome(ctx, generated.GetAnoFormaturaByNomeParams{
|
|
AnoSemestre: item.AnoFormaturaLabel,
|
|
Regiao: pgtype.Text{String: regiao, Valid: true},
|
|
})
|
|
var anoID pgtype.UUID
|
|
if err != nil {
|
|
newAno, errCreate := s.queries.CreateAnoFormatura(ctx, generated.CreateAnoFormaturaParams{
|
|
AnoSemestre: item.AnoFormaturaLabel,
|
|
Regiao: pgtype.Text{String: regiao, Valid: true},
|
|
})
|
|
if errCreate != nil {
|
|
if strings.Contains(errCreate.Error(), "duplicate key") || strings.Contains(errCreate.Error(), "23505") {
|
|
anoRetry, errRetry := s.queries.GetAnoFormaturaByNome(ctx, generated.GetAnoFormaturaByNomeParams{
|
|
AnoSemestre: item.AnoFormaturaLabel,
|
|
Regiao: pgtype.Text{String: regiao, Valid: true},
|
|
})
|
|
if errRetry != nil {
|
|
result.Errors = append(result.Errors, "Row "+strconv.Itoa(i+1)+": Error resolving ano (retry): "+errRetry.Error())
|
|
continue
|
|
}
|
|
anoID = anoRetry.ID
|
|
} else {
|
|
result.Errors = append(result.Errors, "Row "+strconv.Itoa(i+1)+": Error creating ano: "+errCreate.Error())
|
|
continue
|
|
}
|
|
} else {
|
|
anoID = newAno.ID
|
|
}
|
|
} else {
|
|
anoID = ano.ID
|
|
}
|
|
|
|
// 4. Insert Cadastro FOT
|
|
// Check if exists? Or try create and catch duplicate
|
|
// Ideally we update if exists? Let's just try Create for now.
|
|
_, err = s.queries.CreateCadastroFot(ctx, generated.CreateCadastroFotParams{
|
|
Fot: item.Fot,
|
|
EmpresaID: empresaID,
|
|
CursoID: cursoID,
|
|
AnoFormaturaID: anoID,
|
|
Instituicao: pgtype.Text{String: item.Instituicao, Valid: true},
|
|
Cidade: pgtype.Text{String: item.Cidade, Valid: true},
|
|
Estado: pgtype.Text{String: item.Estado, Valid: true},
|
|
Observacoes: pgtype.Text{String: item.Observacoes, Valid: true},
|
|
GastosCaptacao: toPgNumeric(item.GastosCaptacao),
|
|
PreVenda: pgtype.Bool{Bool: item.PreVenda, Valid: true},
|
|
Regiao: pgtype.Text{String: regiao, Valid: true},
|
|
})
|
|
|
|
if err != nil {
|
|
result.Errors = append(result.Errors, "Row "+strconv.Itoa(i+1)+": Error inserting FOT "+item.Fot+": "+err.Error())
|
|
} else {
|
|
result.SuccessCount++
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|