Créé le {{ formatDate(quote.created_at) }}
+| Produit | +Qté | +Prix Unit. | +Remise | +Total HT | +
|---|---|---|---|---|
|
+
+
+ {{ line.product_name || line.description }}+{{ line.product.reference }} + |
+
+ {{ line.packages_qty || line.units_qty || line.quantity || line.qty_base }} + |
+
+ {{ formatCurrency(line.unit_price) }} + |
+
+ {{ line.discount_pct }}% + |
+
+ {{ formatCurrency(line.total_ht) }} + |
+
| Aucune ligne de produit | +||||
| Référence | +Client | +Date | +Validité | +Total TTC | +Statut | +Actions | +
|---|---|---|---|---|---|---|
| + + |
+
+
+
+
+ |
+
+ + + | + + | + + | + + | + |
| Référence | +Client | +Date | +Validité | +Total TTC | +Statut | +Actions | +
|---|---|---|---|---|---|---|
|
+
+
+ |
+
+
+
+
+
+
+
+ Client Inconnu
+
+
+ {{ quote.client.name }}+{{ quote.client.email }} + |
+
+
+ + {{ formatDate(quote.quote_date) }} + | + + ++ {{ formatDate(quote.valid_until) }} + | + + ++ {{ formatCurrency(quote.total_ttc) }} + | + + +
+ |
+
+
+
+
+
+ |
+
+ Aucun devis à afficher pour le moment. +
+Créer un nouveau devis pour un client.
+([]); + const currentQuote = ref(null); + const loading = ref(false); + const error = ref(null); + + // Pagination state + const pagination = ref({ + current_page: 1, + last_page: 1, + per_page: 10, + total: 0, + }); + + // Getters + const allQuotes = computed(() => quotes.value); + const isLoading = computed(() => loading.value); + const hasError = computed(() => error.value !== null); + const getError = computed(() => error.value); + const getQuoteById = computed(() => (id: number) => + quotes.value.find((quote) => quote.id === id) + ); + const getPagination = computed(() => pagination.value); + + // Actions + const setLoading = (isLoading: boolean) => { + loading.value = isLoading; + }; + + const setError = (err: string | null) => { + error.value = err; + }; + + const setQuotes = (newQuotes: Quote[]) => { + quotes.value = newQuotes; + }; + + const setCurrentQuote = (quote: Quote | null) => { + currentQuote.value = quote; + }; + + 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 quotes with optional pagination and filters + */ + const fetchQuotes = async (params?: { + page?: number; + per_page?: number; + search?: string; + status?: string; + client_id?: number; + }) => { + setLoading(true); + setError(null); + + try { + const response = await QuoteService.getAllQuotes(params); + setQuotes(response.data); + if (response.meta) { + setPagination(response.meta); + } + return response; + } catch (err: any) { + const errorMessage = + err.response?.data?.message || err.message || "Failed to fetch quotes"; + setError(errorMessage); + throw err; + } finally { + setLoading(false); + } + }; + + /** + * Fetch a single quote by ID + */ + const fetchQuote = async (id: number) => { + setLoading(true); + setError(null); + + try { + const response = await QuoteService.getQuote(id); + setCurrentQuote(response.data); + return response.data; + } catch (err: any) { + const errorMessage = + err.response?.data?.message || err.message || "Failed to fetch quote"; + setError(errorMessage); + throw err; + } finally { + setLoading(false); + } + }; + + /** + * Create a new quote + */ + const createQuote = async (payload: CreateQuotePayload) => { + setLoading(true); + setError(null); + + try { + const response = await QuoteService.createQuote(payload); + // Add the new quote to the list + quotes.value.push(response.data); + setCurrentQuote(response.data); + return response.data; + } catch (err: any) { + const errorMessage = + err.response?.data?.message || err.message || "Failed to create quote"; + setError(errorMessage); + throw err; + } finally { + setLoading(false); + } + }; + + /** + * Update an existing quote + */ + const updateQuote = async (payload: UpdateQuotePayload) => { + setLoading(true); + setError(null); + + try { + const response = await QuoteService.updateQuote(payload); + const updatedQuote = response.data; + + // Update in the quotes list + const index = quotes.value.findIndex( + (quote) => quote.id === updatedQuote.id + ); + if (index !== -1) { + quotes.value[index] = updatedQuote; + } + + // Update current quote if it's the one being edited + if (currentQuote.value && currentQuote.value.id === updatedQuote.id) { + setCurrentQuote(updatedQuote); + } + + return updatedQuote; + } catch (err: any) { + const errorMessage = + err.response?.data?.message || err.message || "Failed to update quote"; + setError(errorMessage); + throw err; + } finally { + setLoading(false); + } + }; + + /** + * Delete a quote + */ + const deleteQuote = async (id: number) => { + setLoading(true); + setError(null); + + try { + const response = await QuoteService.deleteQuote(id); + + // Remove from the quotes list + quotes.value = quotes.value.filter((quote) => quote.id !== id); + + // Clear current quote if it's the one being deleted + if (currentQuote.value && currentQuote.value.id === id) { + setCurrentQuote(null); + } + + return response; + } catch (err: any) { + const errorMessage = + err.response?.data?.message || err.message || "Failed to delete quote"; + setError(errorMessage); + throw err; + } finally { + setLoading(false); + } + }; + + return { + // State + quotes, + currentQuote, + loading, + error, + pagination, + + // Getters + allQuotes, + isLoading, + hasError, + getError, + getQuoteById, + getPagination, + + // Actions + fetchQuotes, + fetchQuote, + createQuote, + updateQuote, + deleteQuote, + }; +}); diff --git a/thanasoft-front/src/views/pages/Ventes/Devis.vue b/thanasoft-front/src/views/pages/Ventes/Devis.vue index 38df022..018a4d9 100644 --- a/thanasoft-front/src/views/pages/Ventes/Devis.vue +++ b/thanasoft-front/src/views/pages/Ventes/Devis.vue @@ -1,11 +1,7 @@ - -+Devis
-- diff --git a/thanasoft-front/src/views/pages/Ventes/NewQuote.vue b/thanasoft-front/src/views/pages/Ventes/NewQuote.vue new file mode 100644 index 0000000..a029b4c --- /dev/null +++ b/thanasoft-front/src/views/pages/Ventes/NewQuote.vue @@ -0,0 +1,7 @@ + + + + + diff --git a/thanasoft-front/src/views/pages/Ventes/QuoteDetail.vue b/thanasoft-front/src/views/pages/Ventes/QuoteDetail.vue new file mode 100644 index 0000000..6752907 --- /dev/null +++ b/thanasoft-front/src/views/pages/Ventes/QuoteDetail.vue @@ -0,0 +1,12 @@ + + + + +