# MM Module — BOM, Production, Transaction, Allocation Analysis

**Date:** 2026-03-02
**Path:** `GB5Solution/MM/` — BOM, Production, Transaction, Indent, Allocation, Inspection, Lot entities
**Risk Level:** 🔴 CRITICAL — Core manufacturing entities not implemented; SQL injection in Lot delete; circular BOM crash risk

---

## CRITICAL — MISSING CORE IMPLEMENTATIONS

### MM-BOM-01: Transaction, Production, Indent Entities Have No BLL/DAL — CRITICAL

**Severity:** 🔴 CRITICAL — Manufacturing workflow completely non-functional
**Files:** `MM/MMBLL/`, `MM/MMDAL/CustomCode/`

```
ONLY DTOs exist — NO BLL or DAL code:

MM/MMDAL/DTO/Production/   → ProductionDTO.cs only — NO ProductionBLL.cs ❌
MM/MMDAL/DTO/Transaction/  → TransactionDTO.cs only — NO TransactionBLL.cs ❌
MM/MMDAL/DTO/Indent/       → IndentDTO.cs only    — NO IndentBLL.cs ❌

NOT found:
  MM/MMBLL/Production/ProductionBLL.cs      ← doesn't exist
  MM/MMBLL/Transaction/TransactionBLL.cs    ← doesn't exist
  MM/MMBLL/Indent/IndentBLL.cs              ← doesn't exist
  MM/MMDAL/CustomCode/Production/           ← doesn't exist
  MM/MMDAL/CustomCode/Transaction/          ← doesn't exist
  MM/MMDAL/CustomCode/Indent/               ← doesn't exist
```

**Impact:**
- Cannot post any stock movement (IN/OUT/transfer) via API
- Cannot issue material to a production order
- Cannot raise or approve purchase requisitions (Indent)
- Manufacturing module is a shell — all critical operations missing
- Any UI that calls these endpoints will get 404 or DI exception

**Required:** Complete implementation of all three entity stacks (BLL + DAL + QB + endpoints + DTOs complete).

---

### MM-BOM-02: No Circular BOM Reference Detection — CRITICAL

**Severity:** 🔴 CRITICAL — Stack overflow crash when BOM is exploded
**File:** `MM/MMDAL/DTO/BOM/BOMDetailDTO.cs:51`, `MM/MMDAL/Query/BOM/BOMQB.cs`

**Problem:** BOMDetailDTO has a `ParentBOMLineId` field supporting hierarchical BOMs, but no guard exists against circular references:

```csharp
// BOMDetailDTO.cs:51
private int parentbomdetaillineid; // Hierarchical link — no circularity check
```

**Scenario:**
```
Item A BOM → requires Item B (as component)
Item B BOM → requires Item A (as component)

BOM explosion call: ExplodeBOM(A)
  → ExplodeBOM(B)
    → ExplodeBOM(A)
      → ExplodeBOM(B) ...
        → StackOverflowException → application crash
```

**No depth limit or visited-set found in codebase.**

**Fix — BFS with visited tracking:**
```csharp
public async Task ValidateBOMCircularity(int bomId, LoginDTO login)
{
    var visited = new HashSet<int>();
    var queue = new Queue<int>();
    queue.Enqueue(bomId);

    while (queue.TryDequeue(out var current))
    {
        if (!visited.Add(current))
            throw new BusinessException(
                $"Circular BOM reference detected: Item {current} appears more than once in the BOM hierarchy");

        var components = await GetBOMComponents(current, login);
        foreach (var c in components)
            queue.Enqueue(c.ComponentItemId);
    }
}

// In BOMBLL.SaveBOM():
await ValidateBOMCircularity(dto.ProductionItemId, login); // Before save
```

**Also add depth limit to explosion:**
```csharp
private async Task ExplodeBOM(int itemId, int depth, LoginDTO login)
{
    if (depth > 20) throw new BusinessException("BOM nesting exceeds maximum depth of 20 levels");
    var components = await GetComponents(itemId, login);
    foreach (var c in components)
        await ExplodeBOM(c.ComponentItemId, depth + 1, login);
}
```

