mirror of
http://100.103.83.12:3003/fegger/odoo-at-payroll.git
synced 2026-09-17 16:56:42 +00:00
[ADD] l10n_at_payroll_agent: PV-agent review bridge for payout plausibility
PV-agent bridge (D25/M4.1): Odoo stays the system of record; the standalone, KB-bound knowledge service (pv-agent) reviews plausibility and returns evidence-backed answers (mode=review, agent API v1). No back path: the agent never calls Odoo and receives no personal data — only the minimized, schema-bound review context. - context builder: projects a computed draft payslip with an ATP_PRAMIE input (planned one-off payout) onto the review context (facts + computation): gross wage (hr.version.wage), applying collective agreement (version.kv_id) and the draft's DG/LST lines (SVDG_*/LST_*/FLAFDB/KOMMST/DZ/BVG/WIEN_DAG) - client (stdlib urllib, no extra dependency): POST /v1/ask with X-Request-ID, bearer key from company settings (pv_agent_url / pv_agent_api_key, group_hr_payroll_user); neutral UserErrors, technical details only in the server log - review wizard on the draft payslip: agent answer (evidence-backed), structured verdict (plausible / implausible / not checkable) and checks in the dialog - security: wizard bound to hr_payroll.group_hr_payroll_user; agent service key stored on res.company, never logged; 5 tests
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
# Part of the odoo-at-payroll project. License: LGPL-3.
|
||||
from . import pv_agent_review
|
||||
@@ -0,0 +1,74 @@
|
||||
# Part of the odoo-at-payroll project. License: LGPL-3.
|
||||
"""PV-Agent-Review-Wizard (D25/M4.1 Pilot): Plausibilitätsprüfung einer
|
||||
geplanten einmaligen Auszahlung.
|
||||
|
||||
Workflow: Draft-Lohnzettel mit ATP_PRAMIE-Eingabe rechnen → Wizard öffnen →
|
||||
„Prüfung starten“ → Odoo baut den minimierten Kontext und ruft den Agenten
|
||||
(mode=review) → Verdict + belegte Antwort werden im Dialog angezeigt. Odoo
|
||||
bleibt autoritativ für Zahlen; der Agent korrigiert nichts stillschweigend.
|
||||
"""
|
||||
import uuid
|
||||
|
||||
from odoo import _, fields, models
|
||||
|
||||
REVIEW_QUESTION_DEFAULT = (
|
||||
"Prüfe die geplante Auszahlung gegen die Regeln der Wissensbasis."
|
||||
)
|
||||
|
||||
|
||||
class PvAgentReviewWizard(models.TransientModel):
|
||||
_name = "pv.agent.review.wizard"
|
||||
_description = "PV-Agent: Plausibilitätsprüfung"
|
||||
_order = "id desc"
|
||||
|
||||
payslip_id = fields.Many2one(
|
||||
"hr.payslip", string="Lohnzettel (Draft)", required=True,
|
||||
domain="[('state', '=', 'draft')]",
|
||||
help="Gerechneter Draft-Lohnzettel mit einer ATP_PRAMIE-Eingabe "
|
||||
"(geplante Auszahlung).")
|
||||
question = fields.Text(
|
||||
string="Frage an den Agenten", required=True,
|
||||
default=REVIEW_QUESTION_DEFAULT)
|
||||
answer = fields.Text(string="Agent-Antwort (belegt)", readonly=True)
|
||||
verdict = fields.Selection(
|
||||
[("plausible", "Plausibel"),
|
||||
("implausible", "Nicht plausibel"),
|
||||
("not_checkable", "Nicht prüfbar")],
|
||||
string="Verdict", readonly=True)
|
||||
checks = fields.Text(string="Prüfpunkte", readonly=True)
|
||||
request_id = fields.Char(string="Request-ID", readonly=True)
|
||||
|
||||
def action_l10n_at_pv_review(self):
|
||||
self.ensure_one()
|
||||
context = self.env["pv.agent.context.builder"].l10n_at_pv_build_payout_context(
|
||||
self.payslip_id)
|
||||
request_id = "odoo-" + uuid.uuid4().hex[:12]
|
||||
body = self.env["pv.agent.client"].l10n_at_pv_ask_review(
|
||||
self.question, context, request_id)
|
||||
plausibility = body.get("plausibility") or {}
|
||||
self.write({
|
||||
"answer": body.get("answer", ""),
|
||||
"verdict": plausibility.get("verdict") or "not_checkable",
|
||||
"checks": self._l10n_at_pv_render_checks(plausibility),
|
||||
"request_id": body.get("request_id") or request_id,
|
||||
})
|
||||
return {
|
||||
"type": "ir.actions.act_window",
|
||||
"res_model": self._name,
|
||||
"res_id": self.id,
|
||||
"view_mode": "form",
|
||||
"target": "new",
|
||||
"name": _("PV-Agent Prüfung"),
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def _l10n_at_pv_render_checks(plausibility):
|
||||
lines = []
|
||||
for check in plausibility.get("checks", []):
|
||||
label = {"ok": "OK", "warn": "WARN ⚠", "open": "OFFEN"}.get(
|
||||
check.get("status"), check.get("status", ""))
|
||||
ids = ", ".join(check.get("source_ids", []))
|
||||
lines.append(
|
||||
f"[{label}] {check.get('aspect', '')} — {check.get('detail', '')}"
|
||||
+ (f" ({ids})" if ids else ""))
|
||||
return "\n".join(lines)
|
||||
Reference in New Issue
Block a user