ajuste no contas
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -261,6 +261,15 @@ def _preflight_subject(name: str, args: dict[str, Any]) -> dict[str, Any] | None
|
||||
subject = str(args.get("subject") or "").strip()
|
||||
if not msisdn or not subject:
|
||||
return None
|
||||
|
||||
# ``validar_vas_subject`` is the authoritative, side-effect-free resolver
|
||||
# used at the transaction boundary. When it has already canonicalized the
|
||||
# subject and redirected the action to the strategic/bundle workflow, do not
|
||||
# resolve the same subject a second time against invoice evidence. Repeating
|
||||
# resolution with a different catalog can incorrectly turn a previously
|
||||
# ELIGIBLE subject into NEEDS_PARAMETER (the Aya Audiobooks case).
|
||||
if name == "tratar_vas_estrategico" and bool(args.get("_vas_subject_prevalidated")):
|
||||
return None
|
||||
# Contestação pode referenciar qualquer categoria da fatura. Antes do
|
||||
# resolver especializado em serviços/VAS, preserve um item explicitamente
|
||||
# citado no texto que abriu a transação. Isso impede que um plano explícito
|
||||
@@ -507,10 +516,19 @@ def _result_payload(result: Any, *, workflow_name: str) -> dict[str, Any]:
|
||||
|
||||
|
||||
async def _run_workflow(workflow_name: str, args: dict[str, Any]) -> dict[str, Any]:
|
||||
# A workflow execution belongs to one transaction, never to the whole
|
||||
# conversation session. Starting a workflow is therefore always a NEW
|
||||
# execution. The only path allowed to reuse an execution id is
|
||||
# ``retomar_workflow`` below, which calls ``aresume`` explicitly.
|
||||
#
|
||||
# Do not trust/forward a residual ``workflow_execution_id`` from a previous
|
||||
# transaction: WorkflowRuntime.arun() will allocate a fresh UUID.
|
||||
payload_args = dict(args)
|
||||
payload_args.pop("workflow_execution_id", None)
|
||||
result = await get_workflow_runtime().arun(
|
||||
workflow_name,
|
||||
_workflow_payload(workflow_name, args),
|
||||
execution_id=args.get("workflow_execution_id"),
|
||||
_workflow_payload(workflow_name, payload_args),
|
||||
execution_id=None,
|
||||
)
|
||||
payload = _result_payload(result, workflow_name=workflow_name)
|
||||
if workflow_name == "contestacao_tool" and payload.get("status") == "FAILED":
|
||||
@@ -665,10 +683,15 @@ async def _run_cancelamento_com_contestacao(args: dict[str, Any]) -> dict[str, A
|
||||
if social_sec_no:
|
||||
normalized_args["social_sec_no"] = social_sec_no
|
||||
normalized_args["items"] = _normalize_cancel_items(args, holder_msisdn)
|
||||
# Nova transação de cancelamento = nova execução de workflow. Mesmo que
|
||||
# algum envelope antigo carregue ``workflow_execution_id``, ele não pode ser
|
||||
# reutilizado depois que a transação anterior terminou. Resume é tratado
|
||||
# exclusivamente por ``retomar_workflow``/``aresume``.
|
||||
normalized_args.pop("workflow_execution_id", None)
|
||||
cancel_result = await get_workflow_runtime().arun(
|
||||
"cancelamento_vas_avulso",
|
||||
_workflow_payload("cancelamento_vas_avulso", normalized_args),
|
||||
execution_id=args.get("workflow_execution_id"),
|
||||
execution_id=None,
|
||||
)
|
||||
cancel_data = _workflow_result_dict(cancel_result)
|
||||
if cancel_data.get("status") != "COMPLETED":
|
||||
@@ -736,20 +759,48 @@ async def _run_cancelamento_com_contestacao(args: dict[str, Any]) -> dict[str, A
|
||||
total = 0.0
|
||||
for candidate in candidates:
|
||||
subject = str(candidate.get("subject") or (candidate.get("service") or {}).get("name") or "").strip()
|
||||
value = _value_for_subject(normalized_args, subject)
|
||||
requested_value = _value_for_subject(normalized_args, subject)
|
||||
|
||||
# Keep the amount claimed by the user separate from the amount validated
|
||||
# against VAS evidence. The cancellation result already carries the
|
||||
# canonical service object, so a missing user amount must never silently
|
||||
# become R$ 0,00. This also preserves mismatch evidence (e.g. user says
|
||||
# 29,98 while the active VAS is 14,99) for CVAL/observation downstream.
|
||||
service_evidence = candidate.get("service") if isinstance(candidate.get("service"), dict) else {}
|
||||
details = service_evidence.get("details") if isinstance(service_evidence.get("details"), dict) else {}
|
||||
validated_value = (
|
||||
candidate.get("validated_amount")
|
||||
if candidate.get("validated_amount") is not None
|
||||
else candidate.get("validatedAmount")
|
||||
if candidate.get("validatedAmount") is not None
|
||||
else details.get("valor")
|
||||
if details.get("valor") is not None
|
||||
else service_evidence.get("valor")
|
||||
if service_evidence.get("valor") is not None
|
||||
else service_evidence.get("value")
|
||||
if service_evidence.get("value") is not None
|
||||
else service_evidence.get("price")
|
||||
)
|
||||
if validated_value is None:
|
||||
validated_value = requested_value
|
||||
claimed_value = requested_value if requested_value is not None else validated_value
|
||||
|
||||
try:
|
||||
numeric = float(str(value).replace(".", "").replace(",", ".")) if isinstance(value, str) and "," in value else float(value or 0)
|
||||
text = str(validated_value if validated_value is not None else "0")
|
||||
numeric = float(text.replace(".", "").replace(",", ".")) if "," in text else float(text or 0)
|
||||
except Exception:
|
||||
numeric = 0.0
|
||||
total += numeric
|
||||
money = _money_ptbr(value if value is not None else numeric)
|
||||
|
||||
claimed_money = _money_ptbr(claimed_value if claimed_value is not None else 0)
|
||||
validated_money = _money_ptbr(validated_value if validated_value is not None else claimed_value or 0)
|
||||
items.append({
|
||||
"itemName": subject,
|
||||
"item_name": subject,
|
||||
"claimedAmount": money,
|
||||
"validatedAmount": money,
|
||||
"claimed_amount": money,
|
||||
"validated_amount": money,
|
||||
"claimedAmount": claimed_money,
|
||||
"validatedAmount": validated_money,
|
||||
"claimed_amount": claimed_money,
|
||||
"validated_amount": validated_money,
|
||||
})
|
||||
|
||||
contest_payload = {
|
||||
@@ -1334,7 +1385,11 @@ async def _validate_vas_subject(args: dict[str, Any]) -> dict[str, Any]:
|
||||
"resolved_subject": canonical,
|
||||
"entity_resolution": "vas_evidence",
|
||||
"transaction_decision": {
|
||||
"resolved_arguments": {"subject": canonical},
|
||||
"resolved_arguments": {
|
||||
"subject": canonical,
|
||||
"_vas_subject_prevalidated": True,
|
||||
**({"type": resolved_type} if resolved_type else {}),
|
||||
},
|
||||
"target_tool": effective_tool or requested_tool,
|
||||
"action_changed": action_changed,
|
||||
"requires_reconfirmation": action_changed,
|
||||
|
||||
Reference in New Issue
Block a user