# Features — ohlief-app-api

Evidence date: 2026-10-08. Source: `master` `08d6e03` (2020-07-31).

Labels: **Current** (declared in `serverless.yml` at HEAD), **Partial** (declared, but the handler does not finish the work the route name suggests), **Leftover** (starter or abandoned code), **Not deployed** (present in the tree, absent from `serverless.yml`).

No feature list existed in the source repository.

## Current — EDI and catalogue

### Order file import

**Current.** `GET /ftptos3` lists an FTP directory, writes a DynamoDB order row per product match, copies the file to `processed/`, deletes the original, uploads a JSON string to S3, and emails the text.

The schedule is commented out. The IAM authorizer is commented out. The handler returns no status code.

Source: `ftptos3.js`, `serverless.yml`.

### Order list

**Current.** `GET /ohlief` scans `type` = `order`.

Source: `list.js`.

### Order line read

**Current.** `GET /ohlief/{id}` gets (`OrderRow`, `transactionId`). Missing items are HTTP 500 with `Item not found.`

Source: `get.js`.

### Invoice quantity update

**Current.** `PUT /ohlief/{id}` sets `content`, `attachment`, and `invoiceQty` on (`OrderRow`, path id).

Source: `update.js`.

### Mark order number as invoice

**Current.** `GET /invoiceupdate/{id}` scans `orderNumber` and sets `type` to `invoice`. It does not wait for the writes. It does not build the FTP file.

Source: `invoiceupdate.js`. The client calls this path. The client Silicon Ark set (written from the client repo only) left the server effect unknown.

### Invoice list

**Current.** `GET /listinvoices` scans `type` = `invoice`.

Source: `listinvoices.js`.

### Invoice file export and email

**Current.** `GET /invoicetoftp/{id}` builds one pipe file from invoices with `process` `"0"`, uploads it when the text is longer than 10 characters, emails it, and sets `process` to `"1"`. The path id is ignored. The authorizer is commented out.

Source: `invoicetoftp.js`. Added in `f1990b0`, mail and the length check in later commits.

### Order-partition query

**Current.** `GET /listOrders` queries `userId` = `OrderRow` with no `type` filter.

Not referenced by the client document.

Source: `listOrders.js`.

### Product create, read, list, update

**Current.**

| Action | Route | Handler |
|---|---|---|
| Create | `POST /createProduct` | `createProduct.js` |
| Read | `GET /getproduct/{id}` | `getproduct.js` |
| List | `GET /listProducts` | `listProducts.js` |
| Update | `PUT /updateProduct/{id}` | `updateProduct.js` |

All four use partition `ProductRow`. Create sets `type` to `product`. Added in `f23216d`. The shared partition (instead of the caller identity) dates from `a031ae1`.

### Notification email

**Current.** Order import and invoice export send SES mail from `accounts@ohlief.com` to `jaco@overdrive.co.za`, `accounts@ohlief.com`, and `christine@ohlief.com`. HTML links to `http://orders.ohlief.com`. Configuration set name `info`.

Recipient history on these two files: accounts removed (`4e2c14e`, 2020-07-05), accounts added back (`a031ae1`, 2020-07-07), Christine added (`08d6e03`, 2020-07-31). HEAD is the three-address list.

Source: `ftptos3.js` `SendEmail`, `invoicetoftp.js` `sendEmail`.

### CORS on the API

**Current.** Every HTTP event sets `cors: true`. `resources/api-gateway-errors.yml` adds CORS headers on gateway 4XX and 5XX. `libs/response-lib.js` adds them on handler 200 and 500.

## Partial

### Invoice export as a per-order file

**Partial.** The route exists and writes a file. The branch that would start a new file when `orderNumber` changes is commented out. One invocation puts every `process` `"0"` invoice into a single buffer named from the first order number seen.

Source: `invoicetoftp.js`.

### FTP routes as jobs

**Partial.** Comments show `schedule: rate(1 minute)` on `ftptos3` and `invoicetoftp`. Both blocks are commented. At HEAD they run only when something requests the URL.

Source: `serverless.yml`.

### DynamoDB table from this template

**Partial.** `resources/dynamodb-table.yml` creates `${stage}-ohlief` with keys `userId` (hash) and `transactionId` (range), billing mode `PAY_PER_REQUEST`. `provider.environment.tableName` is the literal `order`, and every handler uses `process.env.tableName`. The resource and the handlers do not name the same table.

Source: `serverless.yml`, `resources/dynamodb-table.yml`.

## Leftover — starter, still routed

### Note create

**Leftover**, and still **Current** as a route. `POST /ohlief` inserts `content` and `attachment` under the caller's Cognito identity id. It does not set `type`. `GET /ohlief/{id}` will not load it, because that get uses `OrderRow`.

Source: `create.js`. The client still has a note screen that posts here.

### Delete

**Leftover**, still routed. `DELETE /ohlief/{id}` deletes the caller's identity partition, not `OrderRow` or `ProductRow`.

Source: `delete.js`.

### Stripe billing

**Leftover**, still routed. `POST /billing` charges Stripe in USD for a "Scratch charge" from a `storage` amount. No Oh-lief screen in the client document calls it.

Source: `billing.js`, `libs/billing-lib.js`. Mock: `mocks/billing-event.json` (`storage` 21, source `tok_visa`).

### Hello handler

**Leftover.** `handler.js` exports `hello` ("Go Serverless v1.0!"). It is not in `serverless.yml`. It is the only unit test.

Source: `handler.js`, `tests/handler.test.js`.

## Not deployed

### Earlier invoice export

**Not deployed.** `invoiceToFtp_old.js` is not in `serverless.yml`. It is not wrapped in a handler. It queries `processed` = `"0"` (a different attribute name from `process`). It calls `success()`, whose import is commented out. A commented block names bucket `ohlief-transaction-files`.

Source: `invoiceToFtp_old.js`. Added in `f1990b0`.

### Device CSV loader

**Not deployed.** `filetoDDb.js` reads `devices.csv` into a table named `Device` in region `ap-southeast-2`, with agency `DPTI`. It requires `csv-parse` and `async`, which are not dependencies. Nothing in the Oh-lief handlers imports it.

**Inferred** unrelated script left in the tree. Not an Oh-lief feature at HEAD.

Source: `filetoDDb.js`. Added in `f1990b0`.

### Unused packages

**Not deployed** as behaviour. `unzipper` and `xlsx` are in `package.json` and `package-lock.json`. No `.js` file imports them.

## Removed or replaced

The long starter `README.md` was replaced in `6b25097` by the two-line title. `env.example` (a `SAMPLE_ENV_VAR` placeholder) existed at `74115e6` and was deleted in `7e071a9`. No `.env` is in git. `.gitignore` ignores `.env` and `env.yml`.

Product rows originally used the caller identity as `userId` (`f23216d`). `a031ae1` replaced that with `ProductRow` on create. Get and update already used `ProductRow` by HEAD; this inspection did not find a migration that rewrites older identity-keyed product items.

## Planned

No TODO, roadmap, or ticket file in the repository describes future work. Commented schedules and the commented per-order split are inactive code, not a written plan. Treat them as **Unknown / Requires Confirmation** if someone needs to know whether they were meant to ship.
