import { invoke } from "@tauri-apps/api/core"; import type { BankAccount, Client, DraftSummary, Invoice, InvoiceInput, InvoiceSeries, InvoiceSummary, ItemPreset, Settings, } from "./types"; import type { LogoAsset } from "./logo"; import type { Payment, PaymentInput } from "./payments"; import type { BackupStatus, BackupSummary, PendingRestore } from "./backup"; import type { LedgerRow } from "./historyExport"; import type { ErpnextConfig, ErpnextConfigInput, ErpnextConnectionTest, ErpnextOptions, ErpnextPaymentPushResult, ErpnextPushResult, ErpnextSyncStatus, } from "./erpnext"; import type { FontInspection, ImportMeta, RemoveOutcome, UserFontRow } from "./fontImport"; export interface ArchiveStatus { archived: boolean; sha256?: string; fingerprint?: string; archivedAt?: string; } export const api = { getSettings: () => invoke("get_settings"), saveSettings: (settings: Settings) => invoke("save_settings", { settings }), listBanks: () => invoke("list_banks"), saveBank: (bank: BankAccount) => invoke("save_bank", { bank }), deleteBank: (id: number) => invoke("delete_bank", { id }), getActiveSeries: () => invoke("get_active_series"), listSeries: () => invoke("list_series"), startNewSeries: (prefix: string, padding: number) => invoke("start_new_series", { prefix, padding }), listClients: () => invoke("list_clients"), saveClient: (client: Client) => invoke("save_client", { client }), deleteClient: (id: number) => invoke("delete_client", { id }), listItemPresets: () => invoke("list_item_presets"), saveItemPreset: (preset: ItemPreset) => invoke("save_item_preset", { preset }), deleteItemPreset: (id: number) => invoke("delete_item_preset", { id }), peekNextInvoiceNumber: () => invoke("peek_next_invoice_number"), issueInvoice: (input: InvoiceInput, renderPrefs: object) => invoke("issue_invoice", { input, renderPrefs }), cancelInvoice: (id: number, reason: string) => invoke("cancel_invoice", { id, reason }), listInvoices: () => invoke("list_invoices"), listInvoiceLedger: () => invoke("list_invoice_ledger"), getInvoice: (id: number) => invoke("get_invoice", { id }), recordPayment: (input: PaymentInput) => invoke("record_payment", { input }), listPayments: (invoiceId: number) => invoke("list_payments", { invoiceId }), deletePayment: (id: number) => invoke("delete_payment", { id }), saveDraft: (id: number | null, payload: unknown) => invoke("save_draft", { id, payload }), listDrafts: () => invoke("list_drafts"), getDraft: (id: number) => invoke("get_draft", { id }), deleteDraft: (id: number) => invoke("delete_draft", { id }), importAsset: (sourcePath: string, kind: string) => invoke("import_asset", { sourcePath, kind }), saveAssetBytes: (kind: string, fileName: string, dataBase64: string) => invoke("save_asset_bytes", { kind, fileName, dataBase64 }), importLogo: (sourcePath: string) => invoke("import_logo", { sourcePath }), saveLogoBytes: (fileName: string, dataBase64: string) => invoke("save_logo_bytes", { fileName, dataBase64 }), ensureLogoDerived: () => invoke("ensure_logo_derived"), removeLogo: () => invoke("remove_logo"), readAssetDataUri: (path: string) => invoke("read_asset_data_uri", { path }), removeAsset: (path: string) => invoke("remove_asset", { path }), // Raw-body commands: the payload is the bytes themselves (no base64); metadata goes in headers. // Header values must be visible ASCII, hence the encodeURIComponent on paths. archivePdf: (invoiceId: number, bytes: Uint8Array, fingerprint: string) => invoke("archive_pdf", bytes, { headers: { "x-invoice-id": String(invoiceId), "x-fingerprint": fingerprint }, }), readArchive: async (invoiceId: number): Promise => new Uint8Array(await invoke("read_archive", { invoiceId })), archiveStatus: (invoiceId: number) => invoke("archive_status", { invoiceId }), // Imported fonts (Now, Gotham, Open Sauce One). The font bytes are the raw body; metadata goes in headers. inspectFont: (bytes: Uint8Array) => invoke("inspect_font", bytes), importFont: (bytes: Uint8Array, meta: ImportMeta) => invoke("import_font", bytes, { headers: { "x-face": encodeURIComponent(meta.face), "x-file-name": encodeURIComponent(meta.fileName), "x-licence-ack": String(meta.licenceAcknowledged), ...(meta.weight !== undefined ? { "x-weight": String(meta.weight) } : {}), ...(meta.italic !== undefined ? { "x-style": meta.italic ? "italic" : "normal" } : {}), }, }), listUserFonts: (includeHidden = false) => invoke("list_user_fonts", { includeHidden }), getUserFontBytes: async (sha256: string): Promise => new Uint8Array(await invoke("get_user_font_bytes", { sha256 })), removeUserFont: (id: number) => invoke("remove_user_font", { id }), writeExportFile: (path: string, bytes: Uint8Array) => invoke("write_export_file", bytes, { headers: { "x-path": encodeURIComponent(path) } }), getLastExportDir: () => invoke("get_last_export_dir"), revealInFolder: (path: string) => invoke("reveal_in_folder", { path }), openFile: (path: string) => invoke("open_file", { path }), // Backup and restore. A restore is staged now and applied by the next start (see commands/backup.rs). createBackup: (dest: string) => invoke("create_backup", { dest }), restoreBackup: (path: string) => invoke("restore_backup", { path }), cancelPendingRestore: () => invoke("cancel_pending_restore"), getBackupStatus: () => invoke("get_backup_status"), setAutoBackup: (enabled: boolean) => invoke("set_auto_backup", { enabled }), restartApp: () => invoke("restart_app"), // Stages a wipe of all data; it is carried out by the next start (see commands/reset.rs), so restart afterwards. resetAllData: () => invoke("reset_all_data"), // ERPNext integration. All HTTP runs in Rust; the API secret is write-only from the webview. erpnextGetConfig: () => invoke("erpnext_get_config"), erpnextSaveConfig: (config: ErpnextConfigInput) => invoke("erpnext_save_config", { config }), erpnextTestConnection: (config: ErpnextConfigInput) => invoke("erpnext_test_connection", { config }), erpnextLoadOptions: (config: ErpnextConfigInput) => invoke("erpnext_load_options", { config }), /** `submit` omitted follows the "submit on push" setting. A failed push resolves with `ok: false`. */ erpnextPushInvoice: (id: number, submit?: boolean) => invoke("erpnext_push_invoice", { id, submit: submit ?? null }), /** One result per distinct id, in order; a failing row never stops the rest. */ erpnextPushInvoices: (ids: number[], submit?: boolean) => invoke("erpnext_push_invoices", { ids, submit: submit ?? null }), erpnextPushPayment: (paymentId: number) => invoke("erpnext_push_payment", { paymentId }), erpnextSyncStatus: (id: number) => invoke("erpnext_sync_status", { id }), /** Every invoice that has a sync row (invoices never pushed are absent). */ erpnextSyncStatuses: () => invoke("erpnext_sync_statuses"), /** `/app/sales-invoice/`; rejects when the invoice was never sent. */ erpnextOpenUrl: (id: number) => invoke("erpnext_open_url", { id }), };