fix: correção de duplicidade de preços e melhorias na UX financeira
- Backend: - Ajustada query `GetStandardPrice` para filtrar por região e ordenar por data. - Corrigido `SetPrice` para usar o contexto de região, evitando duplicatas. - Script de limpeza executado para remover entradas duplicadas no banco. - Frontend (Financeiro): - Reset completo do formulário ao abrir "Nova Transação" (limpa busca FOT e eventos). - Preenchimento automático da "Data Evento" ao selecionar um evento encontrado pela busca FOT. - Correção na lógica de busca de preço para usar nome da Função (`tabelaFree`).
This commit is contained in:
parent
6382145442
commit
95a4e441c1
5 changed files with 69 additions and 10 deletions
|
|
@ -118,7 +118,11 @@ 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 ILIKE $1 AND f.nome ILIKE $2 AND te.regiao = $3
|
||||
WHERE te.nome ILIKE $1
|
||||
AND f.nome ILIKE $2
|
||||
AND te.regiao = $3
|
||||
AND p.regiao = $3
|
||||
ORDER BY p.criado_em DESC
|
||||
LIMIT 1
|
||||
`
|
||||
|
||||
|
|
|
|||
|
|
@ -37,7 +37,11 @@ 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 ILIKE $1 AND f.nome ILIKE $2 AND te.regiao = @regiao
|
||||
WHERE te.nome ILIKE $1
|
||||
AND f.nome ILIKE $2
|
||||
AND te.regiao = @regiao
|
||||
AND p.regiao = @regiao
|
||||
ORDER BY p.criado_em DESC
|
||||
LIMIT 1;
|
||||
|
||||
-- name: GetTipoEventoByNome :one
|
||||
|
|
|
|||
|
|
@ -137,7 +137,8 @@ func (h *Handler) SetPrice(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
_, err := h.service.SetPrice(c.Request.Context(), req)
|
||||
regiao := c.GetString("regiao")
|
||||
_, err := h.service.SetPrice(c.Request.Context(), req, regiao)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ type PriceInput struct {
|
|||
Valor float64 `json:"valor"`
|
||||
}
|
||||
|
||||
func (s *Service) SetPrice(ctx context.Context, input PriceInput) (*generated.PrecosTiposEvento, error) {
|
||||
func (s *Service) SetPrice(ctx context.Context, input PriceInput, regiao string) (*generated.PrecosTiposEvento, error) {
|
||||
eventoUUID, err := uuid.Parse(input.TipoEventoID)
|
||||
if err != nil {
|
||||
return nil, errors.New("invalid tipo_evento_id")
|
||||
|
|
@ -100,6 +100,7 @@ func (s *Service) SetPrice(ctx context.Context, input PriceInput) (*generated.Pr
|
|||
TipoEventoID: pgtype.UUID{Bytes: eventoUUID, Valid: true},
|
||||
FuncaoProfissionalID: pgtype.UUID{Bytes: funcaoUUID, Valid: true},
|
||||
Valor: toPgNumeric(input.Valor),
|
||||
Regiao: pgtype.Text{String: regiao, Valid: true},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
|||
|
|
@ -526,7 +526,7 @@ const Finance: React.FC = () => {
|
|||
setFormData(prev => ({
|
||||
...prev,
|
||||
tipoEvento: ev.tipo_evento_nome,
|
||||
// If event has date, we could pre-fill? User request suggests keeping it flexible or maybe they didn't ask explicitly.
|
||||
data: ev.data_evento ? ev.data_evento.split("T")[0] : prev.data,
|
||||
}));
|
||||
setShowEventSelector(false);
|
||||
};
|
||||
|
|
@ -733,12 +733,59 @@ const Finance: React.FC = () => {
|
|||
};
|
||||
|
||||
// Calculations
|
||||
// Calculations
|
||||
// Calculations
|
||||
useEffect(() => {
|
||||
const vFree = Number(formData.valorFree) || 0;
|
||||
const vExtra = Number(formData.valorExtra) || 0;
|
||||
setFormData((prev) => ({ ...prev, totalPagar: vFree + vExtra }));
|
||||
}, [formData.valorFree, formData.valorExtra]);
|
||||
|
||||
// Fetch Price on Event/Service/Function Change
|
||||
useEffect(() => {
|
||||
const vFree = Number(formData.valorFree) || 0;
|
||||
const vExtra = Number(formData.valorExtra) || 0;
|
||||
setFormData((prev) => ({ ...prev, totalPagar: vFree + vExtra }));
|
||||
}, [formData.valorFree, formData.valorExtra]);
|
||||
if (isEditInitializing.current) {
|
||||
isEditInitializing.current = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Use Tabela Free (Function) if available, otherwise Tipo Servico
|
||||
const serviceParam = formData.tabelaFree || formData.tipoServico;
|
||||
|
||||
if (!formData.tipoEvento || !serviceParam) return;
|
||||
|
||||
const fetchPrice = async () => {
|
||||
const token = localStorage.getItem("token");
|
||||
if (!token) return;
|
||||
|
||||
try {
|
||||
// Use the new endpoint
|
||||
const res = await fetch(`${API_BASE_URL}/api/finance/price?event=${encodeURIComponent(formData.tipoEvento!)}&service=${encodeURIComponent(serviceParam)}`, {
|
||||
headers: {
|
||||
"Authorization": `Bearer ${token}`,
|
||||
"x-regiao": localStorage.getItem("photum_selected_region") || "SP"
|
||||
}
|
||||
});
|
||||
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
// Update Valor Free if different
|
||||
// Check if data.valor is a number
|
||||
const price = Number(data.valor);
|
||||
if (!isNaN(price) && price !== formData.valorFree) {
|
||||
setFormData(prev => ({ ...prev, valorFree: price }));
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Error fetching price:", err);
|
||||
}
|
||||
};
|
||||
|
||||
// Debounce slightly or just call
|
||||
const timeoutId = setTimeout(() => {
|
||||
fetchPrice();
|
||||
}, 300);
|
||||
return () => clearTimeout(timeoutId);
|
||||
|
||||
}, [formData.tipoEvento, formData.tipoServico, formData.tabelaFree]);
|
||||
|
||||
const handleExportCSV = () => {
|
||||
if (sortedTransactions.length === 0) {
|
||||
|
|
@ -866,6 +913,8 @@ const Finance: React.FC = () => {
|
|||
pgtoOk: false,
|
||||
});
|
||||
setFotFound(false);
|
||||
setFotQuery(""); // Clear FOT search query
|
||||
setFotEvents([]); // Clear found events
|
||||
setProFunctions([]); // Clear professional functions
|
||||
setSelectedTransaction(null); // Ensure no transaction is selected for add
|
||||
setShowAddModal(true);
|
||||
|
|
|
|||
Loading…
Reference in a new issue