
---

# 📘 SAP IT Declaration JSON Preparation

**Source**: `TDeclaration`, `TDeclarationDetail`
**Target**: SAP Infotypes **581–586** (JSON payloads or direct API calls)
**FY Example**: 2026–27 → `"FiscalYear": "2026"`

---

## 1) Objectives

1. Transform declaration data into **SAP-compliant JSON** per infotype:

   * **581** Rent (HRA)
   * **582** Family/Education counts
   * **583** Other Income
   * **584** House Property
   * **585** Deductions
   * **586** Contributions
2. Provide:

   * **Downloadable JSON** OR
   * **Direct SAP API invocation**
3. Support filters:

   * `OrgId` (tenant/company)
   * `EmployeeId` (single/multiple)
   * `FinancialPeriod` (FY)
   * `Infotypes` (all or selected)

---

## 2) Source Data Model (Assumed)

### 2.1 `TDeclaration` (Header)

| Column        | Notes                      |
| ------------- | -------------------------- |
| DeclarationId | PK                         |
| OrgId         | Tenant                     |
| EmployeeCode  | Source employee identifier |
| FinancialYear | e.g., `2026`               |
| Regime        | `OLD` / `NEW`              |
| Status        | Draft/Submitted/Approved   |
| SubmittedOn   | date                       |
| ApprovedOn    | date                       |

### 2.2 `TDeclarationDetail` (Line Items)

| Column          | Notes                                             |
| --------------- | ------------------------------------------------- |
| DeclarationId   | FK                                                |
| ItemCode        | e.g., `80C5`, `IHP`, `LFHP`, `CHA`                |
| ItemDescription | Raw description                                   |
| Category        | Rent / Section80 / OtherIncome / Education / etc. |
| SapInfoType     | 581–586 or null                                   |
| SapDescription  | Canonical SAP description (authoritative text)    |
| AmountDeclared  | numeric                                           |
| AdditionalJson  | optional structured data (landlord, lender, etc.) |

> **Rule**: `SapInfoType` and `SapDescription` are the **authoritative mapping** to SAP. Rows with `SapDescription = NULL` are **excluded** unless explicitly mapped.

---

## 3) Target Payload (Common Envelope)

```json
{
  "Date": "DD.MM.YYYY",
  "Remarks": "Provisional declaration for FY YYYY",
  "Declarations": [ ... ]
}
```

### Global Field Rules

* `Date`: request date (dd.MM.yyyy)
* `FiscalYear`: `"2026"` (string)
* **EmployeeID format**: **5-digit zero-padded** (e.g., `"00013"`)
* Numeric fields: **numbers only** (no strings)
* No duplicate records per **defined grouping key**

---

## 4) Infotype Specifications

---

## 4.1 **581 – Rent Paid (HRA)**

### Input

* Category: `Rent`
* Amount: annual rent

### Aggregation

* **Per Employee + Landlord**
* If landlord data absent → **single aggregated record per employee**

### Transform

* **Monthly** = `Annual / 12`
* Exclude `Amount = 0`

### Output

```json
{
  "EmployeeID": "00013",
  "FiscalYear": "2026",
  "Particulars": "Rent paid",
  "From": "01-Apr-2026",
  "To": "31-Dec-9999",
  "Type": "Metro",
  "Declared": 15000.0,
  "Approved": 15000.0,
  "LandLordName": "",
  "LandLordPAN": "",
  ...
}
```

### Validations

* If rent > threshold → PAN should exist (warning)
* Monthly rounding: 2 decimals

---

## 4.2 **582 – Family / Education**

### Input

* Codes: `CHA`, `CHE`

### Aggregation

* **Per Employee**

```text
HostelChildren = SUM(CHA)
EducationChildren = SUM(CHE)
```

### Output

```json
{
  "EmployeeID": "00013",
  "FiscalYear": "2026",
  "NoOfChildrenHostel": 2,
  "NoOfChildrenEducation": 1
}
```