---

### MM-BOM-03: SQL Injection in Lot Delete — CRITICAL

**Severity:** 🔴 CRITICAL
**File:** `MM/MMBLL/Lot/LotBLL.cs:51`

```csharp
// Direct string concatenation — SQL injection vulnerability
string ssql = " DELETE FROM TLOTDETAIL where 1=1 AND BIZTRANSACTIONTYPEID ="
    + LotDetailDTOs[i].BizTransactionTypeId
    + " AND OBJECTHEADERTYPEID="
    + LotDetailDTOs[i].ObjectHeaderTypeId
    + " AND OBJECTHEADERID="
    + LotDetailDTOs[i].LotDetailObjectHeaderId
    + " AND OBJECTTYPEID="
    + LotDetailDTOs[i].ObjectTypeId
    + " AND LOTID="
    + LotDetailDTOs[i].LotId;  // ❌ INJECTABLE
```

**Attack:** If `LotId = "1 OR 1=1 --"` → deletes ALL lot details in the table.

**Fix:**
```csharp
const string deleteSql = @"
    DELETE FROM TLOTDETAIL
    WHERE BIZTRANSACTIONTYPEID = @BizTransactionTypeId
      AND OBJECTHEADERTYPEID   = @ObjectHeaderTypeId
      AND OBJECTHEADERID       = @ObjectHeaderId
      AND OBJECTTYPEID         = @ObjectTypeId
      AND LOTID                = @LotId";

await _queryExecutor.ExecuteAsync(login, deleteSql, new
{
    BizTransactionTypeId = lot.BizTransactionTypeId,
    ObjectHeaderTypeId   = lot.ObjectHeaderTypeId,
    ObjectHeaderId       = lot.LotDetailObjectHeaderId,
    ObjectTypeId         = lot.ObjectTypeId,
    LotId                = lot.LotId
});
```

---

### MM-BOM-04: Concurrent Allocation Race Condition — Overselling / Negative Stock

**Severity:** 🔴 CRITICAL
**File:** `MM/MMDAL/CustomCode/Allocation/AllocationDAL.cs:28–29`

**Problem:** Stock availability is read without a lock — two concurrent orders can both see sufficient stock and both allocate it:

```csharp
// No UPDLOCK or ROWLOCK hint — stale read possible
AllocationDTO allocationDTOs = await _QueryExecutor.QuerySingleAsync<AllocationDTO>(
    LoginDTO, Sql, Parameters);
// Gap here — another transaction can take the same stock
// ... then: INSERT allocation for same stock quantity
```

**Scenario:**
```
T=0ms: Order-A reads available stock = 100 units
T=1ms: Order-B reads available stock = 100 units (same snapshot)
T=5ms: Order-A allocates 100 → stock: 0
T=6ms: Order-B allocates 100 → stock: -100  ← NEGATIVE STOCK
```

**Fix — Pessimistic lock:**
```sql
-- In AllocationQB — add locking hint
SELECT ITEMID, AVAILABLEQTY, RESERVEDQTY
FROM TITEMSTOCK WITH (UPDLOCK, ROWLOCK)
WHERE ITEMID = @ItemId AND STOREID = @StoreId

-- Or use atomic UPDATE + check:
UPDATE TITEMSTOCK
SET RESERVEDQTY = RESERVEDQTY + @AllocQty
WHERE ITEMID = @ItemId
  AND STOREID = @StoreId
  AND (AVAILABLEQTY - RESERVEDQTY) >= @AllocQty; -- Prevents over-allocation atomically

IF @@ROWCOUNT = 0
    THROW 50001, 'Insufficient stock for allocation', 1;
```

---

### MM-BOM-05: BOM Transaction Posting — Non-Atomic Stock Check and Issue

**Severity:** 🔴 CRITICAL
**File:** `MM/MMDAL/CustomCode/BOM/BOMDAL.cs:47–158`

