git-subtree-dir: fiscal git-subtree-mainline:d95165fc8fgit-subtree-split:102ba84500
102 lines
4.7 KiB
Python
102 lines
4.7 KiB
Python
"""Pure, review-first questionnaire helpers for local fiscal context."""
|
|
import csv
|
|
import io
|
|
import json
|
|
|
|
from central.app.context_keys import validate_entry
|
|
|
|
|
|
def _options(row):
|
|
value = row.get("VALID_OPTIONS", [])
|
|
if isinstance(value, str):
|
|
try:
|
|
value = json.loads(value)
|
|
except json.JSONDecodeError:
|
|
value = [x.strip() for x in value.split("|") if x.strip()]
|
|
return list(value) if isinstance(value, (list, tuple, set)) else [value]
|
|
|
|
|
|
def build_questionnaire(rows):
|
|
result = []
|
|
for row in rows:
|
|
scope = str(row.get("SCOPE", row.get("scope", ""))).strip().upper()
|
|
key = str(row.get("KEY", row.get("key", ""))).strip()
|
|
scope_key = str(row.get("GROUP") or row.get("NCM") or row.get("scope_key") or "").strip()
|
|
result.append({
|
|
"question_id": str(row.get("QUESTION_ID", row.get("question_id", ""))).strip(),
|
|
"database": str(row.get("DATABASE", row.get("database", ""))).strip(),
|
|
"scope": scope,
|
|
"scope_key": scope_key if scope != "COMPANY" else "",
|
|
"group": str(row.get("GROUP") or "").strip(),
|
|
"key": key,
|
|
"question": row.get("QUESTION", row.get("question", "")),
|
|
"options": _options(row),
|
|
"affected": {"products": int(row.get("PRODUCTS_AFFECTED") or row.get("products_affected") or 0), "cells": int(row.get("CELLS") or row.get("cells") or 0)},
|
|
"examples": row.get("EXAMPLES", row.get("examples", "")),
|
|
"consequence": row.get("WHAT_IT_WILL_RESOLVE", row.get("consequence", "")),
|
|
})
|
|
return sorted(result, key=lambda q: (q["database"], q["scope"], q["scope_key"], q["key"], q["question_id"]))
|
|
|
|
|
|
def group_questions(questions):
|
|
"""Group equivalent human decisions while retaining every affected question ID."""
|
|
groups = {}
|
|
for question in questions:
|
|
key = (question["database"], question["group"], question["scope_key"], question["key"], question["question"], tuple(question["options"]))
|
|
group = groups.setdefault(key, {
|
|
"group_id": "GROUP-" + str(len(groups) + 1),
|
|
"database": question["database"],
|
|
"group": question["group"],
|
|
"scope": question["scope"],
|
|
"scope_key": question["scope_key"],
|
|
"key": question["key"],
|
|
"question": question["question"],
|
|
"options": question["options"],
|
|
"question_ids": [],
|
|
"affected": {"products": 0, "cells": 0},
|
|
"examples": [],
|
|
"consequence": question["consequence"],
|
|
})
|
|
group["question_ids"].append(question["question_id"])
|
|
group["affected"]["products"] += question["affected"]["products"]
|
|
group["affected"]["cells"] += question["affected"]["cells"]
|
|
if question["examples"] and len(group["examples"]) < 5:
|
|
group["examples"].append(str(question["examples"]))
|
|
return list(groups.values())
|
|
|
|
|
|
def validate_answers(questions, answers):
|
|
entries, effects, rollback_plan = [], [], []
|
|
by_id = {q["question_id"]: q for q in questions}
|
|
for question_id, value in answers.items():
|
|
if question_id not in by_id:
|
|
raise ValueError(f"unknown question {question_id!r}; nothing was persisted")
|
|
q = by_id[question_id]
|
|
value = str(value).strip()
|
|
if q["options"] and value not in q["options"]:
|
|
raise ValueError(f"{question_id}: value must be one of {q['options']}; nothing was persisted")
|
|
entry, error = validate_entry({"scope": q["scope"], "scope_key": q["scope_key"], "key": q["key"], "value": value})
|
|
if error:
|
|
raise ValueError(f"{question_id}: {error}; nothing was persisted")
|
|
entry["company_key"] = q["database"]
|
|
entries.append(entry)
|
|
effect = {"question_id": question_id, "database": q["database"], "key": q["key"], "affected": q["affected"], "behavior": "context_entry"}
|
|
if q["key"] == "entry_scope" and value == "MIXED_OR_DYNAMIC":
|
|
effect["behavior"] = "no_static_cfop_entrada"
|
|
effects.append(effect)
|
|
if q["key"] == "store_ipi_profile" and value == "LEAVE_NOT_APPLICABLE":
|
|
rollback_plan.append({"database": q["database"], "field": "IPI", "affected_cells": q["affected"]["cells"]})
|
|
return {"entries": entries, "effects": effects, "rollback_plan": rollback_plan}
|
|
|
|
|
|
def render_json(result):
|
|
return json.dumps(result, ensure_ascii=False, indent=2, sort_keys=True) + "\n"
|
|
|
|
|
|
def render_csv(result):
|
|
output = io.StringIO()
|
|
writer = csv.DictWriter(output, fieldnames=["company_key", "scope", "scope_key", "key", "value", "reason"])
|
|
writer.writeheader()
|
|
writer.writerows({k: entry.get(k, "") for k in writer.fieldnames} for entry in result["entries"])
|
|
return output.getvalue()
|