feat(transport) : upload décharge (useUpload) + câblage MalioInputUpload + i18n erreur (ERP-171)
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { computed, reactive, ref, type Ref } from 'vue'
|
||||
import { useFormErrors } from '~/shared/composables/useFormErrors'
|
||||
import { useUpload } from '~/shared/composables/useUpload'
|
||||
import { extractApiErrorMessage, mapViolationsToRecord } from '~/shared/utils/api'
|
||||
import { removeCollectionRow } from '~/shared/utils/collectionRow'
|
||||
import {
|
||||
@@ -66,6 +67,9 @@ export function useCarrierForm() {
|
||||
// Erreurs de validation par champ (ERP-101) du formulaire principal.
|
||||
const mainErrors = useFormErrors()
|
||||
|
||||
// Upload de la décharge (RG-4.02) — infra partagée /api/uploaded_documents.
|
||||
const { uploading: dischargeUploading, upload: uploadFile } = useUpload()
|
||||
|
||||
// ── État du transporteur créé ─────────────────────────────────────────────
|
||||
const carrierId = ref<number | null>(null)
|
||||
const mainLocked = ref(false)
|
||||
@@ -165,6 +169,24 @@ export function useCarrierForm() {
|
||||
return valid
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload de la décharge (RG-4.02) déclenché par `@file-selected` du champ
|
||||
* Décharge : envoie le fichier, pose l'IRI résultant sur le brouillon. Au 422
|
||||
* (MIME hors whitelist / taille), le message back s'affiche sous le champ
|
||||
* (pas de toast) ; l'IRI est remis à null pour bloquer la validation.
|
||||
*/
|
||||
async function uploadDischarge(file: File): Promise<void> {
|
||||
mainErrors.clearError('dischargeDocument')
|
||||
try {
|
||||
main.dischargeDocumentIri = await uploadFile(file)
|
||||
} catch (error) {
|
||||
main.dischargeDocumentIri = null
|
||||
const message = extractApiErrorMessage((error as { data?: unknown })?.data)
|
||||
|| t('transport.carriers.form.errors.uploadFailed')
|
||||
mainErrors.setError('dischargeDocument', message)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Payload du POST principal (groupe `carrier:write:main`). `name` et
|
||||
* `certificationType` sont omis s'ils sont vides afin que la 422 porte la
|
||||
@@ -732,6 +754,7 @@ export function useCarrierForm() {
|
||||
mainSubmitting,
|
||||
tabSubmitting,
|
||||
mainErrors,
|
||||
dischargeUploading,
|
||||
// affichage conditionnel
|
||||
isLiot,
|
||||
isQualimat,
|
||||
@@ -768,6 +791,7 @@ export function useCarrierForm() {
|
||||
removePrice,
|
||||
submitPrices,
|
||||
// actions
|
||||
uploadDischarge,
|
||||
validateMainFront,
|
||||
buildMainPayload,
|
||||
submitMain,
|
||||
|
||||
@@ -45,12 +45,16 @@
|
||||
/>
|
||||
<MalioInputUpload
|
||||
v-if="showDischarge"
|
||||
:model-value="dischargeFileName"
|
||||
:label="t('transport.carriers.form.main.discharge')"
|
||||
accept="application/pdf,image/*"
|
||||
:required="true"
|
||||
:readonly="dischargeUploading"
|
||||
:clearable="true"
|
||||
:error="mainErrors.errors.dischargeDocument"
|
||||
@clear="main.dischargeDocumentIri = null"
|
||||
@update:model-value="(v: string) => dischargeFileName = v"
|
||||
@file-selected="uploadDischarge"
|
||||
@clear="onClearDischarge"
|
||||
/>
|
||||
<div v-else class="hidden xl:block"></div>
|
||||
<div class="flex h-12 items-center">
|
||||
@@ -231,6 +235,8 @@ const {
|
||||
mainSubmitting,
|
||||
tabSubmitting,
|
||||
mainErrors,
|
||||
dischargeUploading,
|
||||
uploadDischarge,
|
||||
isLiot,
|
||||
certificationReadonly,
|
||||
showCharteredFields,
|
||||
@@ -307,6 +313,12 @@ onMounted(async () => {
|
||||
await load()
|
||||
if (carrier.value) {
|
||||
prefillFrom(carrier.value)
|
||||
// Pré-affiche le nom du fichier de décharge déjà rattaché (s'il existe).
|
||||
const doc = carrier.value.dischargeDocument
|
||||
if (doc && typeof doc !== 'string') {
|
||||
const meta = doc as Record<string, unknown>
|
||||
dischargeFileName.value = String(meta.originalFilename ?? meta.name ?? '')
|
||||
}
|
||||
}
|
||||
loadCountries().catch(() => {})
|
||||
void loadOptions('/clients', clientOptions, m => String(m.companyName ?? m['@id']))
|
||||
@@ -319,6 +331,16 @@ function apiErrorMessage(err: unknown): string {
|
||||
return extractApiErrorMessage(data) || t('transport.carriers.toast.error')
|
||||
}
|
||||
|
||||
// Nom de fichier affiché dans le champ Décharge (alimenté à la sélection ou au
|
||||
// chargement d'un transporteur ayant déjà une décharge).
|
||||
const dischargeFileName = ref('')
|
||||
|
||||
/** Vidage du champ Décharge : retire l'IRI et le nom affiché. */
|
||||
function onClearDischarge(): void {
|
||||
main.dischargeDocumentIri = null
|
||||
dischargeFileName.value = ''
|
||||
}
|
||||
|
||||
// Indexation plafonnée à 100 % : la clé force le ré-affichage du MalioInputAmount
|
||||
// (contrôlé) quand le plafonnement laisse le modelValue inchangé.
|
||||
const indexationKey = ref(0)
|
||||
|
||||
@@ -53,18 +53,20 @@
|
||||
<!-- Colonne 3 RÉSERVÉE à la Décharge (RG-4.02 : visible et obligatoire
|
||||
si certification AUTRE). Si elle n'apparaît pas, on garde la colonne
|
||||
vide (xl) pour qu'« Affréter » reste en colonne 4 de la ligne 1.
|
||||
L'upload reel (File → IRI via useUpload) arrive a ERP-171. -->
|
||||
<!-- TODO ERP-171 : brancher useUpload pour resoudre le File en IRI
|
||||
(main.dischargeDocumentIri). Le champ est deja visible/obligatoire. -->
|
||||
L'upload réel (File → IRI via useUpload, ERP-171) résout le
|
||||
fichier en IRI posé sur main.dischargeDocumentIri. -->
|
||||
<MalioInputUpload
|
||||
v-if="showDischarge"
|
||||
:model-value="dischargeFileName"
|
||||
:label="t('transport.carriers.form.main.discharge')"
|
||||
accept="application/pdf,image/*"
|
||||
:required="true"
|
||||
:readonly="mainLocked"
|
||||
:readonly="mainLocked || dischargeUploading"
|
||||
:clearable="true"
|
||||
:error="mainErrors.errors.dischargeDocument"
|
||||
@clear="main.dischargeDocumentIri = null"
|
||||
@update:model-value="(v: string) => dischargeFileName = v"
|
||||
@file-selected="uploadDischarge"
|
||||
@clear="onClearDischarge"
|
||||
/>
|
||||
<div v-else class="hidden xl:block"></div>
|
||||
|
||||
@@ -398,6 +400,8 @@ const {
|
||||
mainSubmitting,
|
||||
tabSubmitting,
|
||||
mainErrors,
|
||||
dischargeUploading,
|
||||
uploadDischarge,
|
||||
isLiot,
|
||||
isQualimat,
|
||||
certificationReadonly,
|
||||
@@ -429,6 +433,15 @@ const {
|
||||
applyQualimatSelection,
|
||||
} = useCarrierForm()
|
||||
|
||||
// Nom de fichier affiché dans le champ Décharge (alimenté à la sélection).
|
||||
const dischargeFileName = ref('')
|
||||
|
||||
/** Vidage du champ Décharge : retire l'IRI et le nom affiché. */
|
||||
function onClearDischarge(): void {
|
||||
main.dischargeDocumentIri = null
|
||||
dischargeFileName.value = ''
|
||||
}
|
||||
|
||||
const {
|
||||
items: qualimatItems,
|
||||
totalItems: qualimatTotal,
|
||||
|
||||
Reference in New Issue
Block a user