# APAC Effective Sales Owner Sync Task

This Lambda computes one Effective Sales Owner decision for every Tenant in the
same Active/new-signup scope used by Dispatcher. It reads Nexus CRM source
objects from BigQuery in one set-based query and atomically publishes a versioned
snapshot to `regional_support`. In the same transaction it updates
`effective_sales_owner_email` and `effective_sales_owner_source` on rows that
already exist in `apac_active_tenants` and `apac_sales_owned_tenants`.

The snapshot stores only the selected normalized Effective Owner email, not all
candidate emails. Dispatcher passes the successful `sync_id` in every monitor
message. Worker reads that exact snapshot row so a new Tenant that did not yet
exist in either result table receives the same value on its first upsert. Logs
and Lambda responses never contain the email value.

The first rule version is:

```text
active Account Owner
  -> active owner of the selected logically-open Opportunity
  -> none
```

Lead Owner is retained as evidence but does not participate in the first
Effective Owner decision. Account approval is also retained as evidence and is
not a global gate. `Closed Lost` and `Closed Won` Opportunities are excluded even
when `IsClosed` is inconsistent.

## GCP authentication

The service account JSON is read at runtime from AWS Secrets Manager. It must not
be copied into this directory, a Lambda environment variable, a SAM parameter,
or Git. The secret value is the exact JSON content of the local key file.

The Lambda role receives only `secretsmanager:GetSecretValue` for the configured
secret ARN. The secret should use the AWS managed Secrets Manager KMS key unless
a customer-managed key and matching `kms:Decrypt` permission are deliberately
configured.

BigQuery is called through its official REST API using `google-auth` request
signing. This keeps the Lambda package smaller than bundling the full BigQuery
Python client while retaining parameterized Standard SQL and paginated results.

This is a transitional authentication mechanism. AWS-to-GCP Workload Identity
Federation remains preferable because it removes the long-lived GCP private key.

Expected GCP permissions after the service account is enabled:

- BigQuery job creation in `gcp-resilience-dev`;
- read access to the five Nexus CRM source tables.

## Invocation

Full sync:

```json
{}
```

Selected Tenant dry run:

```json
{
  "tenant_ids": ["<tenant-id>"],
  "dry_run": true
}
```

Dry runs query BigQuery and return aggregate counts but never write snapshot or
sync-state rows. Responses and logs contain counts and identifiers for the sync
batch only; they do not contain names or email addresses.

The function has no independent schedule. Dispatcher invokes it synchronously
with the exact selected Tenant IDs and only sends monitor messages after the
sync succeeds. The successful `sync_id` is passed through every message.
