138 lines
9.1 KiB
Markdown
138 lines
9.1 KiB
Markdown
# Form 16 – Full Process: Credit & Debit Notes (Incoming & Outgoing)
|
||
|
||
This document describes the end-to-end flow for Form 16 (Form 16A TDS Credit): 26AS reconciliation, credit/debit note creation, WFM/SAP incoming and outgoing file handling, and how users view SAP responses.
|
||
|
||
---
|
||
|
||
## 1. High-Level Flow
|
||
|
||
- **26AS**: TDS entries are uploaded (RE) and aggregated by TAN + Financial Year + Quarter (Section 194Q, Booking F/O only).
|
||
- **Form 16A submission**: Dealer submits Form 16A (PDF). OCR extracts TAN, FY, Quarter, TDS amount, certificate number, etc.
|
||
- **Credit note**: When a submission is validated, the system matches it against the latest 26AS aggregate for that TAN/FY/Quarter. On match, a **credit note** is created, ledger updated, quarter marked **SETTLED**, and a CSV is pushed to WFM **INCOMING** for SAP (credit note generation).
|
||
- **Debit note**: When a new 26AS upload changes the quarter total and that quarter was already **SETTLED**, the system creates a **debit note** (reversing the earlier credit), updates ledger, sets quarter to **DEBIT_ISSUED_PENDING_FORM16**, and pushes a CSV to WFM **INCOMING** for SAP (debit note generation).
|
||
- **SAP responses**: SAP processes the INCOMING files and drops response CSVs in WFM **OUTGOING**. The backend ingests these (scheduler every 5 min or on-demand Pull), stores them in DB, and users can **View** (and for credit, **Download**) the SAP response.
|
||
|
||
---
|
||
|
||
## 2. Paths (WFM Folder Structure)
|
||
|
||
All paths are relative to **WFM_BASE_PATH** (default `C:\WFM`). Can be overridden via `.env` (e.g. `WFM_BASE_PATH=D:\Form-16 Main`). The job also tries `<process.cwd()>\WFM-QRE\...` if the default path does not exist.
|
||
|
||
| Direction | Type | Default path (under WFM_BASE_PATH) |
|
||
|------------|--------|-------------------------------------|
|
||
| **INCOMING** | Credit | `WFM-QRE\INCOMING\WFM_MAIN\FORM16_CRDT` |
|
||
| **INCOMING** | Debit | `WFM-QRE\INCOMING\WFM_MAIN\FORM16_DEBT` |
|
||
| **OUTGOING** | Credit | `WFM-QRE\OUTGOING\WFM_SAP_MAIN\FORM16_CRDT` |
|
||
| **OUTGOING** | Debit | `WFM-QRE\OUTGOING\WFM_SAP_MAIN\FORM16_DBT` |
|
||
|
||
- **INCOMING** = files we **push** to WFM (for SAP to pick up and process).
|
||
- **OUTGOING** = files **SAP drops** (responses); we read and store them.
|
||
|
||
---
|
||
|
||
## 3. Credit Note Flow
|
||
|
||
### 3.1 When is a credit note created?
|
||
|
||
- On **Form 16A submission validation** (after OCR and 26AS check).
|
||
- `run26asMatchAndCreditNote(submission)` is called (e.g. from submission validation flow).
|
||
- Conditions: TAN + FY + Quarter match latest 26AS aggregate (Section 194Q, F/O), amount within tolerance, quarter not already settled with same amount.
|
||
- On success: create `Form16CreditNote`, ledger entry (CREDIT), set quarter status **SETTLED**, then push **INCOMING** CSV.
|
||
|
||
### 3.2 Credit note – INCOMING (we push to WFM/SAP)
|
||
|
||
- **Path**: `WFM-QRE\INCOMING\WFM_MAIN\FORM16_CRDT`
|
||
- **When**: Immediately after credit note is created.
|
||
- **File name**: `{creditNoteNumber}.csv` (e.g. `CN00628226Q20001.csv`).
|
||
- **Content** (pipe `|` separated):
|
||
`TRNS_UNIQ_NO` (e.g. `F16-CN-{submissionId}-{creditNoteId}-{timestamp}`),
|
||
`TDS_TRNS_ID` (= credit note number),
|
||
`DEALER_CODE`, `TDS_TRNS_DOC_TYP`, `DLR_TAN_NO`, `FIN_YEAR & QUARTER`, `DOC_DATE`, `TDS_AMT`.
|
||
- **TDS_TRNS_ID** = credit note number (format: `CN` + 6-digit dealer code + 2-digit FY + quarter + 4-digit sequence, e.g. `CN00628226Q20001`).
|
||
- A copy is also written to the Form 16 credit archive path (INCOMING archive).
|
||
|
||
### 3.3 Credit note – OUTGOING (SAP response)
|
||
|
||
- **Path**: `WFM-QRE\OUTGOING\WFM_SAP_MAIN\FORM16_CRDT`
|
||
- **Who writes**: SAP (response CSVs placed here by SAP/WFM).
|
||
- **Who reads**: Backend **Form 16 SAP response job** (scheduler every 5 min + on **Pull** button).
|
||
- **What we do**: Read each CSV, parse first “real” data row, match to credit note by `TRNS_UNIQ_NO` or `creditNoteNumber` (TDS_TRNS_ID in response), upload file to storage, insert/update row in **`form16_sap_responses`** with `type = 'credit'`, `credit_note_id`, `storage_url`, etc.
|
||
- **User**: Credit notes list shows **View** when a response exists; **View** opens popup with SAP fields and **Download CSV**; **Pull** triggers ingestion and list refresh.
|
||
|
||
---
|
||
|
||
## 4. Debit Note Flow
|
||
|
||
### 4.1 When is a debit note created?
|
||
|
||
- On **26AS upload** that changes the quarter aggregate for a quarter that is already **SETTLED** (had a credit note).
|
||
- `process26asUploadAggregation(uploadLogId)` is called after 26AS file upload (controller calls it when records are imported).
|
||
- For each (TAN, FY, Quarter) where new 26AS total ≠ previous snapshot and status is SETTLED: create `Form16DebitNote` (linked to the last credit note for that quarter), ledger entry (DEBIT), set quarter status **DEBIT_ISSUED_PENDING_FORM16**, then push **INCOMING** CSV.
|
||
|
||
### 4.2 Debit note – INCOMING (we push to WFM/SAP)
|
||
|
||
- **Path**: `WFM-QRE\INCOMING\WFM_MAIN\FORM16_DEBT`
|
||
- **When**: Immediately after debit note is created in `process26asUploadAggregation`.
|
||
- **File name**: `{debitNoteNumber}.csv` (e.g. `DN00628226Q20001.csv`).
|
||
- **Content** (pipe `|` separated):
|
||
`TRNS_UNIQ_NO` (e.g. `F16-DN-{creditNoteId}-{debitId}-{timestamp}`),
|
||
**`TDS_TRNS_ID`** = **credit note number** (not debit note number),
|
||
`DEALER_CODE`, `TDS_TRNS_DOC_TYP`, `Org.Document Number` (= debit id), `DLR_TAN_NO`, `FIN_YEAR & QUARTER`, `DOC_DATE`, `TDS_AMT`.
|
||
- **TDS_TRNS_ID** in debit incoming = credit note number (same format as credit, e.g. `CN00628226Q20001`). Debit note number = same string with `CN` replaced by `DN` (e.g. `DN00628226Q20001`).
|
||
- A copy is also written to the Form 16 debit archive path.
|
||
|
||
### 4.3 Debit note – OUTGOING (SAP response)
|
||
|
||
- **Path**: `WFM-QRE\OUTGOING\WFM_SAP_MAIN\FORM16_DBT`
|
||
- **Who writes**: SAP (response CSVs placed here).
|
||
- **Who reads**: Same **Form 16 SAP response job** (every 5 min + **Pull** on Debit Notes page).
|
||
- **What we do**: Read each CSV, parse, match to debit note by (in order):
|
||
(1) `TRNS_UNIQ_NO` → `form_16_debit_notes.trns_uniq_no`,
|
||
(2) `CLAIM_NUMBER` → `form_16_debit_notes.debit_note_number`,
|
||
(3) **filename (without .csv)** → `form_16_debit_notes.debit_note_number`.
|
||
Upload file to storage, insert/update row in **`form16_debit_note_sap_responses`** (separate table from credit) with `debit_note_id`, `storage_url`, etc.
|
||
- **User**: Debit notes list shows **View** when a response exists; **View** opens popup (no download); **Pull** triggers ingestion and list refresh.
|
||
|
||
---
|
||
|
||
## 5. Database Tables for SAP Responses
|
||
|
||
| Table | Purpose |
|
||
|-----------------------------------|--------|
|
||
| **form16_sap_responses** | Credit note SAP responses only. Columns: `type` ('credit'), `file_name`, `credit_note_id`, `claim_number`, `sap_document_number`, `msg_typ`, `message`, `raw_row`, `storage_url`, timestamps. |
|
||
| **form16_debit_note_sap_responses**| Debit note SAP responses only. Columns: `file_name`, `debit_note_id`, `claim_number`, `sap_document_number`, `msg_typ`, `message`, `raw_row`, `storage_url`, timestamps. No `type` or `credit_note_id`. |
|
||
|
||
Credit and debit SAP responses are **not** mixed; each has its own table.
|
||
|
||
---
|
||
|
||
## 6. Scheduler and Pull
|
||
|
||
- **Scheduler**: `startForm16SapResponseJob()` runs **every 5 minutes** (cron `*/5 * * * *`). It calls `runForm16SapResponseIngestionOnce()`, which:
|
||
- Scans **OUTGOING** credit dir (`FORM16_CRDT`) and **OUTGOING** debit dir (`FORM16_DBT`) for `.csv` files.
|
||
- For each file: parse, match to credit or debit note, upload to storage, write to `form16_sap_responses` (credit) or `form16_debit_note_sap_responses` (debit).
|
||
- **Pull button** (Credit Notes page and Debit Notes page): `POST /api/v1/form16/sap/pull` triggers the **same** `runForm16SapResponseIngestionOnce()`, then the frontend refetches the list. So Pull = one-off run of the same ingestion logic; no separate “pull-only” path.
|
||
- **View** appears when the corresponding table has a row for that note with a non-null `storage_url` (and for list, we check by `credit_note_id` / `debit_note_id`).
|
||
|
||
---
|
||
|
||
## 7. End-to-End Summary
|
||
|
||
| Step | Credit note | Debit note |
|
||
|------|-------------|------------|
|
||
| **Trigger** | Form 16A submission validated, 26AS match | 26AS upload changes total for a SETTLED quarter |
|
||
| **INCOMING (we push)** | CSV to `INCOMING\WFM_MAIN\FORM16_CRDT` | CSV to `INCOMING\WFM_MAIN\FORM16_DEBT` |
|
||
| **TDS_TRNS_ID in CSV** | Credit note number | Credit note number |
|
||
| **File name** | `{creditNoteNumber}.csv` | `{debitNoteNumber}.csv` |
|
||
| **OUTGOING (SAP writes)** | SAP drops response in `OUTGOING\WFM_SAP_MAIN\FORM16_CRDT` | SAP drops response in `OUTGOING\WFM_SAP_MAIN\FORM16_DBT` |
|
||
| **We read & store** | Job reads CSV, matches, stores in `form16_sap_responses` | Job reads CSV, matches, stores in `form16_debit_note_sap_responses` |
|
||
| **User action** | View / Download CSV (Pull to refresh) | View only (Pull to refresh) |
|
||
|
||
---
|
||
|
||
## 8. Env / Config (relevant)
|
||
|
||
- **WFM_BASE_PATH**: Base folder that contains `WFM-QRE` (e.g. `C:\WFM` or `D:\Form-16 Main`). If not set and default path missing, job tries `process.cwd()\WFM-QRE\...`.
|
||
- **WFM_FORM16_CREDIT_INCOMING_PATH**, **WFM_FORM16_DEBIT_INCOMING_PATH**: Override INCOMING paths.
|
||
- **WFM_FORM16_CREDIT_OUTGOING_PATH**, **WFM_FORM16_DEBIT_OUTGOING_PATH**: Override OUTGOING paths.
|