Files
zeroclock/src/stores/invoices.ts

170 lines
4.5 KiB
TypeScript

import { defineStore } from 'pinia'
import { ref } from 'vue'
import { invoke } from '@tauri-apps/api/core'
import { handleInvokeError } from '../utils/errorHandler'
export interface Invoice {
id?: number
client_id: number
invoice_number: string
date: string
due_date?: string
subtotal: number
tax_rate: number
tax_amount: number
discount: number
total: number
notes?: string
status: string
template_id?: string
}
export interface InvoiceItem {
id?: number
invoice_id: number
description: string
quantity: number
rate: number
amount: number
time_entry_id?: number
}
export const useInvoicesStore = defineStore('invoices', () => {
const invoices = ref<Invoice[]>([])
const loading = ref(false)
async function fetchInvoices() {
loading.value = true
try {
invoices.value = await invoke<Invoice[]>('get_invoices')
} catch (error) {
handleInvokeError(error, 'Failed to fetch invoices', () => fetchInvoices())
} finally {
loading.value = false
}
}
async function createInvoice(invoice: Invoice): Promise<number | null> {
try {
const id = await invoke<number>('create_invoice', { invoice })
invoices.value.unshift({ ...invoice, id: Number(id) })
return Number(id)
} catch (error) {
handleInvokeError(error, 'Failed to create invoice')
return null
}
}
async function updateInvoice(invoice: Invoice): Promise<boolean> {
try {
await invoke('update_invoice', { invoice })
const index = invoices.value.findIndex(i => i.id === invoice.id)
if (index !== -1) {
invoices.value[index] = invoice
}
return true
} catch (error) {
handleInvokeError(error, 'Failed to update invoice')
return false
}
}
async function updateInvoiceTemplate(id: number, templateId: string): Promise<boolean> {
try {
await invoke('update_invoice_template', { id, templateId })
const index = invoices.value.findIndex(i => i.id === id)
if (index !== -1) {
invoices.value[index].template_id = templateId
}
return true
} catch (error) {
handleInvokeError(error, 'Failed to update invoice template')
return false
}
}
async function deleteInvoice(id: number): Promise<boolean> {
try {
await invoke('delete_invoice', { id })
invoices.value = invoices.value.filter(i => i.id !== id)
return true
} catch (error) {
handleInvokeError(error, 'Failed to delete invoice')
return false
}
}
async function getInvoiceItems(invoiceId: number): Promise<InvoiceItem[]> {
try {
return await invoke<InvoiceItem[]>('get_invoice_items', { invoiceId })
} catch (error) {
handleInvokeError(error, 'Failed to fetch invoice items')
return []
}
}
async function createInvoiceItem(item: InvoiceItem): Promise<number | null> {
try {
return await invoke<number>('create_invoice_item', { item })
} catch (error) {
handleInvokeError(error, 'Failed to create invoice item')
return null
}
}
async function saveInvoiceItems(invoiceId: number, items: { description: string; quantity: number; unit_price: number; time_entry_id?: number }[]): Promise<void> {
try {
await invoke('save_invoice_items_batch', {
invoiceId,
items: items.map(item => ({
description: item.description,
quantity: item.quantity,
unit_price: item.unit_price,
time_entry_id: item.time_entry_id || null,
})),
})
} catch (error) {
handleInvokeError(error, 'Failed to save invoice items')
throw error
}
}
async function updateStatus(id: number, status: string): Promise<boolean> {
try {
await invoke('update_invoice_status', { id, status })
const idx = invoices.value.findIndex(i => i.id === id)
if (idx !== -1) invoices.value[idx].status = status
return true
} catch (error) {
handleInvokeError(error, 'Failed to update invoice status')
return false
}
}
async function checkOverdue(): Promise<number> {
try {
const today = new Date().toISOString().split('T')[0]
const count = await invoke<number>('check_overdue_invoices', { today })
if (count > 0) await fetchInvoices()
return count
} catch {
return 0
}
}
return {
invoices,
loading,
fetchInvoices,
createInvoice,
updateInvoice,
updateInvoiceTemplate,
deleteInvoice,
getInvoiceItems,
createInvoiceItem,
saveInvoiceItems,
updateStatus,
checkOverdue
}
})