### Validations

* Must be integers ≥ 0
* Max limits (policy-defined)

---

## 4.3 **583 – Other Income**

### Input Codes

* `IHP` → IncomeFromHouseProperty
* `IOI` → OtherIncome
* **Exclude**: `LFHP` (belongs to 584)

### Aggregation

* **Per Employee (single record)**

### Output

```json
{
  "EmployeeID": "00013",
  "FiscalYear": "2026",
  "IncomeFromHouseProperty": 550000.0,
  "OtherIncome": 250000.0
}
```

### Validations

* Exactly **one record per employee**
* Missing categories → default `0.0`

---

## 4.4 **584 – House Property**

### Input

* `LFHP` (Loss from House Property)

### Derived Logic (if no detailed data)

* Assume **Self Occupied**
* `InterestOnBorrowal = LFHP`
* `AnnualRentalIncome = 0`

### Output

```json
{
  "EmployeeID": "00013",
  "FiscalYear": "2026",
  "Type": "Self Occupied",
  "AnnualRentalIncome": 0.0,
  "InterestOnBorrowal": 200000.0,
  "TotalDeduction": 200000.0,
  "PrinciplePaid": 0.0,
  "InterestPaid": 200000.0
}
```

### Validations

* One record per employee (unless property-level data exists)
* If Let-Out available → allow multiple

### ⚠️ Caution

* This is a **derived fallback**, not tax-accurate modeling

---

## 4.5 **585 – Deductions**

### Input

* `SapInfoType = 585`
* Use **SapDescription**

### Aggregation

```text
Group by: EmployeeID + SapDescription
```

### Output

```json
{
  "EmployeeID": "00013",
  "FiscalYear": "2026",
  "ConsiderActual": "Yes",
  "Section": "Medical Insr Premium (Non-Senior Ctz)",
  "Declared": 65000.0
}
```

### Rules

* **Do not group by section code**; group by **SAP description**
* Exclude `SapDescription IS NULL`

---

## 4.6 **586 – Contributions**

### Input

* `SapInfoType = 586`

### Aggregation

```text
Group by: EmployeeID + SapDescription
```

### Output

```json
{
  "EmployeeID": "00013",
  "FiscalYear": "2026",
  "Section": "Contribution to Public Provident Fund",
  "Declared": 100000.0
}
```

### Rules

* Do **not merge all 80C** → keep per SAP description
* Separate from 585 always

---

## 5) API Design

### Endpoint

```
POST /api/sap/declarations/export
```

### Request

```json
{
  "OrgId": 1,
  "EmployeeIds": ["00013","00045"],
  "FinancialYear": "2026",
  "Infotypes": ["581","585","586"],
  "Mode": "JSON" | "SAP_API"
}
```

### Behavior

| Mode    | Action            |
| ------- | ----------------- |
| JSON    | Return file       |
| SAP_API | Call SAP endpoint |

---

## 6) Processing Pipeline

```text
Load Data → Filter → Normalize → Aggregate → Validate → Transform → Output
```

### Steps

1. Filter by Org / Employee / FY
2. Join Header + Detail
3. Normalize:

   * EmployeeID padding
   * Code mapping
4. Split by Infotype
5. Apply **infotype-specific rules**
6. Validate
7. Output JSON / call SAP

---

## 7) Validation Framework

### Hard Errors (Reject)

* Duplicate Employee + Section (585/586)
* Multiple records for 583
* Non-numeric amounts

### Soft Warnings

* Missing PAN (581)
* Very high values
* Missing lender data (584)

---

## 8) Aggregation Summary

| Infotype | Grouping                     |
| -------- | ---------------------------- |
| 581      | Employee + Landlord          |
| 582      | Employee                     |
| 583      | Employee                     |
| 584      | Employee (or property-level) |
| 585      | Employee + SAP Description   |
| 586      | Employee + SAP Description   |

---

## 9) C# Implementation Blueprint (Vibe-ready)

