# Party & PartyBranch Module — Deep Code Analysis
**Scope:** PartyBLL, IPartyBLL, PartyBranchBLL, IPartyBranchBLL, PartyDAL, IPartyDAL, PartyBranchDAL, IPartyBranchDAL,
PartyQB, PartyBranchQB, all SL endpoints (GetParty/SaveParty/DeleteParty/GetSelectListParty, GetSelectListPartyBranch),
PartyDTO, PartyFlatDTO, PartyReportDTO, PartyPicklistDTO, PartyBranchDTO, PartyBranchFlatDTO, PartyBranchPicklistDTO, PartyBranchWithAddressDTO
**Date:** 2026-03-03

---

## Issue Index

| # | Severity | Category | Location | Summary |
|---|----------|----------|----------|---------|
| 1 | CRITICAL | Security/VAPT | `GetSelectListParty.cs:24` | `AllowAnonymous()` — no authentication on party master endpoint |
| 2 | CRITICAL | Security/VAPT | `PartyQB.cs:23` | `GET_SELECTLIST_PARTY_SQL` — no tenant filter, cross-tenant data exposure |
| 3 | CRITICAL | Security/VAPT | `GetSelectListPartyBranch.cs:22` | `AllowAnonymous()` — no authentication on party branch endpoint |
| 4 | CRITICAL | Security/VAPT | `PartyBranchQB.cs:18-23` | `GET_SELECTLIST_PARTYBRANCH` — no tenant filter, cross-tenant data exposure |
| 5 | CRITICAL | Architecture | `GetParty.cs`, `SaveParty.cs`, `DeleteParty.cs` | All three Party CRUD endpoints are empty stubs — no Get, Save, Delete implemented |
| 6 | HIGH | Security | `GetSelectListParty.cs:58`, `GetSelectListPartyBranch.cs:41` | `ex.Message` leaked to HTTP client in error response |
| 7 | HIGH | Security | `PartyBLL.cs:28` | BLL exception wraps internal method name (`"Error in PartyBLL.GetSelectListParty: {ex.Message}"`) — leaks internal stack info |
| 8 | HIGH | Best Practice | `PartyQB.cs:64-65` | Oracle and MySQL `GET_SELECTLIST_PARTY` queries are empty `@";"` — auth broken for those DB types |
| 9 | HIGH | Best Practice | `PartyBranchQB.cs` | Only one SQL variant (SQL Server) — no PG/Oracle/MySQL variants; `PartyBranchDAL` performs no DB-type switching |
| 10 | HIGH | Best Practice | `GetSelectListParty.cs:37`, `GetSelectListPartyBranch.cs:36` | `CancellationToken ct` received by endpoint but never forwarded to BLL or DAL |
| 11 | HIGH | Logic | `PartyBranchDAL.cs:39-40` | `IsCount == true` branch hardcodes `Count = 0` — pagination count always returns zero |
| 12 | MEDIUM | Performance | `PartyQB.cs:11-36` | CTE + `ROW_NUMBER()` pagination scans full table before filtering — inefficient for large party lists |
| 13 | MEDIUM | Best Practice | `PartyDAL.cs:51-54` | `CriteriaDTO` accepted by all layers but completely ignored — filtering never applied to the query |
| 14 | MEDIUM | Best Practice | `PartyBranchDAL.cs:33` | `CriteriaDTO` accepted by all layers but completely ignored — filtering never applied |
| 15 | MEDIUM | Architecture | `GetSelectListParty.cs:23`, `GetSelectListPartyBranch.cs:21` | HTTP `POST` used for data retrieval — should be `GET` per REST conventions and CLAUDE.md route convention |
| 16 | MEDIUM | Architecture | `PartyBranchBLL.cs`, `PartyBranchDAL.cs` | No `GetPartyBranch`, `SavePartyBranch`, `DeletePartyBranch` implemented — entire CRUD missing |
| 17 | MEDIUM | Memory | `PartyDTO.cs` | ~600 lines of Java-era backing fields with `virtual` accessors — should use C# auto-properties |
| 18 | MEDIUM | Memory | `PartyBranchDTO.cs` | ~500 lines of Java-era backing fields with `virtual` accessors — should use C# auto-properties |
| 19 | MEDIUM | Best Practice | `PartyBranchDTO.cs:18-71` | Mass `"NONE"` string defaults — inserted verbatim into DB if field not explicitly set |
| 20 | MEDIUM | Best Practice | `PartyDTO.cs:89` | `accounttdsstate = "NONE"` and similar defaults — "NONE" stored in financial/TDS fields if not set |
| 21 | MEDIUM | Architecture | `AccountsDAL/DTO/Party/` | Three near-duplicate Party DTOs: `PartyDTO`, `PartyFlatDTO`, `PartyReportDTO` — overlapping fields with type inconsistencies |
| 22 | MEDIUM | Architecture | `AccountsDAL/DTO/PartyBranch/` | Five near-duplicate PartyBranch DTOs: `PartyBranchDTO`, `PartyBranchFlatDTO`, `PartyBranchWithAddressDTO`, `PartyBranchInfoDTO`, `PartyBranchListDTO` |
| 23 | MEDIUM | Security | `PartyBranchDAL.cs:7` | Cross-module import `MMDAL.DTO.Counter` from MM module into Accounts DAL — violates module boundaries |
| 24 | MEDIUM | Performance | `GetSelectListParty.cs:49`, `GetSelectListPartyBranch.cs:37` | `CacheKeyLevel.NOT_REQUIRED` for party master data — should be `CLIENT_LEVEL` for infrequently changed master data |
| 25 | LOW | Best Practice | `PartyPicklistDTO.cs` | Java-era backing field pattern in a picklist DTO — should use auto-properties |
| 26 | LOW | Best Practice | `PartyBranchPicklistDTO.cs` | Java-era backing field pattern in a picklist DTO — should use auto-properties |
| 27 | LOW | Best Practice | `PartyDTO.cs:16` vs `PartyFlatDTO.cs:16` | `PartyIsAccount` is `byte` in `PartyDTO` but `int` in `PartyFlatDTO` — type inconsistency |
| 28 | LOW | Best Practice | `PartyBranchDTO.cs:20` vs `PartyBranchFlatDTO.cs:19` | `PartyBranchPartyPartyVersion` is `Int16` in DTO, `int` in FlatDTO — type inconsistency |
| 29 | LOW | Best Practice | `PartyBLL.cs:21-29` | No input validation in BLL — `FirstNumber`, `MaxResult` not validated (e.g., negative values beyond -1) |
| 30 | LOW | Best Practice | `PartyBranchQB.cs` | No comment indicating required DB indexes — unlike CLAUDE.md requirement |