```csharp
// Query 1: Fetch BOM components (no lock)
var result = await _QueryExecutor.QueryAsync<BOMDetailDTO>(...);

// Query 2: Fetch stock for each component (separate query — stock may change between Q1 and Q2)
var stockResult = await _QueryExecutor.QueryAsync<StockPositionDTO>(...);

// Gap: another transaction issues the same stock here
// Post transaction using stale stock values
```

**Impact:** Material issued for production may exceed actual available quantity — physical count vs system count diverge.

**Fix:**
```csharp
await using var scope = await _queryExecutor.BeginTransactionAsync(login);
try
{
    // Lock the stock rows first
    var components = await GetBOMComponentsWithLock(bomId, login, scope.Transaction);
    ValidateSufficientStock(components); // throws if any short
    await PostMaterialIssue(components, productionOrderId, login, scope.Transaction);
    await scope.Transaction.CommitAsync();
}
catch { await scope.Transaction.RollbackAsync(); throw; }
```

---

## HIGH SEVERITY ISSUES

### MM-BOM-06: BOM Component Allows Zero and Negative Quantities

**Severity:** 🟠 HIGH
**File:** `MM/MMDAL/DTO/BOM/BOMDetailDTO.cs:43–44`, `MM/MMDAL/Query/BOM/BOMQB.cs`

```csharp
private double bomdetailquantity;   // No minimum value constraint — double ❌
private double bomdetailpercentage; // No 0–100 range check
```

The SELECT filters `WHERE (FinalQuantity + IssuedQuantity) > 0` but this does not prevent inserting zero or negative quantities in the first place.

**Also:** Uses `double` for quantities — same precision risk as PayRoll module (see [09_PayRoll_Module_Analysis.md](09_PayRoll_Module_Analysis.md)).

**Fix:**
```csharp
// In BOMBLL.SaveBOMDetail():
if (dto.BOMDetailQuantity <= 0)
    throw new ValidationException("BOM component quantity must be greater than zero");
if (dto.BOMDetailPercentage < 0 || dto.BOMDetailPercentage > 100)
    throw new ValidationException("BOM percentage must be between 0 and 100");
```

```sql
-- Database constraint:
ALTER TABLE MBOMDETAIL ADD CONSTRAINT CK_BOMDETAIL_QTY CHECK (Quantity > 0);
```

---

### MM-BOM-07: BOM Effective Date Not Enforced — Wrong Version Used in Production

**Severity:** 🟠 HIGH
**File:** `MM/MMDAL/Query/BOM/BOMQB.cs:15–27`

```sql
-- GET_SELECTLISTBOM — returns ALL BOM versions, no date filter
SELECT *
FROM MBOM MB
LEFT JOIN MITEM MI ON MB.PRODUCTIONITEMID = MI.ITEMID
-- Missing: WHERE MB.BOMFromDate <= GETDATE() AND (MB.BOMToDate IS NULL OR MB.BOMToDate >= GETDATE())
-- Missing: AND MB.BOMIsDefaultVersion = 1
```

**Impact:** Expired BOM versions are returned alongside active ones. Production orders may use an old design with different component quantities — resulting in wrong material consumption and incorrect cost calculations.

**Fix:**
```sql
WHERE MB.BOMFromDate <= GETDATE()
  AND (MB.BOMToDate IS NULL OR MB.BOMToDate >= GETDATE())
  AND MB.BOMIsDefaultVersion = 1
```

---

### MM-BOM-08: BOM Component Item Not Validated Against Item Master

**Severity:** 🟠 HIGH
**File:** `MM/MMDAL/Query/BOM/BOMQB.cs:62–68`

```sql
LEFT JOIN MItem item ON bomdetail.ItemId = item.ItemId  -- LEFT JOIN allows NULL item!
-- Missing: item.ItemStatus = 1 (Active only)
-- Missing: INNER JOIN to enforce item exists
```

A BOM can reference a deleted or inactive item. At production time, stock cannot be issued for a non-existent item — production order fails at runtime, not at BOM creation time.

**Fix:**
```sql
INNER JOIN MItem item ON bomdetail.ItemId = item.ItemId  -- Enforce existence
WHERE item.ItemStatus = 1  -- Active items only
```