### Core Service Interface

```csharp
public interface ISapDeclarationService
{
    Task<string> GenerateJsonAsync(FilterRequest request);
    Task CallSapApiAsync(FilterRequest request);
}
```

### Key Methods

```csharp
MapEmployeeId(code) => code.PadLeft(5,'0');

Group585(details) =>
    details
      .Where(x => x.SapInfoType == 585 && x.SapDescription != null)
      .GroupBy(x => new { x.EmployeeId, x.SapDescription })
      .Select(g => new {...});

Build581(...) => monthly conversion
Build583(...) => merge IHP + IOI
Build584(...) => LFHP mapping
```

---

## 10) Critical Care Areas

### 🔴 Must Not Miss

* EmployeeID format consistency
* 585 vs 586 separation
* 583 single-record rule
* Excluding `#N/A` mappings

### 🟡 Often Missed

* Monthly conversion (581)
* LFHP misclassification
* Section duplication

### 🟢 Optional Enhancements

* Limit validations (80C caps)
* PAN validation
* Regime-based filtering

---

## 11) Recommended Enhancements

* **Mapping Table**

```text
ItemCode → SapInfoType → SapDescription → Category
```

* **Rule Engine**
* **Config-driven transformations**

---

## 12) Final Notes

This system should be treated as:

```text
Data Transformation Engine (NOT business logic engine)
```

* Do NOT enforce tax limits here
* Do NOT infer missing financial logic beyond defined rules
* Keep transformations deterministic

---

 For SAP integrations, **even a single field name mismatch, casing difference, or missing wrapper node will cause rejection**.


---

# ✅ 1. MANDATORY ROOT STRUCTURE (NON-NEGOTIABLE)

Every payload for **ALL infotypes (581–586)** must follow **exactly this structure**:

```json
{
  "Date": "11.02.2026",
  "Remarks": "Provisional declaration for FY 2025",
  "Declarations": []
}
```

---

## 🔴 STRICT RULES (SAP-SENSITIVE)

| Rule              | Requirement                                     |
| ----------------- | ----------------------------------------------- |
| Root wrapper      | MUST exist                                      |
| Field names       | EXACT match (case-sensitive)                    |
| `Declarations`    | MUST be array                                   |
| Order             | Prefer same order (some SAP parsers are strict) |
| No extra fields   | Not allowed                                     |
| No missing fields | Not allowed                                     |

---

# ❌ COMMON MISTAKES (Observed Earlier)

### ❌ Missing wrapper

```json
[ { "EmployeeID": "00013" } ]
```

👉 **INVALID**

---

### ❌ Wrong casing

```json
"declarations": []
```

👉 ❌ INVALID (must be `Declarations`)

---

### ❌ Field mismatch

```json
"employeeId"
```

👉 ❌ INVALID (must be `EmployeeID`)

---

# ✅ 2. CORRECT STRUCTURE PER INFOTYPE

---

## ✅ 581 (Rent)

```json
{
  "Date": "01.05.2026",
  "Remarks": "Provisional declaration for FY 2026",
  "Declarations": [
    {
      "EmployeeID": "00013",
      "FiscalYear": "2026",
      "Particulars": "Rent paid",
      "From": "01-Apr-2026",
      "To": "31-Dec-9999",
      "Type": "Metro",
      "Declared": 15000.0,
      "Approved": 15000.0,
      "Remarks": "",
      "LandLordName": "",
      "LandLordPAN": "",
      "LandLordTAN": "",
      "LandLordAddressLine1": "",
      "LandLordAddressLine2": "",
      "LandLordAddressLine3": "",
      "LandLordAddressLine4": "",
      "LandLordAddressLine5": ""
    }
  ]
}
```

---

## ✅ 585 (Deductions)

