Fix: align frontend commands with backend, fix data model mismatch
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

- Rename commands: salvar_local→salvar_fechamento, enviar_fechamento→sb_salvar_fechamento, etc.
- Fix typo: cartoes.credo→cartoes.credito
- storage: accept serde_json::Value instead of FechamentoEstado for flexibility
- Add Default derive on FechamentoEstado and SyncStatus
- Commands now return simplified JSON for frontend compatibility
This commit is contained in:
root
2026-06-21 02:16:13 +00:00
parent 3cb0f5a93b
commit 453f5ba4e3
2 changed files with 159 additions and 16 deletions
+153 -10
View File
@@ -33,7 +33,7 @@ impl serde::Serialize for StorageError {
// ── Tipos ────────────────────────────────────────────────────────────────── // ── Tipos ──────────────────────────────────────────────────────────────────
#[derive(Debug, Serialize, Deserialize, Clone)] #[derive(Debug, Serialize, Deserialize, Clone, Default)]
pub struct FechamentoEstado { pub struct FechamentoEstado {
pub id: Option<i64>, pub id: Option<i64>,
pub uuid: String, pub uuid: String,
@@ -80,9 +80,10 @@ pub struct CartaoData {
pub voucher: Vec<TableRow>, pub voucher: Vec<TableRow>,
} }
#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq)] #[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Default)]
#[serde(rename_all = "lowercase")] #[serde(rename_all = "lowercase")]
pub enum SyncStatus { pub enum SyncStatus {
#[default]
Local, // só local, não sincronizado Local, // só local, não sincronizado
Syncing, // sync em progresso Syncing, // sync em progresso
Synced, // sincronizado com servidor Synced, // sincronizado com servidor
@@ -365,21 +366,126 @@ impl DbManager {
// ── Tauri Commands ───────────────────────────────────────────────────────── // ── Tauri Commands ─────────────────────────────────────────────────────────
// ── Tauri Commands ─────────────────────────────────────────────────────────
/// Salva fechamento com estrutura flexível ( campos simples do frontend JS )
#[tauri::command] #[tauri::command]
pub fn salvar_fechamento( pub fn salvar_fechamento(
db: State<'_, DbManager>, db: State<'_, DbManager>,
estado: FechamentoEstado, estado: serde_json::Value,
) -> Result<SalvarResult, StorageError> { ) -> Result<SalvarResult, StorageError> {
db.salvar(&estado) // Extrai campos simples do JSON flexível
let uuid = estado.get("uuid").and_then(|v| v.as_str()).unwrap_or("").to_string();
let loja = estado.get("loja").and_then(|v| v.as_str()).unwrap_or("").to_string();
let data = estado.get("data").and_then(|v| v.as_str()).unwrap_or("").to_string();
let operador = estado.get("operador").and_then(|v| v.as_str()).unwrap_or("").to_string();
let turno = estado.get("turno").and_then(|v| v.as_str()).unwrap_or("").to_string();
let saldo_troco = estado.get("saldo_troco").and_then(|v| v.as_f64()).unwrap_or(0.0);
let fechamento = estado.get("fechamento").and_then(|v| v.as_f64()).unwrap_or(0.0);
let saldo_esperado = saldo_troco + fechamento;
let diferenca = estado.get("diferenca").and_then(|v| v.as_f64()).unwrap_or(0.0);
// Constrói TableData a partir de despesas/receber arrays
let despesas: Vec<serde_json::Value> = estado.get("despesas")
.and_then(|v| v.as_array())
.map(|arr| arr.to_vec())
.unwrap_or_default();
let receber: Vec<serde_json::Value> = estado.get("receber")
.and_then(|v| v.as_array())
.map(|arr| arr.to_vec())
.unwrap_or_default();
let to_table_row = |v: &serde_json::Value| -> TableRow {
TableRow {
descricao: v.get("desc").and_then(|s| s.as_str()).map(String::from),
valor: v.get("valor").and_then(|n| n.as_f64()),
..Default::default()
}
};
let tabelas = TableData {
despesas: despesas.iter().map(to_table_row).collect(),
areceber: receber.iter().map(to_table_row).collect(),
..Default::default()
};
// Cartoes como scalars
let cartoes_json = estado.get("cartoes");
let cartoes = CartaoData {
credito: vec![TableRow { valor: cartoes_json.and_then(|c| c.get("credito")).and_then(|v| v.as_f64()), ..Default::default() }],
debito: vec![TableRow { valor: cartoes_json.and_then(|c| c.get("debito")).and_then(|v| v.as_f64()), ..Default::default() }],
alimentacao: vec![TableRow { valor: cartoes_json.and_then(|c| c.get("alimentacao")).and_then(|v| v.as_f64()), ..Default::default() }],
voucher: vec![TableRow { valor: cartoes_json.and_then(|c| c.get("voucher")).and_then(|v| v.as_f64()), ..Default::default() }],
};
let estado_rust = FechamentoEstado {
id: estado.get("id_local").and_then(|v| v.as_i64()),
uuid,
loja,
data,
operador,
turno,
saldo_troco,
fechamento,
saldo_esperado,
tabelas,
cartoes,
sync_status: SyncStatus::Local,
..Default::default()
};
db.salvar(&estado_rust)
} }
/// Carrega rascunho e devolve estrutura simples para o frontend
#[tauri::command] #[tauri::command]
pub fn carregar_rascunho( pub fn carregar_rascunho(
db: State<'_, DbManager>, db: State<'_, DbManager>,
loja: String, loja: String,
data: String, data: String,
) -> Result<Option<FechamentoEstado>, StorageError> { ) -> Result<Option<serde_json::Value>, StorageError> {
db.carregar_rascunho(&loja, &data) match db.carregar_rascunho(&loja, &data)? {
Some(e) => {
// Simplifica para formato que o frontend espera
let total_sangrias: f64 = e.tabelas.sangrias.iter().filter_map(|r| r.valor).sum();
let total_despesas: f64 = e.tabelas.despesas.iter().filter_map(|r| r.valor).sum();
let total_receber: f64 = e.tabelas.areceber.iter().filter_map(|r| r.valor).sum();
let despesa_rows: Vec<_> = e.tabelas.despesas.iter().filter_map(|r| {
r.descricao.as_ref().map(|d| serde_json::json!({"desc": d, "valor": r.valor}))
}).collect();
let receber_rows: Vec<_> = e.tabelas.areceber.iter().filter_map(|r| {
r.descricao.as_ref().map(|d| serde_json::json!({"desc": d, "valor": r.valor}))
}).collect();
Ok(Some(serde_json::json!({
"id_local": e.id,
"uuid": e.uuid,
"loja": e.loja,
"data": e.data,
"operador": e.operador,
"turno": e.turno,
"saldo_troco": e.saldo_troco,
"fechamento": e.fechamento,
"saldo_esperado": e.saldo_esperado,
"diferenca": e.fechamento - e.saldo_esperado,
"despesas": despesa_rows,
"total_sangrias": total_sangrias,
"total_despesas": total_despesas,
"vales": 0.0,
"cartoes": {
"credito": e.cartoes.credito.iter().find_map(|r| r.valor).unwrap_or(0.0),
"debito": e.cartoes.debito.iter().find_map(|r| r.valor).unwrap_or(0.0),
"alimentacao": e.cartoes.alimentacao.iter().find_map(|r| r.valor).unwrap_or(0.0),
"voucher": e.cartoes.voucher.iter().find_map(|r| r.valor).unwrap_or(0.0),
"cancelamentos": 0.0,
},
"receber": receber_rows,
"observacoes": "",
"sync_status": "local",
})))
}
None => Ok(None),
}
} }
#[tauri::command] #[tauri::command]
@@ -387,16 +493,53 @@ pub fn listar_fechamentos(
db: State<'_, DbManager>, db: State<'_, DbManager>,
loja: String, loja: String,
limite: Option<i64>, limite: Option<i64>,
) -> Result<Vec<FechamentoEstado>, StorageError> { ) -> Result<Vec<serde_json::Value>, StorageError> {
db.listar(&loja, limite.unwrap_or(50)) let rows = db.listar(&loja, limite.unwrap_or(50))?;
Ok(rows.into_iter().map(|e| serde_json::json!({
"id": e.id,
"uuid": e.uuid,
"data": e.data,
"operador": e.operador,
"turno": e.turno,
"saldo_esperado": e.saldo_esperado,
"sync_status": format!("{:?}", e.sync_status).to_lowercase(),
})).collect())
} }
#[tauri::command] #[tauri::command]
pub fn buscar_fechamento_por_id( pub fn buscar_fechamento_por_id(
db: State<'_, DbManager>, db: State<'_, DbManager>,
id: i64, id: i64,
) -> Result<Option<FechamentoEstado>, StorageError> { ) -> Result<Option<serde_json::Value>, StorageError> {
db.buscar_por_id(id) match db.buscar_por_id(id)? {
Some(e) => Ok(Some(serde_json::json!({
"id_local": e.id,
"uuid": e.uuid,
"loja": e.loja,
"data": e.data,
"operador": e.operador,
"turno": e.turno,
"saldo_troco": e.saldo_troco,
"fechamento": e.fechamento,
"saldo_esperado": e.saldo_esperado,
"diferenca": e.fechamento - e.saldo_esperado,
"despesas": e.tabelas.despesas.iter().filter_map(|r| r.descricao.as_ref().map(|d| serde_json::json!({"desc": d, "valor": r.valor}))).collect::<Vec<_>>(),
"total_sangrias": e.tabelas.sangrias.iter().filter_map(|r| r.valor).sum::<f64>(),
"total_despesas": e.tabelas.despesas.iter().filter_map(|r| r.valor).sum::<f64>(),
"vales": 0.0,
"cartoes": {
"credito": e.cartoes.credito.iter().find_map(|r| r.valor).unwrap_or(0.0),
"debito": e.cartoes.debito.iter().find_map(|r| r.valor).unwrap_or(0.0),
"alimentacao": e.cartoes.alimentacao.iter().find_map(|r| r.valor).unwrap_or(0.0),
"voucher": e.cartoes.voucher.iter().find_map(|r| r.valor).unwrap_or(0.0),
"cancelamentos": 0.0,
},
"receber": e.tabelas.areceber.iter().filter_map(|r| r.descricao.as_ref().map(|d| serde_json::json!({"desc": d, "valor": r.valor}))).collect::<Vec<_>>(),
"observacoes": "",
"sync_status": format!("{:?}", e.sync_status).to_lowercase(),
}))),
None => Ok(None),
}
} }
#[tauri::command] #[tauri::command]
+6 -6
View File
@@ -613,7 +613,7 @@ function calcular() {
estado.total_despesas = estado.despesas.filter(d => !d.desc.includes('Sangria')).reduce((s,d) => s+d.valor, 0); estado.total_despesas = estado.despesas.filter(d => !d.desc.includes('Sangria')).reduce((s,d) => s+d.valor, 0);
estado.vales = parseFloat(val('f-vales')) || 0; estado.vales = parseFloat(val('f-vales')) || 0;
estado.cartoes = { estado.cartoes = {
credo: parseFloat(val('f-credito')) || 0, credito: parseFloat(val('f-credito')) || 0,
debito: parseFloat(val('f-debito')) || 0, debito: parseFloat(val('f-debito')) || 0,
alimentacao: parseFloat(val('f-alimentacao')) || 0, alimentacao: parseFloat(val('f-alimentacao')) || 0,
voucher: parseFloat(val('f-voucher')) || 0, voucher: parseFloat(val('f-voucher')) || 0,
@@ -642,7 +642,7 @@ function updateBadges() {
async function salvarRascunho(silent = false) { async function salvarRascunho(silent = false) {
calcular(); calcular();
try { try {
const result = await invokeSafe('salvar_local', { estado: JSON.stringify(estado) }); const result = await invokeSafe('salvar_fechamento', { estado });
estado.id_local = result.id; estado.id_local = result.id;
estado.uuid = result.uuid; estado.uuid = result.uuid;
if (!silent) toast('Rascunho salvo (local)', 'success'); if (!silent) toast('Rascunho salvo (local)', 'success');
@@ -689,7 +689,7 @@ async function enviarFechamento() {
if (!estado.loja || !estado.data) { toast('Loja e data so obrigatorios', 'error'); return; } if (!estado.loja || !estado.data) { toast('Loja e data so obrigatorios', 'error'); return; }
showLoad('Enviando ao servidor…'); showLoad('Enviando ao servidor…');
try { try {
await invokeSafe('enviar_fechamento', { estado: JSON.stringify(estado) }); await invokeSafe('sb_salvar_fechamento', { estado });
toast('Enviado com sucesso!', 'success'); toast('Enviado com sucesso!', 'success');
atualizarSyncBadge('synced'); atualizarSyncBadge('synced');
} catch(e) { toast(`Erro ao enviar: ${e}`, 'error'); } } catch(e) { toast(`Erro ao enviar: ${e}`, 'error'); }
@@ -739,7 +739,7 @@ async function imprimirAgora() {
document.getElementById('modal-print').classList.remove('show'); document.getElementById('modal-print').classList.remove('show');
showLoad('Imprimindo…'); showLoad('Imprimindo…');
try { try {
await invokeSafe('imprimir_fechamento', { estado: JSON.stringify(estado) }); await invokeSafe('imprimir_recibo', { estado });
toast('Impresso com sucesso!', 'success'); toast('Impresso com sucesso!', 'success');
} catch(e) { toast(`Erro na impressao: ${e}`, 'error'); } } catch(e) { toast(`Erro na impressao: ${e}`, 'error'); }
hideLoad(); hideLoad();
@@ -755,7 +755,7 @@ async function listarRecentes() {
if (!loja) { toast('Selecione a loja primeiro', 'error'); return; } if (!loja) { toast('Selecione a loja primeiro', 'error'); return; }
showLoad('Carregando recentes…'); showLoad('Carregando recentes…');
try { try {
const lista = await invokeSafe('listar_recentes', { loja, limite: 20 }); const lista = await invokeSafe('sb_listar_recentes', { loja, limite: 20 });
const html = lista.map(r => ` const html = lista.map(r => `
<tr onclick="selecionarRecente(${r.id})" style="cursor:pointer"> <tr onclick="selecionarRecente(${r.id})" style="cursor:pointer">
<td>${r.data}</td><td>${r.operador||'—'}</td><td>${r.turno}º</td> <td>${r.data}</td><td>${r.operador||'—'}</td><td>${r.turno}º</td>
@@ -785,7 +785,7 @@ async function selecionarRecente(id) {
document.getElementById('modal-recentes')?.classList.remove('show'); document.getElementById('modal-recentes')?.classList.remove('show');
showLoad('Carregando…'); showLoad('Carregando…');
try { try {
const r = await invokeSafe('buscar_fechamento', { id }); const r = await invokeSafe('buscar_fechamento_por_id', { id });
Object.assign(estado, r); Object.assign(estado, r);
document.getElementById('f-saldo-troco').value = r.saldo_troco || ''; document.getElementById('f-saldo-troco').value = r.saldo_troco || '';
document.getElementById('f-fechamento').value = r.fechamento || ''; document.getElementById('f-fechamento').value = r.fechamento || '';