---

### MM-BOM-09: Allocation Entity — Write Operations Completely Missing

**Severity:** 🟠 HIGH
**File:** `MM/MMBLL/Allocation/IAllocationBLL.cs`

```csharp
public interface IAllocationBLL
{
    Task<string> GetAllocation(int AllocationId, LoginDTO LoginDTO);
    // No SaveAllocation()   ❌
    // No UpdateAllocation() ❌
    // No DeleteAllocation() ❌
    // No AllocateStock()    ❌
    // No DeallocateStock()  ❌
}
```

The Allocation entity exists as read-only. No mechanism to reserve stock against a sales order or production order exists in the API.

---

### MM-BOM-10: Inspection Has No Quality Workflow — No Defect Tracking

**Severity:** 🟠 HIGH
**File:** `MM/MMBLL/Inspection/InspectionBLL.cs`

```csharp
public async Task<string> SaveInspection(InspectionDTO dto, LoginDTO login)
{
    return await _InspectionDAL.SaveInspection(dto, login);
    // No status workflow (Draft → Submitted → Approved/Rejected)
    // No defect recording linked to inspection
    // No quarantine trigger for rejected goods
    // No approval authority check
}
```

**Impact:** Quality inspections are recorded but have no enforcement effect. Rejected batches are not quarantined. Defective materials can be freely used in production.

---

## MEDIUM SEVERITY ISSUES

### MM-BOM-11: BOM Uses `double` for All Quantities

**Severity:** 🟡 MEDIUM — Same issue as PayRoll
**Files:** `MM/MMDAL/DTO/BOM/BOMDetailDTO.cs`, all Lot/Production DTOs

```csharp
private double bomdetailquantity;   // Should be decimal
private double bomdetailpercentage; // Should be decimal
```

For production with quantities like `0.333` (1/3 of a batch), `double` accumulation error affects material planning.

---

### MM-BOM-12: No BOM Version History / ECR Integration

**Severity:** 🟡 MEDIUM
An ECR (Engineering Change Request) entity exists in MM but no visible linkage from BOM changes → ECR. Engineering changes to BOM (component, quantity changes) should be traceable to an approved ECR.

---

## Summary Table

| # | Issue | Severity | Category | File | Priority |
|---|-------|----------|----------|------|----------|
| MM-BOM-01 | Transaction/Production/Indent BLL/DAL missing | 🔴 CRITICAL | Architecture | Multiple | P0 |
| MM-BOM-02 | No circular BOM detection → crash | 🔴 CRITICAL | BOM | BOMDetailDTO.cs:51 | P0 |
| MM-BOM-03 | SQL injection in Lot delete | 🔴 CRITICAL | Security | LotBLL.cs:51 | P0 |
| MM-BOM-04 | Concurrent allocation race → negative stock | 🔴 CRITICAL | Concurrency | AllocationDAL.cs:28 | P0 |
| MM-BOM-05 | Non-atomic BOM stock check + issue | 🔴 CRITICAL | Data Integrity | BOMDAL.cs:47–158 | P0 |
| MM-BOM-06 | Zero/negative component quantity allowed | 🟠 HIGH | Validation | BOMDetailDTO.cs:43 | P1 |
| MM-BOM-07 | Effective date not enforced on BOM | 🟠 HIGH | Business Logic | BOMQB.cs:15 | P1 |
| MM-BOM-08 | Component item not validated vs item master | 🟠 HIGH | Data Integrity | BOMQB.cs:62 | P1 |
| MM-BOM-09 | Allocation write operations missing | 🟠 HIGH | Architecture | IAllocationBLL.cs | P1 |
| MM-BOM-10 | Inspection has no quality workflow | 🟠 HIGH | Business Logic | InspectionBLL.cs | P1 |
| MM-BOM-11 | `double` for quantities (precision loss) | 🟡 MEDIUM | Data Quality | BOMDetailDTO.cs | P2 |
| MM-BOM-12 | No BOM→ECR traceability | 🟡 MEDIUM | Audit | Multiple | P2 |
