151 lines
4.4 KiB
Python
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(' ', ''))
|