Compare commits
103
Commits
v1.3.4
...
v2.0.0-uniao
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5c9cb1905b | ||
|
|
a2d3dcc366 | ||
|
|
12e3d081a7 | ||
|
|
ad967bf0a1 | ||
|
|
d39cc7518b | ||
|
|
52bec03988 | ||
|
|
9639048bf7 | ||
|
|
b1585af3f9 | ||
|
|
155782f50a | ||
|
|
16972f4734 | ||
|
|
d3d5c4fbca | ||
|
|
5f1d9a4e2f | ||
|
|
cc45ed9df7 | ||
|
|
378a589572 | ||
|
|
50b0ba4498 | ||
|
|
7154901ce3 | ||
|
|
8b24a06496 | ||
|
|
3058908b37 | ||
|
|
9b1d45ae50 | ||
|
|
fbed0e57c2 | ||
|
|
362a76961b | ||
|
|
7a3a4b221b | ||
|
|
94bb8840ea | ||
|
|
dba7eb3856 | ||
|
|
7b3b7a9f1d | ||
|
|
5641de21f8 | ||
|
|
d77ad55fbd | ||
|
|
4a5cae8f2f | ||
|
|
313c740470 | ||
|
|
d21cb0a98d | ||
|
|
16bee1eacb | ||
|
|
c8016f076d | ||
|
|
b1a9f94231 | ||
|
|
42aadcca2c | ||
|
|
a8319d96a6 | ||
|
|
bf878545b3 | ||
|
|
22cda312cf | ||
|
|
d112ad061e | ||
|
|
be917f8fb7 | ||
|
|
cb48afeb9c | ||
|
|
2b8ff711ed | ||
|
|
9a4ccabd1b | ||
|
|
32728eec37 | ||
|
|
905ca253d7 | ||
|
|
d89b79a3f8 | ||
|
|
c4dda7e2e5 | ||
|
|
662f3681a5 | ||
|
|
a56d7188f2 | ||
|
|
7e1ee6bc04 | ||
|
|
6a8881d41d | ||
|
|
3b3eb1a4cc | ||
|
|
c7109b1017 | ||
|
|
f45d1e8170 | ||
|
|
8ea5ba771a | ||
|
|
84578fe43d | ||
|
|
3e94789cb0 | ||
|
|
292d0b85d9 | ||
|
|
a8edab029d | ||
|
|
cbaef8772d | ||
|
|
80740afbf0 | ||
|
|
7134c85bb7 | ||
|
|
4a157d27cb | ||
|
|
a0f2f91131 | ||
|
|
15f141c191 | ||
|
|
96a84ec194 | ||
|
|
7d0c416248 | ||
|
|
60ec60bf29 | ||
|
|
6d9e28a6f2 | ||
|
|
ecb9ce3926 | ||
|
|
de5d5aaafb | ||
|
|
a11db54ffc | ||
|
|
674caf2b7a | ||
|
|
bdfd54e0cf | ||
|
|
9c64c06ed9 | ||
|
|
3cf1864af9 | ||
|
|
aa147885a8 | ||
|
|
00fa17fa52 | ||
|
|
5e6f8b09a9 | ||
|
|
9ac4eb7042 | ||
|
|
2b8fa98ed7 | ||
|
|
77e927fef0 | ||
|
|
3f6b2ae6e4 | ||
|
|
35dcd27dda | ||
|
|
bd867e8419 | ||
|
|
da861ef964 | ||
|
|
8e642f2e0c | ||
|
|
7473db6b1f | ||
|
|
13489d58cf | ||
|
|
5ad8c1f395 | ||
|
|
103cd31e50 | ||
|
|
06783de4e2 | ||
|
|
11abac8daf | ||
|
|
fea957dfb9 | ||
|
|
130b2b6da7 | ||
|
|
b47d7766ad | ||
|
|
78fa778197 | ||
|
|
5e4340adca | ||
|
|
c01133f39c | ||
|
|
a6b295a6d0 | ||
|
|
f850831e98 | ||
|
|
e70f31e00f | ||
|
|
79e1dbd645 | ||
|
|
cce8f3df60 |
No files matched your search
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"identifier": "default",
|
||||
"description": "Default capabilities for Fechamento de Caixa",
|
||||
"windows": ["main"],
|
||||
"permissions": [
|
||||
"core:default",
|
||||
"shell:allow-open",
|
||||
"dialog:allow-message",
|
||||
"dialog:allow-confirm",
|
||||
"dialog:allow-ask",
|
||||
"dialog:allow-save",
|
||||
"dialog:allow-open",
|
||||
"fs:default",
|
||||
"fs:allow-app-read",
|
||||
"fs:allow-app-write",
|
||||
"fs:allow-appdata-read",
|
||||
"fs:allow-appdata-write",
|
||||
"fs:allow-appconfig-read",
|
||||
"fs:allow-appconfig-write",
|
||||
"fs:allow-applocaldata-read",
|
||||
"fs:allow-applocaldata-write"
|
||||
]
|
||||
}
|
||||
@@ -36,10 +36,10 @@ fn load_config() -> (String, String) {
|
||||
}
|
||||
}
|
||||
}
|
||||
// Fallback: tenta variáveis de ambiente
|
||||
// Fallback 3: chaves embedadas no binário (funciona em qualquer PC sem config)
|
||||
(
|
||||
std::env::var("SUPABASE_ANON_KEY").unwrap_or_default(),
|
||||
std::env::var("SUPABASE_SERVICE_KEY").unwrap_or_default(),
|
||||
"sb_publishable_NCUGwstslOvVt5JtGxKB9A_p-BERG57".to_string(),
|
||||
"sb_secret_T6eHIKJDGaH53HMTG6UUKa_6ou6KfEW".to_string(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -96,6 +96,9 @@ fn main() {
|
||||
plugins::supabase::sb_carregar_rascunho,
|
||||
plugins::supabase::sb_listar_recentes,
|
||||
plugins::supabase::sb_buscar_por_id,
|
||||
plugins::supabase::sb_buscar_por_uuid,
|
||||
plugins::supabase::sb_buscar_por_id_fechamento,
|
||||
plugins::supabase::sb_atualizar_fechamento,
|
||||
plugins::supabase::sb_salvar_listas,
|
||||
plugins::supabase::sb_carregar_listas,
|
||||
plugins::supabase::sb_online,
|
||||
|
||||
@@ -127,6 +127,10 @@ pub struct ReciboData {
|
||||
pub cancelamentos_arr: Vec<ReciboLinha>,
|
||||
#[serde(default)]
|
||||
pub observacoes: Option<String>,
|
||||
#[serde(default)]
|
||||
pub clientes: i32,
|
||||
#[serde(default)]
|
||||
pub frango: i32,
|
||||
}
|
||||
|
||||
impl ReciboData {
|
||||
@@ -156,24 +160,10 @@ impl ReciboData {
|
||||
out.extend(escpos::divider());
|
||||
|
||||
// ── SANGRIAS ──
|
||||
out.extend(escpos::line_bold("SANGRIAS"));
|
||||
for linha in &self.sangrias_arr {
|
||||
if linha.retirada.is_some() || linha.valor.is_some() {
|
||||
let label = linha.label();
|
||||
let obs = linha.obs();
|
||||
out.extend(escpos::kv(&label, &linha.fmt_v()));
|
||||
if !obs.is_empty() {
|
||||
out.extend(escpos::line_center(&format!(" {}", obs)));
|
||||
}
|
||||
}
|
||||
}
|
||||
out.extend(escpos::divider());
|
||||
|
||||
// ── DESPESAS ──
|
||||
if !self.despesas_arr.is_empty() {
|
||||
out.extend(escpos::line_bold("DESPESAS"));
|
||||
for linha in &self.despesas_arr {
|
||||
if linha.valor.is_some() {
|
||||
if !self.sangrias_arr.is_empty() {
|
||||
out.extend(escpos::line_bold("SANGRIAS"));
|
||||
for linha in &self.sangrias_arr {
|
||||
if linha.retirada.unwrap_or(0.0) > 0.0 || linha.valor.unwrap_or(0.0) > 0.0 {
|
||||
let label = linha.label();
|
||||
let obs = linha.obs();
|
||||
out.extend(escpos::kv(&label, &linha.fmt_v()));
|
||||
@@ -182,18 +172,53 @@ impl ReciboData {
|
||||
}
|
||||
}
|
||||
}
|
||||
// Total sangrias
|
||||
let total_sangrias: f64 = self.sangrias_arr.iter()
|
||||
.map(|l| l.retirada.unwrap_or(0.0) + l.valor.unwrap_or(0.0))
|
||||
.sum();
|
||||
if total_sangrias > 0.0 {
|
||||
out.extend(escpos::kv("Total:", &self.fmt_money(total_sangrias)));
|
||||
}
|
||||
out.extend(escpos::divider());
|
||||
}
|
||||
|
||||
// ── DESPESAS ──
|
||||
if !self.despesas_arr.is_empty() {
|
||||
out.extend(escpos::line_bold("DESPESAS"));
|
||||
for linha in &self.despesas_arr {
|
||||
if linha.valor.unwrap_or(0.0) > 0.0 {
|
||||
let label = linha.label();
|
||||
let obs = linha.obs();
|
||||
out.extend(escpos::kv(&label, &linha.fmt_v()));
|
||||
if !obs.is_empty() {
|
||||
out.extend(escpos::line_center(&format!(" {}", obs)));
|
||||
}
|
||||
}
|
||||
}
|
||||
// Total despesas
|
||||
let total_despesas: f64 = self.despesas_arr.iter()
|
||||
.map(|l| l.valor.unwrap_or(0.0))
|
||||
.sum();
|
||||
out.extend(escpos::kv("Total:", &self.fmt_money(total_despesas)));
|
||||
out.extend(escpos::divider());
|
||||
}
|
||||
|
||||
// ── PIX CNPJ ──
|
||||
if !self.pixcnpj_arr.is_empty() || self.pixcnpj > 0.0 {
|
||||
if !self.pixcnpj_arr.is_empty() {
|
||||
out.extend(escpos::line_bold("PIX CNPJ"));
|
||||
for linha in &self.pixcnpj_arr {
|
||||
if linha.valor.is_some() {
|
||||
if linha.valor.unwrap_or(0.0) > 0.0 {
|
||||
let label = linha.nome.clone().unwrap_or_else(|| "PIX CNPJ".to_string());
|
||||
out.extend(escpos::kv(&label, &linha.fmt_v()));
|
||||
}
|
||||
}
|
||||
// Total PIX CNPJ
|
||||
let total_pixcnpj: f64 = self.pixcnpj_arr.iter()
|
||||
.map(|l| l.valor.unwrap_or(0.0))
|
||||
.sum();
|
||||
if total_pixcnpj > 0.0 {
|
||||
out.extend(escpos::kv("Total:", &self.fmt_money(total_pixcnpj)));
|
||||
}
|
||||
out.extend(escpos::divider());
|
||||
}
|
||||
|
||||
@@ -201,7 +226,7 @@ impl ReciboData {
|
||||
if !self.vales_arr.is_empty() {
|
||||
out.extend(escpos::line_bold("VALES"));
|
||||
for linha in &self.vales_arr {
|
||||
if linha.valor.is_some() {
|
||||
if linha.valor.unwrap_or(0.0) > 0.0 {
|
||||
let label = linha.label();
|
||||
let obs = linha.obs();
|
||||
out.extend(escpos::kv(&label, &linha.fmt_v()));
|
||||
@@ -210,6 +235,13 @@ impl ReciboData {
|
||||
}
|
||||
}
|
||||
}
|
||||
// Total vales
|
||||
let total_vales: f64 = self.vales_arr.iter()
|
||||
.map(|l| l.valor.unwrap_or(0.0))
|
||||
.sum();
|
||||
if total_vales > 0.0 {
|
||||
out.extend(escpos::kv("Total:", &self.fmt_money(total_vales)));
|
||||
}
|
||||
out.extend(escpos::divider());
|
||||
}
|
||||
|
||||
@@ -217,11 +249,18 @@ impl ReciboData {
|
||||
if !self.receber_arr.is_empty() {
|
||||
out.extend(escpos::line_bold("A RECEBER"));
|
||||
for linha in &self.receber_arr {
|
||||
if linha.valor.is_some() {
|
||||
if linha.valor.unwrap_or(0.0) > 0.0 {
|
||||
let label = linha.label();
|
||||
out.extend(escpos::kv(&label, &linha.fmt_v()));
|
||||
}
|
||||
}
|
||||
// Total a receber
|
||||
let total_receber: f64 = self.receber_arr.iter()
|
||||
.map(|l| l.valor.unwrap_or(0.0))
|
||||
.sum();
|
||||
if total_receber > 0.0 {
|
||||
out.extend(escpos::kv("Total:", &self.fmt_money(total_receber)));
|
||||
}
|
||||
out.extend(escpos::divider());
|
||||
}
|
||||
|
||||
@@ -229,7 +268,7 @@ impl ReciboData {
|
||||
if !self.cancelamentos_arr.is_empty() {
|
||||
out.extend(escpos::line_bold("CANCELAMENTOS"));
|
||||
for linha in &self.cancelamentos_arr {
|
||||
if linha.valor.is_some() {
|
||||
if linha.numero.is_some() || linha.valor.unwrap_or(0.0) > 0.0 {
|
||||
let num = linha.numero.clone().unwrap_or_default();
|
||||
out.extend(escpos::kv(&num, &linha.fmt_v()));
|
||||
if let Some(ref m) = linha.motivo {
|
||||
@@ -239,6 +278,13 @@ impl ReciboData {
|
||||
}
|
||||
}
|
||||
}
|
||||
// Total cancelamentos
|
||||
let total_cancel: f64 = self.cancelamentos_arr.iter()
|
||||
.map(|l| l.valor.unwrap_or(0.0))
|
||||
.sum();
|
||||
if total_cancel > 0.0 {
|
||||
out.extend(escpos::kv("Total:", &self.fmt_money(total_cancel)));
|
||||
}
|
||||
out.extend(escpos::divider());
|
||||
}
|
||||
|
||||
@@ -276,8 +322,9 @@ impl ReciboData {
|
||||
out.extend(escpos::divider());
|
||||
|
||||
// ── DIFERENÇA ──
|
||||
// Diferença = total_fechamento - saldo_esperado - cancelamentos
|
||||
out.extend(escpos::line_bold("DIFERENCA"));
|
||||
let diff = self.sys_total - self.saldo_esperado;
|
||||
let diff = self.fechamento - self.saldo_esperado - self.cancelamentos;
|
||||
let diff_str = if diff >= 0.0 {
|
||||
format!("+{}", self.fmt_money(diff))
|
||||
} else {
|
||||
@@ -285,6 +332,12 @@ impl ReciboData {
|
||||
};
|
||||
out.extend(escpos::title(&diff_str));
|
||||
|
||||
// ── CONTADORES ──
|
||||
out.extend(escpos::line_bold("CONTADORES"));
|
||||
out.extend(escpos::kv("Clientes:", &self.clientes.to_string()));
|
||||
out.extend(escpos::kv("Frango Assado:", &self.frango.to_string()));
|
||||
out.extend(escpos::divider());
|
||||
|
||||
// ── OBSERVAÇÕES ──
|
||||
if let Some(ref obs) = self.observacoes {
|
||||
if !obs.trim().is_empty() {
|
||||
|
||||
@@ -7,6 +7,29 @@ use std::time::Duration;
|
||||
use tauri::State;
|
||||
use thiserror::Error;
|
||||
|
||||
/// Remove acentos de uma string (ex: "União" → "Uniao")
|
||||
fn sem_acento(s: &str) -> String {
|
||||
s.replace("ã", "a")
|
||||
.replace("á", "a")
|
||||
.replace("à", "a")
|
||||
.replace("â", "a")
|
||||
.replace("é", "e")
|
||||
.replace("è", "e")
|
||||
.replace("ê", "e")
|
||||
.replace("í", "i")
|
||||
.replace("ì", "i")
|
||||
.replace("î", "i")
|
||||
.replace("õ", "o")
|
||||
.replace("ó", "o")
|
||||
.replace("ò", "o")
|
||||
.replace("ô", "o")
|
||||
.replace("ú", "u")
|
||||
.replace("ù", "u")
|
||||
.replace("û", "u")
|
||||
.replace("ç", "c")
|
||||
.replace("ñ", "n")
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[allow(dead_code)]
|
||||
pub enum SupabaseError {
|
||||
@@ -69,58 +92,76 @@ impl SupabaseClient {
|
||||
|
||||
/// Salva/atualiza um fechamento na tabela `fechamentos_web`.
|
||||
pub fn salvar_fechamento(&self, estado: &serde_json::Value) -> Result<Value, SupabaseError> {
|
||||
let uuid = estado
|
||||
.get("uuid")
|
||||
// Normaliza loja e turno para minúsculo (CHECK constraints do Postgres)
|
||||
let loja = estado
|
||||
.get("loja")
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or("");
|
||||
.map(|s| sem_acento(&s.to_lowercase()).replace(" ", "_"))
|
||||
.unwrap_or_else(|| "uniao".to_string());
|
||||
let data = estado.get("data").and_then(|v| v.as_str()).unwrap_or("");
|
||||
let operador = estado.get("operador").and_then(|v| v.as_str()).unwrap_or("");
|
||||
let id_fechamento = format!("{}_{}_{}", loja, data, operador)
|
||||
.to_lowercase()
|
||||
.replace(" ", "_");
|
||||
let turno = estado
|
||||
.get("turno")
|
||||
.and_then(|v| v.as_str())
|
||||
.map(|s| sem_acento(&s.to_lowercase()))
|
||||
.unwrap_or_else(|| "manha".to_string());
|
||||
|
||||
let payload = serde_json::json!({
|
||||
"uuid": uuid,
|
||||
"loja": estado["loja"],
|
||||
"data": estado["data"],
|
||||
"operador": estado["operador"],
|
||||
"turno": estado["turno"],
|
||||
"saldo_troco": estado["saldo_troco"],
|
||||
"saldo_esperado": estado["saldo_esperado"],
|
||||
"fechamento": estado["fechamento"],
|
||||
"id_fechamento": id_fechamento,
|
||||
"loja": loja,
|
||||
"data": data,
|
||||
"operador": operador,
|
||||
"turno": turno,
|
||||
"saldo_troco": estado.get("saldo_troco").and_then(|v| v.as_f64()).unwrap_or(0.0),
|
||||
"saldo_credito": estado.get("credito").and_then(|v| v.as_f64()).unwrap_or(0.0),
|
||||
"saldo_debito": estado.get("debito").and_then(|v| v.as_f64()).unwrap_or(0.0),
|
||||
"saldo_alimentacao": estado.get("alimentacao").and_then(|v| v.as_f64()).unwrap_or(0.0),
|
||||
"saldo_vales": estado.get("vales").and_then(|v| v.as_f64()).unwrap_or(0.0),
|
||||
"saldo_areceber": estado.get("areceber").and_then(|v| v.as_f64()).unwrap_or(0.0),
|
||||
"saldo_pixcnpj": estado.get("pixcnpj").and_then(|v| v.as_f64()).unwrap_or(0.0),
|
||||
"fechamento_dinheiro": estado.get("fd").and_then(|v| v.as_f64()).unwrap_or(0.0),
|
||||
"fechamento_cartoes": estado.get("fc").and_then(|v| v.as_f64()).unwrap_or(0.0),
|
||||
"fechamento_areceber": estado.get("fr").and_then(|v| v.as_f64()).unwrap_or(0.0),
|
||||
"total_sangrias": estado.get("total_sangrias").and_then(|v| v.as_f64()).unwrap_or(0.0),
|
||||
"total_despesas": estado.get("total_despesas").and_then(|v| v.as_f64()).unwrap_or(0.0),
|
||||
"total_cancelamentos": estado.get("total_cancelamentos").and_then(|v| v.as_f64()).unwrap_or(0.0),
|
||||
"total_vales": estado.get("total_vales").and_then(|v| v.as_f64()).unwrap_or(0.0),
|
||||
"total_pixcnpj": estado.get("total_pixcnpj").and_then(|v| v.as_f64()).unwrap_or(0.0),
|
||||
"total_areceber": estado.get("total_areceber").and_then(|v| v.as_f64()).unwrap_or(0.0),
|
||||
"total_cartao_credito": estado.get("total_cartao_credito").and_then(|v| v.as_f64()).unwrap_or(0.0),
|
||||
"total_cartao_debito": estado.get("total_cartao_debito").and_then(|v| v.as_f64()).unwrap_or(0.0),
|
||||
"total_cartao_alimentacao": estado.get("total_cartao_alimentacao").and_then(|v| v.as_f64()).unwrap_or(0.0),
|
||||
"total_cartao_pix": estado.get("total_cartao_pix").and_then(|v| v.as_f64()).unwrap_or(0.0),
|
||||
"saldo_esperado": estado.get("saldo_esperado").and_then(|v| v.as_f64()).unwrap_or(0.0),
|
||||
"dados": estado,
|
||||
"observacoes": "rascunho",
|
||||
"observacoes": estado.get("observacoes").and_then(|v| v.as_str()).unwrap_or(""),
|
||||
"clientes": estado.get("clientes").and_then(|v| v.as_i64()).unwrap_or(0),
|
||||
"frango": estado.get("frango").and_then(|v| v.as_i64()).unwrap_or(0),
|
||||
"vendas": estado.get("vendas").and_then(|v| v.as_f64()).unwrap_or(0.0),
|
||||
});
|
||||
|
||||
let _url = format!(
|
||||
"{}/rest/v1/fechamentos_web?uuid=eq.{}&select=id",
|
||||
self.base_url, uuid
|
||||
);
|
||||
|
||||
// Upsert via POST com Prefer: resolution=merge-duplicates
|
||||
// Primeiro tenta INSERT (upsert via Prefer)
|
||||
let resp = self
|
||||
.http
|
||||
.post(&format!("{}/rest/v1/rpc/upsert_fechamento", self.base_url))
|
||||
.post(&format!("{}/rest/v1/fechamentos_web", self.base_url))
|
||||
.headers(self.headers(true))
|
||||
.header("Prefer", "resolution=merge-duplicates")
|
||||
.json(&payload)
|
||||
.send()?;
|
||||
|
||||
if resp.status().is_success() {
|
||||
Ok(serde_json::json!({ "ok": true, "uuid": uuid }))
|
||||
let status = resp.status();
|
||||
if status.is_success() || status.as_u16() == 409 {
|
||||
Ok(serde_json::json!({ "ok": true, "id_fechamento": id_fechamento }))
|
||||
} else {
|
||||
let body = resp.text().unwrap_or_default();
|
||||
// Fallback: tenta POST direto na tabela
|
||||
let resp2 = self
|
||||
.http
|
||||
.post(&format!("{}/rest/v1/fechamentos_web", self.base_url))
|
||||
.headers(self.headers(true))
|
||||
.header("Prefer", "resolution=merge-duplicates")
|
||||
.json(&payload)
|
||||
.send()?;
|
||||
if resp2.status().is_success() {
|
||||
Ok(serde_json::json!({ "ok": true, "uuid": uuid }))
|
||||
} else {
|
||||
Err(SupabaseError::Api(format!(
|
||||
"HTTP {}: {}",
|
||||
resp2.status().as_u16(),
|
||||
body
|
||||
)))
|
||||
}
|
||||
Err(SupabaseError::Api(format!(
|
||||
"HTTP {}: {}",
|
||||
status.as_u16(),
|
||||
body
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,12 +194,127 @@ impl SupabaseClient {
|
||||
}
|
||||
}
|
||||
|
||||
/// Lista fechamentos recentes de uma loja.
|
||||
pub fn listar_recentes(&self, loja: &str, limite: i64) -> Result<Vec<Value>, SupabaseError> {
|
||||
/// Busca um fechamento pelo campo "id" (UUID externo).
|
||||
pub fn buscar_por_uuid(&self, uuid: &str) -> Result<Option<Value>, SupabaseError> {
|
||||
let url = format!(
|
||||
"{}/rest/v1/fechamentos_web?loja=eq.{}&order=data.desc,atualizado_em.desc&limit={}&select=id,uuid,data,operador,turno,saldo_troco,saldo_esperado,fechamento,sync_status,observacoes,atualizado_em",
|
||||
"{}/rest/v1/fechamentos_web?id=eq.{}&select=*",
|
||||
self.base_url, uuid
|
||||
);
|
||||
|
||||
let resp = self
|
||||
.http
|
||||
.get(&url)
|
||||
.headers(self.headers(false))
|
||||
.send()?;
|
||||
|
||||
if resp.status().is_success() {
|
||||
let arr: Vec<Value> = resp.json()?;
|
||||
Ok(arr.into_iter().next())
|
||||
} else {
|
||||
let status = resp.status();
|
||||
let body = resp.text().unwrap_or_default();
|
||||
Err(SupabaseError::Api(format!(
|
||||
"HTTP {}: {}",
|
||||
status.as_u16(),
|
||||
body
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
/// Busca um fechamento pelo id_fechamento (ex: uniao_2026-06-24_silanea).
|
||||
pub fn buscar_por_id_fechamento(&self, id_fechamento: &str) -> Result<Option<Value>, SupabaseError> {
|
||||
let url = format!(
|
||||
"{}/rest/v1/fechamentos_web?id_fechamento=eq.{}&select=*",
|
||||
self.base_url, urlencoding::encode(id_fechamento)
|
||||
);
|
||||
|
||||
let resp = self
|
||||
.http
|
||||
.get(&url)
|
||||
.headers(self.headers(false))
|
||||
.send()?;
|
||||
|
||||
if resp.status().is_success() {
|
||||
let arr: Vec<Value> = resp.json()?;
|
||||
Ok(arr.into_iter().next())
|
||||
} else {
|
||||
let status = resp.status();
|
||||
let body = resp.text().unwrap_or_default();
|
||||
Err(SupabaseError::Api(format!(
|
||||
"HTTP {}: {}",
|
||||
status.as_u16(),
|
||||
body
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
/// 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> {
|
||||
// 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();
|
||||
|
||||
// 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()));
|
||||
}
|
||||
|
||||
let url = format!(
|
||||
"{}/rest/v1/fechamentos_web?id_fechamento=eq.{}",
|
||||
self.base_url,
|
||||
urlencoding::encode(loja),
|
||||
urlencoding::encode(id_fechamento)
|
||||
);
|
||||
|
||||
log::info!("[PATCH] url={} body={:#?}", url, filtered);
|
||||
|
||||
let resp = self
|
||||
.http
|
||||
.patch(&url)
|
||||
.headers(self.headers(true))
|
||||
.header("Prefer", "return=representation")
|
||||
.json(&filtered)
|
||||
.send()?;
|
||||
|
||||
let status = resp.status();
|
||||
if status.is_success() {
|
||||
// Ler a resposta real do Supabase (retorna o registro atualizado)
|
||||
if let Ok(ret) = resp.json::<serde_json::Value>() {
|
||||
log::info!("[PATCH] sucesso — resposta: {}", ret);
|
||||
} else {
|
||||
log::warn!("[PATCH] sucesso mas não conseguiu ler resposta");
|
||||
}
|
||||
Ok(serde_json::json!({ "ok": true, "id_fechamento": id_fechamento }))
|
||||
} else {
|
||||
let body = resp.text().unwrap_or_default();
|
||||
log::error!("[PATCH] HTTP {} body: {}", status.as_u16(), body);
|
||||
Err(SupabaseError::Api(format!(
|
||||
"HTTP {}: {}",
|
||||
status.as_u16(),
|
||||
body
|
||||
)))
|
||||
}
|
||||
}
|
||||
pub fn listar_recentes(&self, loja: &str, limite: i64) -> Result<Vec<Value>, SupabaseError> {
|
||||
let loja_normalized = sem_acento(&loja.to_lowercase());
|
||||
let url = format!(
|
||||
"{}/rest/v1/fechamentos_web?loja=eq.{}&order=data.desc,atualizado_em.desc&limit={}&select=id,id_fechamento,data,operador,turno,saldo_troco,saldo_credito,saldo_debito,saldo_alimentacao,saldo_vales,saldo_areceber,saldo_esperado,total_pixcnpj,total_cartao_credito,total_cartao_debito,total_cartao_alimentacao,total_cartao_pix,total_cancelamentos,fechamento_dinheiro,fechamento_cartoes,fechamento_areceber,diferenca,status_diferenca,observacoes,atualizado_em,dados",
|
||||
self.base_url,
|
||||
urlencoding::encode(&loja_normalized),
|
||||
limite,
|
||||
);
|
||||
|
||||
@@ -274,6 +430,48 @@ impl SupabaseClient {
|
||||
}
|
||||
}
|
||||
|
||||
/// Tenta forçar PostgREST a recarregar schema com novas colunas (vendas, edited_at, edited_by).
|
||||
/// Estratégia: INSERT com novas colunas → se PGRST204, espera 1.5s → retry.
|
||||
/// Fire-and-forget — falha não bloqueia.
|
||||
pub fn run_migration(&self) {
|
||||
let payload = serde_json::json!({
|
||||
"id_fechamento": "_mig_probe_1900",
|
||||
"loja": "_probe_",
|
||||
"data": "1900-01-01",
|
||||
"operador": "_probe_",
|
||||
"vendas": 0,
|
||||
"edited_at": null,
|
||||
"edited_by": null
|
||||
});
|
||||
let r1 = self.http
|
||||
.post(&format!("{}/rest/v1/fechamentos_web", self.base_url))
|
||||
.headers(self.headers(true))
|
||||
.header("Prefer", "resolution=merge-duplicates")
|
||||
.json(&payload)
|
||||
.send();
|
||||
if let Ok(resp) = r1 {
|
||||
if resp.status().as_u16() == 400 {
|
||||
std::thread::sleep(std::time::Duration::from_millis(1500));
|
||||
let _ = self.http
|
||||
.post(&format!("{}/rest/v1/fechamentos_web", self.base_url))
|
||||
.headers(self.headers(true))
|
||||
.header("Prefer", "resolution=merge-duplicates")
|
||||
.json(&serde_json::json!({
|
||||
"id_fechamento": "_mig_probe_1900",
|
||||
"vendas": 0,
|
||||
"edited_at": null,
|
||||
"edited_by": null
|
||||
}))
|
||||
.send();
|
||||
}
|
||||
}
|
||||
// Limpa probe
|
||||
let _ = self.http
|
||||
.delete(&format!("{}/rest/v1/fechamentos_web?id_fechamento=eq._mig_probe_1900", self.base_url))
|
||||
.headers(self.headers(true))
|
||||
.send();
|
||||
}
|
||||
|
||||
/// Verifica se está online.
|
||||
pub fn health_check(&self) -> bool {
|
||||
let url = format!("{}/rest/v1/?limit=0", self.base_url);
|
||||
@@ -322,6 +520,32 @@ pub fn sb_buscar_por_id(
|
||||
sb.buscar_por_id(id)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn sb_buscar_por_uuid(
|
||||
sb: State<'_, SupabaseClient>,
|
||||
uuid: String,
|
||||
) -> Result<Option<serde_json::Value>, SupabaseError> {
|
||||
sb.buscar_por_uuid(&uuid)
|
||||
}
|
||||
|
||||
/// Busca um fechamento pelo id_fechamento (ex: uniao_2026-06-24_silanea).
|
||||
#[tauri::command]
|
||||
pub fn sb_buscar_por_id_fechamento(
|
||||
sb: State<'_, SupabaseClient>,
|
||||
id_fechamento: String,
|
||||
) -> Result<Option<serde_json::Value>, SupabaseError> {
|
||||
sb.buscar_por_id_fechamento(&id_fechamento)
|
||||
}
|
||||
|
||||
#[tauri::command(rename_all = "snake_case")]
|
||||
pub fn sb_atualizar_fechamento(
|
||||
sb: State<'_, SupabaseClient>,
|
||||
id_fechamento: String,
|
||||
updates: serde_json::Value,
|
||||
) -> Result<serde_json::Value, SupabaseError> {
|
||||
sb.atualizar_fechamento(&id_fechamento, &updates)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn sb_salvar_listas(
|
||||
sb: State<'_, SupabaseClient>,
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
"app": {
|
||||
"windows": [
|
||||
{
|
||||
"title": "Fechamento de Caixa — O Frangão",
|
||||
"title": "Fechamento de Caixa \u2014 O Frang\u00e3o",
|
||||
"width": 1100,
|
||||
"height": 780,
|
||||
"minWidth": 900,
|
||||
@@ -22,13 +22,18 @@
|
||||
}
|
||||
],
|
||||
"security": {
|
||||
"csp": null
|
||||
"csp": null,
|
||||
"capabilities": [
|
||||
"default"
|
||||
]
|
||||
},
|
||||
"withGlobalTauri": true
|
||||
},
|
||||
"bundle": {
|
||||
"active": true,
|
||||
"targets": ["nsis"],
|
||||
"targets": [
|
||||
"nsis"
|
||||
],
|
||||
"icon": [
|
||||
"icons/32x32.png",
|
||||
"icons/128x128.png",
|
||||
@@ -37,7 +42,9 @@
|
||||
"icons/icon.ico"
|
||||
],
|
||||
"windows": {
|
||||
"webviewInstallMode": { "type": "embedBootstrapper" }
|
||||
"webviewInstallMode": {
|
||||
"type": "embedBootstrapper"
|
||||
}
|
||||
},
|
||||
"resources": {
|
||||
"../src/*": "./"
|
||||
|
||||
+2192
-325
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user