From b1c3952d09c96cb38000b076bca0a0c4b66cda9b Mon Sep 17 00:00:00 2001 From: Matteo Date: Thu, 12 Feb 2026 07:31:40 +0000 Subject: [PATCH] =?UTF-8?q?[#271]Cr=C3=A9er=20une=20nouvelle=20exp=C3=A9di?= =?UTF-8?q?tion=20(=C3=A9tape=201)=20(!12)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit | Numéro du ticket | Titre du ticket | |------------------|-----------------| | #271 | Créer une nouvelle expédition (étape 1) | ## Description de la PR ## Modification du .env ## Check list [x ] Pas de régression TU/TI/TF rédigée [x ] TU/TI/TF OK [x ] CHANGELOG modifié Co-authored-by: kevin Reviewed-on: https://gitea.malio.fr/MALIO-DEV/Ferme/pulls/12 Reviewed-by: Autin Co-authored-by: Matteo Co-committed-by: Matteo --- .idea/inspectionProfiles/Project_Default.xml | 6 - .idea/php.xml | 1 + .idea/workspace.xml | 209 +++--- CHANGELOG.md | 5 + .../components/reception/reception-form.vue | 4 +- .../components/reception/reception-weight.vue | 4 +- .../components/shipment/shipment-form.vue | 605 ++++++++++++++++++ .../components/shipment/shipment-weight.vue | 101 +++ frontend/components/ui/UiNumberInput.vue | 7 +- frontend/components/user/user-form.vue | 123 ---- frontend/composables/usePdfPrinter.ts | 30 +- frontend/composables/useWeighing.ts | 103 ++- frontend/constants/steps.ts | 9 +- frontend/i18n/locales/fr.json | 20 +- frontend/pages/admin/dashboard.vue | 2 - frontend/pages/admin/user/[[id]].vue | 119 +++- frontend/pages/index.vue | 2 +- frontend/pages/reception/update/[[id]].vue | 4 +- frontend/pages/shipment/[[id]].vue | 82 +++ frontend/services/bovin-shipment.ts | 50 ++ frontend/services/customer.ts | 23 + frontend/services/dto/bovin-shipment-data.ts | 18 + frontend/services/dto/customer-data.ts | 8 + frontend/services/dto/shipment-data.ts | 65 ++ frontend/services/dto/shipment-type-data.ts | 5 + frontend/services/shipment-type.ts | 24 + frontend/services/shipment.ts | 40 ++ frontend/services/weight.ts | 22 +- frontend/stores/shipment.ts | 58 ++ frontend/utils/constants.ts | 2 +- makefile | 2 +- migrations/Version20260204101625.php | 64 ++ migrations/Version20260204102423.php | 35 + migrations/Version20260211075656.php | 49 ++ migrations/Version20260211123000.php | 38 ++ src/Dto/PontBasculeReading.php | 6 +- src/Entity/Address.php | 53 +- src/Entity/BovinShipment.php | 101 +++ src/Entity/Carrier.php | 6 +- src/Entity/Customer.php | 94 +++ src/Entity/Driver.php | 4 +- src/Entity/Shipment.php | 366 +++++++++++ src/Entity/ShipmentType.php | 71 ++ src/Entity/Truck.php | 4 +- src/Entity/User.php | 4 +- src/Entity/Weight.php | 38 +- src/State/ShipmentReceiptProvider.php | 63 ++ src/State/ShipmentWeighingProvider.php | 30 + templates/reception_voucher.html.twig | 106 +-- templates/shipment_voucher.html.twig | 292 +++++++++ 50 files changed, 2842 insertions(+), 335 deletions(-) delete mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 frontend/components/shipment/shipment-form.vue create mode 100644 frontend/components/shipment/shipment-weight.vue delete mode 100644 frontend/components/user/user-form.vue create mode 100644 frontend/pages/shipment/[[id]].vue create mode 100644 frontend/services/bovin-shipment.ts create mode 100644 frontend/services/customer.ts create mode 100644 frontend/services/dto/bovin-shipment-data.ts create mode 100644 frontend/services/dto/customer-data.ts create mode 100644 frontend/services/dto/shipment-data.ts create mode 100644 frontend/services/dto/shipment-type-data.ts create mode 100644 frontend/services/shipment-type.ts create mode 100644 frontend/services/shipment.ts create mode 100644 frontend/stores/shipment.ts create mode 100644 migrations/Version20260204101625.php create mode 100644 migrations/Version20260204102423.php create mode 100644 migrations/Version20260211075656.php create mode 100644 migrations/Version20260211123000.php create mode 100644 src/Entity/BovinShipment.php create mode 100644 src/Entity/Customer.php create mode 100644 src/Entity/Shipment.php create mode 100644 src/Entity/ShipmentType.php create mode 100644 src/State/ShipmentReceiptProvider.php create mode 100644 src/State/ShipmentWeighingProvider.php create mode 100644 templates/shipment_voucher.html.twig diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml deleted file mode 100644 index d829d01..0000000 --- a/.idea/inspectionProfiles/Project_Default.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/php.xml b/.idea/php.xml index a873179..a081fbb 100644 --- a/.idea/php.xml +++ b/.idea/php.xml @@ -15,6 +15,7 @@ + diff --git a/.idea/workspace.xml b/.idea/workspace.xml index a3262d4..42ee722 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -4,11 +4,28 @@ - + + + + + + + + + + + + - - + + + + + + + + + + + + + + + @@ -757,14 +781,23 @@ - - - - - - - - + + + + + file://$PROJECT_DIR$/src/Entity/ReceptionPelletBuilding.php + 6 + + + file://$PROJECT_DIR$/frontend/services/shipment.ts + + + + @@ -778,4 +811,4 @@ - + \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 4016f64..02a1d49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,11 @@ Ajouter dans le fichier .env du frontend * [#317] Admin modification creation transporteur * [#318] Affichage modification reception terminée * [#320] Affichage modification reception terminée suite +* [#271] Créer une nouvelle expédition (étape 1) +* [#272] Créer une nouvelle expédition (étape 2) +* [#273] Créer une nouvelle expédition (étape 3) +* [#256] Créer une nouvelle réception (étape 3 - bovin) +* [#314] Création d'une page d'administration : listing des utilisateurs ### Changed diff --git a/frontend/components/reception/reception-form.vue b/frontend/components/reception/reception-form.vue index d5bee42..52ec0d3 100644 --- a/frontend/components/reception/reception-form.vue +++ b/frontend/components/reception/reception-form.vue @@ -143,7 +143,7 @@ import type {DriverData} from '~/services/dto/driver-data' import {getDriverList} from '~/services/driver' import type {VehicleData} from '~/services/dto/vehicle-data' import {getVehicleList} from '~/services/vehicle' -import {RECEPTION_TYPE_CODES, SUPLLIER_CODE} from "~/utils/constants"; +import {RECEPTION_TYPE_CODES, SUPPLIER_CODE} from "~/utils/constants"; import {deleteReceptionBovine, getReceptionBovineList} from "~/services/reception-bovine"; import type {ReceptionFormData} from "~/services/dto/reception-data"; @@ -184,7 +184,7 @@ const selectedCarrier = computed(() => carriers.value.find((carrier) => String(carrier.id) === form.carrierId) ?? null ) // Indique si le transporteur est LIOT -const isLiotCarrier = computed(() => selectedCarrier.value?.code === SUPLLIER_CODE.LIOT) +const isLiotCarrier = computed(() => selectedCarrier.value?.code === SUPPLIER_CODE.LIOT) // Adresses disponibles pour le fournisseur sélectionné const supplierAddresses = computed(() => { const supplierId = Number(form.supplierId) diff --git a/frontend/components/reception/reception-weight.vue b/frontend/components/reception/reception-weight.vue index 7e624d7..5547810 100644 --- a/frontend/components/reception/reception-weight.vue +++ b/frontend/components/reception/reception-weight.vue @@ -74,7 +74,9 @@ const printReceipt = async () => { } await saveWeight() - await printPdf(`/receptions/${receptionStore.current.id}/receipt`) + const reception = receptionStore.current + const filename = `${reception.identificationNumber ?? reception.id}_${reception.supplier?.name ?? 'fournisseur'}_${reception.licensePlate ?? 'immat'}.pdf` + await printPdf(`/receptions/${reception.id}/receipt`, filename) // Laisse le temps a la boite de dialogue d'impression de s'ouvrir. await new Promise((resolve) => setTimeout(resolve, 600)) diff --git a/frontend/components/shipment/shipment-form.vue b/frontend/components/shipment/shipment-form.vue new file mode 100644 index 0000000..1eb026a --- /dev/null +++ b/frontend/components/shipment/shipment-form.vue @@ -0,0 +1,605 @@ + + diff --git a/frontend/components/shipment/shipment-weight.vue b/frontend/components/shipment/shipment-weight.vue new file mode 100644 index 0000000..cf4d7f0 --- /dev/null +++ b/frontend/components/shipment/shipment-weight.vue @@ -0,0 +1,101 @@ + + + diff --git a/frontend/components/ui/UiNumberInput.vue b/frontend/components/ui/UiNumberInput.vue index aa5407f..3ebc130 100644 --- a/frontend/components/ui/UiNumberInput.vue +++ b/frontend/components/ui/UiNumberInput.vue @@ -3,7 +3,7 @@ @@ -24,7 +25,7 @@ :step="step" :disabled="disabled" v-bind="attrs" - class="border-b border-black text-xl bg-transparent w-48" + class="border-b border-black text-xl bg-transparent w-12" :class="[ isEmpty ? 'text-neutral-400' : 'text-black', disabled ? 'cursor-not-allowed' : 'cursor-text', diff --git a/frontend/components/user/user-form.vue b/frontend/components/user/user-form.vue deleted file mode 100644 index 137d299..0000000 --- a/frontend/components/user/user-form.vue +++ /dev/null @@ -1,123 +0,0 @@ - - - diff --git a/frontend/composables/usePdfPrinter.ts b/frontend/composables/usePdfPrinter.ts index ff6a057..3e34846 100644 --- a/frontend/composables/usePdfPrinter.ts +++ b/frontend/composables/usePdfPrinter.ts @@ -1,30 +1,26 @@ -import {useApi} from '~/composables/useApi' +import { useApi } from '~/composables/useApi' export const usePdfPrinter = () => { const api = useApi() - const receptionStore = useReceptionStore() - const currentReception = receptionStore.current - const printPdf = async (url: string): Promise => { - const blob = await api.getBlob(url); + const printPdf = async (url: string, filename = 'document.pdf'): Promise => { + const blob = await api.getBlob(url) const pdfBlob = blob.type === 'application/pdf' ? blob - : new Blob([blob], { type: 'application/pdf' }); + : new Blob([blob], { type: 'application/pdf' }) - const blobUrl = URL.createObjectURL(pdfBlob); + const blobUrl = URL.createObjectURL(pdfBlob) - const filename = `${currentReception.identificationNumber}_${currentReception.supplier.name}_${currentReception.licensePlate}.pdf`; - - const a = document.createElement('a'); - a.href = blobUrl; - a.download = filename; - a.style.display = 'none'; - document.body.appendChild(a); - a.click(); - a.remove(); + const a = document.createElement('a') + a.href = blobUrl + a.download = filename + a.style.display = 'none' + document.body.appendChild(a) + a.click() + a.remove() // L'ouverture dans un nouvel onglet déclenche un 2e PDF sans le nom personnalisé. - setTimeout(() => URL.revokeObjectURL(blobUrl), 60_000); + setTimeout(() => URL.revokeObjectURL(blobUrl), 60_000) } return { diff --git a/frontend/composables/useWeighing.ts b/frontend/composables/useWeighing.ts index 9aea037..f632b5b 100644 --- a/frontend/composables/useWeighing.ts +++ b/frontend/composables/useWeighing.ts @@ -3,23 +3,20 @@ import {computed, ref} from 'vue' import type {ReceptionData, ReceptionPayload, WeightEntryData} from '~/services/dto/reception-data' import type {WeightData} from '~/services/dto/weight-data' import {getWeight} from '~/services/reception' +import {getWeightShipment} from '~/services/shipment' import {createWeight, updateWeight} from '~/services/weight' +import type {UseWeighingShipmentOptions, UseWeighingOptions} from '~/services/weight' +import type {WeightShipmentEntryData} from "~/services/dto/shipment-data"; export type WeighingMode = 'gross' | 'tare' -type UseWeighingOptions = { - mode: WeighingMode - reception: Ref - updateReception: (id: number, payload: ReceptionPayload) => Promise - loadReception?: (id: number) => Promise -} export const useWeighing = ({ - mode, - reception, - updateReception, - loadReception -}: UseWeighingOptions) => { + mode, + reception, + updateReception, + loadReception + }: UseWeighingOptions) => { const weightData = ref(null) const isFetching = ref(false) @@ -97,3 +94,87 @@ export const useWeighing = ({ saveWeight } } + +export const useWeighingShipment = ({ + modeShipment, + shipment, + updateShipment, + loadShipment + }: UseWeighingShipmentOptions) => { + const weightData = ref(null) + const isFetching = ref(false) + + const currentWeightEntry = computed(() => { + const weights = shipment.value?.weights ?? [] + return weights.find((entry) => entry.type === modeShipment) ?? null + }) + + const displayWeight = computed(() => weightData.value?.weight ?? currentWeightEntry.value?.weight ?? null) + const displayDsd = computed(() => weightData.value?.dsd ?? currentWeightEntry.value?.dsd ?? '-') + const title = computed(() => (modeShipment === 'gross' ? 'Pesée à plein' : 'Pesée à vide')) + const showLoadingBox = computed( + () => isFetching.value || (displayWeight.value === null && currentWeightEntry.value === null) + ) + + const fetchWeight = async () => { + isFetching.value = true + weightData.value = await getWeightShipment().finally(() => { + isFetching.value = false + }) + } + + const saveWeight = async () => { + if (!shipment.value) { + return + } + + const existingEntry = currentWeightEntry.value + const baseDsd = weightData.value?.dsd ?? existingEntry?.dsd ?? null + const baseWeight = weightData.value?.weight ?? existingEntry?.weight ?? null + const baseWeighedAt = weightData.value?.weighedAt ?? existingEntry?.weighedAt ?? null + + if (baseWeight === null) { + return + } + + if (existingEntry?.id) { + await updateWeight(existingEntry.id, { + type: modeShipment, + dsd: baseDsd, + weight: baseWeight, + weighedAt: baseWeighedAt + }) + } else { + await createWeight({ + shipment: `api/shipments/${shipment.value.id}`, + type: modeShipment, + dsd: baseDsd, + weight: baseWeight, + weighedAt: baseWeighedAt + }) + } + + const nextStep = modeShipment === 'tare' + ? shipment.value.currentStep + : shipment.value.currentStep + 1 + await updateShipment(shipment.value.id, { + currentStep: nextStep, + isValid: shipment.value.isValid + }) + + if (loadShipment) { + await loadShipment(shipment.value.id) + } + } + + return { + weightData, + currentWeightEntry, + displayWeight, + displayDsd, + title, + showLoadingBox, + fetchWeight, + saveWeight + } +} diff --git a/frontend/constants/steps.ts b/frontend/constants/steps.ts index 2e51937..d42ae66 100644 --- a/frontend/constants/steps.ts +++ b/frontend/constants/steps.ts @@ -2,7 +2,8 @@ export enum StepLabel { Reception = 'Réception', GrossWeighing = 'Pesée à plein', Selection = 'Sélection réceptionnées', - TareWeighing = 'Pesée à vide' + TareWeighing = 'Pesée à vide', + Shipment = 'Expédition', } export const RECEPTION_STEP_LABELS = [ @@ -11,3 +12,9 @@ export const RECEPTION_STEP_LABELS = [ StepLabel.Selection, StepLabel.TareWeighing ] + +export const SHIPMENT_STEP_LABELS = [ + StepLabel.Shipment, + StepLabel.TareWeighing, + StepLabel.GrossWeighing, +] diff --git a/frontend/i18n/locales/fr.json b/frontend/i18n/locales/fr.json index 2150e3e..5341750 100644 --- a/frontend/i18n/locales/fr.json +++ b/frontend/i18n/locales/fr.json @@ -17,6 +17,22 @@ "weight": { "update": "Impossible de mettre à jour la pesée" }, + "shipment": { + "list": "Impossible de récupérer la liste des éxpeditions.", + "fetch": "Impossible de récupérer l'éxpeditions.", + "create": "Impossible de créer l'éxpeditions.", + "update": "Impossible de mettre à jour l'éxpeditions.", + "weigh": "Impossible de récupérer la pesée." + }, + "shipmentBovine": { + "list": "Impossible de récupérer la liste des bovins de l'éxpedition.", + "create": "Impossible d'enregistrer le bovin.", + "delete": "Impossible de supprimer le bovin.", + "update": "Impossible de mettre à jour le bovin." + }, + "shipmentType": { + "list": "Impossible de récupérer la liste des types d'éxpedition." + }, "receptionType": { "list": "Impossible de récupérer la liste des types de réception." }, @@ -53,7 +69,6 @@ "fetch": "Impossible de récupérer les données du transporteur", "update": "Impossible de mettre à jour le transporteur", "create": "Impossible de créer le transporteur" - }, "driver": { "list": "Impossible de récupérer la liste des chauffeurs." @@ -73,6 +88,9 @@ "reception": { "update": "Réception mise à jour avec succès." }, + "shipment": { + "update": "Éxpedition mise à jour avec succès." + }, "auth": { "update": "Utilisateur mis à jour avec succès.", "create": "Utilisateur créé avec succès.", diff --git a/frontend/pages/admin/dashboard.vue b/frontend/pages/admin/dashboard.vue index 788fdd1..28f3f30 100644 --- a/frontend/pages/admin/dashboard.vue +++ b/frontend/pages/admin/dashboard.vue @@ -1,7 +1,5 @@ - diff --git a/frontend/pages/index.vue b/frontend/pages/index.vue index b7e90a4..52d3da7 100644 --- a/frontend/pages/index.vue +++ b/frontend/pages/index.vue @@ -3,7 +3,7 @@