feat: add daily manual price review workflow

This commit is contained in:
Wyndham ARR
2026-08-06 22:40:18 +08:00
parent aae3d8e1db
commit ca3e8e18fa
77 changed files with 7750 additions and 602 deletions

View File

@@ -37,9 +37,36 @@ class LocalDailyProcessor:
if not (self.policy.skill_root / "scripts" / "process_daily.py").is_file():
raise ValueError("ARR daily processor is unavailable")
def run(self, source_xml: Path, output_dir: Path) -> LocalProcessingOutput:
def run(
self,
source_xml: Path,
output_dir: Path,
*,
manual_overrides: Optional[Path] = None,
review_job_id: Optional[str] = None,
review_case_id: Optional[str] = None,
manual_override_sha256: Optional[str] = None,
) -> LocalProcessingOutput:
if not source_xml.is_file():
raise IngestionError("SOURCE_NOT_FOUND", "registered source XML is unavailable")
manual_arguments = (
manual_overrides,
review_job_id,
review_case_id,
manual_override_sha256,
)
if any(value is not None for value in manual_arguments):
if (
manual_overrides is None
or not manual_overrides.is_file()
or not review_job_id
or not review_case_id
or not manual_override_sha256
):
raise IngestionError(
"MANUAL_OVERRIDE_INVALID",
"frozen manual-price overrides are unavailable",
)
output_dir.mkdir(parents=True, exist_ok=False, mode=0o700)
result_path = output_dir / "result.json"
structured_path = output_dir / "structured-result.json"
@@ -55,6 +82,19 @@ class LocalDailyProcessor:
"--structured-result-json",
str(structured_path.resolve()),
]
if manual_overrides is not None:
command.extend(
(
"--manual-override-json",
str(manual_overrides.resolve()),
"--review-job-id",
str(review_job_id),
"--review-case-id",
str(review_case_id),
"--manual-override-sha256",
str(manual_override_sha256),
)
)
try:
completed = subprocess.run(
command,
@@ -68,20 +108,22 @@ class LocalDailyProcessor:
raise IngestionError(
"PROCESSOR_TIMEOUT",
"deterministic XML processing exceeded its time limit",
retryable=True,
) from None
except OSError:
raise IngestionError(
"PROCESSOR_UNAVAILABLE",
"deterministic XML processing could not start",
retryable=True,
) from None
result = strict_json_file(result_path, "processor result")
structured = strict_json_file(structured_path, "structured processor result")
status = result.get("status")
if (
status not in {"success", "failed"}
status not in {"success", "failed", "review_required"}
or structured.get("status") != status
or (completed.returncode == 0) != (status == "success")
or (completed.returncode == 0) != (status in {"success", "review_required"})
):
raise IngestionError(
"PROCESSOR_RESULT_INVALID",
@@ -121,6 +163,15 @@ class LocalDailyProcessor:
raise IngestionError(
"PROCESSOR_RESULT_INVALID", "successful processor output is incomplete"
)
elif status == "review_required":
if (
business_date is None
or outputs.get("daily_report") is not None
or outputs.get("exception_report") is not None
):
raise IngestionError(
"PROCESSOR_RESULT_INVALID", "review processor output is inconsistent"
)
else:
artifacts["exception_report"] = self._output_path(
output_dir, outputs.get("exception_report"), ".xlsx"