//! Outbound integrations. `InvoiceSink` is the seam between Voiced and a system that receives issued //! invoices (ERPNext now; a webhook, Zoho or a Tally import later). Everything target-specific lives in a //! submodule; the rest of the app only talks to this trait. // Parts of the sink API are only used by the later UI step. #![allow(dead_code)] pub mod erpnext; use serde::Serialize; use std::future::Future; /// What a sink reports after accepting an invoice. #[derive(Debug, Clone, PartialEq, Eq)] pub struct PushedInvoice { /// The document name on the remote system. pub remote_name: String, /// 0 draft, 1 submitted. pub remote_docstatus: i64, /// False when the remote already had this invoice (an idempotent re-push). pub created: bool, /// Non-fatal notes, e.g. "pushed without the PDF because the invoice is not archived". pub warnings: Vec, } pub struct PushRequest { pub invoice_id: i64, /// Submit on the remote system instead of leaving a draft for review; `None` follows the sink's setting. pub submit: Option, } pub struct PaymentRequest { pub payment_id: i64, } #[derive(Debug, Clone, PartialEq, Eq)] pub struct PushedPayment { pub remote_name: String, /// False when the payment had already been sent (an idempotent re-push). pub created: bool, } /// The locally recorded sync state of one invoice. `status` is `none` until something was pushed, then /// `synced`, `error` or `conflict`. #[derive(Debug, Clone, PartialEq, Eq, Serialize)] #[serde(rename_all = "camelCase")] pub struct SyncStatus { pub invoice_id: i64, pub status: String, pub remote_name: String, /// 0 draft, 1 submitted. pub remote_docstatus: i64, pub last_error: String, pub synced_at: Option, /// The archived PDF was uploaded to the remote document. pub attached: bool, } impl SyncStatus { pub fn none(invoice_id: i64) -> Self { SyncStatus { invoice_id, status: "none".into(), remote_name: String::new(), remote_docstatus: 0, last_error: String::new(), synced_at: None, attached: false, } } } pub trait InvoiceSink: Send + Sync { /// Stable identifier used in the sync table (`erpnext`, ...). fn id(&self) -> &'static str; fn push_invoice<'a>( &'a self, request: PushRequest, ) -> impl Future> + Send + 'a; fn push_payment<'a>( &'a self, request: PaymentRequest, ) -> impl Future> + Send + 'a; /// The locally recorded state; never touches the network. fn status(&self, invoice_id: i64) -> Result; }