Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 06783de4e2 | |||
| 11abac8daf | |||
| fea957dfb9 | |||
| 130b2b6da7 | |||
| b47d7766ad | |||
| 78fa778197 | |||
| 5e4340adca | |||
| c01133f39c | |||
| a6b295a6d0 | |||
| f850831e98 | |||
| e70f31e00f | |||
| 79e1dbd645 | |||
| cce8f3df60 | |||
| 318d44e934 | |||
| bfbcd89be9 | |||
| 1848c1aa35 |
@@ -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"
|
||||
]
|
||||
}
|
||||
@@ -96,6 +96,8 @@ 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_atualizar_fechamento,
|
||||
plugins::supabase::sb_salvar_listas,
|
||||
plugins::supabase::sb_carregar_listas,
|
||||
plugins::supabase::sb_online,
|
||||
|
||||
@@ -82,9 +82,18 @@ pub struct ReciboData {
|
||||
pub operador: String,
|
||||
pub turno: String,
|
||||
pub saldo_troco: f64,
|
||||
/// Valor contado no fechamento (dinheiro + cartões + receber)
|
||||
pub fechamento: f64,
|
||||
/// Saldo esperado = total operações − sangrias − despesas
|
||||
pub saldo_esperado: f64,
|
||||
/// Diferença = fechamento − saldo_esperado
|
||||
pub diferenca: f64,
|
||||
/// Total do sistema (troco + crédito + débito + alimentação + voucher + pixcnpj + vales + receber)
|
||||
#[serde(default)]
|
||||
pub sys_total: f64,
|
||||
/// Soma de todas operações creditadas (crédito + débito + alimentação + voucher + pixcnpj) — usado no BLACK section como TOTAL
|
||||
#[serde(default)]
|
||||
pub total_operado: f64,
|
||||
// Totais de cartões
|
||||
pub credito: f64,
|
||||
pub debito: f64,
|
||||
@@ -126,7 +135,7 @@ impl ReciboData {
|
||||
}
|
||||
|
||||
/// Gera todos os bytes ESC/POS do recibo 80mm
|
||||
/// Layout: SANGRIAS → ENTRADAS → DESPESAS → PIX CNPJ → VALES → A RECEBER → CARTÕES → CANCELAMENTOS → TROCO → OBSERVAÇÕES → TOTAL SALDO CAIXA → SALDO ESPERADO → DIFERENÇA
|
||||
/// Layout: Header → SANGRIAS → DESPESAS → PIX CNPJ → VALES → A RECEBER → CANCELAMENTOS → CARTÕES → TROCO/Saldo Caixa → SALDO ESPERADO → DIFERENÇA
|
||||
pub fn to_escpos(&self) -> Vec<u8> {
|
||||
let mut out = Vec::new();
|
||||
|
||||
@@ -147,31 +156,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());
|
||||
|
||||
// ── ENTRADAS ──
|
||||
out.extend(escpos::line_bold("ENTRADAS"));
|
||||
out.extend(escpos::kv("Saldo Troco:", &self.fmt_money(self.saldo_troco)));
|
||||
out.extend(escpos::kv("Fechamento:", &self.fmt_money(self.fechamento)));
|
||||
out.extend(escpos::kv("Saldo Esperado:", &self.fmt_money(self.saldo_esperado)));
|
||||
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()));
|
||||
@@ -180,18 +168,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());
|
||||
}
|
||||
|
||||
@@ -199,7 +222,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()));
|
||||
@@ -208,6 +231,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());
|
||||
}
|
||||
|
||||
@@ -215,11 +245,42 @@ 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());
|
||||
}
|
||||
|
||||
// ── CANCELAMENTOS ──
|
||||
if !self.cancelamentos_arr.is_empty() {
|
||||
out.extend(escpos::line_bold("CANCELAMENTOS"));
|
||||
for linha in &self.cancelamentos_arr {
|
||||
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 {
|
||||
if !m.is_empty() {
|
||||
out.extend(escpos::line_center(&format!(" {}", m)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 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());
|
||||
}
|
||||
|
||||
@@ -232,28 +293,42 @@ impl ReciboData {
|
||||
out.extend(escpos::kv("Cancelamentos:", &self.fmt_money(self.cancelamentos)));
|
||||
out.extend(escpos::divider());
|
||||
|
||||
// ── CANCELAMENTOS (detalhe) ──
|
||||
if !self.cancelamentos_arr.is_empty() {
|
||||
out.extend(escpos::line_bold("CANCELAMENTOS"));
|
||||
for linha in &self.cancelamentos_arr {
|
||||
if linha.valor.is_some() {
|
||||
let num = linha.numero.clone().unwrap_or_default();
|
||||
out.extend(escpos::kv(&num, &linha.fmt_v()));
|
||||
if let Some(ref m) = linha.motivo {
|
||||
if !m.is_empty() {
|
||||
out.extend(escpos::line_center(&format!(" {}", m)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// ── TROCO / SALDO DE CAIXA ──
|
||||
out.extend(escpos::line_bold("SALDO DE CAIXA"));
|
||||
out.extend(escpos::kv("Troco (+):", &self.fmt_money(self.saldo_troco)));
|
||||
out.extend(escpos::kv("Credito (+):", &self.fmt_money(self.credito)));
|
||||
out.extend(escpos::kv("Debito (+):", &self.fmt_money(self.debito)));
|
||||
out.extend(escpos::kv("Alimentacao (+):", &self.fmt_money(self.alimentacao)));
|
||||
out.extend(escpos::kv("PIX Cartao (+):", &self.fmt_money(self.voucher)));
|
||||
out.extend(escpos::kv("Vales (+):", &self.fmt_money(self.vales)));
|
||||
out.extend(escpos::kv("A Receber (+):", &self.fmt_money(self.areceber)));
|
||||
out.extend(escpos::kv("PIX CNPJ (+):", &self.fmt_money(self.pixcnpj)));
|
||||
out.extend(escpos::divider());
|
||||
|
||||
// ── TOTAL ──
|
||||
if self.sys_total > 0.0 {
|
||||
out.extend(escpos::line_bold("TOTAL"));
|
||||
out.extend(escpos::kv("Total:", &self.fmt_money(self.sys_total)));
|
||||
out.extend(escpos::divider());
|
||||
}
|
||||
|
||||
// ── TROCO ──
|
||||
out.extend(escpos::line_bold("TROCO"));
|
||||
out.extend(escpos::kv("Troco:", &self.fmt_money(self.saldo_troco)));
|
||||
// ── SALDO ESPERADO ──
|
||||
out.extend(escpos::line_bold("SALDO ESPERADO"));
|
||||
out.extend(escpos::kv("Esperado:", &self.fmt_money(self.saldo_esperado)));
|
||||
out.extend(escpos::divider());
|
||||
|
||||
// ── DIFERENÇA ──
|
||||
// Usa self.diferenca que o frontend calcula como sys_total - saldo_esperado
|
||||
// (não recalcula de fechamento, que é o valor contado e não deve ser usado na diferença)
|
||||
out.extend(escpos::line_bold("DIFERENCA"));
|
||||
let diff = self.diferenca;
|
||||
let diff_str = if diff >= 0.0 {
|
||||
format!("+{}", self.fmt_money(diff))
|
||||
} else {
|
||||
self.fmt_money(diff)
|
||||
};
|
||||
out.extend(escpos::title(&diff_str));
|
||||
|
||||
// ── OBSERVAÇÕES ──
|
||||
if let Some(ref obs) = self.observacoes {
|
||||
if !obs.trim().is_empty() {
|
||||
@@ -263,25 +338,6 @@ impl ReciboData {
|
||||
}
|
||||
}
|
||||
|
||||
// ── TOTAL SALDO CAIXA ──
|
||||
out.extend(escpos::line_bold("TOTAL SALDO DE CAIXA"));
|
||||
out.extend(escpos::kv("Total:", &self.fmt_money(self.fechamento)));
|
||||
out.extend(escpos::divider());
|
||||
|
||||
// ── SALDO ESPERADO ──
|
||||
out.extend(escpos::line_bold("SALDO ESPERADO"));
|
||||
out.extend(escpos::kv("Esperado:", &self.fmt_money(self.saldo_esperado)));
|
||||
out.extend(escpos::divider());
|
||||
|
||||
// ── DIFERENÇA ──
|
||||
out.extend(escpos::line_bold("DIFERENCA"));
|
||||
let diff_str = if self.diferenca >= 0.0 {
|
||||
format!("+{}", self.fmt_money(self.diferenca))
|
||||
} else {
|
||||
self.fmt_money(self.diferenca)
|
||||
};
|
||||
out.extend(escpos::title(&diff_str));
|
||||
|
||||
// Rodapé
|
||||
out.extend(escpos::divider());
|
||||
out.extend(escpos::line_center(
|
||||
|
||||
@@ -153,7 +153,59 @@ impl SupabaseClient {
|
||||
}
|
||||
}
|
||||
|
||||
/// Lista fechamentos recentes de uma loja.
|
||||
/// Busca um fechamento pelo UUID.
|
||||
pub fn buscar_por_uuid(&self, uuid: &str) -> Result<Option<Value>, SupabaseError> {
|
||||
let url = format!(
|
||||
"{}/rest/v1/fechamentos_web?uuid=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
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
/// Atualiza campos específicos de um fechamento pelo UUID (PATCH).
|
||||
pub fn atualizar_fechamento(&self, uuid: &str, updates: &serde_json::Value) -> Result<Value, SupabaseError> {
|
||||
let url = format!(
|
||||
"{}/rest/v1/fechamentos_web?uuid=eq.{}",
|
||||
self.base_url, uuid
|
||||
);
|
||||
|
||||
let resp = self
|
||||
.http
|
||||
.patch(&url)
|
||||
.headers(self.headers(true))
|
||||
.json(updates)
|
||||
.send()?;
|
||||
|
||||
if resp.status().is_success() {
|
||||
Ok(serde_json::json!({ "ok": true, "uuid": uuid }))
|
||||
} else {
|
||||
let status = resp.status();
|
||||
let body = resp.text().unwrap_or_default();
|
||||
Err(SupabaseError::Api(format!(
|
||||
"HTTP {}: {}",
|
||||
status.as_u16(),
|
||||
body
|
||||
)))
|
||||
}
|
||||
}
|
||||
pub fn listar_recentes(&self, loja: &str, limite: i64) -> Result<Vec<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",
|
||||
@@ -322,6 +374,23 @@ 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)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn sb_atualizar_fechamento(
|
||||
sb: State<'_, SupabaseClient>,
|
||||
uuid: String,
|
||||
updates: serde_json::Value,
|
||||
) -> Result<serde_json::Value, SupabaseError> {
|
||||
sb.atualizar_fechamento(&uuid, &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/*": "./"
|
||||
|
||||
+737
-236
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user