Add business Finance hub with invoices and templates.
Ship invoice issue/list/templates under Finance, fix tenant theme reset and owner role badge, and show customers-joined charts by Persian months. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
722933dda9
commit
917b840ee5
@@ -0,0 +1,159 @@
|
||||
import { apiRequest } from '../lib/api'
|
||||
import { getActiveBusinessId } from '../lib/businessContext'
|
||||
import type {
|
||||
CreateInvoiceItemTemplatePayload,
|
||||
CreateInvoicePayload,
|
||||
CreateInvoiceTemplatePayload,
|
||||
Invoice,
|
||||
InvoiceItemTemplate,
|
||||
InvoiceItemTemplatesResponse,
|
||||
InvoiceStatus,
|
||||
InvoiceTemplate,
|
||||
InvoiceTemplatesListResponse,
|
||||
InvoicesListResponse,
|
||||
UpdateInvoiceItemTemplatePayload,
|
||||
UpdateInvoicePayload,
|
||||
UpdateInvoiceTemplatePayload,
|
||||
} from '../types/invoice'
|
||||
|
||||
function businessPath(suffix = '') {
|
||||
const businessId = getActiveBusinessId()
|
||||
if (!businessId) {
|
||||
throw new Error('No active business selected. Please sign in again.')
|
||||
}
|
||||
return `/businesses/${businessId}${suffix}`
|
||||
}
|
||||
|
||||
export function listInvoiceItemTemplates(signal?: AbortSignal) {
|
||||
return apiRequest<InvoiceItemTemplatesResponse>(businessPath('/invoice-item-templates'), {
|
||||
auth: true,
|
||||
signal,
|
||||
})
|
||||
}
|
||||
|
||||
export function createInvoiceItemTemplate(payload: CreateInvoiceItemTemplatePayload) {
|
||||
return apiRequest<InvoiceItemTemplate>(businessPath('/invoice-item-templates'), {
|
||||
method: 'POST',
|
||||
auth: true,
|
||||
body: payload,
|
||||
})
|
||||
}
|
||||
|
||||
export function updateInvoiceItemTemplate(
|
||||
templateId: string,
|
||||
payload: UpdateInvoiceItemTemplatePayload,
|
||||
) {
|
||||
return apiRequest<InvoiceItemTemplate>(
|
||||
businessPath(`/invoice-item-templates/${templateId}`),
|
||||
{
|
||||
method: 'PATCH',
|
||||
auth: true,
|
||||
body: payload,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
export function deleteInvoiceItemTemplate(templateId: string) {
|
||||
return apiRequest<{ ok: boolean }>(businessPath(`/invoice-item-templates/${templateId}`), {
|
||||
method: 'DELETE',
|
||||
auth: true,
|
||||
})
|
||||
}
|
||||
|
||||
export function listInvoiceTemplates(signal?: AbortSignal) {
|
||||
return apiRequest<InvoiceTemplatesListResponse>(businessPath('/invoice-templates'), {
|
||||
auth: true,
|
||||
signal,
|
||||
})
|
||||
}
|
||||
|
||||
export function getInvoiceTemplate(templateId: string, signal?: AbortSignal) {
|
||||
return apiRequest<InvoiceTemplate>(businessPath(`/invoice-templates/${templateId}`), {
|
||||
auth: true,
|
||||
signal,
|
||||
})
|
||||
}
|
||||
|
||||
export function createInvoiceTemplate(payload: CreateInvoiceTemplatePayload) {
|
||||
return apiRequest<InvoiceTemplate>(businessPath('/invoice-templates'), {
|
||||
method: 'POST',
|
||||
auth: true,
|
||||
body: payload,
|
||||
})
|
||||
}
|
||||
|
||||
export function updateInvoiceTemplate(templateId: string, payload: UpdateInvoiceTemplatePayload) {
|
||||
return apiRequest<InvoiceTemplate>(businessPath(`/invoice-templates/${templateId}`), {
|
||||
method: 'PATCH',
|
||||
auth: true,
|
||||
body: payload,
|
||||
})
|
||||
}
|
||||
|
||||
export function deleteInvoiceTemplate(templateId: string) {
|
||||
return apiRequest<{ ok: boolean }>(businessPath(`/invoice-templates/${templateId}`), {
|
||||
method: 'DELETE',
|
||||
auth: true,
|
||||
})
|
||||
}
|
||||
|
||||
export interface ListInvoicesParams {
|
||||
page?: number
|
||||
pageSize?: number
|
||||
status?: InvoiceStatus
|
||||
userId?: string
|
||||
}
|
||||
|
||||
export function listInvoices(params: ListInvoicesParams = {}, signal?: AbortSignal) {
|
||||
const search = new URLSearchParams()
|
||||
if (params.page) search.set('page', String(params.page))
|
||||
if (params.pageSize) search.set('pageSize', String(params.pageSize))
|
||||
if (params.status) search.set('status', params.status)
|
||||
if (params.userId) search.set('userId', params.userId)
|
||||
const qs = search.toString()
|
||||
return apiRequest<InvoicesListResponse>(businessPath(`/invoices${qs ? `?${qs}` : ''}`), {
|
||||
auth: true,
|
||||
signal,
|
||||
})
|
||||
}
|
||||
|
||||
export function getInvoice(invoiceId: string, signal?: AbortSignal) {
|
||||
return apiRequest<Invoice>(businessPath(`/invoices/${invoiceId}`), {
|
||||
auth: true,
|
||||
signal,
|
||||
})
|
||||
}
|
||||
|
||||
export function createInvoice(payload: CreateInvoicePayload) {
|
||||
return apiRequest<Invoice>(businessPath('/invoices'), {
|
||||
method: 'POST',
|
||||
auth: true,
|
||||
body: payload,
|
||||
})
|
||||
}
|
||||
|
||||
export function updateInvoice(invoiceId: string, payload: UpdateInvoicePayload) {
|
||||
return apiRequest<Invoice>(businessPath(`/invoices/${invoiceId}`), {
|
||||
method: 'PUT',
|
||||
auth: true,
|
||||
body: payload,
|
||||
})
|
||||
}
|
||||
|
||||
export function updateInvoiceStatus(
|
||||
invoiceId: string,
|
||||
payload: { status: InvoiceStatus; notes?: string },
|
||||
) {
|
||||
return apiRequest<Invoice>(businessPath(`/invoices/${invoiceId}`), {
|
||||
method: 'PATCH',
|
||||
auth: true,
|
||||
body: payload,
|
||||
})
|
||||
}
|
||||
|
||||
export function deleteInvoice(invoiceId: string) {
|
||||
return apiRequest<{ ok: boolean }>(businessPath(`/invoices/${invoiceId}`), {
|
||||
method: 'DELETE',
|
||||
auth: true,
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user