"""Edit Attachments Lambda handler: strip and re-attach UPCs/ISRCs across contract terms.""" from __future__ import annotations from typing import Any from src import config # noqa: F401 -- initializes Sentry from src.attachment_resolver import resolve_attachments_for_job from src.contract_attachments import remove_upcs_from_originating_account from src.contract_attachments_add import add_to_destination_for_each_term from src.errors import PermanentError, TransientError from src.staged_terms import fetch_staged_terms_for_job from src.transfer_job import get_transfer_job def _validate_event(event: dict[str, Any]) -> None: if not event: raise PermanentError("Event payload is empty.") if "job_id" not in event: raise PermanentError('Missing required field "job_id" in event payload.') if not isinstance(event["job_id"], int): raise PermanentError('Field "job_id" must be an integer.') def handler(event: dict[str, Any], context: object) -> dict[str, Any]: del context try: _validate_event(event) job_id: int = event["job_id"] config.logger.info("Fetching transfer job %d.", job_id) job = get_transfer_job(job_id) originating_vendor_id: int = job["originating_vendor_id"] destination_vendor_id: int = job["destination_vendor_id"] config.logger.info("Step 1: Resolving attachments for job %d.", job_id) attachments = resolve_attachments_for_job(job_id) upcs = attachments["upcs"] isrcs = attachments["isrcs"] config.logger.info( "Step 2: Removing %d UPCs, %d ISRCs from account %d contract terms.", len(upcs), len(isrcs), originating_vendor_id, ) removal_result = remove_upcs_from_originating_account( account_id=originating_vendor_id, upcs=upcs, isrcs=isrcs, ) config.logger.info("Step 3: Fetching staged terms for job %d.", job_id) staged_terms = fetch_staged_terms_for_job(job_id) if not staged_terms: config.logger.info("No staged terms for job %d — nothing to add to destination.", job_id) config.logger.info( "Step 4: Bulk-adding to %d staged term(s) on account %d.", len(staged_terms), destination_vendor_id, ) add_result = add_to_destination_for_each_term( destination_vendor_id=destination_vendor_id, staged_terms=staged_terms, ) # No separate write-back step: transfer-create records # destination_contract_term_id on the staged term server-side, in the # same transaction that creates the contract_term. result: dict[str, Any] = { "status": "complete", "job_id": job_id, "originating_vendor_id": originating_vendor_id, "destination_vendor_id": destination_vendor_id, "attachments": {"upcs": upcs, "isrcs": isrcs}, "attachment_removal": removal_result, "attachment_addition": add_result, } config.logger.info("Edit-attachments step complete for job %d.", job_id) return result except TransientError as exc: config.logger.warning("Transient error: %s", exc) raise except PermanentError as exc: config.logger.error("Permanent error: %s", exc) raise except Exception: config.logger.exception("Unexpected error") raise