Feature #151 » README.md
type: redmine-readme
project: Canofy Process Standardization
subject: NSDL SEZ BOE/DTA Lambda pipeline: Phase A (fetch, QC, stage) and Phase B (deliver to Frappe)
story_key: nsdl-lambda-core-pipeline
generated_at: 2026-10-15T16:16:51+05:30
story_type: feature
attachments:
- file: nsdl-lambda-core-pipeline_20261015-1616__NSDL_SEZ_BOE_Automation_Blueprint.md.attach
description: Architecture and design rationale this build followed (Blueprint) - file: nsdl-lambda-core-pipeline_20261015-1616__NSDL_SEZ_BOE_Automation_BuildSpec_ClaudeCode.md.attach
description: Phase-by-phase build specification (Phases 0-11)
NSDL SEZ BOE/DTA Lambda pipeline: Phase A (fetch, QC, stage) and Phase B (deliver to Frappe)
An AWS Lambda pipeline that downloads SEZ trade data from the NSDL portal, validates it per client, and creates the matching BOE / DTA documents with PDF attachments in each client's Frappe/Canofy site.
1. Summary
The existing fw_verify script did this for one client from a developer machine. This story turns it into a multi-client, scheduled AWS service. Phase A logs in to NSDL, exports the Bill of Entry (BE) and DTA Sale reports, routes each row to a client by IEC code, runs six QC checks per client and stages the records in DynamoDB. Phase B delivers one client's staged records to that client's Frappe site and attaches the NSDL print PDF. It lives in the repo canofy-NSDL-data-extraction (app/phase_a_fetch, app/phase_b_deliver, shared, template.yaml) and is deployed to the TEST stack nsdl-sez-boe-automation-test in ap-south-1. PROD is not deployed.
2. Requirement
| # | Requirement | Source | Status |
|---|---|---|---|
| R1 | Port the proven fw_verify logic instead of rewriting it |
user (first message) | Done |
| R2 | Several clients from one NSDL export, routed by IEC; an IEC under two clients is a hard error | Blueprint | Done |
| R3 | Rolling date window computed from the run date, not from config | Build Spec | Done (later changed, see Decisions) |
| R4 | Per-client QC (six checks) and gating before anything is delivered | Build Spec | Done |
| R5 | Phase A stages into DynamoDB and never talks to Frappe | Build Spec | Done |
| R6 | Phase B pushes with each client's own scoped Frappe credentials | Build Spec | Done |
| R7 | Credentials only in AWS Secrets Manager; non-secret constants as Lambda env vars | user | Done |
| R8 | Monitoring: alert topic, dead-letter queue, alarms, stuck-record watchdog | Build Spec Phase 8 | Done (one alert not built, see section 8) |
| R9 | Never invent values the spec marks as human-confirmed (retention days, cron time, credentials) | user | Done |
Requirement that changed: R3 started as a 7-day window ending on the run date; the window later ended the day before the run date (NSDL data lags a day). That change is recorded in nsdl-aws-deploy-lambda-hardening.
3. Decisions
| Decision | Options considered | Why this one | Date |
|---|---|---|---|
| Config strategy | Config file, Secrets Manager for everything, env vars | Credentials in Secrets Manager; tuning constants as env vars in template.yaml
|
2026-09-12 |
| Shared code packaging | Lambda Layer, Docker context at repo root |
DockerContext: . with the Dockerfile in each app folder; no layer to publish |
2026-09-13 |
| Required deploy parameters | Invent defaults, require a value |
S3RetentionDays and the cron expression are required with no default |
2026-09-13 |
| Secret name | Unsuffixed (spec), suffixed |
nsdl-boe-automation-secrets-<Environment> so TEST and PROD can coexist |
2026-09-13 |
| Phase B runtime | Spec's 300 s zip function, container image with Playwright | Image with a 900 s timeout, because Phase B performs a real NSDL login and print-to-PDF | 2026-09-13 |
| Phase B query scope | STAGED only (spec), STAGED and PUSHED | A record that is pushed but fails its PDF attach must be retried | 2026-09-13 |
| Phase B trigger | Synchronous API Gateway call, asynchronous | API Gateway REST caps synchronous calls at 29 s; asynchronous returns 202 and Phase B updates the audit record itself | 2026-09-13 |
| Testing | Local only, real AWS | Test against deployed Lambdas and real AWS resources; no dry-run mode exists | 2026-09-13 |
4. Logic & Workflow
Phase A (daily, EventBridge): run_date (default today UTC) -> window -> NSDL login -> export BE and DTA -> validate files -> store in S3 + run control -> parse -> route rows by IEC -> six QC checks per client -> stage as STAGED
- The event may carry
run_dateandforce_refresh; a same-day re-run reuses the stored export unlessforce_refreshis set. - Both exports are checked: file exists, is readable as NSDL's tab-delimited text, and has the columns the parser needs.
- Each row's IEC is normalised (stripped, zero-padded to 10 digits if numeric) and mapped to a client through the reverse map built from the secret. Unmapped IECs are logged and skipped.
- Records already in a terminal status in DynamoDB are excluded, then six QC checks run per client and pipeline: header count, invoice count, item count, shipment count, sum checks (quantity, duty, assessable value) and IEC consistency within a request.
- A passing client's records are staged as
STAGED; strict mode withholds a client whose QC fails.
Phase B (per client, started by an API call or an invoke with client_name): read secret -> query STAGED/PUSHED -> create in Frappe (skip if exists) -> NSDL login -> fetch print PDF -> attach -> mark ATTACHED
- Look up the client's Frappe URL and API credentials in the secret.
- For each record, check Frappe by
request_idfirst and create it only if missing; mark the recordPUSHED. - Log in to NSDL once, fetch each pending record's print PDF, attach it, mark
ATTACHED(a TTL is set only at this terminal status). - A failed push or attach leaves the status unchanged, so the next run retries it; there is no permanent-failure state.
- If a
sync_run_namewas given, update that audit document with the result.
5. Files & Components Changed
| File / resource | Type | Change | Purpose |
|---|---|---|---|
app/phase_a_fetch/* |
Lambda code | New | Phase A: download, parse, route, QC, stage |
app/phase_b_deliver/* |
Lambda code | New | Phase B: deliver and attach |
app/watchdog/lambda_handler.py |
Lambda code | New | Hourly scan for records stuck in STAGED over 24 h |
shared/ (iec, secrets_loader, json_utils, logger, frappe_client) |
Shared code | New | IEC routing, secret loading, Frappe REST client |
template.yaml |
SAM template | New | S3, three DynamoDB tables, three functions, API Gateway, SNS, SQS, alarms, secret |
events/*.json |
Test events | New | Payloads for manual invokes |
README.md |
Docs | New | Secret shape, deploy parameters, testing, monitoring |
6. Configuration & Deployment
-
Secret
nsdl-boe-automation-secrets-<ENV>:NSDL_ACCOUNTS.<account>(username, password, entity id) andCLIENTS.<client>(IEC codes, Frappe URL, API key and secret). Values are entered by a human, never committed. -
Env vars (names only):
RAW_FILES_BUCKET,RUN_CONTROL_TABLE,STAGED_RECORDS_TABLE,RUN_GATE_TABLE,NSDL_BOE_SECRET_NAME,NSDL_BASE_URL,NSDL_MODULE_BE,NSDL_MODULE_DTA,NSDL_ACCOUNT_KEY,STEP2_QC_STRICT,STAGED_TTL_DAYS, PDF pause settings. -
Parameters with no default:
S3RetentionDays,DailyScheduleCronExpression. -
Deploy:
sam buildthensam deploy(container images). After deploy, populate the secret and subscribe a real endpoint to the alerts topic.
7. Testing
| Test | Data / Site | Expected | Actual | Status |
|---|---|---|---|---|
| Phase A real run | Live NSDL, run date 2026-09-13 | Records staged, QC pass | 64 eap records staged: BE 31 requests / 161 invoices / 227 items; DTA 33 / 140 / 210; all QC checks passed |
Pass |
| Phase B real run |
eap TEST Frappe site |
All staged records delivered | 64 of 64 delivered; 31 BE PDFs attached | Pass |
| Idempotency stress test | All 64 reset to STAGED, Frappe emptied | No duplicates after a timeout and retry | One Frappe document per request after the first run timed out at 900 s and was retried | Pass |
8. Known Limitations & Open Items
- No dry-run mode: any invoke of Phase A logs in to the real NSDL portal, and any Phase B invoke writes to the Frappe site in the secret.
- Blueprint's per-client "Phase B gate fails" alert has no infrastructure; the alerts topic has no subscribers until someone subscribes.
- DynamoDB status is treated as the truth: a Frappe document deleted by hand is not re-created unless its record is reset.
- A full-size batch can exceed the 900 s limit; the batch cap is in
nsdl-pdf-attachment-be-dta. - Not confirmed: PROD deployment and the production Frappe deployment of the audit doctype.
9. Status
| Item | Value |
|---|---|
| Branch |
main (initial commit) |
| Commits |
2e9ba74 Initial commit: NSDL SEZ BOE/DTA automation Lambda pipeline (2026-09-15) |
| PR | None (direct initial push) |
| Version |
VERSION file added later (see nsdl-github-actions-deployment) |
| Deployed to | TEST stack nsdl-sez-boe-automation-test, ap-south-1
|
10. References
Related stories: nsdl-aws-deploy-lambda-hardening, nsdl-pdf-attachment-be-dta, nsdl-frappe-trigger-sync-run, nsdl-architecture-documentation, nsdl-github-actions-deployment. Attachments: Blueprint and Build Spec (see front matter).