fix: (codigo-acesso) ajustado filtros

This commit is contained in:
NANDO9322 2026-02-03 11:29:45 -03:00
parent 8e95828f85
commit 67a82f2189
7 changed files with 169 additions and 47 deletions

View file

@ -54,6 +54,14 @@ func (h *Handler) List(c *gin.Context) {
resp := make([]map[string]interface{}, len(codes))
for i, v := range codes {
var empID, empNome string
if v.EmpresaID.Valid {
empID = uuid.UUID(v.EmpresaID.Bytes).String()
}
if v.EmpresaNome.Valid {
empNome = v.EmpresaNome.String
}
resp[i] = map[string]interface{}{
"id": uuid.UUID(v.ID.Bytes).String(),
"codigo": v.Codigo,
@ -63,6 +71,8 @@ func (h *Handler) List(c *gin.Context) {
"expira_em": v.ExpiraEm.Time,
"ativo": v.Ativo,
"usos": v.Usos,
"empresa_id": empID,
"empresa_nome": empNome,
}
}
c.JSON(http.StatusOK, resp)
@ -103,7 +113,7 @@ func (h *Handler) Verify(c *gin.Context) {
return
}
err := h.service.Verify(c.Request.Context(), code)
codeData, err := h.service.Verify(c.Request.Context(), code)
if err != nil {
// Distinguish validation error from DB error strictly?
// For security, just say invalid.
@ -117,5 +127,14 @@ func (h *Handler) Verify(c *gin.Context) {
return
}
c.JSON(http.StatusOK, gin.H{"valid": true})
// Prepare response
resp := gin.H{"valid": true}
if codeData.EmpresaID.Valid {
resp["empresa_id"] = uuid.UUID(codeData.EmpresaID.Bytes).String()
}
if codeData.EmpresaNome.Valid {
resp["empresa_nome"] = codeData.EmpresaNome.String
}
c.JSON(http.StatusOK, resp)
}

View file

@ -24,6 +24,7 @@ type CreateCodigoInput struct {
Codigo string `json:"codigo"`
Descricao string `json:"descricao"`
ValidadeDias int32 `json:"validade_dias"`
EmpresaID string `json:"empresa_id"`
}
func (s *Service) Create(ctx context.Context, input CreateCodigoInput) (generated.CodigosAcesso, error) {
@ -40,16 +41,26 @@ func (s *Service) Create(ctx context.Context, input CreateCodigoInput) (generate
expiraEm = time.Now().Add(time.Duration(days) * 24 * time.Hour)
}
var empresaUUID pgtype.UUID
if input.EmpresaID != "" {
parsed, err := uuid.Parse(input.EmpresaID)
if err == nil {
empresaUUID.Bytes = parsed
empresaUUID.Valid = true
}
}
return s.q.CreateCodigoAcesso(ctx, generated.CreateCodigoAcessoParams{
Codigo: input.Codigo,
Descricao: pgtype.Text{String: input.Descricao, Valid: input.Descricao != ""},
ValidadeDias: days,
ExpiraEm: pgtype.Timestamptz{Time: expiraEm, Valid: true},
Ativo: true,
EmpresaID: empresaUUID,
})
}
func (s *Service) List(ctx context.Context) ([]generated.CodigosAcesso, error) {
func (s *Service) List(ctx context.Context) ([]generated.ListCodigosAcessoRow, error) {
return s.q.ListCodigosAcesso(ctx)
}
@ -67,7 +78,7 @@ func (s *Service) Delete(ctx context.Context, id string) error {
return s.q.DeleteCodigoAcesso(ctx, pgUUID)
}
func (s *Service) GetByCode(ctx context.Context, code string) (generated.CodigosAcesso, error) {
func (s *Service) GetByCode(ctx context.Context, code string) (generated.GetCodigoAcessoRow, error) {
return s.q.GetCodigoAcesso(ctx, code)
}
@ -87,19 +98,19 @@ func (e *AppError) Error() string {
return e.Message
}
func (s *Service) Verify(ctx context.Context, code string) error {
func (s *Service) Verify(ctx context.Context, code string) (*generated.GetCodigoAcessoRow, error) {
c, err := s.q.GetCodigoAcesso(ctx, code)
if err != nil {
return err // Not found or DB error
return nil, err // Not found or DB error
}
if !c.Ativo {
return &AppError{Message: "Código inativo"}
return nil, &AppError{Message: "Código inativo"}
}
if time.Now().After(c.ExpiraEm.Time) {
return &AppError{Message: "Código expirado"}
return nil, &AppError{Message: "Código expirado"}
}
return nil
return &c, nil
}

View file

@ -13,11 +13,11 @@ import (
const createCodigoAcesso = `-- name: CreateCodigoAcesso :one
INSERT INTO codigos_acesso (
codigo, descricao, validade_dias, expira_em, ativo
codigo, descricao, validade_dias, expira_em, ativo, empresa_id
) VALUES (
$1, $2, $3, $4, $5
$1, $2, $3, $4, $5, $6
)
RETURNING id, codigo, descricao, validade_dias, criado_em, expira_em, ativo, usos
RETURNING id, codigo, descricao, validade_dias, criado_em, expira_em, ativo, usos, empresa_id
`
type CreateCodigoAcessoParams struct {
@ -26,6 +26,7 @@ type CreateCodigoAcessoParams struct {
ValidadeDias int32 `json:"validade_dias"`
ExpiraEm pgtype.Timestamptz `json:"expira_em"`
Ativo bool `json:"ativo"`
EmpresaID pgtype.UUID `json:"empresa_id"`
}
func (q *Queries) CreateCodigoAcesso(ctx context.Context, arg CreateCodigoAcessoParams) (CodigosAcesso, error) {
@ -35,6 +36,7 @@ func (q *Queries) CreateCodigoAcesso(ctx context.Context, arg CreateCodigoAcesso
arg.ValidadeDias,
arg.ExpiraEm,
arg.Ativo,
arg.EmpresaID,
)
var i CodigosAcesso
err := row.Scan(
@ -46,6 +48,7 @@ func (q *Queries) CreateCodigoAcesso(ctx context.Context, arg CreateCodigoAcesso
&i.ExpiraEm,
&i.Ativo,
&i.Usos,
&i.EmpresaID,
)
return i, err
}
@ -61,13 +64,28 @@ func (q *Queries) DeleteCodigoAcesso(ctx context.Context, id pgtype.UUID) error
}
const getCodigoAcesso = `-- name: GetCodigoAcesso :one
SELECT id, codigo, descricao, validade_dias, criado_em, expira_em, ativo, usos FROM codigos_acesso
WHERE codigo = $1 LIMIT 1
SELECT c.id, c.codigo, c.descricao, c.validade_dias, c.criado_em, c.expira_em, c.ativo, c.usos, c.empresa_id, e.nome as empresa_nome
FROM codigos_acesso c
LEFT JOIN empresas e ON c.empresa_id = e.id
WHERE c.codigo = $1 LIMIT 1
`
func (q *Queries) GetCodigoAcesso(ctx context.Context, codigo string) (CodigosAcesso, error) {
type GetCodigoAcessoRow struct {
ID pgtype.UUID `json:"id"`
Codigo string `json:"codigo"`
Descricao pgtype.Text `json:"descricao"`
ValidadeDias int32 `json:"validade_dias"`
CriadoEm pgtype.Timestamptz `json:"criado_em"`
ExpiraEm pgtype.Timestamptz `json:"expira_em"`
Ativo bool `json:"ativo"`
Usos int32 `json:"usos"`
EmpresaID pgtype.UUID `json:"empresa_id"`
EmpresaNome pgtype.Text `json:"empresa_nome"`
}
func (q *Queries) GetCodigoAcesso(ctx context.Context, codigo string) (GetCodigoAcessoRow, error) {
row := q.db.QueryRow(ctx, getCodigoAcesso, codigo)
var i CodigosAcesso
var i GetCodigoAcessoRow
err := row.Scan(
&i.ID,
&i.Codigo,
@ -77,6 +95,8 @@ func (q *Queries) GetCodigoAcesso(ctx context.Context, codigo string) (CodigosAc
&i.ExpiraEm,
&i.Ativo,
&i.Usos,
&i.EmpresaID,
&i.EmpresaNome,
)
return i, err
}
@ -93,19 +113,34 @@ func (q *Queries) IncrementCodigoAcessoUso(ctx context.Context, id pgtype.UUID)
}
const listCodigosAcesso = `-- name: ListCodigosAcesso :many
SELECT id, codigo, descricao, validade_dias, criado_em, expira_em, ativo, usos FROM codigos_acesso
ORDER BY criado_em DESC
SELECT c.id, c.codigo, c.descricao, c.validade_dias, c.criado_em, c.expira_em, c.ativo, c.usos, c.empresa_id, e.nome as empresa_nome
FROM codigos_acesso c
LEFT JOIN empresas e ON c.empresa_id = e.id
ORDER BY c.criado_em DESC
`
func (q *Queries) ListCodigosAcesso(ctx context.Context) ([]CodigosAcesso, error) {
type ListCodigosAcessoRow struct {
ID pgtype.UUID `json:"id"`
Codigo string `json:"codigo"`
Descricao pgtype.Text `json:"descricao"`
ValidadeDias int32 `json:"validade_dias"`
CriadoEm pgtype.Timestamptz `json:"criado_em"`
ExpiraEm pgtype.Timestamptz `json:"expira_em"`
Ativo bool `json:"ativo"`
Usos int32 `json:"usos"`
EmpresaID pgtype.UUID `json:"empresa_id"`
EmpresaNome pgtype.Text `json:"empresa_nome"`
}
func (q *Queries) ListCodigosAcesso(ctx context.Context) ([]ListCodigosAcessoRow, error) {
rows, err := q.db.Query(ctx, listCodigosAcesso)
if err != nil {
return nil, err
}
defer rows.Close()
var items []CodigosAcesso
var items []ListCodigosAcessoRow
for rows.Next() {
var i CodigosAcesso
var i ListCodigosAcessoRow
if err := rows.Scan(
&i.ID,
&i.Codigo,
@ -115,6 +150,8 @@ func (q *Queries) ListCodigosAcesso(ctx context.Context) ([]CodigosAcesso, error
&i.ExpiraEm,
&i.Ativo,
&i.Usos,
&i.EmpresaID,
&i.EmpresaNome,
); err != nil {
return nil, err
}

View file

@ -135,6 +135,7 @@ type CodigosAcesso struct {
ExpiraEm pgtype.Timestamptz `json:"expira_em"`
Ativo bool `json:"ativo"`
Usos int32 `json:"usos"`
EmpresaID pgtype.UUID `json:"empresa_id"`
}
type Curso struct {

View file

@ -1,22 +1,26 @@
-- name: CreateCodigoAcesso :one
INSERT INTO codigos_acesso (
codigo, descricao, validade_dias, expira_em, ativo
codigo, descricao, validade_dias, expira_em, ativo, empresa_id
) VALUES (
$1, $2, $3, $4, $5
$1, $2, $3, $4, $5, $6
)
RETURNING *;
-- name: ListCodigosAcesso :many
SELECT * FROM codigos_acesso
ORDER BY criado_em DESC;
SELECT c.*, e.nome as empresa_nome
FROM codigos_acesso c
LEFT JOIN empresas e ON c.empresa_id = e.id
ORDER BY c.criado_em DESC;
-- name: DeleteCodigoAcesso :exec
DELETE FROM codigos_acesso
WHERE id = $1;
-- name: GetCodigoAcesso :one
SELECT * FROM codigos_acesso
WHERE codigo = $1 LIMIT 1;
SELECT c.*, e.nome as empresa_nome
FROM codigos_acesso c
LEFT JOIN empresas e ON c.empresa_id = e.id
WHERE c.codigo = $1 LIMIT 1;
-- name: IncrementCodigoAcessoUso :exec
UPDATE codigos_acesso

View file

@ -440,7 +440,8 @@ CREATE TABLE IF NOT EXISTS codigos_acesso (
criado_em TIMESTAMPTZ NOT NULL DEFAULT NOW(),
expira_em TIMESTAMPTZ NOT NULL,
ativo BOOLEAN NOT NULL DEFAULT TRUE,
usos INT NOT NULL DEFAULT 0
usos INT NOT NULL DEFAULT 0,
empresa_id UUID REFERENCES empresas(id) ON DELETE SET NULL
);
-- Financeiro Extrato

View file

@ -376,26 +376,75 @@ export const ImportData: React.FC = () => {
if (activeTab === 'fot') {
const fot = getStr(0);
if (!fot) continue;
// Parse Gastos
let gastosStr = getStr(8);
gastosStr = gastosStr.replace(/[R$\s.]/g, '').replace(',', '.');
const gastos = parseFloat(gastosStr) || 0;
// Dynamic Mapping for FOT
let headerIdx = -1;
const colMap: {[key: string]: number} = {};
// 1. Find header row
for (let i = 0; i < 20 && i < rows.length; i++) {
const rowStrings = (rows[i] as any[]).map(c => String(c).toLowerCase().trim());
if (rowStrings.some(s => s === 'fot' || s === 'empresa' || s.includes('contrato'))) {
headerIdx = i;
rowStrings.forEach((h, idx) => {
if (h === 'fot' || h.includes('contrato')) colMap['fot'] = idx;
else if (h === 'empresa' || h.includes('cliente')) colMap['empresa'] = idx;
else if (h.includes('curso')) colMap['curso'] = idx;
else if (h.includes('obs') || h.includes('observa')) colMap['obs'] = idx;
else if (h.includes('institui')) colMap['instituicao'] = idx;
else if (h.includes('ano') || h.includes('formatur')) colMap['ano'] = idx;
else if (h === 'cidade') colMap['cidade'] = idx;
else if (h === 'estado' || h === 'uf') colMap['estado'] = idx;
else if (h.includes('gasto') || h.includes('capta')) colMap['gastos'] = idx;
else if (h.includes('pré') || h.includes('pre') || h.includes('venda')) colMap['prevenda'] = idx;
});
break;
}
}
const item: ImportFotInput = {
fot: fot,
empresa_nome: getStr(1),
curso_nome: getStr(2),
observacoes: getStr(3),
instituicao: getStr(4),
ano_formatura_label: getStr(5),
cidade: getStr(6),
estado: getStr(7),
gastos_captacao: gastos,
pre_venda: getStr(9).toLowerCase().includes('sim'),
};
mappedData.push(item);
if (headerIdx === -1) {
// Fallback to indices if not found (Legacy/Default)
headerIdx = 0;
// Default: FOT, Empresa, Curso, Obs, Inst, Ano, Cid, UF, Gastos, Prevenda
colMap['fot'] = 0; colMap['empresa'] = 1; colMap['curso'] = 2; colMap['obs'] = 3;
colMap['instituicao'] = 4; colMap['ano'] = 5; colMap['cidade'] = 6; colMap['estado'] = 7;
colMap['gastos'] = 8; colMap['prevenda'] = 9;
}
// Iterate
for (let i = headerIdx + 1; i < rows.length; i++) {
const row = rows[i] as any[];
if (!row || row.length === 0) continue;
const getCol = (key: string) => {
const idx = colMap[key];
if (idx === undefined || idx < 0) return "";
return row[idx] !== undefined ? String(row[idx]).trim() : "";
};
const fot = getCol('fot');
// Strict validation: Must have at least 3 chars to be valid FOT (usually 5 digits)
if (!fot || fot.length < 3) continue;
// Parse Gastos
let gastosStr = getCol('gastos');
gastosStr = gastosStr.replace(/[R$\s.]/g, '').replace(',', '.');
const gastos = parseFloat(gastosStr) || 0;
const item: ImportFotInput = {
fot: fot,
empresa_nome: getCol('empresa'),
curso_nome: getCol('curso'),
observacoes: getCol('obs'),
instituicao: getCol('instituicao'),
ano_formatura_label: getCol('ano'),
cidade: getCol('cidade'),
estado: getCol('estado'),
gastos_captacao: gastos,
pre_venda: getCol('prevenda').toLowerCase().includes('sim'),
};
mappedData.push(item);
}
} else if (activeTab === 'agenda') {
const fot = getStr(0);