# MaterialManagement Component — Deep Analysis
**Date:** 2026-02-24
**Scope:** `projects/sales/transaction/materialmanagement/` — all 4 files
**Role:** Config-driven, multi-module transaction shell covering Purchase, Sales, Store, Jobwork, Subcontract — the most functionally rich component in the MFE

---

## 1. Component Inventory

| File | Lines | Purpose |
|---|---|---|
| `materialmanagement.component.ts` | 3,804 | Main transaction component — form, validation, charges, workflow |
| `materialmanagement.component.html` | 735 | Form template with 15+ lightbox modals |
| `materialmanagement.component.scss` | ~80 | Lightbox and form styling |
| `materialmanagement.component.spec.ts` | 1 | **Empty** |

**Total: ~4,620 lines. 0 tests.**

---

## 2. Critical Issues — P0 (Fix Before Any Release)

### SEC-01 — LoginDTO from sessionStorage (Known P0 Violation)
**File:** [materialmanagement.component.ts:210](projects/sales/transaction/materialmanagement/materialmanagement.component.ts#L210)

```typescript
// Constructor — line 210
this.LoginDTO = JSON.parse(sessionStorage.getItem('LoginDTO') as any);
```

`LoginDTO` is accessed in the constructor from sessionStorage and used throughout the component for `WorkOUId`, `WorkPeriodId`, `UserId`, `UserCode`, `OuName`, `WorkPeriodFromDate`, `WorkPeriodToDate`. This is the same P0 pattern listed in CLAUDE.md.

**Impact:** Any XSS payload on the page can steal `LoginDTO`, which contains user identity, OU, and period context.

**Fix:** Read from `DataPassingService` or `GbAppStateService` signals, which should hold auth context via httpOnly cookies. Never touch sessionStorage in a component.

---

### PERF-01 — Missing ChangeDetectionStrategy.OnPush
**File:** [materialmanagement.component.ts:79](projects/sales/transaction/materialmanagement/materialmanagement.component.ts#L79)

```typescript
@Component({
  selector: 'gb-materialmanagement',
  // ❌ No changeDetection: ChangeDetectionStrategy.OnPush
})
```

**Impact:** Angular's default CD runs on every browser event — click, keypress, mousemove — re-evaluating all bindings in a 3,804-line component that renders a full grid plus 15+ lightbox conditionals. This is one of the most expensive components in the system.

**Fix:** Add `changeDetection: ChangeDetectionStrategy.OnPush`. This requires eliminating the 5 `cdr.detectChanges()` calls via signals.

---

### FUNC-01 — Delete Operation is Permanently Broken
**File:** [materialmanagement.component.ts:3311-3340](projects/sales/transaction/materialmanagement/materialmanagement.component.ts#L3311)

```typescript
private handleDelete(): void {
  // ...shows dialog...
  dialogRef.afterClosed().subscribe(result => {
    if (result === 'Delete') {
      // Commented out in original - keep as is
      // this.executeDelete();
    } else if (result === 'Mark as Cancel') {
      // Commented out in original - keep as is
      // this.executeMarkAsCancel();
    }
  });
}
```

The delete dialog opens and the user selects "Delete" or "Mark as Cancel" — but both branches have only commented-out code. No action is executed. Delete is silently broken.

**Fix:** Implement `executeDelete()` and `executeMarkAsCancel()` methods, or surface this as a known broken feature.

---

### FUNC-02 — `GridRefresh.set(this.GridRefresh())` Does Not Trigger Refresh
**File:** [materialmanagement.component.ts:958](projects/sales/transaction/materialmanagement/materialmanagement.component.ts#L958)

```typescript
public GridOutPut(GridData: any[]): void {
  this.form.get('MMDetailArray')?.patchValue(GridData);
  this.GridRefresh.set(this.GridRefresh());  // ← Sets signal to its CURRENT value — no change detected
  // ...
}
```

Setting a signal to its identical current value does not trigger change notification. The grid child component watching `GridRefresh()` sees no change. This call is a no-op in `GridOutPut`. The correct call `this.GridRefresh.set(!this.GridRefresh())` is correctly used on line 523, 1158, 1302, 2440, etc.

**Fix:** Change line 958 to `this.GridRefresh.set(!this.GridRefresh())`.

---

### FUNC-03 — Wrong Disabled Condition on Stock Transfer Load Button
**File:** [materialmanagement.component.html:615-619](projects/sales/transaction/materialmanagement/materialmanagement.component.html#L615)

```html
<!-- Stock Transfer Load modal -->
<button (click)="FromStoretoTostore()"
  [disabled]="form.get('SOMMHeadId')?.value == -1
    || form.get('PRtoPOBizTransactionTypeId')?.value == -1
    || !SOtoPOPostAllowed">
```

The Stock Transfer Post button is disabled based on **SO-to-PO fields** (`SOMMHeadId`, `PRtoPOBizTransactionTypeId`, `SOtoPOPostAllowed`). These have nothing to do with stock transfers. The button should check `StockTransferFromStoreId`, `StockTransferToStoreId`, and `StockTransferDate`.

**Fix:** Change the `[disabled]` condition to validate stock transfer fields.

---

### FUNC-04 — Duplicate Charges on Document Load
**File:** [materialmanagement.component.ts:2516-2535](projects/sales/transaction/materialmanagement/materialmanagement.component.ts#L2516)

```typescript
public OnpicklistLoad(event: any): void {
  this.service.formloadservice('materialmanagement', selectedId)
    .pipe(takeUntil(this.destroy$))
    .subscribe((response: any) => {
      const data = response.responseValue[0];
      this.form.patchValue(data);           // ← (1) Charges come in via patchValue
      // ...
      setTimeout(() => this.MMDocumentChargesOutput(data.MMChargesArray), 500);  // ← (2) Charges pushed AGAIN
    });
}

public MMDocumentChargesOutput(chargeArray: MMChargesArray[]): void {
  const chargesArray = this.form.get('MMChargesArray') as FormArray;
  chargeArray.forEach((item: any) => {
    chargesArray.push(fg);  // ← Appends, does not replace
  });
}
```

When loading a document, `patchValue(data)` sets `MMChargesArray`. Then 500ms later, `MMDocumentChargesOutput` pushes the same charges again into the FormArray **without clearing first**. Every document load doubles the charges in the array.

**Fix:** `MMDocumentChargesOutput` must clear the FormArray before pushing, or the `setTimeout` call in `OnpicklistLoad` should be removed if charges come in via `patchValue`.

---

### FUNC-05 — `createMMChargeDetail` Has Known Field Mapping Bugs
**File:** [materialmanagement.component.ts:2150-2169](projects/sales/transaction/materialmanagement/materialmanagement.component.ts#L2150)

```typescript
// Lines 2151-2152 — confirmed bugs (noted in comments)
ContraAccountCode: chargeDetail.PartyAccountCode,  // ← Bug: uses Party field for Contra
ContraAccountName: chargeDetail.PartyAccountName,  // ← Bug: uses Party field for Contra

// Line 2164
CurrencyCode: chargeDetail.CurrencyId,  // ← Bug: uses Id for Code

// Line 2169
MMChargesReason: chargeDetail.PartyAccountId,  // ← Bug: uses an account ID as a reason
```

The developer noticed these bugs and left comments. They will cause wrong account data in charge postings — contra account will show party account names, and currency code will be an integer ID.

**Fix:** Map `ContraAccountCode` from `chargeDetail.ContraAccountCode`, `ContraAccountName` from `chargeDetail.ContraAccountName`, `CurrencyCode` from `chargeDetail.CurrencyCode`.

---

## 3. Memory Leaks

### LEAK-01 — BiztransactionService and Nested Subscribes Have No takeUntil
**File:** [materialmanagement.component.ts:424-471](projects/sales/transaction/materialmanagement/materialmanagement.component.ts#L424)

```typescript
public BiztransactionService(IsResetRequires = true): void {
  this.service.BizTransactionClassService(...)
    .subscribe((BizTransactionSelectlist: any) => {  // ← No takeUntil
      // ...
      this.loadBizTransactionDetails(bizTrans, IsResetRequires);
    });
}

private loadBizTransactionDetails(bizTrans: any, resetForm: boolean): void {
  this.service.MMBizTransactionType(bizTrans.Id, bizTrans.StoreId)
    .subscribe((response: any) => {   // ← No takeUntil — nested subscribe
      // ...
      this.CheckingSetting();
    });
}

public CheckingSetting(): void {
  this.service.CheckingSetting(transactionTypeId, transactionClassId)
    .subscribe((response: any) => {  // ← No takeUntil — 3rd level
      // ...
    });
}
```

Three levels of nested subscribes with no `takeUntil`. If the component is destroyed (user navigates away) during any of these HTTP calls, the callbacks execute on a destroyed component.

**Impact:** `cdr.detectChanges()` is called inside these callbacks — calling CD on a destroyed component view causes "view has been destroyed" exceptions.

**Fix:** Add `.pipe(takeUntil(this.destroy$))` to all subscribe calls listed below.

### LEAK-02 — Complete List of Subscribes Missing takeUntil

| Line | Method | Call |
|---|---|---|
| 424 | `BiztransactionService` | `BizTransactionClassService` |
| 441 | `loadBizTransactionDetails` | `MMBizTransactionType` |
| 544 | `loadCriteriaConfig` | `CommonGetService` |
| 599 | `loadPartyDetails` | `GetPartyDetailsForInventory` |
| 691 | `CheckingSetting` | `CheckingSetting` |
| 936 | `PartyRefNoInputFocusOutput` | `CommonPostService` |
| 1826 | `fetchAccountPostingDetails` | `AccountPosting` |
| 1848 | `LoadPRtoPO` | `CommonPostService` |
| 1942 | `executeProformaPosting` | `CommonPostService` |
| 2197 | `LoadFromDocument` | `CommonGetService` |
| 2429 | `showConfirmDialog` | `dialogRef.afterClosed()` |
| 2457 | `LoadFromLead` | `CommonGetService` |
| 2592 | `handleBizTransactionTypeChange` | `MMBizTransactionType` |
| 2778 | `handleSOMMHeadChange` | `CommonGetService` |
| 2793 | `handleCurrencyChange` | `CommonPostService` |
| 3229 | `handleSaveSuccess` | `dialogRef.afterClosed()` |
| 3331 | `handleDelete` | `dialogRef.afterClosed()` |

### LEAK-03 — Multiple setTimeouts Without Cleanup
**File:** [materialmanagement.component.ts:229,234,2528,3263,3767](projects/sales/transaction/materialmanagement/materialmanagement.component.ts#L229)

```typescript
// ngOnInit
setTimeout(() => this.initializeFormDefaults(), 500);     // Line 229
setTimeout(() => { this.form.setControl(...) }, 1000);    // Line 234

// OnpicklistLoad
setTimeout(() => this.MMDocumentChargesOutput(...), 500); // Line 2528

// fetchDocumentForPrint
setTimeout(() => this.handleFormResult(saveResult), 1000); // Line 3263

// handleAddNew
setTimeout(() => { this.form.patchValue({...}); }, 1000); // Line 3767
```

Five `setTimeout` calls with no cleanup. If the component is destroyed before any timeout fires, the callback executes against a destroyed form/view.

**Fix per CLAUDE.md:** Use `timer(ms).pipe(take(1), takeUntil(this.destroy$)).subscribe(...)`.

---

## 4. Performance Issues

### PERF-02 — All HTTP Data via Raw subscribe — Not Signals-Based
**File:** Multiple subscribe calls throughout

The component uses raw `.subscribe()` for all 17+ HTTP calls. As per CLAUDE.md, HTTP data should come via `toSignal()` or `rxResource()`. The current pattern requires:
- Manual `cdr.detectChanges()` calls after every subscribe callback (lines 227, 469, 606, 1527, 2606)
- `takeUntil(this.destroy$)` on every subscription (many missing — see LEAK-02)
- Manual loading state management

**Recommended pattern for initialization data:**
```typescript
// Replace nested BiztransactionService → loadBizTransactionDetails → CheckingSetting
bizTypeResource = rxResource({
  request: () => ({ classId: this.menuRights.BizTransactionClassId }),
  loader: ({ request }) => this.service.BizTransactionClassService(request.classId).pipe(
    switchMap(list => this.service.MMBizTransactionType(list.responseValue[0].Id, list.responseValue[0].StoreId))
  )
});
```

### PERF-03 — 5× Manual cdr.detectChanges() Calls
**File:** Lines 227, 469, 606, 1527, 2606

```typescript
this.cdr.detectChanges();  // After subscribe callbacks
```

Manual CD calls cause synchronous, synchronous rendering and are unnecessary with OnPush + signals.

**Fix:** Eliminate by adopting signals pattern. Once `ChangeDetectionStrategy.OnPush` is in place and state is signal-based, these calls are counterproductive.

### PERF-04 — `calculateTotals` and `calculateOtherValueTotals` are Nearly Identical
**Files:** Lines 979-1057 and 1079-1121

Two separate reduce functions iterate the same array calculating the same fields (`TotalQty`, `TotalRate`, `TotalFCValue`, `TotalGrossValue`, `TotalTax`). `calculateTotals` (called from `GridOutPut`) and `calculateOtherValueTotals` (called from `DocumentCharges`) compute overlapping totals separately. When grid has many rows (50+), this is wasted work.

**Fix:** Compute OtherValue as a `computed()` derived from grid totals signal.

---

## 5. Code Quality & Maintainability

### QUAL-01 — Excessive `any` Types (30+ Instances)

Key untyped properties:
```typescript
BizTransactionType: any;          // Line 103 — core config object used everywhere
LoginDTO: any;                     // Line 115 — auth context
ItemCharges: any;                  // Line 127
ItemDetail: any;                   // Line 131
AccountPostingArray: any = {};     // Line 144
CheckingSettingDetail: any;        // Line 149
PartyBranchDetail: any;            // Line 162
ModifiedMMDetaiArray: any;         // Line 204
```

Every service subscribe typed as `(response: any) => {}`. All 23 `handleXxx(setting: any)` methods.

**Fix:** Define interfaces:
```typescript
interface BIZTransactionType {
  BIZTransactionTypeId: number;
  BIZTransactionTypeName: string;
  BIZTransactionTypeNOGenerationType: number;
  BIZTransactionTypeBackDateEntryDay: number;
  BIZTransactionTypeForwardEntryDay: number;
  BIZTransactionTypeBackDateUpdateDay: number;
  BizTransactionClassStockPostType: number;
  BizTransactionClassIsPartyApplicable: number;
  BIZTransactionTypeIsCashCredit: number;
  OtherChargeArray: OtherChargeArray[];
  AdditionalChargeId: number;
  TotalChargeId: number;
  StoreId: number;
  StoreName: string;
  CurrencyId: number;
  CurrencyCode: string;
  // ...
}

interface CheckingSetting {
  CheckingCode: string;
  CheckingSettingDetailType: number;
}
```

### QUAL-02 — `CheckingSettingCondition` is Not Typed

The `CheckingSettingCondition` object (lines 163-198) is initialized with specific keys but `handleXxx` methods add additional properties (`EDITRATETYPE`, `ITEMREPEATTYPE`, `LOADCHREDTTYPE`, etc.) dynamically that are not in the initial object declaration. This is an untyped expanding object — TypeScript cannot warn about typos.

**Fix:** Define a `CheckingCondition` interface with all required keys as optional, and type the object strictly.

### QUAL-03 — 15+ console.log Calls (Violates CLAUDE.md)
**Lines:** 232, 239, 423, 438, 439, 448, 514, 562, 694, 766, 1130, 1131, 1149, 1154, 1157, 2233, 2236, 2255, 2266, 2272, 2294, 2725, 2729, 3725, 3729, 3738, 3745, 3760, 3766

Many logs include form values and response data. Per CLAUDE.md, use `GbConsoleService` and remove debug logs before commit.

### QUAL-04 — Hardcoded Module ID Magic Numbers Bypass Defined Constants
**File:** Lines 2678-2683, 2744-2745

```typescript
// Uses raw values instead of MODULE_IDS constants defined at the top
if (this.MenuRights.ModuleId === -1899999994) {    // Should be MODULE_IDS.SALES
  this.form.patchValue({ IsSaleable: 0 });
} else if (this.MenuRights.ModuleId === -1899999995) {  // Should be MODULE_IDS.PURCHASE
  // ...
}

const isSalesModule = [-1899999994, -1399999998].includes(this.MenuRights.ModuleId);
// Should be: [MODULE_IDS.SALES, MODULE_IDS.JOBWORK].includes(...)
```

`MODULE_IDS` is defined at lines 40-46 but only partially used. Raw values re-appear in `setModuleSpecificFlags()`, `updatePartyPaymentTerms()`, `applyPaymentTermsByModule()`.

Additional magic numbers not covered by any constant:
- Line 580: `-1799999922` (BizTransactionTypeClassId check)
- Line 2262: `1399999909` (special class check in multi-load)
- Line 2732: `-1899999977`, `-1899999839` (entity IDs — these ARE in ENTITY_IDS but raw values used)

**Fix:** Define additional constants and use them consistently.

### QUAL-05 — Three Duplicate Party Payment Term Methods

| Method | Location | Purpose |
|---|---|---|
| `applyPartyDetails` | Line 637 | Applies party info incl. payment terms |
| `updatePartyPaymentTerms` | Line 2743 | Same — applied in `loadPartyDetails` path |
| `applyPaymentTermsByModule` | Line 3004 | Same — applied in `handlePartySelection` path |

All three do the same thing: check module ID and set `PaymentTermId/Name` and optionally `InchargeId/Name`. They will diverge and introduce bugs as edge cases are fixed in one but not others.

**Fix:** Consolidate into a single `applyPaymentTermsForParty(partyDetail)` method.

### QUAL-06 — `applyPartyDetails` and `applyBasicPartyInfo` are Duplicate Methods
**File:** Lines 637-677 and 2968-2999

`applyPartyDetails` is defined but appears **never called** (it's shadowed by `applyBasicPartyInfo` in the picklist path). It's dead code that will diverge from the active path.

### QUAL-07 — `updateEntityId` Has Dead Else-If Branch
**File:** [materialmanagement.component.ts:2730-2738](projects/sales/transaction/materialmanagement/materialmanagement.component.ts#L2730)

```typescript
private updateEntityId(bizType: any): void {
  if (bizType.BizTransactionClassFromAddress === 0) {
    this.MenuRights.EntityId = -1899999977;    // PARTY_BRANCH
  } else if (parseInt(bizType.BIZTransactionTypeClassId) === -1799999922) {
    this.MenuRights.EntityId = -1899999839;    // ADDRESS
  } else {
    this.MenuRights.EntityId = -1899999839;    // ADDRESS — SAME AS else if!
  }
}
```

Both `else if` and `else` set the same value. The `else if` condition is unreachable in practice.

### QUAL-08 — `(this as any)[config.dialogFlag] = false` Bypasses Type Safety
**File:** [materialmanagement.component.ts:1947](projects/sales/transaction/materialmanagement/materialmanagement.component.ts#L1947)

```typescript
(this as any)[config.dialogFlag] = false;
```

Dynamic property access via `as any`. Typos in `dialogFlag` strings (`'PRtoPOShow'`, `'SOtoPOShow'`) will silently fail at runtime.

**Fix:** Use a signal map or a typed method:
```typescript
// Option 1: signal map
private lightboxSignals: Record<string, WritableSignal<boolean>> = {
  PRtoPOShow: signal(false),
  SOtoPOShow: signal(false),
};

// Option 2: typed method
private closeLightbox(flag: 'PRtoPOShow' | 'SOtoPOShow'): void {
  this[flag] = false;
}
```

### QUAL-09 — Mutation of Injected `MenuRights` Input
**File:** Lines 576-578, 2732-2737

```typescript
this.MenuRights.EntityId = responseValue.BizTransactionClassFromAddress === 0
  ? ENTITY_IDS.PARTY_BRANCH
  : ENTITY_IDS.ADDRESS;
```

`MenuRights` is injected via `@Inject('MenuRights')`. The component mutates it directly, which mutates shared state owned by the parent. This is a side-effect violation — inputs should be treated as immutable.

**Fix:** Maintain `EntityId` as a local component property.

### QUAL-10 — Commented-Out Code That Should be Removed or Implemented

| Line | Code | Status |
|---|---|---|
| 215-220 | Commented-out `effect()` setup | Should be implemented or deleted |
| 94 | Commented `private fb = inject(FormGroup)` | Delete |
| 3332-3337 | `executeDelete()`, `executeMarkAsCancel()` both commented | Critical — delete is broken |
| 186, 960-961 | `console.log` in template comment | Clean up |

### QUAL-11 — `createMMChargesFormGroup` is Not Type-Safe
**File:** [materialmanagement.component.ts:3748-3757](projects/sales/transaction/materialmanagement/materialmanagement.component.ts#L3748)

```typescript
createMMChargesFormGroup(data: any): FormGroup {
  const group: any = {};
  Object.keys(data).forEach(key => {
    group[key] = [data[key]];
  });
  return this.fb.group(group);
}
```

Dynamically builds a FormGroup from unknown keys. TypeScript cannot validate the controls. If `data` has unexpected fields, they all get added to the FormGroup silently.

**Fix:** Define `MMChargeFormModel` interface and build the group from explicit typed fields.

---

## 6. Functional Issues

### FUNC-06 — Timezone Hardcoded to IST (19800000ms = UTC+5:30)
**File:** Lines 39, 339, 1393, 2031, 2789, 3393

```typescript
const GMT_OFFSET = 19800000; // IST offset in milliseconds
```

This constant is used for date validation (backdated entry, forward entry), currency exchange rate lookup, and proforma invoice billing date. The entire date system assumes IST (India Standard Time). Any deployment in a different timezone will have systematically wrong date validation and exchange rate lookups.

**Fix:** Compute timezone offset from the server session context or use `Intl.DateTimeFormat` to get the local offset.

### FUNC-07 — `loadPartyDetails` Called with Wrong Argument
**File:** [materialmanagement.component.ts:465-466](projects/sales/transaction/materialmanagement/materialmanagement.component.ts#L465)

```typescript
if (this.BizTransactionType.BIZTransactionTypePartyId !== -1) {
  this.loadPartyDetails(this.BizTransactionType.BIZTransactionTypePartyId);
  // ↑ Passes a number (PartyId)
}

private loadPartyDetails(bizType: any): void {
  this.form.patchValue({
    PartyId: bizType.BIZTransactionTypePartyId,  // ← Tries to access .BIZTransactionTypePartyId on a number!
```

`loadPartyDetails` expects the whole `BizTransactionType` object but is called with just `BIZTransactionTypePartyId` (a number). The internal `form.patchValue` will set `PartyId` to `undefined`.

The same function is called correctly at line 2603: `this.loadPartyDetails(response.responseValue)`.

**Fix:** Standardize the call to pass the full object, or rename/overload appropriately.

### FUNC-08 — `MMHeadDate: 'today'` is a Magic String Dependency
**File:** Lines 2303, 2363, 2469

```typescript
this.form.patchValue({
  MMHeadDate: 'today',   // Relies on gb-date component to interpret 'today'
});
```

The string `'today'` is a contract between this component and `gb-date`. If `gb-date` changes how it handles this string, date initialization silently breaks. This should use the actual current date in the `/Date(timestamp)/` format.

### FUNC-09 — `FromStoretoTostore` Subscribe Callback is Empty
**File:** [materialmanagement.component.ts:3690-3695](projects/sales/transaction/materialmanagement/materialmanagement.component.ts#L3690)

```typescript
this.service.GetFromStoretoTostore(fromStoreId, toStoreId, transferDate)
  .pipe(takeUntil(this.destroy$))
  .subscribe((result: any) => {
    // TODO: Handle result - currently empty handler
  });
```

The API is called, network resources consumed, but the response is never used. Stock transfer load is non-functional.

### FUNC-10 — `MultiLoadCopyFrom`, `FillPODetails`, `ProcessDetailOutPut`, `getMMDetailCriteria` are Empty

```typescript
public MultiLoadCopyFrom() { }                           // Line 3715
public FillPODetails(event: any) { console.log(...) }   // Line 3724
public ProcessDetailOutPut(event: any) { console.log(...) } // Line 3728
public getMMDetailCriteria() { }                         // Line 3732
```

Four public methods bound in the template are either empty or do nothing useful. These represent incomplete features that users may accidentally trigger.

### FUNC-11 — Credit Limit Validation Uses `PartyBranchDetail[0]` Without Guard
**File:** [materialmanagement.component.ts:3172-3173](projects/sales/transaction/materialmanagement/materialmanagement.component.ts#L3172)

```typescript
const partyBalance = parseFloat(this.PartyBranchDetail[0]?.PartyClosingBalance || '0');
const creditLimit = this.PartyBranchDetail[0]?.CreditLimit || 0;
```

`PartyBranchDetail` is typed as `any` and initialized to `undefined`. If a save is attempted before a party is selected (or when loading from a document without party selection), `PartyBranchDetail[0]` throws. Optional chaining is used inconsistently here.

**Fix:** Guard: `if (!this.PartyBranchDetail?.length) return true;` before credit limit check.

### FUNC-12 — `parseDateValue` and `parseDate` are the Same Function

```typescript
// Line 332
private parseDateValue(dateStr: string): number {
  return parseInt(dateStr.replace('/Date(', '').replace(')/', ''));
}

// Line 2396
private parseDate(dateString: string): number {
  return parseInt(dateString.replace('/Date(', '').replace(')/', ''));
}
```

Identical implementations — one was added without finding the other. Should be consolidated to a single `parseMicrosoftDate(dateStr: string): number` utility method.

### FUNC-13 — `handleSaveSuccess` Parses `documentNumber` with Fragile String Split
**File:** [materialmanagement.component.ts:3217](projects/sales/transaction/materialmanagement/materialmanagement.component.ts#L3217)

```typescript
const documentNumber = result.responseValue.Body.split(":")[1];
```

If `Body` doesn't contain `:`, this returns `undefined`. If `Body` contains multiple colons (e.g., a timestamp or URL in the message), the split produces the wrong segment.

**Fix:** Parse the document number from a structured field rather than splitting a display string.

---

## 7. i18n Issues

### I18N-01 — All Error/Info Dialog Messages are Hardcoded English

Affected strings in TypeScript (should use Transloco keys):
- `"Please Select date between Financial Year"` (line 345)
- `"BackDated Entry"` (line 355, 374)
- `"Forward Entry"` (line 364)
- `"No Biz Transaction Type Found"` (line 427)
- `"Please Fill partyrefno."` (line 3112)
- `"BackDated Entry. Not Possible to Update this Document."` (line 3106)
- `"BackDated Entry. Not Possible to Delete this Document."` (line 3316)
- `"Do you want print?"` (line 3222)
- `"Do you want to?"` (line 3323)
- `"No Biztransaction Type is created for Purchase Requisition."` (line 1852)
- `"Please Select Document before Load"` (line 2190)
- `"Please Select Lead before Load"` (line 2450)
- `"Same Party reference number found for current Year"` (line 3940)
- Charge configuration mismatch message (line 2335)

### I18N-02 — Template Hardcoded English Strings

- `"Bill Value (INR)"` (line 135) — also hardcodes INR currency
- `"Total Quantity"`, `"Basic Value"`, `"Gross Value"` etc. — all grid labels
- `"Cancelled"` watermark (line 20)
- All lightbox titles: `"Load From"`, `"Document Charges"`, `"Item Charges"`, etc.
- `"Load Doc"`, `"Load Item"`, `"Load Lead"` buttons

### I18N-03 — Currency Code Hardcoded in Template
**File:** [materialmanagement.component.html:135](projects/sales/transaction/materialmanagement/materialmanagement.component.html#L135)

```html
<span style="font-size: 20px;margin-right: 8px;">Bill Value (INR)</span>
```

System supports multi-currency (form has `CurrencyCode`). Should be:
```html
<span>{{ 'mm.total.bill_value' | transloco }} ({{ form.get('CurrencyCode')?.value }})</span>
```

---

## 8. Responsive Design Issues

### RESP-01 — All Lightbox Widths are Fixed Pixels (No 95vw Fallback)

All 15+ lightboxes use fixed pixel widths without `min(Xpx, 95vw)` per CLAUDE.md:

| Lightbox | Current Width | Fix |
|---|---|---|
| FormButtonLightBox | `width: 620px` | `min(620px, 95vw)` |
| PartyOutstanding | `width: 1260px` | `min(1260px, 95vw)` |
| DocumentPrinting | `width: 1260px` | `min(1260px, 95vw)` |
| AccountPostingShow | `width: 1260px` | `min(1260px, 95vw)` |
| PRtoPOShow | `width: 750px; height: 150px` | `min(750px, 95vw)` |
| SOtoPOShow | `width: 750px; height: 150px` | `min(750px, 95vw)` |
| ShowReservationLight | `width: 1260px` | `min(1260px, 95vw)` |
| ItemExtendedSearch | `width: 1260px` | `min(1260px, 95vw)` |
| StockTransferShow | `width: 750px; height: 350px` | `min(750px, 95vw)` |
| CopyFrom | `width: 750px; height: 350px` | `min(750px, 95vw)` |
| ShowProcessLight | `width: 1150px; height: 470px` | `min(1150px, 95vw)` |
| ShowTotalChargesmodal | `width: 1270px` | `min(1270px, 95vw)` |
| ShowBOMmodal | `height: 400px` | `min(400px, 90vh)` |

### RESP-02 — 60+ Hardcoded Inline Styles in Template

Virtually all layout is done via inline `style=""` attributes. Examples:
- `style="width: 528px;"` — form section
- `style="font-size: 210px; color: gray;"` — Cancelled watermark
- `style="height: 180px;"`, `style="height: 75px;"`, `style="height: 85px;"` — field containers
- `style="position: relative; top: -15px;"` — multiple date/input positioning

This makes the layout completely unmaintainable and not responsive.

### RESP-03 — No RTL CSS Support
No `[dir="rtl"]` considerations in component styles or template. The directional layout (left-aligned form, lightbox positioning) will break in RTL locales.

---

## 9. Architecture Issues

### ARCH-01 — Component is 3,804 Lines — Must be Decomposed

The component handles too many distinct concerns:

| Concern | Lines | Suggested Extraction |
|---|---|---|
| Form initialization & defaults | ~100 | `MMFormInitService` |
| Date validation logic | ~80 | `MMDateValidationService` |
| Business transaction type loading | ~250 | Part of `MaterialManagementService` |
| Checking settings (23 handlers) | ~250 | `MMCheckingSettingsService` |
| Party selection & details | ~150 | Part of `MaterialManagementService` |
| Charge distribution logic | ~300 | `MMChargeCalculationService` |
| Account posting aggregation | ~200 | `MMAccountPostingService` |
| Document load (single/multi/lead/copy) | ~400 | `MMDocumentLoadService` |
| PR-to-PO / SO-to-PO workflow | ~200 | `MMAllocationService` |
| Currency conversion | ~100 | Part of `MaterialManagementService` |
| Printing | ~100 | Reuse existing print service |
| 15 lightbox state flags | ~50 | Signal map |
| Item search mapping (240-field object) | ~250 | `MMDetailMapper` |

### ARCH-02 — No Signals Architecture

The component uses zero signals for derived state. All totals (`TotalQty`, `TotalBasicValue`, etc.) are plain number properties updated imperatively. They should be `computed()` signals derived from the grid data signal.

```typescript
// Target state
private gridData = signal<MMDetailArray[]>([]);

readonly TotalQty = computed(() =>
  this.gridData().reduce((sum, item) =>
    sum + (parseFloat(item.MMDetailTransactionActualQuantity) || 0), 0)
);

readonly TotalGrossValue = computed(() =>
  this.gridData().reduce((sum, item) =>
    sum + (parseFloat(item.MMDetailItemGrossValue) || 0), 0)
);
```

This eliminates the `calculateTotals()` and `Object.assign(this, totals)` pattern and makes totals automatically reactive.

### ARCH-03 — `mapSearchedItemsToMMDetailArray` Maps 80+ Fields in a Single Function
**File:** [materialmanagement.component.ts:3444-3679](projects/sales/transaction/materialmanagement/materialmanagement.component.ts#L3444)

The function creates an object with 80+ fields across 12 categories. This is the full MM detail object initialized from a search result. It contains:
- Many hardcoded default values (`MMDetailExciseRegisterLastNo: 1`, `CostCenterId: 1`)
- Potential issues with field types (mixing strings and numbers: `PackId: item.ItemPackId ?? "-1"` vs `MMDetailId: 0`)
- `InterOUId: -1500000000` — hardcoded regardless of context

This function belongs in a `MMDetailMapper` service, should return a typed `MMDetailArray` object, and hardcoded defaults should come from configuration.

---

## 10. Summary — Priority Matrix

| ID | Issue | Severity | Effort |
|---|---|---|---|
| SEC-01 | LoginDTO from sessionStorage | P0 Critical | Medium |
| PERF-01 | Missing OnPush on component | P0 Critical | Low |
| FUNC-01 | Delete operation is broken | P0 Critical | Medium |
| FUNC-02 | GridRefresh.set(same value) — no refresh | P0 Critical | Trivial |
| FUNC-03 | Wrong disabled condition on Stock Transfer button | P0 Critical | Trivial |
| FUNC-04 | Duplicate charges on document load | P0 High | Low |
| FUNC-05 | createMMChargeDetail has 4 confirmed field mapping bugs | P0 High | Low |
| FUNC-07 | loadPartyDetails called with wrong argument type | P0 High | Low |
| LEAK-01 | 17 subscribes missing takeUntil | P1 High | Medium |
| LEAK-03 | 5 setTimeouts without cleanup | P1 High | Low |
| QUAL-03 | 15+ console.log calls | P1 High | Low |
| FUNC-06 | IST timezone hardcoded | P1 High | Medium |
| FUNC-09 | Stock transfer response never used | P1 High | Medium |
| FUNC-10 | 4 public methods are empty/stub | P1 High | High |
| FUNC-11 | Credit limit crashes if no party selected | P1 High | Trivial |
| QUAL-01 | 30+ `any` types — define interfaces | P2 Medium | High |
| QUAL-02 | CheckingSettingCondition not typed | P2 Medium | Medium |
| QUAL-04 | Magic number IDs bypass MODULE_IDS constants | P2 Medium | Low |
| QUAL-05 | 3 duplicate payment term methods | P2 Medium | Low |
| QUAL-06 | applyPartyDetails is dead code | P2 Medium | Trivial |
| QUAL-07 | updateEntityId dead else-if branch | P2 Medium | Trivial |
| QUAL-08 | `(this as any)[flag]` dynamic property bypass | P2 Medium | Low |
| QUAL-09 | MenuRights input mutated directly | P2 Medium | Low |
| QUAL-11 | createMMChargesFormGroup not type-safe | P2 Medium | Low |
| FUNC-08 | 'today' magic string date dependency | P2 Medium | Low |
| FUNC-12 | parseDateValue and parseDate are duplicated | P2 Medium | Trivial |
| FUNC-13 | documentNumber parsed via fragile string split | P2 Medium | Low |
| PERF-02 | All HTTP via subscribe — not rxResource/toSignal | P2 Medium | High |
| PERF-03 | 5× manual detectChanges() calls | P2 Medium | Low |
| PERF-04 | Duplicate total calculation passes | P2 Medium | Low |
| I18N-01 | 14+ hardcoded English dialog messages | P2 Medium | Medium |
| I18N-02 | All template strings hardcoded English | P2 Medium | Medium |
| I18N-03 | INR hardcoded in bill value label | P2 Medium | Trivial |
| RESP-01 | All 15 lightboxes use fixed pixel widths | P2 Medium | Low |
| RESP-02 | 60+ hardcoded inline styles | P3 Low | High |
| RESP-03 | No RTL support | P3 Low | Medium |
| QUAL-10 | Dead/commented code throughout | P3 Low | Low |
| ARCH-01 | 3,804-line component needs decomposition | P3 Low | Very High |
| ARCH-02 | No signals architecture | P3 Low | Very High |
| ARCH-03 | mapSearchedItemsToMMDetailArray 80-field mapping | P3 Low | Medium |

---

## 11. Quick Wins (< 30 min each)

1. **FUNC-02:** Change `this.GridRefresh.set(this.GridRefresh())` → `this.GridRefresh.set(!this.GridRefresh())` on line 958
2. **FUNC-03:** Fix Stock Transfer button `[disabled]` to use stock transfer fields, not SO-to-PO fields
3. **QUAL-07:** Remove dead `else if` in `updateEntityId` — collapse to `else`
4. **QUAL-06:** Delete the never-called `applyPartyDetails` method (lines 637-677)
5. **FUNC-12:** Remove `parseDate()` (line 2396) — use `parseDateValue()` everywhere
6. **FUNC-11:** Add `if (!this.PartyBranchDetail?.length) return true;` guard at credit limit check
7. **PERF-01:** Add `changeDetection: ChangeDetectionStrategy.OnPush` to component decorator
8. **QUAL-04:** Replace 6 raw module ID literals with `MODULE_IDS.SALES` / `MODULE_IDS.PURCHASE` constants
9. **I18N-03:** Change `"Bill Value (INR)"` to use dynamic `CurrencyCode` from form
10. **QUAL-03:** Remove all `console.log` calls (30+)
11. **RESP-01:** Add `max-width: 95vw` to all lightbox `.lightboxmodal-content` styles in SCSS
12. **FUNC-07:** Fix `loadPartyDetails` call at line 465 — pass `this.BizTransactionType` not `this.BizTransactionType.BIZTransactionTypePartyId`

---

## 12. Critical Bugs Summary (Confirmed Defects)

These are confirmed bugs that affect live functionality:

| # | Bug | Location | Symptom |
|---|---|---|---|
| B1 | Delete dialog shows but does nothing | L3332-3337 | Users cannot delete documents |
| B2 | GridRefresh no-op in GridOutPut | L958 | Grid may not reflect form state changes |
| B3 | Stock Transfer button disabled by wrong condition | HTML:615 | Stock transfer Post always disabled if SO fields empty |
| B4 | Duplicate charges on document load | L2528 | Every picklist load doubles charge entries |
| B5 | ContraAccount mapped from PartyAccount fields | L2151-2152 | Wrong account names in charge posting |
| B6 | CurrencyCode set to CurrencyId integer | L2164 | Account posting shows numeric ID as code |
| B7 | loadPartyDetails called with number not object | L465 | Party details not set when BizTransactionType has fixed party |
| B8 | fromStoretoTostore response never handled | L3692 | Stock transfer load functionality incomplete |
