Checkpoint: Ajout d'un champ texte libre qui s'affiche quand "Autre..." est sélectionné dans les selects typeAchat, serviceConcerne et ventilationComptable des règles d'automatisme. Le texte saisi remplace "AUTRE" dans le tampon BAP. Disponible dans les deux modes (wizard et avancé).

This commit is contained in:
Manus
2026-04-12 14:40:18 -04:00
parent 8acfb4eae1
commit 120f56bb23

View File

@@ -68,6 +68,21 @@ export default function AutomationRules() {
const [conditionsLogic, setConditionsLogic] = useState<"AND" | "OR">("AND"); const [conditionsLogic, setConditionsLogic] = useState<"AND" | "OR">("AND");
const [conditions, setConditions] = useState<Condition[]>([{ field: "supplierName", operator: "contains", value: "" }]); const [conditions, setConditions] = useState<Condition[]>([{ field: "supplierName", operator: "contains", value: "" }]);
const [actions, setActions] = useState<Actions>({}); const [actions, setActions] = useState<Actions>({});
// Textes personnalisés quand "Autre" est sélectionné
const [customTypeAchat, setCustomTypeAchat] = useState("");
const [customServiceConcerne, setCustomServiceConcerne] = useState("");
const [customVentilation, setCustomVentilation] = useState("");
// Helper : détecte si une valeur est "Autre" (insensible à la casse)
const isAutre = (val?: string) => val?.toUpperCase() === "AUTRE";
// Helper : résout la valeur finale (si Autre + texte perso, retourne le texte perso)
const resolveAction = (val?: string, custom?: string) => {
if (!val || val === "__NONE__") return undefined;
if (isAutre(val) && custom?.trim()) return custom.trim().toUpperCase();
if (isAutre(val)) return "AUTRE";
return val;
};
const createMutation = trpc.automationRules.create.useMutation({ const createMutation = trpc.automationRules.create.useMutation({
onSuccess: (rule) => { onSuccess: (rule) => {
@@ -149,6 +164,9 @@ export default function AutomationRules() {
setConditionsLogic("AND"); setConditionsLogic("AND");
setConditions([{ field: "supplierName", operator: "contains", value: "" }]); setConditions([{ field: "supplierName", operator: "contains", value: "" }]);
setActions({}); setActions({});
setCustomTypeAchat("");
setCustomServiceConcerne("");
setCustomVentilation("");
setDialogOpen(true); setDialogOpen(true);
}; };
@@ -162,9 +180,36 @@ export default function AutomationRules() {
setConditions([{ field: "supplierName", operator: "contains", value: "" }]); setConditions([{ field: "supplierName", operator: "contains", value: "" }]);
} }
try { try {
setActions(JSON.parse(rule.actions)); const parsedActions = JSON.parse(rule.actions);
// Détecter si les valeurs stockées ne correspondent pas aux valeurs fixes
// (ce qui signifie qu'un texte personnalisé avait été saisi)
const knownTypeAchat = ["CAPEX", "OPEX", "AUTRE", undefined];
const deptNames = departments?.map(d => d.name) || [];
const allocNames = allocations?.map(a => a.name) || [];
if (parsedActions.typeAchat && !knownTypeAchat.includes(parsedActions.typeAchat)) {
setCustomTypeAchat(parsedActions.typeAchat);
parsedActions.typeAchat = "AUTRE";
} else {
setCustomTypeAchat("");
}
if (parsedActions.serviceConcerne && !deptNames.includes(parsedActions.serviceConcerne) && parsedActions.serviceConcerne !== "AUTRE") {
setCustomServiceConcerne(parsedActions.serviceConcerne);
parsedActions.serviceConcerne = "AUTRE";
} else {
setCustomServiceConcerne("");
}
if (parsedActions.ventilationComptable && !allocNames.includes(parsedActions.ventilationComptable) && parsedActions.ventilationComptable !== "AUTRE") {
setCustomVentilation(parsedActions.ventilationComptable);
parsedActions.ventilationComptable = "AUTRE";
} else {
setCustomVentilation("");
}
setActions(parsedActions);
} catch { } catch {
setActions({}); setActions({});
setCustomTypeAchat("");
setCustomServiceConcerne("");
setCustomVentilation("");
} }
setDialogOpen(true); setDialogOpen(true);
}; };
@@ -181,7 +226,17 @@ export default function AutomationRules() {
} }
const conditionsJSON = JSON.stringify(conditions); const conditionsJSON = JSON.stringify(conditions);
const actionsJSON = JSON.stringify(actions); // Résoudre les valeurs finales (remplacer AUTRE par le texte personnalisé si fourni)
const resolvedActions: Actions = {
typeAchat: resolveAction(actions.typeAchat, customTypeAchat),
serviceConcerne: resolveAction(actions.serviceConcerne, customServiceConcerne),
ventilationComptable: resolveAction(actions.ventilationComptable, customVentilation),
};
// Supprimer les clés undefined
if (!resolvedActions.typeAchat) delete resolvedActions.typeAchat;
if (!resolvedActions.serviceConcerne) delete resolvedActions.serviceConcerne;
if (!resolvedActions.ventilationComptable) delete resolvedActions.ventilationComptable;
const actionsJSON = JSON.stringify(resolvedActions);
if (editingRule) { if (editingRule) {
updateMutation.mutate({ updateMutation.mutate({
@@ -476,7 +531,7 @@ export default function AutomationRules() {
<Label htmlFor="typeAchat">Type d'achat</Label> <Label htmlFor="typeAchat">Type d'achat</Label>
<Select <Select
value={actions.typeAchat || "__NONE__"} value={actions.typeAchat || "__NONE__"}
onValueChange={(value) => setActions({ ...actions, typeAchat: value === "__NONE__" ? undefined : value })} onValueChange={(value) => { setActions({ ...actions, typeAchat: value === "__NONE__" ? undefined : value }); if (value !== "AUTRE") setCustomTypeAchat(""); }}
> >
<SelectTrigger> <SelectTrigger>
<SelectValue placeholder="Ne pas modifier" /> <SelectValue placeholder="Ne pas modifier" />
@@ -485,15 +540,24 @@ export default function AutomationRules() {
<SelectItem value="__NONE__">Ne pas modifier</SelectItem> <SelectItem value="__NONE__">Ne pas modifier</SelectItem>
<SelectItem value="CAPEX">CAPEX</SelectItem> <SelectItem value="CAPEX">CAPEX</SelectItem>
<SelectItem value="OPEX">OPEX</SelectItem> <SelectItem value="OPEX">OPEX</SelectItem>
<SelectItem value="AUTRE">Autre...</SelectItem>
</SelectContent> </SelectContent>
</Select> </Select>
{isAutre(actions.typeAchat) && (
<Input
value={customTypeAchat}
onChange={(e) => setCustomTypeAchat(e.target.value)}
placeholder="Texte à afficher sur le BAP (ex: INVESTISSEMENT)"
className="mt-1 border-orange-300 focus:border-orange-500"
/>
)}
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="serviceConcerne">Service concerné</Label> <Label htmlFor="serviceConcerne">Service concerné</Label>
<Select <Select
value={actions.serviceConcerne || "__NONE__"} value={actions.serviceConcerne || "__NONE__"}
onValueChange={(value) => setActions({ ...actions, serviceConcerne: value === "__NONE__" ? undefined : value })} onValueChange={(value) => { setActions({ ...actions, serviceConcerne: value === "__NONE__" ? undefined : value }); if (value !== "AUTRE") setCustomServiceConcerne(""); }}
> >
<SelectTrigger> <SelectTrigger>
<SelectValue placeholder="Ne pas modifier" /> <SelectValue placeholder="Ne pas modifier" />
@@ -505,15 +569,24 @@ export default function AutomationRules() {
{dept.name} {dept.name}
</SelectItem> </SelectItem>
))} ))}
<SelectItem value="AUTRE">Autre...</SelectItem>
</SelectContent> </SelectContent>
</Select> </Select>
{isAutre(actions.serviceConcerne) && (
<Input
value={customServiceConcerne}
onChange={(e) => setCustomServiceConcerne(e.target.value)}
placeholder="Texte à afficher sur le BAP (ex: LOGISTIQUE)"
className="mt-1 border-orange-300 focus:border-orange-500"
/>
)}
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="ventilationComptable">Ventilation comptable</Label> <Label htmlFor="ventilationComptable">Ventilation comptable</Label>
<Select <Select
value={actions.ventilationComptable || "__NONE__"} value={actions.ventilationComptable || "__NONE__"}
onValueChange={(value) => setActions({ ...actions, ventilationComptable: value === "__NONE__" ? undefined : value })} onValueChange={(value) => { setActions({ ...actions, ventilationComptable: value === "__NONE__" ? undefined : value }); if (value !== "AUTRE") setCustomVentilation(""); }}
> >
<SelectTrigger> <SelectTrigger>
<SelectValue placeholder="Ne pas modifier" /> <SelectValue placeholder="Ne pas modifier" />
@@ -525,8 +598,17 @@ export default function AutomationRules() {
{alloc.name} {alloc.name}
</SelectItem> </SelectItem>
))} ))}
<SelectItem value="AUTRE">Autre...</SelectItem>
</SelectContent> </SelectContent>
</Select> </Select>
{isAutre(actions.ventilationComptable) && (
<Input
value={customVentilation}
onChange={(e) => setCustomVentilation(e.target.value)}
placeholder="Texte à afficher sur le BAP (ex: SANITAIRE)"
className="mt-1 border-orange-300 focus:border-orange-500"
/>
)}
</div> </div>
</div> </div>
</div> </div>
@@ -667,7 +749,7 @@ export default function AutomationRules() {
<Label htmlFor="typeAchat">Type d'achat</Label> <Label htmlFor="typeAchat">Type d'achat</Label>
<Select <Select
value={actions.typeAchat || "__NONE__"} value={actions.typeAchat || "__NONE__"}
onValueChange={(value) => setActions({ ...actions, typeAchat: value === "__NONE__" ? undefined : value })} onValueChange={(value) => { setActions({ ...actions, typeAchat: value === "__NONE__" ? undefined : value }); if (value !== "AUTRE") setCustomTypeAchat(""); }}
> >
<SelectTrigger> <SelectTrigger>
<SelectValue placeholder="Ne pas modifier" /> <SelectValue placeholder="Ne pas modifier" />
@@ -676,15 +758,25 @@ export default function AutomationRules() {
<SelectItem value="__NONE__">Ne pas modifier</SelectItem> <SelectItem value="__NONE__">Ne pas modifier</SelectItem>
<SelectItem value="CAPEX">CAPEX</SelectItem> <SelectItem value="CAPEX">CAPEX</SelectItem>
<SelectItem value="OPEX">OPEX</SelectItem> <SelectItem value="OPEX">OPEX</SelectItem>
<SelectItem value="AUTRE">Autre...</SelectItem>
</SelectContent> </SelectContent>
</Select> </Select>
{isAutre(actions.typeAchat) && (
<Input
value={customTypeAchat}
onChange={(e) => setCustomTypeAchat(e.target.value)}
placeholder="Saisir le texte à afficher sur le BAP (ex: INVESTISSEMENT)"
className="mt-1 border-orange-300 focus:border-orange-500"
autoFocus
/>
)}
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="serviceConcerne">Service concerné</Label> <Label htmlFor="serviceConcerne">Service concerné</Label>
<Select <Select
value={actions.serviceConcerne || "__NONE__"} value={actions.serviceConcerne || "__NONE__"}
onValueChange={(value) => setActions({ ...actions, serviceConcerne: value === "__NONE__" ? undefined : value })} onValueChange={(value) => { setActions({ ...actions, serviceConcerne: value === "__NONE__" ? undefined : value }); if (value !== "AUTRE") setCustomServiceConcerne(""); }}
> >
<SelectTrigger> <SelectTrigger>
<SelectValue placeholder="Ne pas modifier" /> <SelectValue placeholder="Ne pas modifier" />
@@ -696,15 +788,24 @@ export default function AutomationRules() {
{dept.name} {dept.name}
</SelectItem> </SelectItem>
))} ))}
<SelectItem value="AUTRE">Autre...</SelectItem>
</SelectContent> </SelectContent>
</Select> </Select>
{isAutre(actions.serviceConcerne) && (
<Input
value={customServiceConcerne}
onChange={(e) => setCustomServiceConcerne(e.target.value)}
placeholder="Saisir le texte à afficher sur le BAP (ex: LOGISTIQUE)"
className="mt-1 border-orange-300 focus:border-orange-500"
/>
)}
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="ventilationComptable">Ventilation comptable</Label> <Label htmlFor="ventilationComptable">Ventilation comptable</Label>
<Select <Select
value={actions.ventilationComptable || "__NONE__"} value={actions.ventilationComptable || "__NONE__"}
onValueChange={(value) => setActions({ ...actions, ventilationComptable: value === "__NONE__" ? undefined : value })} onValueChange={(value) => { setActions({ ...actions, ventilationComptable: value === "__NONE__" ? undefined : value }); if (value !== "AUTRE") setCustomVentilation(""); }}
> >
<SelectTrigger> <SelectTrigger>
<SelectValue placeholder="Ne pas modifier" /> <SelectValue placeholder="Ne pas modifier" />
@@ -716,8 +817,17 @@ export default function AutomationRules() {
{alloc.name} {alloc.name}
</SelectItem> </SelectItem>
))} ))}
<SelectItem value="AUTRE">Autre...</SelectItem>
</SelectContent> </SelectContent>
</Select> </Select>
{isAutre(actions.ventilationComptable) && (
<Input
value={customVentilation}
onChange={(e) => setCustomVentilation(e.target.value)}
placeholder="Saisir le texte à afficher sur le BAP (ex: SANITAIRE)"
className="mt-1 border-orange-300 focus:border-orange-500"
/>
)}
</div> </div>
</div> </div>
</div> </div>