diff --git a/backend/cmd/api/main.go b/backend/cmd/api/main.go
index 4da8c27..8c08998 100644
--- a/backend/cmd/api/main.go
+++ b/backend/cmd/api/main.go
@@ -73,6 +73,7 @@ func main() {
// Initialize services
notificationService := notification.NewService()
profissionaisService := profissionais.NewService(queries)
+ financeService := finance.NewService(queries, profissionaisService)
authService := auth.NewService(queries, profissionaisService, cfg)
funcoesService := funcoes.NewService(queries)
cursosService := cursos.NewService(queries)
@@ -81,7 +82,7 @@ func main() {
tiposServicosService := tipos_servicos.NewService(queries)
tiposEventosService := tipos_eventos.NewService(queries)
cadastroFotService := cadastro_fot.NewService(queries)
- agendaService := agenda.NewService(queries, notificationService, cfg)
+ agendaService := agenda.NewService(queries, notificationService, cfg, financeService)
availabilityService := availability.NewService(queries)
s3Service := storage.NewS3Service(cfg)
@@ -105,7 +106,7 @@ func main() {
escalasHandler := escalas.NewHandler(escalas.NewService(queries))
logisticaHandler := logistica.NewHandler(logistica.NewService(queries, notificationService, cfg))
codigosHandler := codigos.NewHandler(codigos.NewService(queries))
- financeHandler := finance.NewHandler(finance.NewService(queries, profissionaisService))
+ financeHandler := finance.NewHandler(financeService)
r := gin.Default()
diff --git a/backend/internal/agenda/service.go b/backend/internal/agenda/service.go
index 089345c..e7b6a61 100644
--- a/backend/internal/agenda/service.go
+++ b/backend/internal/agenda/service.go
@@ -14,21 +14,25 @@ import (
"photum-backend/internal/db/generated"
"photum-backend/internal/notification"
+ "photum-backend/internal/finance"
+
"github.com/google/uuid"
"github.com/jackc/pgx/v5/pgtype"
)
type Service struct {
- queries *generated.Queries
- notification *notification.Service
- cfg *config.Config
+ queries *generated.Queries
+ notification *notification.Service
+ financeService *finance.Service
+ cfg *config.Config
}
-func NewService(db *generated.Queries, notif *notification.Service, cfg *config.Config) *Service {
+func NewService(db *generated.Queries, notif *notification.Service, cfg *config.Config, fin *finance.Service) *Service {
return &Service{
- queries: db,
- notification: notif,
- cfg: cfg,
+ queries: db,
+ notification: notif,
+ cfg: cfg,
+ financeService: fin,
}
}
@@ -751,6 +755,87 @@ func (s *Service) UpdateAssignmentStatus(ctx context.Context, agendaID, professi
return fmt.Errorf("conflito de horário: profissional já confirmou presença em outro evento às %s", c.Horario.String)
}
}
+
+ }
+
+ // --- AUTO-CREATE FINANCIAL TRANSACTION ---
+ // 1. Fetch Assignment to know the Function/Role
+ assign, err := s.queries.GetAssignment(ctx, generated.GetAssignmentParams{
+ AgendaID: pgtype.UUID{Bytes: agendaID, Valid: true},
+ ProfissionalID: pgtype.UUID{Bytes: professionalID, Valid: true},
+ })
+ if err != nil {
+ log.Printf("[AutoFinance] Error fetching assignment: %v", err)
+ } else {
+ // 2. Determine Service Name
+ var serviceName string
+ if assign.FuncaoID.Valid {
+ fn, err := s.queries.GetFuncaoByID(ctx, assign.FuncaoID)
+ if err == nil {
+ serviceName = fn.Nome
+ }
+ }
+
+ // Fallback to professional's default function
+ prof, errProf := s.queries.GetProfissionalByID(ctx, generated.GetProfissionalByIDParams{
+ ID: pgtype.UUID{Bytes: professionalID, Valid: true},
+ Regiao: pgtype.Text{String: regiao, Valid: true},
+ })
+ if serviceName == "" && errProf == nil && prof.FuncaoProfissionalID.Valid {
+ fn, err := s.queries.GetFuncaoByID(ctx, prof.FuncaoProfissionalID)
+ if err == nil {
+ serviceName = fn.Nome
+ }
+ }
+
+ // 3. Fetch Event Type Name
+ tipoEvento, errType := s.queries.GetTipoEventoByID(ctx, generated.GetTipoEventoByIDParams{
+ ID: agenda.TipoEventoID,
+ Regiao: pgtype.Text{String: regiao, Valid: true},
+ })
+
+ if serviceName != "" && errType == nil && errProf == nil {
+ // 4. Fetch Standard Price
+ price, errPrice := s.financeService.GetStandardPrice(ctx, tipoEvento.Nome, serviceName, regiao)
+
+ baseValue := 0.0
+ var priceNumeric pgtype.Numeric
+ if errPrice == nil {
+ priceNumeric = price
+ if val, err := price.Float64Value(); err == nil && val.Valid {
+ baseValue = val.Float64
+ }
+ } else {
+ log.Printf("[AutoFinance] Price not found for %s / %s: %v", tipoEvento.Nome, serviceName, errPrice)
+ // We continue with 0.0 or handle error?
+ // User wants it to appear. If 0, it appears as 0.
+ priceNumeric.Scan("0")
+ }
+
+ // 5. Create Transaction
+ // Check if already exists? (Optional, but good practice. For now simpler is better)
+
+ _, errCreate := s.financeService.Create(ctx, generated.CreateTransactionParams{
+ FotID: agenda.FotID,
+ DataCobranca: agenda.DataEvento,
+ TipoEvento: pgtype.Text{String: tipoEvento.Nome, Valid: true},
+ TipoServico: pgtype.Text{String: serviceName, Valid: true},
+ ProfessionalName: pgtype.Text{String: prof.Nome, Valid: true},
+ Whatsapp: prof.Whatsapp,
+ Cpf: prof.CpfCnpjTitular,
+ TotalPagar: priceNumeric, // Base Value
+ ValorFree: priceNumeric, // Populate Free value for frontend calc
+ ProfissionalID: pgtype.UUID{Bytes: professionalID, Valid: true},
+ PgtoOk: pgtype.Bool{Bool: false, Valid: true},
+ // Others default/null
+ }, regiao)
+
+ if errCreate != nil {
+ log.Printf("[AutoFinance] Failed to create transaction: %v", errCreate)
+ } else {
+ log.Printf("[AutoFinance] Transaction created for %s: %.2f", prof.Nome, baseValue)
+ }
+ }
}
}
}
diff --git a/backend/internal/db/generated/agenda.sql.go b/backend/internal/db/generated/agenda.sql.go
index 0251d4a..45ba2f5 100644
--- a/backend/internal/db/generated/agenda.sql.go
+++ b/backend/internal/db/generated/agenda.sql.go
@@ -427,6 +427,33 @@ func (q *Queries) GetAgendaProfessionals(ctx context.Context, agendaID pgtype.UU
return items, nil
}
+const getAssignment = `-- name: GetAssignment :one
+SELECT id, agenda_id, profissional_id, status, motivo_rejeicao, funcao_id, posicao, criado_em, is_coordinator FROM agenda_profissionais
+WHERE agenda_id = $1 AND profissional_id = $2
+`
+
+type GetAssignmentParams struct {
+ AgendaID pgtype.UUID `json:"agenda_id"`
+ ProfissionalID pgtype.UUID `json:"profissional_id"`
+}
+
+func (q *Queries) GetAssignment(ctx context.Context, arg GetAssignmentParams) (AgendaProfissionai, error) {
+ row := q.db.QueryRow(ctx, getAssignment, arg.AgendaID, arg.ProfissionalID)
+ var i AgendaProfissionai
+ err := row.Scan(
+ &i.ID,
+ &i.AgendaID,
+ &i.ProfissionalID,
+ &i.Status,
+ &i.MotivoRejeicao,
+ &i.FuncaoID,
+ &i.Posicao,
+ &i.CriadoEm,
+ &i.IsCoordinator,
+ )
+ return i, err
+}
+
const listAgendas = `-- name: ListAgendas :many
SELECT
a.id, a.user_id, a.fot_id, a.data_evento, a.tipo_evento_id, a.observacoes_evento, a.local_evento, a.endereco, a.horario, a.qtd_formandos, a.qtd_fotografos, a.qtd_recepcionistas, a.qtd_cinegrafistas, a.qtd_estudios, a.qtd_ponto_foto, a.qtd_ponto_id, a.qtd_ponto_decorado, a.qtd_pontos_led, a.qtd_plataforma_360, a.status_profissionais, a.foto_faltante, a.recep_faltante, a.cine_faltante, a.logistica_observacoes, a.pre_venda, a.criado_em, a.atualizado_em, a.status, a.logistica_notificacao_enviada_em, a.regiao, a.contatos,
diff --git a/backend/internal/db/generated/tipos_eventos.sql.go b/backend/internal/db/generated/tipos_eventos.sql.go
index aedbe6f..d6f3d1b 100644
--- a/backend/internal/db/generated/tipos_eventos.sql.go
+++ b/backend/internal/db/generated/tipos_eventos.sql.go
@@ -118,7 +118,7 @@ SELECT p.valor
FROM precos_tipos_eventos p
JOIN tipos_eventos te ON p.tipo_evento_id = te.id
JOIN funcoes_profissionais f ON p.funcao_profissional_id = f.id
-WHERE te.nome = $1 AND f.nome = $2 AND te.regiao = $3
+WHERE te.nome ILIKE $1 AND f.nome ILIKE $2 AND te.regiao = $3
LIMIT 1
`
diff --git a/backend/internal/db/queries/agenda.sql b/backend/internal/db/queries/agenda.sql
index e9a0834..1651555 100644
--- a/backend/internal/db/queries/agenda.sql
+++ b/backend/internal/db/queries/agenda.sql
@@ -255,4 +255,8 @@ ORDER BY a.data_evento;
-- name: SetCoordinator :exec
UPDATE agenda_profissionais
SET is_coordinator = $3
-WHERE agenda_id = $1 AND profissional_id = $2;
\ No newline at end of file
+WHERE agenda_id = $1 AND profissional_id = $2;
+
+-- name: GetAssignment :one
+SELECT * FROM agenda_profissionais
+WHERE agenda_id = $1 AND profissional_id = $2;
diff --git a/backend/internal/db/queries/tipos_eventos.sql b/backend/internal/db/queries/tipos_eventos.sql
index 37993bc..2a800b3 100644
--- a/backend/internal/db/queries/tipos_eventos.sql
+++ b/backend/internal/db/queries/tipos_eventos.sql
@@ -37,7 +37,7 @@ SELECT p.valor
FROM precos_tipos_eventos p
JOIN tipos_eventos te ON p.tipo_evento_id = te.id
JOIN funcoes_profissionais f ON p.funcao_profissional_id = f.id
-WHERE te.nome = $1 AND f.nome = $2 AND te.regiao = @regiao
+WHERE te.nome ILIKE $1 AND f.nome ILIKE $2 AND te.regiao = @regiao
LIMIT 1;
-- name: GetTipoEventoByNome :one
diff --git a/frontend/pages/Dashboard.tsx b/frontend/pages/Dashboard.tsx
index 2123952..b1785f1 100644
--- a/frontend/pages/Dashboard.tsx
+++ b/frontend/pages/Dashboard.tsx
@@ -22,7 +22,7 @@ import {
AlertCircle,
Star,
} from "lucide-react";
-import { setCoordinator, finalizeFOT } from "../services/apiService";
+import { setCoordinator, finalizeFOT, getPrice } from "../services/apiService";
import { useAuth } from "../contexts/AuthContext";
import { useData } from "../contexts/DataContext";
import { STATUS_COLORS } from "../constants";
@@ -151,6 +151,97 @@ export const Dashboard: React.FC
0 ? "text-green-700" : "text-gray-500"}`}> + Valor Base +
+0 ? "text-green-800" : "text-gray-700"}`}> + {basePrice > 0 ? basePrice.toLocaleString("pt-BR", { style: "currency", currency: "BRL" }) : "R$ 0,00"} +
+