---

## Detailed Findings

---

### CRITICAL-1 — `AllowAnonymous()` on Party Select List
**File:** [GB5Solution/Accounts/AccountsSL/EndPoints/Party/GetSelectListParty.cs:24](GB5Solution/Accounts/AccountsSL/EndPoints/Party/GetSelectListParty.cs#L24)

```csharp
public override void Configure()
{
    Post("/Party/GetSelectListParty");
    AllowAnonymous();    // ← no authentication required
}
```

The party master (customers, vendors, banks, prospects) is sensitive business data. Combined with CRITICAL-2 (no tenant filter), any unauthenticated HTTP client can enumerate all parties across all tenants on the server with a single POST request.

---

### CRITICAL-2 — `GET_SELECTLIST_PARTY_SQL` Has No Tenant Filter
**File:** [GB5Solution/Accounts/AccountsDAL/Query/Party/PartyQB.cs:23](GB5Solution/Accounts/AccountsDAL/Query/Party/PartyQB.cs#L23)

```sql
FROM MPARTY A
LEFT JOIN MCURRENCY C ON C.CURRENCYID = A.CURRENCYID
WHERE A.STATUS = 1
-- No CLIENTID / DATABASENAME filter!
```

Same issue in `GET_SELECTLIST_PARTY_PG` (line 49-50). The `Parameters` passed from `PartyDAL` only include `firstnumber` and `maxresult` — no `LoginDTO.ClientId` or `LoginDTO.DatabaseName` is bound.

**Impact:** Calling `POST /Party/GetSelectListParty` with no authentication token returns every active party (customer, vendor, bank, prospect) from every tenant on the shared SQL Server instance — names, codes, currency assignments, and TDS classification — in one response.

---

### CRITICAL-3 — `AllowAnonymous()` on PartyBranch Select List
**File:** [GB5Solution/Accounts/AccountsSL/EndPoints/PartyBranch/GetSelectListPartyBranch.cs:22](GB5Solution/Accounts/AccountsSL/EndPoints/PartyBranch/GetSelectListPartyBranch.cs#L22)

```csharp
public override void Configure()
{
    Post("/PartyBranch/GetSelectListPartyBranch");
    AllowAnonymous();    // ← no authentication required
}
```

---

### CRITICAL-4 — `GET_SELECTLIST_PARTYBRANCH` Has No Tenant Filter
**File:** [GB5Solution/Accounts/AccountsDAL/Query/PartyBranch/PartyBranchQB.cs:11-24](GB5Solution/Accounts/AccountsDAL/Query/PartyBranch/PartyBranchQB.cs#L11-L24)

```sql
WITH PARTYBRANCH AS (
    SELECT
        A.PARTYBRANCHID AS Id,
        A.PARTYBRANCHCODE AS Code,
        A.PARTYBRANCHNAME AS Name,
        A.PARTYBRANCHSHORTNAME AS ShortName,
        ROW_NUMBER() OVER (ORDER BY A.PARTYBRANCHID) AS RowNum
    FROM MPARTYBRANCH A
    -- No WHERE clause filtering by CLIENTID / DATABASENAME!
)
```

`PartyBranchDAL` passes only `firstnumber` and `maxresult` as parameters, never binding any tenant identity.

**Impact:** Every party branch — including `PartyBranchCode`, `PartyBranchName`, organizational flags (`IsEOU`, `IsSalesApplicable`, `IsPurchaseApplicable`), price list assignments, and payment terms — is exposed without authentication across all tenants.

---

### CRITICAL-5 — All Party CRUD Endpoints Are Empty Stubs
**Files:**
- [GB5Solution/Accounts/AccountsSL/EndPoints/Party/GetParty.cs](GB5Solution/Accounts/AccountsSL/EndPoints/Party/GetParty.cs)
- [GB5Solution/Accounts/AccountsSL/EndPoints/Party/SaveParty.cs](GB5Solution/Accounts/AccountsSL/EndPoints/Party/SaveParty.cs)
- [GB5Solution/Accounts/AccountsSL/EndPoints/Party/DeleteParty.cs](GB5Solution/Accounts/AccountsSL/EndPoints/Party/DeleteParty.cs)

```csharp
// GetParty.cs
namespace AccountsSL.EndPoints.Party
{
    public class GetParty { }   // empty — no route, no implementation
}

// SaveParty.cs
public class SaveParty { }     // empty stub

// DeleteParty.cs
public class DeleteParty { }   // empty stub
```

Party is one of the most foundational entities in the ERP — every transaction references a party or party branch. With no `GetParty`, `SaveParty`, or `DeleteParty` implemented, the entire party master management is non-functional. The only working endpoint is the unauthenticated picklist.

Additionally, `IPartyBLL` and `IPartyDAL` only declare `GetSelectListParty` — no interface contracts for Get, Save, Delete, Update.

---

### HIGH-6 — `ex.Message` Leaked to HTTP Client
**Files:**
- [GB5Solution/Accounts/AccountsSL/EndPoints/Party/GetSelectListParty.cs:58](GB5Solution/Accounts/AccountsSL/EndPoints/Party/GetSelectListParty.cs#L58)
- [GB5Solution/Accounts/AccountsSL/EndPoints/PartyBranch/GetSelectListPartyBranch.cs:41](GB5Solution/Accounts/AccountsSL/EndPoints/PartyBranch/GetSelectListPartyBranch.cs#L41)

```csharp
catch (Exception ex)
{
    return await GB5Shared.ResponseStandard.Response
        .CreateExceptionError<string>(ex, CacheKeyLevel.NOT_REQUIRED, LoginDTO,
            ex.Message,    // ← raw exception message sent to client
            500);
}
```

SQL error text, null reference paths, and table names are returned verbatim to callers. Because the endpoints are `AllowAnonymous`, this is exploitable by unauthenticated attackers for reconnaissance.

---

### HIGH-7 — BLL Wraps Exception With Internal Method Name
**File:** [GB5Solution/Accounts/AccountsBLL/Party/PartyBLL.cs:28](GB5Solution/Accounts/AccountsBLL/Party/PartyBLL.cs#L28)

```csharp
catch (Exception ex)
{
    throw new Exception($"Error in PartyBLL.GetSelectListParty: {ex.Message}", ex);
}
```

This explicitly embeds the BLL class name, method name, and the original `ex.Message` into the new exception. When this propagates to the SL's `ex.Message` logging, the full string `"Error in PartyBLL.GetSelectListParty: <SQL error>"` is returned to the HTTP client.

Contrast with `PartyBranchBLL` (line 27) which correctly uses `throw;` — inconsistency between the two BLLs.

**Fix:** Use `throw;` (bare rethrow) — do not wrap with a new exception. Let the SL's error handler return a generic message.

---

### HIGH-8 — Oracle and MySQL Party Queries Are Empty Stubs
**File:** [GB5Solution/Accounts/AccountsDAL/Query/Party/PartyQB.cs:64-65](GB5Solution/Accounts/AccountsDAL/Query/Party/PartyQB.cs#L64-L65)

```csharp
public const string GET_SELECTLIST_PARTY_ORACLE = @";";
public const string GET_SELECTLIST_PARTY_MYSQL  = @";";
```

`PartyDAL` correctly switches on `DBType` and throws `"Unsupported database type"` for `default` — but Oracle and MySQL are not `default`; they match explicit `case` branches and execute empty SQL. This silently sends `";"` to the database engine, producing a runtime error or empty result.

---

### HIGH-9 — `PartyBranchQB` Has Only SQL Server Variant
**File:** [GB5Solution/Accounts/AccountsDAL/Query/PartyBranch/PartyBranchQB.cs](GB5Solution/Accounts/AccountsDAL/Query/PartyBranch/PartyBranchQB.cs)

`PartyBranchQB` contains exactly one SQL constant: `GET_SELECTLIST_PARTYBRANCH`, written for SQL Server syntax (`@firstnumber`, `@maxresult` parameters). `PartyBranchDAL` performs no `DatabaseType` switch — it sends this SQL Server query to any database type. On PostgreSQL, the named parameters use `:` not `@`, causing a Npgsql parameter binding failure.

---

### HIGH-10 — `CancellationToken` Not Forwarded to BLL or DAL
**Files:**
- [GB5Solution/Accounts/AccountsSL/EndPoints/Party/GetSelectListParty.cs:34-46](GB5Solution/Accounts/AccountsSL/EndPoints/Party/GetSelectListParty.cs#L34-L46)
- [GB5Solution/Accounts/AccountsSL/EndPoints/PartyBranch/GetSelectListPartyBranch.cs:32-43](GB5Solution/Accounts/AccountsSL/EndPoints/PartyBranch/GetSelectListPartyBranch.cs#L32-L43)

```csharp
protected override async Task<ResponseStandardDTO<object>> ExecuteAsync(
    GetSelectListPartyParameters req, LoginDTO LoginDTO, CancellationToken ct)
{
    var result = await _partyBLL.GetSelectListParty(
        req.FirstNumber, req.MaxResult, req.CriteriaDTO, LoginDTO!
        // ct is DROPPED here — not passed to BLL
    );
```

`IPartyBLL.GetSelectListParty` and `IPartyDAL.GetSelectListParty` signatures do not include `CancellationToken`. Similarly for `IPartyBranchBLL`/`IPartyBranchDAL`. Long-running party queries cannot be cancelled when the client disconnects.

---

### HIGH-11 — `IsCount` Branch Returns Hardcoded Zero
**File:** [GB5Solution/Accounts/AccountsDAL/CustomCode/PartyBranch/PartyBranchDAL.cs:39-40](GB5Solution/Accounts/AccountsDAL/CustomCode/PartyBranch/PartyBranchDAL.cs#L39-L40)

```csharp
else  // IsCount == true
{
    int Count = 0;
    Json = Count.ToString();  // always "0" — never queries DB
}
```

When the caller requests a pagination count, the DAL returns `0` instead of executing a `COUNT(*)` query. Any UI component relying on this to determine total pages will show 0 pages, breaking pagination entirely.

---

### MEDIUM-12 — CTE Pagination Scans Full Table Before Filtering
**File:** [GB5Solution/Accounts/AccountsDAL/Query/Party/PartyQB.cs:11-36](GB5Solution/Accounts/AccountsDAL/Query/Party/PartyQB.cs#L11-L36)

```sql
WITH Party AS (
    SELECT ..., ROW_NUMBER() OVER (ORDER BY A.PARTYID) AS RowNum
    FROM MPARTY A
    LEFT JOIN MCURRENCY C ON ...
    WHERE A.STATUS = 1      -- no partition — processes ALL active parties
)
SELECT ... FROM Party
WHERE (@firstnumber = -1 AND @maxresult = -1)
   OR (RowNum BETWEEN @firstnumber AND @maxresult);
```

SQL Server materializes the full CTE result set (potentially tens of thousands of rows with joins) before the outer `WHERE RowNum BETWEEN` filter is applied. Since there is no tenant filter, this scans `MPARTY` and `MCURRENCY` across all tenants.

**Fix:** Use `OFFSET @offset ROWS FETCH NEXT @pagesize ROWS ONLY` (SQL Server 2012+) or `ROW_NUMBER()` in a subquery with a push-down hint. Add the tenant filter to reduce the scan range.

---

### MEDIUM-13 & MEDIUM-14 — `CriteriaDTO` Accepted Everywhere But Never Used
**Files:**
- [GB5Solution/Accounts/AccountsDAL/CustomCode/Party/PartyDAL.cs:51-54](GB5Solution/Accounts/AccountsDAL/CustomCode/Party/PartyDAL.cs#L51-L54)
- [GB5Solution/Accounts/AccountsDAL/CustomCode/PartyBranch/PartyBranchDAL.cs:33](GB5Solution/Accounts/AccountsDAL/CustomCode/PartyBranch/PartyBranchDAL.cs#L33)

```csharp
// PartyDAL
var Parameters = new
{
    firstnumber = FirstNumber,
    maxresult   = MaxResult
    // CriteriaDTO — completely ignored, never bound
};
```

`CriteriaDTO` flows through the entire chain — SL endpoint → BLL → DAL — but is never applied to the SQL. Users who expect name/code/type search filtering will always receive unfiltered results.

---

### MEDIUM-15 — HTTP `POST` Used for Read Operations
**Files:**
- [GB5Solution/Accounts/AccountsSL/EndPoints/Party/GetSelectListParty.cs:23](GB5Solution/Accounts/AccountsSL/EndPoints/Party/GetSelectListParty.cs#L23)
- [GB5Solution/Accounts/AccountsSL/EndPoints/PartyBranch/GetSelectListPartyBranch.cs:21](GB5Solution/Accounts/AccountsSL/EndPoints/PartyBranch/GetSelectListPartyBranch.cs#L21)

```csharp
Post("/Party/GetSelectListParty");         // should be Get
Post("/PartyBranch/GetSelectListPartyBranch");  // should be Get
```

CLAUDE.md defines: `GET /Entity/GetSelectListEntity ← dropdown data`. Using POST for retrieval:
- Prevents HTTP caching by CDN/proxy layers
- Creates ambiguity for REST clients expecting idempotent GET semantics
- Conflicts with the project route convention

---

### MEDIUM-16 — No PartyBranch CRUD Implemented
**File:** [GB5Solution/Accounts/AccountsBLL/PartyBranch/IPartyBranchBLL.cs](GB5Solution/Accounts/AccountsBLL/PartyBranch/IPartyBranchBLL.cs)

```csharp
public interface IPartyBranchBLL
{
    Task<string> GetSelectListPartyBranch(...);
    // No GetPartyBranch, SavePartyBranch, DeletePartyBranch, UpdatePartyBranch
}
```

`IPartyBranchBLL`, `IPartyBranchDAL`, `PartyBranchBLL`, and `PartyBranchDAL` each define only the picklist method. There is no endpoint folder for party branch Get/Save/Delete — these operations are completely absent.

---

### MEDIUM-17 — `PartyDTO` Uses Java-Era Backing Field Pattern (~600 lines)
**File:** [GB5Solution/Accounts/AccountsDAL/DTO/Party/PartyDTO.cs](GB5Solution/Accounts/AccountsDAL/DTO/Party/PartyDTO.cs)

```csharp
private int partyid;                          // backing field
public virtual int PartyId                    // property with get/set
{
    get { return partyid; }
    set { partyid = value; }
}
```

Approximately 70+ properties are implemented this way across ~600 lines. All properties are `virtual` (NHibernate proxy remnant). The same pattern inflates `PartyBranchDTO` (~500 lines).

**Fix:** Replace with C# auto-properties:
```csharp
public int PartyId { get; set; }
```
This reduces both files by ~70% while maintaining identical runtime behavior for Dapper mapping.

---

### MEDIUM-18 — `PartyBranchDTO` Has Pervasive `"NONE"` String Defaults
**File:** [GB5Solution/Accounts/AccountsDAL/DTO/PartyBranch/PartyBranchDTO.cs:18-114](GB5Solution/Accounts/AccountsDAL/DTO/PartyBranch/PartyBranchDTO.cs#L18-L114)

```csharp
private string partybranchpartycode    = "NONE";
private string partybranchpartyname    = "NONE";
private string partybranchcode         = "NONE";
private string partybranchname         = "NONE";
private string partybranchshortname    = "NONE";
private string currencycode            = "NONE";
private string currencyname            = "NONE";
private string purchasepricelistcode   = "NONE";
private string purchasepricelistname   = "NONE";
private string salespricelistcode      = "NONE";
// ... 15+ more "NONE" defaults
```

If any field is not populated from a query result (e.g., due to a missing JOIN alias) and the DTO is then used for a save/insert operation, the literal string `"NONE"` is written to the database. This was observed causing data quality issues in related modules (Address). The same pattern exists in `PartyDTO` (`accounttdsstate = "NONE"`).

---

### MEDIUM-19 — Three Near-Duplicate Party DTOs With Type Inconsistencies
**Files:** `PartyDTO.cs`, `PartyFlatDTO.cs`, `PartyReportDTO.cs`

| Field | `PartyDTO` type | `PartyFlatDTO` type |
|-------|-----------------|---------------------|
| `partytype` | `byte` | `int` |
| `partyisaccount` | `byte` | `int` |
| `partyisprospect` | `byte` | `int` |
| `partyisbank` | `int` | `int` |

The same logical entity is represented by three DTOs with different field types for the same columns. A `byte` field in `PartyDTO` and `int` in `PartyFlatDTO` for `PartyIsAccount` can cause silent truncation when mapping results or copying between DTOs.

`PartyReportDTO` is minimal (4 fields) and could be replaced with a subset projection of `PartyDTO`.

---

### MEDIUM-20 — Five Near-Duplicate PartyBranch DTOs
**Folder:** [GB5Solution/Accounts/AccountsDAL/DTO/PartyBranch/](GB5Solution/Accounts/AccountsDAL/DTO/PartyBranch/)

| DTO | Approximate Size | Key Difference |
|-----|-----------------|----------------|
| `PartyBranchDTO` | ~500 lines | `"NONE"` defaults, `virtual` properties, `byte` flag types |
| `PartyBranchFlatDTO` | ~400 lines | `null` defaults, `int` flag types, no `virtual` |
| `PartyBranchWithAddressDTO` | ~300+ lines | Embeds address fields directly |
| `PartyBranchInfoDTO` | Unknown | Informational subset |
| `PartyBranchListDTO` | Unknown | List/grid projection |

`PartyBranchDTO` and `PartyBranchFlatDTO` have identical field sets with different defaults and type choices. Maintaining five DTOs for the same table multiplies the surface area for bugs — any column added to `MPARTYBRANCH` must be added in 5 places.

---

### MEDIUM-21 — Cross-Module Import in `PartyBranchDAL`
**File:** [GB5Solution/Accounts/AccountsDAL/CustomCode/PartyBranch/PartyBranchDAL.cs:6](GB5Solution/Accounts/AccountsDAL/CustomCode/PartyBranch/PartyBranchDAL.cs#L6)

```csharp
using MMDAL.DTO.Counter;   // ← MM (Materials Management) module DTO in Accounts DAL
```

`AccountsDAL` references `MMDAL` — importing a DTO from the MM module into the Accounts module creates a bidirectional module dependency. If `MMDAL.DTO.Counter.CounterDTO` changes, `AccountsDAL` is broken. This `using` appears to be unused (no `CounterDTO` reference visible in the class body), making it dead code that introduces a build coupling.

---

### MEDIUM-22 — `CacheKeyLevel.NOT_REQUIRED` for Party Master Data
**Files:**
- [GB5Solution/Accounts/AccountsSL/EndPoints/Party/GetSelectListParty.cs:49](GB5Solution/Accounts/AccountsSL/EndPoints/Party/GetSelectListParty.cs#L49)
- [GB5Solution/Accounts/AccountsSL/EndPoints/PartyBranch/GetSelectListPartyBranch.cs:37](GB5Solution/Accounts/AccountsSL/EndPoints/PartyBranch/GetSelectListPartyBranch.cs#L37)

```csharp
return await GB5Shared.ResponseStandard.Response
    .CreateSuccessResponse(result, CacheKeyLevel.NOT_REQUIRED, LoginDTO);
```

Party and party branch master data changes infrequently (only when a party is added or modified). Using `NOT_REQUIRED` means every dropdown request issues a full table scan. The appropriate level is `CLIENT_LEVEL` with cache invalidation on party save/delete. Party picklists are frequently rendered in transaction screens (Purchase Orders, Sales Orders, AR/AP entries).

---

### MEDIUM-23 — No Input Validation in BLL
**File:** [GB5Solution/Accounts/AccountsBLL/Party/PartyBLL.cs:19-30](GB5Solution/Accounts/AccountsBLL/Party/PartyBLL.cs#L19-L30)

```csharp
public async Task<string> GetSelectListParty(int FirstNumber, int MaxResult, CriteriaDTO CriteriaDTO, LoginDTO LoginDTO)
{
    try
    {
        var result = await _PartyDAL.GetSelectListParty(FirstNumber, MaxResult, CriteriaDTO, LoginDTO);
        return result;
    }
    // No validation: negative FirstNumber beyond -1? MaxResult = 0? MaxResult = 10000000?
```

No guard on `FirstNumber`/`MaxResult` means a caller can pass `MaxResult = 1000000` and receive an unbounded result set. The SQL's CTE would materialise every row before the `RowNum BETWEEN` filter.

---

### LOW-25 & LOW-26 — Java-Era Backing Fields in Picklist DTOs
**Files:** `PartyPicklistDTO.cs`, `PartyBranchPicklistDTO.cs`

```csharp
public class PartyPicklistDTO
{
    private int id;
    private string code;
    public int Id { get { return id; } set { id = value; } }
    public string Code { get { return code; } set { code = value; } }
    // ...
}
```

Even small 5-field picklist DTOs use the Java backing field pattern. These should be:
```csharp
public class PartyPicklistDTO
{
    public int Id { get; set; }
    public string Code { get; set; }
    public string Name { get; set; }
    public string ShortName { get; set; }
    public int CurrencyId { get; set; }
    public string CurrencyName { get; set; }
}
```

---

### LOW-27 & LOW-28 — Type Inconsistencies Between DTO Variants

| DTO Pair | Field | Type in DTO | Type in FlatDTO |
|----------|-------|-------------|-----------------|
| Party | `partytype` | `byte` | `int` |
| Party | `partyisaccount` | `byte` | `int` |
| Party | `partyisprospect` | `byte` | `int` |
| PartyBranch | `partybranchpartypartyversion` | `Int16` | `int` |
| PartyBranch | `partybranchtype` | `byte` | `int` |
| PartyBranch | `partybranchiseou` | `byte` | `int` |

When business logic copies values between DTO variants, implicit widening conversions hide these inconsistencies. On 64-bit platforms `int` and `byte` differ in memory footprint and JSON serialization (byte serializes as number 0-255 in STJ; int also 0-255 but declared as a wider type).

---

### LOW-29 — No Required DB Index Comments in QBs
**Files:** `PartyQB.cs`, `PartyBranchQB.cs`

CLAUDE.md requires QB comments to note required indexes. Neither QB includes any comment about expected indexes. The party picklist queries join `MPARTY` to `MCURRENCY` on `CURRENCYID` and order/paginate by `PARTYID` — at minimum:
- `MPARTY (STATUS, PARTYID)` — covering index for the filtered, ordered scan
- `MPARTYBRANCH (PARTYBRANCHID)` — for the ordering

---

## Architectural Gap Summary

The Party and PartyBranch modules in the Accounts solution have a **critical implementation gap**: only the picklist (select-list) endpoints are partially implemented. The full CRUD chain for both entities is absent:

| Operation | Party | PartyBranch |
|-----------|-------|-------------|
| Get (single record) | Empty stub | Not present |
| Save (create) | Empty stub | Not present |
| Update | Not present | Not present |
| Delete | Empty stub | Not present |
| Select List (picklist) | Implemented (with bugs) | Implemented (with bugs) |

Party is referenced by virtually every transactional module in the ERP (AR, AP, Purchase, Sales, Stock). Without a working Party CRUD, all dependent modules cannot function correctly.

---

## Priority Remediation Plan

### Tier 1 — Immediate (security, data integrity)

| # | Action |
|---|--------|
| C-1, C-3 | Remove `AllowAnonymous()` from both endpoints; require authenticated login |
| C-2, C-4 | Add `AND A.DATABASENAME = @databasename` (SQL) / `AND A.tenantid = @clientid` (PG) to both QBs; bind from `LoginDTO` |
| H-6 | Replace `ex.Message` in `CreateExceptionError` with a generic "An error occurred" message; log full exception server-side |
| H-7 | Replace `throw new Exception($"Error in PartyBLL...")` with `throw;` |

### Tier 2 — High Priority (functionality, correctness)

| # | Action |
|---|--------|
| C-5, M-16 | Implement `GetParty`, `SaveParty`, `DeleteParty` endpoints with proper BLL validation and DAL queries |
| H-8 | Implement Oracle and MySQL `GET_SELECTLIST_PARTY` variants or throw `NotSupportedException` |
| H-9 | Add PG/Oracle/MySQL variants to `PartyBranchQB`; add DB-type switching to `PartyBranchDAL` |
| H-10 | Add `CancellationToken` to all interface and implementation signatures |
| H-11 | Implement a real `COUNT(*)` query for `IsCount = true` in `PartyBranchDAL` |

### Tier 3 — Performance and Refactor

| # | Action |
|---|--------|
| M-12 | Replace CTE + ROW_NUMBER pagination with `OFFSET/FETCH NEXT` (SQL Server) and `LIMIT/OFFSET` (PG) |
| M-13, M-14 | Implement `CriteriaDTO`-based filtering (at minimum: name contains, code starts-with, type filter) |
| M-15 | Change `Post` to `Get` in both `Configure()` methods |
| M-22 | Set cache level to `CLIENT_LEVEL` with invalidation on Party/PartyBranch save |
| M-17, M-18 | Migrate `PartyDTO` and `PartyBranchDTO` to auto-properties |
| M-19, M-20 | Replace `"NONE"` string defaults with `null` or `string.Empty` |
| M-21, M-22 | Consolidate Party and PartyBranch DTO variants; create single authoritative DTO per entity with projection views |
| M-23 | Remove `using MMDAL.DTO.Counter` from `PartyBranchDAL` |
| L-25, L-26 | Migrate picklist DTOs to auto-properties |

---

## Issue Totals

| Severity | Count |
|----------|-------|
| CRITICAL | 5 |
| HIGH | 6 |
| MEDIUM | 12 |
| LOW | 7 |
| **Total** | **30** |
