Files
aufmass-web/_aufmass_web/app/services/custom_module_executor.py
T

151 lines
4.4 KiB
Python

import json
import re
from app.extensions import db
from app.models.lv import LVPosition
def execute_rules(form_data, rules_json, company_id):
rules = json.loads(rules_json) if isinstance(rules_json, str) else rules_json
positions = []
for rule in rules:
if not _evaluate_conditions(rule.get('conditions', {}), form_data):
continue
for action in rule.get('actions', []):
pos = _create_position(action, form_data, company_id)
if pos:
positions.append(pos)
return positions
def _evaluate_conditions(conditions, form_data):
if not conditions or not conditions.get('items'):
return True
operator = conditions.get('operator', 'and')
items = conditions.get('items', [])
results = []
for cond in items:
field = cond.get('field', '')
op = cond.get('operator', 'eq')
value = cond.get('value', '')
value2 = cond.get('value2', '')
raw = form_data.get(field, '')
if op == 'is_checked':
results.append(raw == 'an')
elif op == 'is_empty':
results.append(raw == '' or raw is None)
elif op == 'not_empty':
results.append(raw != '' and raw is not None)
else:
try:
f_val = _to_float(raw)
f_cond = _to_float(value)
if op == 'eq':
results.append(f_val == f_cond)
elif op == 'neq':
results.append(f_val != f_cond)
elif op == 'gt':
results.append(f_val > f_cond)
elif op == 'gte':
results.append(f_val >= f_cond)
elif op == 'lt':
results.append(f_val < f_cond)
elif op == 'lte':
results.append(f_val <= f_cond)
elif op == 'between':
f_val2 = _to_float(value2)
results.append(f_cond <= f_val <= f_val2)
else:
results.append(str(raw) == str(value))
except (ValueError, TypeError):
results.append(str(raw) == str(value))
if operator == 'or':
return any(results)
return all(results)
def _create_position(action, form_data, company_id):
pos_nr = action.get('pos_nr', '')
if not pos_nr:
return None
lv_lookup = action.get('lv_lookup', True)
columns = action.get('columns', {})
# Default values
pos = {
'pos_nr': pos_nr,
'kurztext': '',
'einheit': 'ST',
'menge': 1,
'faktor': 1,
'laenge': 0,
'breite': 0,
'tiefe': 0,
'bemerkung': '',
'einzelpreis': 0,
}
# LV lookup
if lv_lookup:
lv_pos = LVPosition.query.filter_by(company_id=company_id, pos_nr=pos_nr).first()
if lv_pos:
pos['kurztext'] = lv_pos.kurztext or ''
pos['einheit'] = lv_pos.einheit or 'ST'
pos['einzelpreis'] = lv_pos.einzelpreis or 0
# Apply overrides
for col_key, col_def in columns.items():
if col_key not in pos and col_key != 'pos_nr':
continue
val = _resolve_value(col_def, form_data)
if val is not None:
if col_key in ('menge', 'faktor', 'laenge', 'breite', 'tiefe', 'einzelpreis'):
pos[col_key] = _to_float(val)
else:
pos[col_key] = str(val)
return pos
def _resolve_value(col_def, form_data):
if not col_def:
return None
ctype = col_def.get('type', 'fixed')
value = col_def.get('value', '')
if ctype == 'fixed':
return value
elif ctype == 'field':
return form_data.get(value, '')
elif ctype == 'formula':
return _eval_formula(value, form_data)
return value
def _eval_formula(expr, form_data):
expr = str(expr)
def _replace_field(m):
fname = m.group(1)
raw = form_data.get(fname, '0')
return str(raw).replace(',', '.')
expr = re.sub(r'\[([^\]]+)\]', _replace_field, expr)
safe = re.sub(r'[^\d\+\-\*\/\(\)\. ]', '', expr)
if not safe.strip():
return 0
try:
return eval(safe, {'__builtins__': {}}, {})
except Exception:
return 0
def _to_float(val):
if val is None or val == '':
return 0.0
return float(str(val).replace(',', '.').replace(' ', ''))