246 lines
5.9 KiB
TypeScript
246 lines
5.9 KiB
TypeScript
import { defineStore } from "pinia";
|
|
import { ref, computed } from "vue";
|
|
import InvoiceService, {
|
|
Invoice,
|
|
CreateInvoicePayload,
|
|
UpdateInvoicePayload,
|
|
} from "@/services/invoice";
|
|
|
|
export const useInvoiceStore = defineStore("invoice", () => {
|
|
// State
|
|
const invoices = ref<Invoice[]>([]);
|
|
const currentInvoice = ref<Invoice | null>(null);
|
|
const loading = ref(false);
|
|
const error = ref<string | null>(null);
|
|
|
|
// Pagination state
|
|
const pagination = ref({
|
|
current_page: 1,
|
|
last_page: 1,
|
|
per_page: 10,
|
|
total: 0,
|
|
});
|
|
|
|
// Getters
|
|
const allInvoices = computed(() => invoices.value);
|
|
const isLoading = computed(() => loading.value);
|
|
const hasError = computed(() => error.value !== null);
|
|
const getError = computed(() => error.value);
|
|
const getInvoiceById = computed(() => (id: number) =>
|
|
invoices.value.find((invoice) => invoice.id === id)
|
|
);
|
|
const getPagination = computed(() => pagination.value);
|
|
|
|
// Actions
|
|
const setLoading = (isLoading: boolean) => {
|
|
loading.value = isLoading;
|
|
};
|
|
|
|
const setError = (err: string | null) => {
|
|
error.value = err;
|
|
};
|
|
|
|
const setInvoices = (newInvoices: Invoice[]) => {
|
|
invoices.value = newInvoices;
|
|
};
|
|
|
|
const setCurrentInvoice = (invoice: Invoice | null) => {
|
|
currentInvoice.value = invoice;
|
|
};
|
|
|
|
const setPagination = (meta: any) => {
|
|
if (meta) {
|
|
pagination.value = {
|
|
current_page: meta.current_page || 1,
|
|
last_page: meta.last_page || 1,
|
|
per_page: meta.per_page || 10,
|
|
total: meta.total || 0,
|
|
};
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Fetch all invoices with optional pagination and filters
|
|
*/
|
|
const fetchInvoices = async (params?: {
|
|
page?: number;
|
|
per_page?: number;
|
|
search?: string;
|
|
status?: string;
|
|
client_id?: number;
|
|
}) => {
|
|
setLoading(true);
|
|
setError(null);
|
|
|
|
try {
|
|
const response = await InvoiceService.getAllInvoices(params);
|
|
setInvoices(response.data);
|
|
if (response.meta) {
|
|
setPagination(response.meta);
|
|
}
|
|
return response;
|
|
} catch (err: any) {
|
|
const errorMessage =
|
|
err.response?.data?.message || err.message || "Failed to fetch invoices";
|
|
setError(errorMessage);
|
|
throw err;
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Fetch a single invoice by ID
|
|
*/
|
|
const fetchInvoice = async (id: number) => {
|
|
setLoading(true);
|
|
setError(null);
|
|
|
|
try {
|
|
const response = await InvoiceService.getInvoice(id);
|
|
setCurrentInvoice(response.data);
|
|
return response.data;
|
|
} catch (err: any) {
|
|
const errorMessage =
|
|
err.response?.data?.message || err.message || "Failed to fetch invoice";
|
|
setError(errorMessage);
|
|
throw err;
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Create a new invoice
|
|
*/
|
|
const createInvoice = async (payload: CreateInvoicePayload) => {
|
|
setLoading(true);
|
|
setError(null);
|
|
|
|
try {
|
|
const response = await InvoiceService.createInvoice(payload);
|
|
invoices.value.push(response.data);
|
|
setCurrentInvoice(response.data);
|
|
return response.data;
|
|
} catch (err: any) {
|
|
const errorMessage =
|
|
err.response?.data?.message || err.message || "Failed to create invoice";
|
|
setError(errorMessage);
|
|
throw err;
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Create an invoice from a quote
|
|
*/
|
|
const createFromQuote = async (quoteId: number) => {
|
|
setLoading(true);
|
|
setError(null);
|
|
|
|
try {
|
|
const response = await InvoiceService.createFromQuote(quoteId);
|
|
invoices.value.push(response.data);
|
|
setCurrentInvoice(response.data);
|
|
return response.data;
|
|
} catch (err: any) {
|
|
const errorMessage =
|
|
err.response?.data?.message || err.message || "Failed to create invoice from quote";
|
|
setError(errorMessage);
|
|
throw err;
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Update an existing invoice
|
|
*/
|
|
const updateInvoice = async (payload: UpdateInvoicePayload) => {
|
|
setLoading(true);
|
|
setError(null);
|
|
|
|
try {
|
|
const response = await InvoiceService.updateInvoice(payload);
|
|
const updatedInvoice = response.data;
|
|
|
|
// Update in the invoices list
|
|
const index = invoices.value.findIndex(
|
|
(invoice) => invoice.id === updatedInvoice.id
|
|
);
|
|
if (index !== -1) {
|
|
invoices.value[index] = updatedInvoice;
|
|
}
|
|
|
|
// Update current invoice if it's the one being edited
|
|
if (currentInvoice.value && currentInvoice.value.id === updatedInvoice.id) {
|
|
setCurrentInvoice(updatedInvoice);
|
|
}
|
|
|
|
return updatedInvoice;
|
|
} catch (err: any) {
|
|
const errorMessage =
|
|
err.response?.data?.message || err.message || "Failed to update invoice";
|
|
setError(errorMessage);
|
|
throw err;
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Delete an invoice
|
|
*/
|
|
const deleteInvoice = async (id: number) => {
|
|
setLoading(true);
|
|
setError(null);
|
|
|
|
try {
|
|
const response = await InvoiceService.deleteInvoice(id);
|
|
|
|
// Remove from the invoices list
|
|
invoices.value = invoices.value.filter((invoice) => invoice.id !== id);
|
|
|
|
// Clear current invoice if it's the one being deleted
|
|
if (currentInvoice.value && currentInvoice.value.id === id) {
|
|
setCurrentInvoice(null);
|
|
}
|
|
|
|
return response;
|
|
} catch (err: any) {
|
|
const errorMessage =
|
|
err.response?.data?.message || err.message || "Failed to delete invoice";
|
|
setError(errorMessage);
|
|
throw err;
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
return {
|
|
// State
|
|
invoices,
|
|
currentInvoice,
|
|
loading,
|
|
error,
|
|
pagination,
|
|
|
|
// Getters
|
|
allInvoices,
|
|
isLoading,
|
|
hasError,
|
|
getError,
|
|
getInvoiceById,
|
|
getPagination,
|
|
|
|
// Actions
|
|
fetchInvoices,
|
|
fetchInvoice,
|
|
createInvoice,
|
|
createFromQuote,
|
|
updateInvoice,
|
|
deleteInvoice,
|
|
};
|
|
});
|