Compare commits

...

2 Commits

Author SHA1 Message Date
root a8319d96a6 fix: parseNum strip 'R$', fmtMR/fmtBR mostram R$ 0,00 para zero
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-01 14:38:32 +00:00
root bf878545b3 fix: denylist移除arrays/objetos — 解决PATCH成功但字段不保存
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-01 14:17:38 +00:00
2 changed files with 26 additions and 9 deletions
+21 -6
View File
@@ -250,12 +250,27 @@ impl SupabaseClient {
/// Atualiza campos específicos de um fechamento pelo id_fechamento.
pub fn atualizar_fechamento(&self, id_fechamento: &str, updates: &serde_json::Value) -> Result<Value, SupabaseError> {
// Sem whitelist — o frontend envia só o que o usuário editou.
// O Supabase/Postgres valida as colunas existentes.
// edited_at é atualizado pelo frontend no objeto updates.
// Campos que o frontend envia mas NÃO existem como colunas na tabela.
// Arrays e objetos são guardados no JSONB `dados` — não em colunas escalar.
static BLOCKED: &[&str] = &[
"id", "uuid", "criado_em", "atualizado_em", "enviado_por",
"ip_origem", "user_agent", "id_fechamento",
// arrays e objetos que existem dentro do JSONB `dados`
"vales", "despesas", "sangrias", "cancelamentos",
"pixcnpj", "receber",
"cartoes_credito", "cartoes_debito",
"cartoes_alimentacao", "cartoes_pix",
];
let map = updates.as_object().cloned().unwrap_or_default();
if map.is_empty() {
// Remove campos bloqueados
let filtered: serde_json::Map<String, serde_json::Value> = map
.into_iter()
.filter(|(k, _)| !BLOCKED.contains(&k.as_str()))
.collect();
if filtered.is_empty() {
return Err(SupabaseError::Api("Nenhum campo para atualizar".into()));
}
@@ -265,14 +280,14 @@ impl SupabaseClient {
urlencoding::encode(id_fechamento)
);
log::info!("[PATCH] url={} body={:#?}", url, map);
log::info!("[PATCH] url={} body={:#?}", url, filtered);
let resp = self
.http
.patch(&url)
.headers(self.headers(true))
.header("Prefer", "return=representation")
.json(&map)
.json(&filtered)
.send()?;
let status = resp.status();
+5 -3
View File
@@ -613,7 +613,7 @@ const fmt = v => 'R$ ' + (parseFloat(v) || 0).toLocaleString('pt-BR', { minimumF
const parseNum = v => {
// Already a number? Return directly (don't treat '.' as thousand separator)
if (typeof v === 'number') return isNaN(v) ? 0 : v;
const s = String(v || '0').trim();
const s = String(v || '0').trim().replace(/^R\$\s*/, ''); // strip currency prefix first
if (!s) return 0;
// Brazilian format: 1.234,56 → 1234.56
// Strategy: if string has comma, treat as BRL (decimal comma)
@@ -2707,7 +2707,8 @@ function openEditModal(full) {
// helper para fmt dinheiro
const fmtMR = v => {
const n = parseFloat(v||0);
if (isNaN(n) || n === 0) return '';
if (isNaN(n)) return '';
if (n === 0) return 'R$ 0,00';
return 'R$ ' + n.toLocaleString('pt-BR', { minimumFractionDigits: 2 });
};
@@ -2936,7 +2937,8 @@ function openEditModal(full) {
// Formata campo de dinheiro brasileiro
function fmtBR(inp) {
const v = parseNum(inp.value);
if (isNaN(v) || v === 0) return '';
if (isNaN(v)) return '';
if (v === 0) return 'R$ 0,00';
return 'R$ ' + v.toLocaleString('pt-BR', { minimumFractionDigits: 2 });
}