```json
{
  "Date": "01.05.2026",
  "Remarks": "Provisional declaration for FY 2026",
  "Declarations": [
    {
      "EmployeeID": "00013",
      "FiscalYear": "2026",
      "ConsiderActual": "Yes",
      "Section": "Medical Insr Premium (Non-Senior Ctz)",
      "Particulars": "Medical Insr Premium (Non-Senior Ctz)",
      "Declared": 65000.0,
      "Approved": 65000.0,
      "Remarks": ""
    }
  ]
}
```

---

## ✅ 586 (Contributions)

```json
{
  "Date": "01.05.2026",
  "Remarks": "Provisional declaration for FY 2026",
  "Declarations": [
    {
      "EmployeeID": "00013",
      "FiscalYear": "2026",
      "ConsiderActual": "Yes",
      "Section": "Contribution to Public Provident Fund",
      "Particulars": "Contribution to Public Provident Fund",
      "Declared": 100000.0,
      "Approved": 100000.0,
      "Remarks": ""
    }
  ]
}
```

---

## ✅ 583 (Other Income)

```json
{
  "Date": "01.05.2026",
  "Remarks": "Provisional declaration for FY 2026",
  "Declarations": [
    {
      "EmployeeID": "00013",
      "FiscalYear": "2026",
      "IncomeFromHouseProperty": 550000.0,
      "OtherIncome": 250000.0,
      "Remarks": ""
    }
  ]
}
```

---

## ✅ 584 (House Property)

```json
{
  "Date": "01.05.2026",
  "Remarks": "Provisional declaration for FY 2026",
  "Declarations": [
    {
      "EmployeeID": "00013",
      "FiscalYear": "2026",
      "Type": "Self Occupied",
      "Particulars1": "Self Occupied Property",
      "AnnualRentalIncome": 0.0,
      "LocalTaxPaid": 0.0,
      "NetAnnualValue": 0.0,
      "StandardDeduction": 0.0,
      "InterestOnBorrowal": 200000.0,
      "TotalDeduction": 200000.0,
      "NetIncome": 0.0,
      "EligibleFor80EEA": "No",
      "PossesionObtained": "Yes",
      "Remarks1": "",
      "Particulars2": "Home Loan",
      "PrinciplePaid": 0.0,
      "InterestPaid": 200000.0,
      "Remarks2": "",
      "LenderName": "",
      "LenderPAN": "",
      "LenderTAN": "",
      "LenderAddressLine1": "",
      "LenderAddressLine2": "",
      "LenderAddressLine3": "",
      "LenderAddressLine4": "",
      "LenderAddressLine5": ""
    }
  ]
}
```

---

## ✅ 582 (Family)

```json
{
  "Date": "01.05.2026",
  "Remarks": "Provisional declaration for FY 2026",
  "Declarations": [
    {
      "EmployeeID": "00013",
      "FiscalYear": "2026",
      "NoOfChildrenHostel": 2,
      "NoOfChildrenEducation": 1
    }
  ]
}
```

---

# 🔴 3. IMPLEMENTATION GUARANTEE (VERY IMPORTANT)

In your code:

### DO NOT do:

```csharp
return JsonConvert.SerializeObject(list);
```

### ALWAYS do:

```csharp
var payload = new {
    Date = date,
    Remarks = remarks,
    Declarations = list
};

return JsonConvert.SerializeObject(payload);
```

---

# 🔒 4. HARD VALIDATION CHECKLIST (Before Sending to SAP)

Implement a validator:

```text
✔ Root has Date
✔ Root has Remarks
✔ Root has Declarations[]
✔ All fields exact case
✔ No null fields (use "" or 0)
✔ EmployeeID = 5-digit
✔ No duplicate records
✔ Infotype-specific rules satisfied
```

---

# 🚨 5. FINAL WARNING (Based on Experience)

SAP failures often happen due to:

* `"Declarations"` vs `"declarations"` ❌
* Missing `"Remarks"` ❌
* `"EmployeeId"` vs `"EmployeeID"` ❌
* Sending array instead of object ❌

👉 These are **silent failures** sometimes.

---
 