diff --git a/screens/data_actions/compute_operation.py b/screens/data_actions/compute_operation.py
index 6f1ca773..eb0822c2 100644
--- a/screens/data_actions/compute_operation.py
+++ b/screens/data_actions/compute_operation.py
@@ -1,3 +1,6 @@
+import random
+
+
def compute_new_value(operation, current, raw_value, is_decimal):
"""Calcule la nouvelle valeur d'un champ d'objet ou d'une variable
globale pour une opération de DATA_OPERATIONS (voir
@@ -32,4 +35,47 @@ def compute_new_value(operation, current, raw_value, is_decimal):
amount = -amount
result = (current or 0) + amount
return result if is_decimal else int(result)
+ # Phase 2 — hasard + opérations mathématiques. "multiplier"/"diviser"/
+ # "modulo" reprennent le même patron qu'incrementer/decrementer (un
+ # seul opérande numérique dans raw_value) ; "minimum"/"maximum"
+ # bornent la valeur ACTUELLE (utile pour une variable globale, qui n'a
+ # pas de min_value/max_value comme un champ d'objet, voir
+ # _clamp_to_field_bounds dans apply_data_action.py) ; "alea" est la
+ # seule opération à deux opérandes ("min,max" dans raw_value plutôt
+ # qu'une nouvelle colonne de nœud — voir updateDataValuePlaceholder()
+ # dans static/js/screen_edit/flow-editor.js).
+ if operation == "multiplier":
+ try:
+ factor = float(raw_value) if raw_value not in (None, "") else 1
+ except (TypeError, ValueError):
+ factor = 1
+ result = (current or 0) * factor
+ return result if is_decimal else int(result)
+ if operation in ("diviser", "modulo"):
+ try:
+ divisor = float(raw_value) if raw_value not in (None, "") else 0
+ except (TypeError, ValueError):
+ divisor = 0
+ if divisor == 0:
+ # Garde-fou division par zéro : valeur inchangée plutôt qu'une
+ # ZeroDivisionError qui interromprait tout le graphe de logique.
+ return current
+ result = (current or 0) / divisor if operation == "diviser" else (current or 0) % divisor
+ return result if is_decimal else int(result)
+ if operation in ("minimum", "maximum"):
+ try:
+ bound = float(raw_value)
+ except (TypeError, ValueError):
+ return current
+ result = max(current or 0, bound) if operation == "minimum" else min(current or 0, bound)
+ return result if is_decimal else int(result)
+ if operation == "alea":
+ parts = (raw_value or "").split(",")
+ try:
+ lo, hi = float(parts[0]), float(parts[1])
+ except (IndexError, TypeError, ValueError):
+ lo, hi = 0, 0
+ if lo > hi:
+ lo, hi = hi, lo
+ return random.uniform(lo, hi) if is_decimal else random.randint(int(lo), int(hi))
raise ValueError(f"opération inconnue : {operation!r}")
diff --git a/screens/labels/data_operations.py b/screens/labels/data_operations.py
index 5159fd92..922bb940 100644
--- a/screens/labels/data_operations.py
+++ b/screens/labels/data_operations.py
@@ -8,5 +8,13 @@ DATA_OPERATIONS = [
("basculer_bool", "Basculer Vrai / Faux"),
("incrementer", "Augmenter de..."),
("decrementer", "Diminuer de..."),
+ # Phase 2 (hasard + opérations mathématiques) — voir
+ # screens/data_actions/compute_operation.py pour le calcul de chacune.
+ ("multiplier", "Multiplier par..."),
+ ("diviser", "Diviser par..."),
+ ("modulo", "Reste de la division par... (modulo)"),
+ ("minimum", "Ne jamais descendre sous..."),
+ ("maximum", "Ne jamais dépasser..."),
+ ("alea", "Tirer un nombre aléatoire entre... (min,max)"),
]
DATA_OPERATION_LABELS = dict(DATA_OPERATIONS)
diff --git a/static/js/screen_edit/flow-editor.js b/static/js/screen_edit/flow-editor.js
index 245f64a8..acd12e4e 100644
--- a/static/js/screen_edit/flow-editor.js
+++ b/static/js/screen_edit/flow-editor.js
@@ -457,6 +457,19 @@ function toggleFlowActionFields() {
document.getElementById('nf-action-element-property-wrap').style.display = isOnglet ? 'none' : 'block';
if (typeSel.value === 'modifier_element') toggleFlowElementPropertyValue();
if (typeSel.value === 'modifier_donnee') onFlowDefinitionChange('action');
+ updateDataValuePlaceholder('nf-action-data-operation', 'nf-action-data-value');
+ updateDataValuePlaceholder('nf-action-variable-operation', 'nf-action-variable-value');
+}
+
+// "alea" (Phase 2 — hasard) attend "min,max" dans data_value (voir
+// screens/data_actions/compute_operation.py) plutôt qu'un seul nombre
+// comme les autres opérations — indice visuel dans le champ plutôt que
+// d'ajouter un champ dédié (aucun changement de schéma nécessaire).
+function updateDataValuePlaceholder(operationSelectId, valueInputId) {
+ var opSel = document.getElementById(operationSelectId);
+ var valueInp = document.getElementById(valueInputId);
+ if (!opSel || !valueInp) return;
+ valueInp.placeholder = opSel.value === 'alea' ? 'min,max (ex. 1,6)' : 'Valeur / montant';
}
function toggleFlowElementPropertyValue() {
diff --git a/templates/screen_edit.html b/templates/screen_edit.html
index c5a15e26..28b9fbe2 100644
--- a/templates/screen_edit.html
+++ b/templates/screen_edit.html
@@ -419,7 +419,7 @@
-