Avoirs et factures fournisseur: harmonisation des écrans, formulaires et stores
This commit is contained in:
parent
ecfe25d3ca
commit
dc87b0f720
@ -38,11 +38,15 @@
|
|||||||
<div class="row g-3 mb-3">
|
<div class="row g-3 mb-3">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<label class="form-label">Client</label>
|
<label class="form-label">Client</label>
|
||||||
<div class="info-value">{{ avoir.client?.name || 'Client inconnu' }}</div>
|
<div class="info-value">
|
||||||
|
{{ avoir.client?.name || "Client inconnu" }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<label class="form-label">Facture d'origine</label>
|
<label class="form-label">Facture d'origine</label>
|
||||||
<div class="info-value">{{ avoir.invoice?.invoice_number || 'Non spécifiée' }}</div>
|
<div class="info-value">
|
||||||
|
{{ avoir.invoice?.invoice_number || "Non spécifiée" }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -51,11 +55,15 @@
|
|||||||
<div class="row g-3">
|
<div class="row g-3">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<label class="form-label">Motif</label>
|
<label class="form-label">Motif</label>
|
||||||
<div class="info-value">{{ getReasonLabel(avoir.reason_type) }}</div>
|
<div class="info-value">
|
||||||
|
{{ getReasonLabel(avoir.reason_type) }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<label class="form-label">Mode de remboursement</label>
|
<label class="form-label">Mode de remboursement</label>
|
||||||
<div class="info-value">{{ getRefundMethodLabel(avoir.refund_method) }}</div>
|
<div class="info-value">
|
||||||
|
{{ getRefundMethodLabel(avoir.refund_method) }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -71,11 +79,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="lines-container">
|
<div class="lines-container">
|
||||||
<div
|
<div v-for="line in avoir.lines" :key="line.id" class="line-item">
|
||||||
v-for="line in avoir.lines"
|
|
||||||
:key="line.id"
|
|
||||||
class="line-item"
|
|
||||||
>
|
|
||||||
<div class="row g-2 align-items-center">
|
<div class="row g-2 align-items-center">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<label class="form-label text-xs">Désignation</label>
|
<label class="form-label text-xs">Désignation</label>
|
||||||
@ -89,7 +93,9 @@
|
|||||||
|
|
||||||
<div class="col-md-2">
|
<div class="col-md-2">
|
||||||
<label class="form-label text-xs">Prix HT</label>
|
<label class="form-label text-xs">Prix HT</label>
|
||||||
<div class="line-price">{{ formatCurrency(line.unit_price) }}</div>
|
<div class="line-price">
|
||||||
|
{{ formatCurrency(line.unit_price) }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-md-2 d-flex flex-column align-items-end">
|
<div class="col-md-2 d-flex flex-column align-items-end">
|
||||||
@ -116,7 +122,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="total-row total-final">
|
<div class="total-row total-final">
|
||||||
<span class="total-label">Total TTC</span>
|
<span class="total-label">Total TTC</span>
|
||||||
<span class="total-amount">{{ formatCurrency(avoir.total_ttc) }}</span>
|
<span class="total-amount">{{
|
||||||
|
formatCurrency(avoir.total_ttc)
|
||||||
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -135,13 +143,17 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<label class="form-label">Contact client</label>
|
<label class="form-label">Contact client</label>
|
||||||
<div class="info-value">{{ avoir.client?.email || avoir.client?.phone || 'Non spécifié' }}</div>
|
<div class="info-value">
|
||||||
|
{{ avoir.client?.email || avoir.client?.phone || "Non spécifié" }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="avoir.reason_description" class="mt-3">
|
<div v-if="avoir.reason_description" class="mt-3">
|
||||||
<label class="form-label">Détail du motif</label>
|
<label class="form-label">Détail du motif</label>
|
||||||
<div class="info-value notes-content">{{ avoir.reason_description }}</div>
|
<div class="info-value notes-content">
|
||||||
|
{{ avoir.reason_description }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -151,8 +163,8 @@
|
|||||||
<soft-button
|
<soft-button
|
||||||
color="secondary"
|
color="secondary"
|
||||||
variant="gradient"
|
variant="gradient"
|
||||||
@click="dropdownOpen = !dropdownOpen"
|
|
||||||
class="btn-status"
|
class="btn-status"
|
||||||
|
@click="dropdownOpen = !dropdownOpen"
|
||||||
>
|
>
|
||||||
<i class="fas fa-exchange-alt me-2"></i>
|
<i class="fas fa-exchange-alt me-2"></i>
|
||||||
Changer le statut
|
Changer le statut
|
||||||
@ -161,14 +173,17 @@
|
|||||||
<ul
|
<ul
|
||||||
v-if="dropdownOpen"
|
v-if="dropdownOpen"
|
||||||
class="dropdown-menu show position-absolute"
|
class="dropdown-menu show position-absolute"
|
||||||
style="top: 100%; left: 0; z-index: 1000;"
|
style="top: 100%; left: 0; z-index: 1000"
|
||||||
>
|
>
|
||||||
<li v-for="status in availableStatuses" :key="status">
|
<li v-for="status in availableStatuses" :key="status">
|
||||||
<a
|
<a
|
||||||
class="dropdown-item"
|
class="dropdown-item"
|
||||||
:class="{ active: status === avoir.status }"
|
:class="{ active: status === avoir.status }"
|
||||||
href="javascript:;"
|
href="javascript:;"
|
||||||
@click="changeStatus(status); dropdownOpen = false;"
|
@click="
|
||||||
|
changeStatus(status);
|
||||||
|
dropdownOpen = false;
|
||||||
|
"
|
||||||
>
|
>
|
||||||
<i :class="getStatusIcon(status) + ' me-2'"></i>
|
<i :class="getStatusIcon(status) + ' me-2'"></i>
|
||||||
{{ getStatusLabel(status) }}
|
{{ getStatusLabel(status) }}
|
||||||
@ -279,9 +294,12 @@ const changeStatus = async (newStatus) => {
|
|||||||
try {
|
try {
|
||||||
await avoirStore.updateAvoir({
|
await avoirStore.updateAvoir({
|
||||||
id: avoir.value.id,
|
id: avoir.value.id,
|
||||||
status: newStatus
|
status: newStatus,
|
||||||
});
|
});
|
||||||
notificationStore.success("Succès", `Statut mis à jour : ${getStatusLabel(newStatus)}`);
|
notificationStore.success(
|
||||||
|
"Succès",
|
||||||
|
`Statut mis à jour : ${getStatusLabel(newStatus)}`
|
||||||
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
notificationStore.error("Erreur", "Échec de la mise à jour du statut.");
|
notificationStore.error("Erreur", "Échec de la mise à jour du statut.");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -76,7 +76,10 @@ const handleExport = () => {
|
|||||||
const url = URL.createObjectURL(blob);
|
const url = URL.createObjectURL(blob);
|
||||||
|
|
||||||
link.setAttribute("href", url);
|
link.setAttribute("href", url);
|
||||||
link.setAttribute("download", `avoirs-export-${new Date().toISOString().split('T')[0]}.csv`);
|
link.setAttribute(
|
||||||
|
"download",
|
||||||
|
`avoirs-export-${new Date().toISOString().split("T")[0]}.csv`
|
||||||
|
);
|
||||||
link.style.visibility = "hidden";
|
link.style.visibility = "hidden";
|
||||||
|
|
||||||
document.body.appendChild(link);
|
document.body.appendChild(link);
|
||||||
@ -102,7 +105,10 @@ const handleDelete = async (id) => {
|
|||||||
await avoirStore.deleteAvoir(id);
|
await avoirStore.deleteAvoir(id);
|
||||||
notificationStore.success("Succès", "Avoir supprimé avec succès");
|
notificationStore.success("Succès", "Avoir supprimé avec succès");
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
notificationStore.error("Erreur", "Erreur lors de la suppression de l'avoir");
|
notificationStore.error(
|
||||||
|
"Erreur",
|
||||||
|
"Erreur lors de la suppression de l'avoir"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -6,7 +6,12 @@
|
|||||||
<div class="card-header pb-0 p-3">
|
<div class="card-header pb-0 p-3">
|
||||||
<div class="d-flex justify-content-between align-items-center">
|
<div class="d-flex justify-content-between align-items-center">
|
||||||
<h6 class="mb-0">Créer un nouvel avoir</h6>
|
<h6 class="mb-0">Créer un nouvel avoir</h6>
|
||||||
<soft-button color="secondary" variant="outline" size="sm" @click="goBack">
|
<soft-button
|
||||||
|
color="secondary"
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
@click="goBack"
|
||||||
|
>
|
||||||
<i class="fas fa-arrow-left me-2"></i>Retour
|
<i class="fas fa-arrow-left me-2"></i>Retour
|
||||||
</soft-button>
|
</soft-button>
|
||||||
</div>
|
</div>
|
||||||
@ -45,7 +50,7 @@ const handleSubmit = async (formData) => {
|
|||||||
avoir_date: formData.date,
|
avoir_date: formData.date,
|
||||||
reason_type: formData.reason,
|
reason_type: formData.reason,
|
||||||
reason_description: formData.reasonDetail,
|
reason_description: formData.reasonDetail,
|
||||||
lines: formData.lines.map(line => ({
|
lines: formData.lines.map((line) => ({
|
||||||
description: line.designation,
|
description: line.designation,
|
||||||
quantity: line.quantity,
|
quantity: line.quantity,
|
||||||
unit_price: line.priceHt,
|
unit_price: line.priceHt,
|
||||||
@ -54,7 +59,10 @@ const handleSubmit = async (formData) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
await avoirStore.createAvoir(payload);
|
await avoirStore.createAvoir(payload);
|
||||||
notificationStore.success("Succès", `Avoir créé avec succès: ${formData.number}`);
|
notificationStore.success(
|
||||||
|
"Succès",
|
||||||
|
`Avoir créé avec succès: ${formData.number}`
|
||||||
|
);
|
||||||
router.push("/avoirs");
|
router.push("/avoirs");
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Error creating avoir:", err);
|
console.error("Error creating avoir:", err);
|
||||||
|
|||||||
@ -38,7 +38,12 @@
|
|||||||
|
|
||||||
<template #actions>
|
<template #actions>
|
||||||
<div class="d-flex justify-content-end gap-2">
|
<div class="d-flex justify-content-end gap-2">
|
||||||
<soft-button color="secondary" variant="outline" size="sm" @click="handleBack">
|
<soft-button
|
||||||
|
color="secondary"
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
@click="handleBack"
|
||||||
|
>
|
||||||
Retour
|
Retour
|
||||||
</soft-button>
|
</soft-button>
|
||||||
<soft-button color="info" size="sm">
|
<soft-button color="info" size="sm">
|
||||||
|
|||||||
@ -33,7 +33,7 @@ const currentFilter = ref(null);
|
|||||||
|
|
||||||
const filteredFactures = computed(() => {
|
const filteredFactures = computed(() => {
|
||||||
if (!currentFilter.value) return factures.value;
|
if (!currentFilter.value) return factures.value;
|
||||||
return factures.value.filter(f => f.status === currentFilter.value);
|
return factures.value.filter((f) => f.status === currentFilter.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleCreate = () => {
|
const handleCreate = () => {
|
||||||
|
|||||||
@ -5,7 +5,9 @@
|
|||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header pb-0">
|
<div class="card-header pb-0">
|
||||||
<h5 class="mb-0">Nouvelle Facture Fournisseur</h5>
|
<h5 class="mb-0">Nouvelle Facture Fournisseur</h5>
|
||||||
<p class="text-sm mb-0">Saisissez les informations de la facture reçue.</p>
|
<p class="text-sm mb-0">
|
||||||
|
Saisissez les informations de la facture reçue.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<new-facture-fournisseur-form
|
<new-facture-fournisseur-form
|
||||||
|
|||||||
@ -4,9 +4,7 @@
|
|||||||
<h3 class="mb-1">
|
<h3 class="mb-1">
|
||||||
<strong>{{ avoirNumber }}</strong>
|
<strong>{{ avoirNumber }}</strong>
|
||||||
</h3>
|
</h3>
|
||||||
<p class="text-muted mb-0">
|
<p class="text-muted mb-0">Créé le {{ formatDate(date) }}</p>
|
||||||
Créé le {{ formatDate(date) }}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<form @submit.prevent="submitForm" class="space-y-6">
|
<form class="space-y-6" @submit.prevent="submitForm">
|
||||||
<!-- Row 1: N° Avoir, Date d'émission, Statut -->
|
<!-- Row 1: N° Avoir, Date d'émission, Statut -->
|
||||||
<div class="row g-3 mb-4">
|
<div class="row g-3 mb-4">
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
@ -13,10 +13,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<label class="form-label">Date d'émission</label>
|
<label class="form-label">Date d'émission</label>
|
||||||
<soft-input
|
<soft-input v-model="formData.date" type="date" />
|
||||||
v-model="formData.date"
|
|
||||||
type="date"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<label class="form-label">Statut</label>
|
<label class="form-label">Statut</label>
|
||||||
@ -39,23 +36,32 @@
|
|||||||
v-model="invoiceSearchQuery"
|
v-model="invoiceSearchQuery"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Rechercher une facture..."
|
placeholder="Rechercher une facture..."
|
||||||
|
class="search-input"
|
||||||
@input="handleInvoiceSearch"
|
@input="handleInvoiceSearch"
|
||||||
@focus="showInvoiceResults = true"
|
@focus="showInvoiceResults = true"
|
||||||
class="search-input"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Search Results Dropdown -->
|
<!-- Search Results Dropdown -->
|
||||||
<div
|
<div
|
||||||
v-if="showInvoiceResults && (invoiceSearchResults.length > 0 || isSearchingInvoices)"
|
v-if="
|
||||||
|
showInvoiceResults &&
|
||||||
|
(invoiceSearchResults.length > 0 || isSearchingInvoices)
|
||||||
|
"
|
||||||
class="search-dropdown"
|
class="search-dropdown"
|
||||||
>
|
>
|
||||||
<div v-if="isSearchingInvoices" class="dropdown-loading">
|
<div v-if="isSearchingInvoices" class="dropdown-loading">
|
||||||
<div class="spinner-border spinner-border-sm text-primary" role="status">
|
<div
|
||||||
|
class="spinner-border spinner-border-sm text-primary"
|
||||||
|
role="status"
|
||||||
|
>
|
||||||
<span class="visually-hidden">Chargement...</span>
|
<span class="visually-hidden">Chargement...</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="invoiceSearchResults.length === 0" class="dropdown-empty">
|
<div
|
||||||
|
v-else-if="invoiceSearchResults.length === 0"
|
||||||
|
class="dropdown-empty"
|
||||||
|
>
|
||||||
Aucune facture trouvée
|
Aucune facture trouvée
|
||||||
</div>
|
</div>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
@ -94,14 +100,18 @@
|
|||||||
<option value="erreur_facturation">Erreur de facturation</option>
|
<option value="erreur_facturation">Erreur de facturation</option>
|
||||||
<option value="retour_marchandise">Retour de marchandise</option>
|
<option value="retour_marchandise">Retour de marchandise</option>
|
||||||
<option value="geste_commercial">Geste commercial</option>
|
<option value="geste_commercial">Geste commercial</option>
|
||||||
<option value="annulation_prestation">Annulation de prestation</option>
|
<option value="annulation_prestation">
|
||||||
|
Annulation de prestation
|
||||||
|
</option>
|
||||||
<option value="autre">Autre</option>
|
<option value="autre">Autre</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<label class="form-label">Mode de remboursement</label>
|
<label class="form-label">Mode de remboursement</label>
|
||||||
<select v-model="formData.refundMethod" class="form-select">
|
<select v-model="formData.refundMethod" class="form-select">
|
||||||
<option value="deduction_facture">Déduction sur prochaine facture</option>
|
<option value="deduction_facture">
|
||||||
|
Déduction sur prochaine facture
|
||||||
|
</option>
|
||||||
<option value="virement">Virement bancaire</option>
|
<option value="virement">Virement bancaire</option>
|
||||||
<option value="cheque">Chèque</option>
|
<option value="cheque">Chèque</option>
|
||||||
<option value="especes">Espèces</option>
|
<option value="especes">Espèces</option>
|
||||||
@ -125,12 +135,7 @@
|
|||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||||
<label class="form-label mb-0">Lignes de l'avoir</label>
|
<label class="form-label mb-0">Lignes de l'avoir</label>
|
||||||
<soft-button
|
<soft-button type="button" color="primary" size="sm" @click="addLine">
|
||||||
type="button"
|
|
||||||
color="primary"
|
|
||||||
size="sm"
|
|
||||||
@click="addLine"
|
|
||||||
>
|
|
||||||
<i class="fas fa-plus me-1"></i> Ajouter une ligne
|
<i class="fas fa-plus me-1"></i> Ajouter une ligne
|
||||||
</soft-button>
|
</soft-button>
|
||||||
</div>
|
</div>
|
||||||
@ -171,8 +176,8 @@
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-sm btn-danger"
|
class="btn btn-sm btn-danger"
|
||||||
@click="removeLine(index)"
|
|
||||||
:disabled="formData.lines.length === 1"
|
:disabled="formData.lines.length === 1"
|
||||||
|
@click="removeLine(index)"
|
||||||
>
|
>
|
||||||
<i class="fas fa-trash"></i>
|
<i class="fas fa-trash"></i>
|
||||||
</button>
|
</button>
|
||||||
@ -196,7 +201,9 @@
|
|||||||
<span>{{ formatCurrency(calculateTotalTva()) }}</span>
|
<span>{{ formatCurrency(calculateTotalTva()) }}</span>
|
||||||
</p>
|
</p>
|
||||||
<h5 class="mb-0 text-info">
|
<h5 class="mb-0 text-info">
|
||||||
<strong>Total TTC : {{ formatCurrency(calculateTotalTtc()) }}</strong>
|
<strong
|
||||||
|
>Total TTC : {{ formatCurrency(calculateTotalTtc()) }}</strong
|
||||||
|
>
|
||||||
</h5>
|
</h5>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -225,12 +232,7 @@
|
|||||||
>
|
>
|
||||||
Annuler
|
Annuler
|
||||||
</soft-button>
|
</soft-button>
|
||||||
<soft-button
|
<soft-button type="submit" color="success"> Créer l'avoir </soft-button>
|
||||||
type="submit"
|
|
||||||
color="success"
|
|
||||||
>
|
|
||||||
Créer l'avoir
|
|
||||||
</soft-button>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
@ -312,11 +314,12 @@ const handleInvoiceSearch = () => {
|
|||||||
showInvoiceResults.value = true;
|
showInvoiceResults.value = true;
|
||||||
try {
|
try {
|
||||||
// Simulate API search
|
// Simulate API search
|
||||||
await new Promise(resolve => setTimeout(resolve, 300));
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
||||||
const query = invoiceSearchQuery.value.toLowerCase();
|
const query = invoiceSearchQuery.value.toLowerCase();
|
||||||
invoiceSearchResults.value = sampleInvoices.filter(invoice =>
|
invoiceSearchResults.value = sampleInvoices.filter(
|
||||||
invoice.invoice_number.toLowerCase().includes(query) ||
|
(invoice) =>
|
||||||
invoice.clientName.toLowerCase().includes(query)
|
invoice.invoice_number.toLowerCase().includes(query) ||
|
||||||
|
invoice.clientName.toLowerCase().includes(query)
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error searching invoices:", error);
|
console.error("Error searching invoices:", error);
|
||||||
@ -349,18 +352,18 @@ const selectInvoice = (invoice) => {
|
|||||||
|
|
||||||
// Close dropdowns on click outside
|
// Close dropdowns on click outside
|
||||||
const handleClickOutside = (event) => {
|
const handleClickOutside = (event) => {
|
||||||
const invoiceContainer = document.querySelector('.invoice-search-container');
|
const invoiceContainer = document.querySelector(".invoice-search-container");
|
||||||
if (invoiceContainer && !invoiceContainer.contains(event.target)) {
|
if (invoiceContainer && !invoiceContainer.contains(event.target)) {
|
||||||
showInvoiceResults.value = false;
|
showInvoiceResults.value = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
document.addEventListener('click', handleClickOutside);
|
document.addEventListener("click", handleClickOutside);
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
document.removeEventListener('click', handleClickOutside);
|
document.removeEventListener("click", handleClickOutside);
|
||||||
});
|
});
|
||||||
|
|
||||||
const formData = ref({
|
const formData = ref({
|
||||||
@ -420,7 +423,10 @@ const formatCurrency = (value) => {
|
|||||||
|
|
||||||
const submitForm = () => {
|
const submitForm = () => {
|
||||||
if (!formData.value.invoiceId) {
|
if (!formData.value.invoiceId) {
|
||||||
notificationStore.error("Erreur", "Veuillez sélectionner une facture d'origine");
|
notificationStore.error(
|
||||||
|
"Erreur",
|
||||||
|
"Veuillez sélectionner une facture d'origine"
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (formData.value.lines.length === 0) {
|
if (formData.value.lines.length === 0) {
|
||||||
|
|||||||
@ -3,16 +3,24 @@
|
|||||||
<table class="table align-items-center mb-0">
|
<table class="table align-items-center mb-0">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="text-uppercase text-secondary text-xxs font-weight-bolder opacity-7 ps-2">
|
<th
|
||||||
|
class="text-uppercase text-secondary text-xxs font-weight-bolder opacity-7 ps-2"
|
||||||
|
>
|
||||||
Description
|
Description
|
||||||
</th>
|
</th>
|
||||||
<th class="text-center text-uppercase text-secondary text-xxs font-weight-bolder opacity-7">
|
<th
|
||||||
|
class="text-center text-uppercase text-secondary text-xxs font-weight-bolder opacity-7"
|
||||||
|
>
|
||||||
Qté
|
Qté
|
||||||
</th>
|
</th>
|
||||||
<th class="text-center text-uppercase text-secondary text-xxs font-weight-bolder opacity-7">
|
<th
|
||||||
|
class="text-center text-uppercase text-secondary text-xxs font-weight-bolder opacity-7"
|
||||||
|
>
|
||||||
Prix Unit. HT
|
Prix Unit. HT
|
||||||
</th>
|
</th>
|
||||||
<th class="text-center text-uppercase text-secondary text-xxs font-weight-bolder opacity-7">
|
<th
|
||||||
|
class="text-center text-uppercase text-secondary text-xxs font-weight-bolder opacity-7"
|
||||||
|
>
|
||||||
Total HT
|
Total HT
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
@ -27,13 +35,19 @@
|
|||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="align-middle text-center text-sm">
|
<td class="align-middle text-center text-sm">
|
||||||
<span class="text-secondary text-xs font-weight-bold">{{ line.quantity }}</span>
|
<span class="text-secondary text-xs font-weight-bold">{{
|
||||||
|
line.quantity
|
||||||
|
}}</span>
|
||||||
</td>
|
</td>
|
||||||
<td class="align-middle text-center text-sm">
|
<td class="align-middle text-center text-sm">
|
||||||
<span class="text-secondary text-xs font-weight-bold">{{ formatCurrency(line.priceHt) }}</span>
|
<span class="text-secondary text-xs font-weight-bold">{{
|
||||||
|
formatCurrency(line.priceHt)
|
||||||
|
}}</span>
|
||||||
</td>
|
</td>
|
||||||
<td class="align-middle text-center text-sm">
|
<td class="align-middle text-center text-sm">
|
||||||
<span class="text-secondary text-xs font-weight-bold">{{ formatCurrency(line.totalHt) }}</span>
|
<span class="text-secondary text-xs font-weight-bold">{{
|
||||||
|
formatCurrency(line.totalHt)
|
||||||
|
}}</span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
@ -1,7 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="d-sm-flex justify-content-between">
|
<div class="d-sm-flex justify-content-between">
|
||||||
<div>
|
<div>
|
||||||
<soft-button color="success" variant="gradient" size="sm" @click="$emit('create')">
|
<soft-button
|
||||||
|
color="success"
|
||||||
|
variant="gradient"
|
||||||
|
size="sm"
|
||||||
|
@click="$emit('create')"
|
||||||
|
>
|
||||||
<i class="fas fa-plus me-1"></i> Ajouter une facture
|
<i class="fas fa-plus me-1"></i> Ajouter une facture
|
||||||
</soft-button>
|
</soft-button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -9,7 +9,9 @@
|
|||||||
<span class="text-sm">Total HT:</span>
|
<span class="text-sm">Total HT:</span>
|
||||||
</td>
|
</td>
|
||||||
<td class="text-end">
|
<td class="text-end">
|
||||||
<span class="text-sm text-dark font-weight-bold">{{ formatCurrency(ht) }}</span>
|
<span class="text-sm text-dark font-weight-bold">{{
|
||||||
|
formatCurrency(ht)
|
||||||
|
}}</span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
@ -17,15 +19,21 @@
|
|||||||
<span class="text-sm">TVA (20%):</span>
|
<span class="text-sm">TVA (20%):</span>
|
||||||
</td>
|
</td>
|
||||||
<td class="text-end">
|
<td class="text-end">
|
||||||
<span class="text-sm text-dark font-weight-bold">{{ formatCurrency(tva) }}</span>
|
<span class="text-sm text-dark font-weight-bold">{{
|
||||||
|
formatCurrency(tva)
|
||||||
|
}}</span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<span class="text-sm font-weight-bold text-info">Total TTC:</span>
|
<span class="text-sm font-weight-bold text-info"
|
||||||
|
>Total TTC:</span
|
||||||
|
>
|
||||||
</td>
|
</td>
|
||||||
<td class="text-end">
|
<td class="text-end">
|
||||||
<span class="text-sm font-weight-bold text-info">{{ formatCurrency(ttc) }}</span>
|
<span class="text-sm font-weight-bold text-info">{{
|
||||||
|
formatCurrency(ttc)
|
||||||
|
}}</span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
@ -13,20 +13,25 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<label class="form-label">Fournisseur *</label>
|
<label class="form-label">Fournisseur *</label>
|
||||||
<select v-model="formData.supplierId" class="form-select" @change="updateSupplierInfo" required>
|
<select
|
||||||
|
v-model="formData.supplierId"
|
||||||
|
class="form-select"
|
||||||
|
required
|
||||||
|
@change="updateSupplierInfo"
|
||||||
|
>
|
||||||
<option value="">-- Sélectionner un fournisseur --</option>
|
<option value="">-- Sélectionner un fournisseur --</option>
|
||||||
<option v-for="supplier in suppliers" :key="supplier.id" :value="supplier.id">
|
<option
|
||||||
|
v-for="supplier in suppliers"
|
||||||
|
:key="supplier.id"
|
||||||
|
:value="supplier.id"
|
||||||
|
>
|
||||||
{{ supplier.name }}
|
{{ supplier.name }}
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<label class="form-label">Date facture *</label>
|
<label class="form-label">Date facture *</label>
|
||||||
<soft-input
|
<soft-input v-model="formData.date" type="date" required />
|
||||||
v-model="formData.date"
|
|
||||||
type="date"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -55,13 +60,27 @@
|
|||||||
<!-- Articles Section -->
|
<!-- Articles Section -->
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<div class="flex items-center justify-between mb-4">
|
<div class="flex items-center justify-between mb-4">
|
||||||
<label class="peer-disabled:cursor-not-allowed peer-disabled:opacity-70 text-lg font-bold">Lignes de facture</label>
|
<label
|
||||||
|
class="peer-disabled:cursor-not-allowed peer-disabled:opacity-70 text-lg font-bold"
|
||||||
|
>Lignes de facture</label
|
||||||
|
>
|
||||||
<button
|
<button
|
||||||
class="inline-flex items-center justify-center gap-2 whitespace-nowrap font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 bg-primary text-primary-foreground shadow hover:bg-primary/90 h-8 rounded-md px-3 text-xs"
|
class="inline-flex items-center justify-center gap-2 whitespace-nowrap font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 bg-primary text-primary-foreground shadow hover:bg-primary/90 h-8 rounded-md px-3 text-xs"
|
||||||
type="button"
|
type="button"
|
||||||
@click="addLine"
|
@click="addLine"
|
||||||
>
|
>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-plus w-4 h-4 mr-2">
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
class="lucide lucide-plus w-4 h-4 mr-2"
|
||||||
|
>
|
||||||
<path d="M5 12h14"></path>
|
<path d="M5 12h14"></path>
|
||||||
<path d="M12 5v14"></path>
|
<path d="M12 5v14"></path>
|
||||||
</svg>
|
</svg>
|
||||||
@ -114,8 +133,8 @@
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-sm btn-link text-danger mb-0"
|
class="btn btn-sm btn-link text-danger mb-0"
|
||||||
@click="removeLine(index)"
|
|
||||||
:disabled="formData.lines.length === 1"
|
:disabled="formData.lines.length === 1"
|
||||||
|
@click="removeLine(index)"
|
||||||
>
|
>
|
||||||
<i class="fas fa-trash"></i>
|
<i class="fas fa-trash"></i>
|
||||||
</button>
|
</button>
|
||||||
@ -131,16 +150,22 @@
|
|||||||
<div class="card-body p-3">
|
<div class="card-body p-3">
|
||||||
<div class="d-flex justify-content-between mb-2">
|
<div class="d-flex justify-content-between mb-2">
|
||||||
<span class="text-sm">Total HT:</span>
|
<span class="text-sm">Total HT:</span>
|
||||||
<span class="text-sm font-weight-bold">{{ formatCurrency(calculateTotalHt()) }}</span>
|
<span class="text-sm font-weight-bold">{{
|
||||||
|
formatCurrency(calculateTotalHt())
|
||||||
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex justify-content-between mb-2">
|
<div class="d-flex justify-content-between mb-2">
|
||||||
<span class="text-sm">TVA (20%):</span>
|
<span class="text-sm">TVA (20%):</span>
|
||||||
<span class="text-sm font-weight-bold">{{ formatCurrency(calculateTotalTva()) }}</span>
|
<span class="text-sm font-weight-bold">{{
|
||||||
|
formatCurrency(calculateTotalTva())
|
||||||
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
<hr class="horizontal dark my-2">
|
<hr class="horizontal dark my-2" />
|
||||||
<div class="d-flex justify-content-between">
|
<div class="d-flex justify-content-between">
|
||||||
<span class="text-base font-weight-bold">Total TTC:</span>
|
<span class="text-base font-weight-bold">Total TTC:</span>
|
||||||
<span class="text-base font-weight-bold text-info">{{ formatCurrency(calculateTotalTtc()) }}</span>
|
<span class="text-base font-weight-bold text-info">{{
|
||||||
|
formatCurrency(calculateTotalTtc())
|
||||||
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -157,10 +182,7 @@
|
|||||||
>
|
>
|
||||||
Annuler
|
Annuler
|
||||||
</soft-button>
|
</soft-button>
|
||||||
<soft-button
|
<soft-button type="submit" color="success">
|
||||||
type="submit"
|
|
||||||
color="success"
|
|
||||||
>
|
|
||||||
Enregistrer la facture
|
Enregistrer la facture
|
||||||
</soft-button>
|
</soft-button>
|
||||||
</div>
|
</div>
|
||||||
@ -201,7 +223,7 @@ const formData = ref({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const updateSupplierInfo = () => {
|
const updateSupplierInfo = () => {
|
||||||
const supplier = suppliers.find(s => s.id === formData.value.supplierId);
|
const supplier = suppliers.find((s) => s.id === formData.value.supplierId);
|
||||||
if (supplier) {
|
if (supplier) {
|
||||||
formData.value.supplierName = supplier.name;
|
formData.value.supplierName = supplier.name;
|
||||||
}
|
}
|
||||||
@ -254,10 +276,10 @@ const submitForm = () => {
|
|||||||
totalTva: calculateTotalTva(),
|
totalTva: calculateTotalTva(),
|
||||||
totalTtc: calculateTotalTtc(),
|
totalTtc: calculateTotalTtc(),
|
||||||
// Map lines to include totalHt for store consistency
|
// Map lines to include totalHt for store consistency
|
||||||
lines: formData.value.lines.map(line => ({
|
lines: formData.value.lines.map((line) => ({
|
||||||
...line,
|
...line,
|
||||||
totalHt: line.quantity * line.priceHt
|
totalHt: line.quantity * line.priceHt,
|
||||||
}))
|
})),
|
||||||
};
|
};
|
||||||
|
|
||||||
emit("submit", payload);
|
emit("submit", payload);
|
||||||
@ -277,21 +299,54 @@ const submitForm = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Tailwind-like utilities used in the provided snippet */
|
/* Tailwind-like utilities used in the provided snippet */
|
||||||
.flex { display: flex; }
|
.flex {
|
||||||
.items-center { align-items: center; }
|
display: flex;
|
||||||
.justify-between { justify-content: space-between; }
|
}
|
||||||
.mb-4 { margin-bottom: 1.5rem; }
|
.items-center {
|
||||||
.text-lg { font-size: 1.125rem; }
|
align-items: center;
|
||||||
.font-bold { font-weight: 700; }
|
}
|
||||||
.gap-2 { gap: 0.5rem; }
|
.justify-between {
|
||||||
.whitespace-nowrap { white-space: nowrap; }
|
justify-content: space-between;
|
||||||
.transition-colors { transition-property: background-color, border-color, color, fill, stroke; transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); transition-duration: 150ms; }
|
}
|
||||||
.shadow { box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06); }
|
.mb-4 {
|
||||||
.rounded-md { border-radius: 0.375rem; }
|
margin-bottom: 1.5rem;
|
||||||
.px-3 { padding-left: 0.75rem; padding-right: 0.75rem; }
|
}
|
||||||
.h-8 { height: 2rem; }
|
.text-lg {
|
||||||
.text-xs { font-size: 0.75rem; }
|
font-size: 1.125rem;
|
||||||
.font-medium { font-weight: 500; }
|
}
|
||||||
|
.font-bold {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
.gap-2 {
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
.whitespace-nowrap {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.transition-colors {
|
||||||
|
transition-property: background-color, border-color, color, fill, stroke;
|
||||||
|
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
transition-duration: 150ms;
|
||||||
|
}
|
||||||
|
.shadow {
|
||||||
|
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
|
||||||
|
}
|
||||||
|
.rounded-md {
|
||||||
|
border-radius: 0.375rem;
|
||||||
|
}
|
||||||
|
.px-3 {
|
||||||
|
padding-left: 0.75rem;
|
||||||
|
padding-right: 0.75rem;
|
||||||
|
}
|
||||||
|
.h-8 {
|
||||||
|
height: 2rem;
|
||||||
|
}
|
||||||
|
.text-xs {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
}
|
||||||
|
.font-medium {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
.space-y-3 > div + div {
|
.space-y-3 > div + div {
|
||||||
margin-top: 1rem;
|
margin-top: 1rem;
|
||||||
|
|||||||
@ -36,7 +36,9 @@
|
|||||||
|
|
||||||
<!-- Total TTC -->
|
<!-- Total TTC -->
|
||||||
<td class="text-xs font-weight-bold">
|
<td class="text-xs font-weight-bold">
|
||||||
<span class="my-2 text-xs">{{ formatCurrency(facture.totalTtc) }}</span>
|
<span class="my-2 text-xs">{{
|
||||||
|
formatCurrency(facture.totalTtc)
|
||||||
|
}}</span>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<!-- Status -->
|
<!-- Status -->
|
||||||
@ -47,7 +49,10 @@
|
|||||||
variant="outline"
|
variant="outline"
|
||||||
class="btn-icon-only btn-rounded mb-0 me-2 btn-sm d-flex align-items-center justify-content-center"
|
class="btn-icon-only btn-rounded mb-0 me-2 btn-sm d-flex align-items-center justify-content-center"
|
||||||
>
|
>
|
||||||
<i :class="getStatusIcon(facture.status)" aria-hidden="true"></i>
|
<i
|
||||||
|
:class="getStatusIcon(facture.status)"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
</soft-button>
|
</soft-button>
|
||||||
<span>{{ getStatusLabel(facture.status) }}</span>
|
<span>{{ getStatusLabel(facture.status) }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
<div class="card-header p-3 pb-0">
|
<div class="card-header p-3 pb-0">
|
||||||
<slot name="header"></slot>
|
<slot name="header"></slot>
|
||||||
</div>
|
</div>
|
||||||
<hr class="horizontal dark my-3">
|
<hr class="horizontal dark my-3" />
|
||||||
<div class="card-body p-3 pt-0">
|
<div class="card-body p-3 pt-0">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
@ -20,7 +20,7 @@
|
|||||||
<slot name="summary"></slot>
|
<slot name="summary"></slot>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr class="horizontal dark mt-4 mb-3">
|
<hr class="horizontal dark mt-4 mb-3" />
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<slot name="actions"></slot>
|
<slot name="actions"></slot>
|
||||||
|
|||||||
@ -131,7 +131,9 @@ export const AvoirService = {
|
|||||||
return response;
|
return response;
|
||||||
},
|
},
|
||||||
|
|
||||||
async deleteAvoir(id: number): Promise<{ success: boolean; message: string }> {
|
async deleteAvoir(
|
||||||
|
id: number
|
||||||
|
): Promise<{ success: boolean; message: string }> {
|
||||||
const response = await request<{ success: boolean; message: string }>({
|
const response = await request<{ success: boolean; message: string }>({
|
||||||
url: `/api/avoirs/${id}`,
|
url: `/api/avoirs/${id}`,
|
||||||
method: "delete",
|
method: "delete",
|
||||||
|
|||||||
@ -22,7 +22,7 @@ export interface SupplierInvoice {
|
|||||||
invoice_number: string;
|
invoice_number: string;
|
||||||
invoice_date: string;
|
invoice_date: string;
|
||||||
due_date: string | null;
|
due_date: string | null;
|
||||||
status: 'brouillon' | 'en_attente' | 'payee' | 'annulee';
|
status: "brouillon" | "en_attente" | "payee" | "annulee";
|
||||||
currency: string;
|
currency: string;
|
||||||
total_ht: number;
|
total_ht: number;
|
||||||
total_tva: number;
|
total_tva: number;
|
||||||
@ -70,7 +70,8 @@ export interface CreateSupplierInvoicePayload {
|
|||||||
lines?: CreateSupplierInvoiceLinePayload[];
|
lines?: CreateSupplierInvoiceLinePayload[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UpdateSupplierInvoicePayload extends Partial<CreateSupplierInvoicePayload> {
|
export interface UpdateSupplierInvoicePayload
|
||||||
|
extends Partial<CreateSupplierInvoicePayload> {
|
||||||
id: number;
|
id: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,7 +99,9 @@ export const SupplierInvoiceService = {
|
|||||||
return response;
|
return response;
|
||||||
},
|
},
|
||||||
|
|
||||||
async createSupplierInvoice(payload: CreateSupplierInvoicePayload): Promise<SupplierInvoiceResponse> {
|
async createSupplierInvoice(
|
||||||
|
payload: CreateSupplierInvoicePayload
|
||||||
|
): Promise<SupplierInvoiceResponse> {
|
||||||
const response = await request<SupplierInvoiceResponse>({
|
const response = await request<SupplierInvoiceResponse>({
|
||||||
url: "/api/supplier-invoices",
|
url: "/api/supplier-invoices",
|
||||||
method: "post",
|
method: "post",
|
||||||
@ -107,7 +110,9 @@ export const SupplierInvoiceService = {
|
|||||||
return response;
|
return response;
|
||||||
},
|
},
|
||||||
|
|
||||||
async updateSupplierInvoice(payload: UpdateSupplierInvoicePayload): Promise<SupplierInvoiceResponse> {
|
async updateSupplierInvoice(
|
||||||
|
payload: UpdateSupplierInvoicePayload
|
||||||
|
): Promise<SupplierInvoiceResponse> {
|
||||||
const { id, ...updateData } = payload;
|
const { id, ...updateData } = payload;
|
||||||
const response = await request<SupplierInvoiceResponse>({
|
const response = await request<SupplierInvoiceResponse>({
|
||||||
url: `/api/supplier-invoices/${id}`,
|
url: `/api/supplier-invoices/${id}`,
|
||||||
@ -117,7 +122,9 @@ export const SupplierInvoiceService = {
|
|||||||
return response;
|
return response;
|
||||||
},
|
},
|
||||||
|
|
||||||
async deleteSupplierInvoice(id: number): Promise<{ success: boolean; message: string }> {
|
async deleteSupplierInvoice(
|
||||||
|
id: number
|
||||||
|
): Promise<{ success: boolean; message: string }> {
|
||||||
const response = await request<{ success: boolean; message: string }>({
|
const response = await request<{ success: boolean; message: string }>({
|
||||||
url: `/api/supplier-invoices/${id}`,
|
url: `/api/supplier-invoices/${id}`,
|
||||||
method: "delete",
|
method: "delete",
|
||||||
@ -125,7 +132,9 @@ export const SupplierInvoiceService = {
|
|||||||
return response;
|
return response;
|
||||||
},
|
},
|
||||||
|
|
||||||
async getByFournisseur(fournisseurId: number): Promise<SupplierInvoiceListResponse> {
|
async getByFournisseur(
|
||||||
|
fournisseurId: number
|
||||||
|
): Promise<SupplierInvoiceListResponse> {
|
||||||
const response = await request<SupplierInvoiceListResponse>({
|
const response = await request<SupplierInvoiceListResponse>({
|
||||||
url: `/api/fournisseurs/${fournisseurId}/supplier-invoices`,
|
url: `/api/fournisseurs/${fournisseurId}/supplier-invoices`,
|
||||||
method: "get",
|
method: "get",
|
||||||
|
|||||||
@ -146,10 +146,7 @@ export const useAvoirStore = defineStore("avoir", () => {
|
|||||||
avoirs.value[index] = updatedAvoir;
|
avoirs.value[index] = updatedAvoir;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (currentAvoir.value && currentAvoir.value.id === updatedAvoir.id) {
|
||||||
currentAvoir.value &&
|
|
||||||
currentAvoir.value.id === updatedAvoir.id
|
|
||||||
) {
|
|
||||||
setCurrentAvoir(updatedAvoir);
|
setCurrentAvoir(updatedAvoir);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,9 +170,7 @@ export const useAvoirStore = defineStore("avoir", () => {
|
|||||||
try {
|
try {
|
||||||
const response = await AvoirService.deleteAvoir(id);
|
const response = await AvoirService.deleteAvoir(id);
|
||||||
|
|
||||||
avoirs.value = avoirs.value.filter(
|
avoirs.value = avoirs.value.filter((avoir) => avoir.id !== id);
|
||||||
(avoir) => avoir.id !== id
|
|
||||||
);
|
|
||||||
|
|
||||||
if (currentAvoir.value && currentAvoir.value.id === id) {
|
if (currentAvoir.value && currentAvoir.value.id === id) {
|
||||||
setCurrentAvoir(null);
|
setCurrentAvoir(null);
|
||||||
|
|||||||
@ -22,88 +22,103 @@ export interface FactureFournisseur {
|
|||||||
lines: FactureFournisseurLine[];
|
lines: FactureFournisseurLine[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useFactureFournisseurStore = defineStore("factureFournisseur", () => {
|
export const useFactureFournisseurStore = defineStore(
|
||||||
// State
|
"factureFournisseur",
|
||||||
const factures = ref<FactureFournisseur[]>([
|
() => {
|
||||||
{
|
// State
|
||||||
id: 1,
|
const factures = ref<FactureFournisseur[]>([
|
||||||
number: "FF-2026-0001",
|
{
|
||||||
supplierId: "1",
|
id: 1,
|
||||||
supplierName: "Produits Funéraires Pro",
|
number: "FF-2026-0001",
|
||||||
date: "2026-01-15",
|
supplierId: "1",
|
||||||
status: "payee",
|
supplierName: "Produits Funéraires Pro",
|
||||||
totalHt: 1250.0,
|
date: "2026-01-15",
|
||||||
totalTva: 250.0,
|
status: "payee",
|
||||||
totalTtc: 1500.0,
|
totalHt: 1250.0,
|
||||||
lines: [
|
totalTva: 250.0,
|
||||||
{ id: 1, designation: "Cercueils Chêne Prestige", quantity: 2, priceHt: 625.0, totalHt: 1250.0 }
|
totalTtc: 1500.0,
|
||||||
]
|
lines: [
|
||||||
},
|
{
|
||||||
{
|
id: 1,
|
||||||
id: 2,
|
designation: "Cercueils Chêne Prestige",
|
||||||
number: "FF-2026-0002",
|
quantity: 2,
|
||||||
supplierId: "2",
|
priceHt: 625.0,
|
||||||
supplierName: "Thanatos Supply",
|
totalHt: 1250.0,
|
||||||
date: "2026-01-20",
|
},
|
||||||
status: "en_attente",
|
],
|
||||||
totalHt: 450.0,
|
},
|
||||||
totalTva: 90.0,
|
{
|
||||||
totalTtc: 540.0,
|
id: 2,
|
||||||
lines: [
|
number: "FF-2026-0002",
|
||||||
{ id: 2, designation: "Urnes Granit Noir", quantity: 5, priceHt: 90.0, totalHt: 450.0 }
|
supplierId: "2",
|
||||||
]
|
supplierName: "Thanatos Supply",
|
||||||
}
|
date: "2026-01-20",
|
||||||
]);
|
status: "en_attente",
|
||||||
const currentFacture = ref<FactureFournisseur | null>(null);
|
totalHt: 450.0,
|
||||||
const loading = ref(false);
|
totalTva: 90.0,
|
||||||
const error = ref<string | null>(null);
|
totalTtc: 540.0,
|
||||||
|
lines: [
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
designation: "Urnes Granit Noir",
|
||||||
|
quantity: 5,
|
||||||
|
priceHt: 90.0,
|
||||||
|
totalHt: 450.0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const currentFacture = ref<FactureFournisseur | null>(null);
|
||||||
|
const loading = ref(false);
|
||||||
|
const error = ref<string | null>(null);
|
||||||
|
|
||||||
// Getters
|
// Getters
|
||||||
const allFactures = computed(() => factures.value);
|
const allFactures = computed(() => factures.value);
|
||||||
const isLoading = computed(() => loading.value);
|
const isLoading = computed(() => loading.value);
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
const fetchFactures = async () => {
|
const fetchFactures = async () => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
// Mock API call
|
// Mock API call
|
||||||
await new Promise(resolve => setTimeout(resolve, 500));
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
};
|
|
||||||
|
|
||||||
const fetchFacture = async (id: number) => {
|
|
||||||
loading.value = true;
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 300));
|
|
||||||
const found = factures.value.find(f => f.id === id);
|
|
||||||
currentFacture.value = found || null;
|
|
||||||
loading.value = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
const createFacture = async (payload: any) => {
|
|
||||||
loading.value = true;
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 800));
|
|
||||||
const newFacture = {
|
|
||||||
...payload,
|
|
||||||
id: factures.value.length + 1,
|
|
||||||
};
|
};
|
||||||
factures.value.push(newFacture);
|
|
||||||
loading.value = false;
|
|
||||||
return newFacture;
|
|
||||||
};
|
|
||||||
|
|
||||||
const deleteFacture = async (id: number) => {
|
const fetchFacture = async (id: number) => {
|
||||||
factures.value = factures.value.filter(f => f.id !== id);
|
loading.value = true;
|
||||||
};
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
||||||
|
const found = factures.value.find((f) => f.id === id);
|
||||||
|
currentFacture.value = found || null;
|
||||||
|
loading.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
const createFacture = async (payload: any) => {
|
||||||
factures,
|
loading.value = true;
|
||||||
currentFacture,
|
await new Promise((resolve) => setTimeout(resolve, 800));
|
||||||
loading,
|
const newFacture = {
|
||||||
error,
|
...payload,
|
||||||
allFactures,
|
id: factures.value.length + 1,
|
||||||
isLoading,
|
};
|
||||||
fetchFactures,
|
factures.value.push(newFacture);
|
||||||
fetchFacture,
|
loading.value = false;
|
||||||
createFacture,
|
return newFacture;
|
||||||
deleteFacture
|
};
|
||||||
};
|
|
||||||
});
|
const deleteFacture = async (id: number) => {
|
||||||
|
factures.value = factures.value.filter((f) => f.id !== id);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
factures,
|
||||||
|
currentFacture,
|
||||||
|
loading,
|
||||||
|
error,
|
||||||
|
allFactures,
|
||||||
|
isLoading,
|
||||||
|
fetchFactures,
|
||||||
|
fetchFacture,
|
||||||
|
createFacture,
|
||||||
|
deleteFacture,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|||||||
@ -84,7 +84,9 @@ export const useSupplierInvoiceStore = defineStore("supplierInvoice", () => {
|
|||||||
setError(null);
|
setError(null);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await SupplierInvoiceService.getAllSupplierInvoices(params);
|
const response = await SupplierInvoiceService.getAllSupplierInvoices(
|
||||||
|
params
|
||||||
|
);
|
||||||
setSupplierInvoices(response.data);
|
setSupplierInvoices(response.data);
|
||||||
if (response.meta) {
|
if (response.meta) {
|
||||||
setPagination(response.meta);
|
setPagination(response.meta);
|
||||||
@ -122,12 +124,16 @@ export const useSupplierInvoiceStore = defineStore("supplierInvoice", () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const createSupplierInvoice = async (payload: CreateSupplierInvoicePayload) => {
|
const createSupplierInvoice = async (
|
||||||
|
payload: CreateSupplierInvoicePayload
|
||||||
|
) => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
setError(null);
|
setError(null);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await SupplierInvoiceService.createSupplierInvoice(payload);
|
const response = await SupplierInvoiceService.createSupplierInvoice(
|
||||||
|
payload
|
||||||
|
);
|
||||||
supplierInvoices.value.push(response.data);
|
supplierInvoices.value.push(response.data);
|
||||||
setCurrentSupplierInvoice(response.data);
|
setCurrentSupplierInvoice(response.data);
|
||||||
return response.data;
|
return response.data;
|
||||||
@ -143,12 +149,16 @@ export const useSupplierInvoiceStore = defineStore("supplierInvoice", () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateSupplierInvoice = async (payload: UpdateSupplierInvoicePayload) => {
|
const updateSupplierInvoice = async (
|
||||||
|
payload: UpdateSupplierInvoicePayload
|
||||||
|
) => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
setError(null);
|
setError(null);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await SupplierInvoiceService.updateSupplierInvoice(payload);
|
const response = await SupplierInvoiceService.updateSupplierInvoice(
|
||||||
|
payload
|
||||||
|
);
|
||||||
const updatedInvoice = response.data;
|
const updatedInvoice = response.data;
|
||||||
|
|
||||||
const index = supplierInvoices.value.findIndex(
|
const index = supplierInvoices.value.findIndex(
|
||||||
@ -189,7 +199,10 @@ export const useSupplierInvoiceStore = defineStore("supplierInvoice", () => {
|
|||||||
(invoice) => invoice.id !== id
|
(invoice) => invoice.id !== id
|
||||||
);
|
);
|
||||||
|
|
||||||
if (currentSupplierInvoice.value && currentSupplierInvoice.value.id === id) {
|
if (
|
||||||
|
currentSupplierInvoice.value &&
|
||||||
|
currentSupplierInvoice.value.id === id
|
||||||
|
) {
|
||||||
setCurrentSupplierInvoice(null);
|
setCurrentSupplierInvoice(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,7 +224,9 @@ export const useSupplierInvoiceStore = defineStore("supplierInvoice", () => {
|
|||||||
setError(null);
|
setError(null);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await SupplierInvoiceService.getByFournisseur(fournisseurId);
|
const response = await SupplierInvoiceService.getByFournisseur(
|
||||||
|
fournisseurId
|
||||||
|
);
|
||||||
setSupplierInvoices(response.data);
|
setSupplierInvoices(response.data);
|
||||||
return response.data;
|
return response.data;
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
|
|||||||
@ -5,4 +5,3 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import CommandeListPresentation from "@/components/Organism/Commande/CommandeListPresentation.vue";
|
import CommandeListPresentation from "@/components/Organism/Commande/CommandeListPresentation.vue";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user