feat: add governed assisted form intake
This commit is contained in:
@@ -34,10 +34,13 @@ from govoplan_core.core.institutional import (
|
||||
ServiceLaunchResult,
|
||||
)
|
||||
from govoplan_forms_runtime.backend.db.models import (
|
||||
FormAssistedConfirmation,
|
||||
FormInstanceEvent,
|
||||
FormInstanceIdentity,
|
||||
FormInstanceRevision,
|
||||
FormIntakeSession,
|
||||
)
|
||||
from govoplan_forms_runtime.backend.assisted import assisted_submission_payload_sha256
|
||||
from govoplan_forms_runtime.backend.domain import FormInstance
|
||||
from govoplan_forms_runtime.backend.evidence import FormEvidenceCoordinator
|
||||
|
||||
@@ -329,7 +332,7 @@ class FormRuntimeService:
|
||||
action="submit",
|
||||
instance=current,
|
||||
)
|
||||
return self._revise(
|
||||
submitted = self._revise(
|
||||
session,
|
||||
principal,
|
||||
current=current,
|
||||
@@ -348,6 +351,21 @@ class FormRuntimeService:
|
||||
definition=definition,
|
||||
allowed_current_statuses=("started", "draft"),
|
||||
)
|
||||
assisted_session = (
|
||||
session.query(FormIntakeSession)
|
||||
.filter(
|
||||
FormIntakeSession.tenant_id == submitted.tenant_id,
|
||||
FormIntakeSession.instance_id == submitted.instance_id,
|
||||
FormIntakeSession.mode == "assisted",
|
||||
)
|
||||
.one_or_none()
|
||||
)
|
||||
if assisted_session is not None and assisted_session.status != "submitted":
|
||||
assisted_session.status = "submitted"
|
||||
assisted_session.submitted_at = recorded_at
|
||||
session.add(assisted_session)
|
||||
session.flush()
|
||||
return submitted
|
||||
|
||||
def transition_instance(
|
||||
self,
|
||||
@@ -821,6 +839,46 @@ class FormRuntimeService:
|
||||
)
|
||||
if replay is not None:
|
||||
return replay
|
||||
if operation == "submitted" and _is_assisted_instance(current):
|
||||
assisted_session = (
|
||||
session.query(FormIntakeSession)
|
||||
.filter(
|
||||
FormIntakeSession.tenant_id == current.tenant_id,
|
||||
FormIntakeSession.instance_id == current.instance_id,
|
||||
FormIntakeSession.mode == "assisted",
|
||||
)
|
||||
.one_or_none()
|
||||
)
|
||||
if (
|
||||
assisted_session is None
|
||||
or assisted_session.status != "active"
|
||||
or _aware(assisted_session.expires_at) <= recorded_at
|
||||
):
|
||||
raise FormRuntimeError(
|
||||
"Assisted intake submission requires an active, unexpired session."
|
||||
)
|
||||
expected_confirmation_digest = assisted_submission_payload_sha256(
|
||||
current,
|
||||
values=clean_values,
|
||||
attachment_refs=attachments,
|
||||
signature_refs=signatures,
|
||||
)
|
||||
confirmation = (
|
||||
session.query(FormAssistedConfirmation)
|
||||
.filter(
|
||||
FormAssistedConfirmation.tenant_id == current.tenant_id,
|
||||
FormAssistedConfirmation.instance_id == current.instance_id,
|
||||
FormAssistedConfirmation.instance_revision == current.revision,
|
||||
FormAssistedConfirmation.payload_sha256
|
||||
== expected_confirmation_digest,
|
||||
)
|
||||
.order_by(FormAssistedConfirmation.confirmed_at.desc())
|
||||
.first()
|
||||
)
|
||||
if confirmation is None:
|
||||
raise FormRuntimeError(
|
||||
"Assisted intake submission requires a current read-back confirmation for the exact values and evidence."
|
||||
)
|
||||
if allowed_current_statuses is not None and current.status not in set(
|
||||
allowed_current_statuses
|
||||
):
|
||||
@@ -1123,6 +1181,11 @@ def validate_form_values(
|
||||
return tuple(diagnostics)
|
||||
|
||||
|
||||
def _is_assisted_instance(instance: FormInstance) -> bool:
|
||||
intake = instance.metadata.get("intake")
|
||||
return isinstance(intake, Mapping) and intake.get("mode") == "assisted"
|
||||
|
||||
|
||||
def normalize_form_values(
|
||||
definition: FormDefinition,
|
||||
values: Mapping[str, object],
|
||||
|
||||
Reference in New Issue
Block a user