# Batch 13 Controllers Analysis (Controllers 121-130)

## Controllers in this batch:
1. ProfileOtherEmployeeController
2. ProfileOtherEmployeeTempController
3. ProfileSalaryController
4. ProfileSalaryEmployeeController
5. ProfileSalaryEmployeeTempController
6. ProfileSalaryTempController
7. ProfileTrainingController
8. ProfileTrainingEmployeeController
9. ProfileTrainingEmployeeTempController
10. ProvinceController

---

## Critical Issues Found

### 1. **ProfileSalaryTempController** - Multiple Unhandled forEach Async Operations

**File & Location:** [ProfileSalaryTempController.ts](src/controllers/ProfileSalaryTempController.ts) - Methods: `listSalary()`, `confirmDoneSalary()`, `changeSortEditGenAll()`, `changeSortEdit()`

**Problem Type:** 1. Unhandled Exception / 2. Missing Error Handle

**Root Cause:**
Multiple methods use `forEach()` with async operations without proper error handling or awaiting. When errors occur in these async callbacks, they become unhandled rejections that can crash the Node.js process.

**Affected Code Locations:**
- Line 1058-1061: `salaryOld.forEach(async (p, i) => { ... })` in `deleteSalary()`
- Line 1115-1118: `salaryList.forEach(async (p, i) => { ... })` in `deleteSalary()`
- Line 202-205: `salaryOld.forEach((item: any, i) => { ... })` in `listSalary()` (sync operations but no error handling)
- Line 1729-1741: `for await` loop with database operations without error handling in `changeSortEditGenAll()`
- Line 1763-1766: `salaryOld.forEach()` in `changeSortEdit()`

**Code Examples:**

```typescript
// Line 1115-1118 - DANGEROUS: async forEach without error handling
salaryList.forEach(async (p, i) => {
  p.order = i + 1;
  await this.salaryRepo.save(p);  // If this fails, error is unhandled
});
```

```typescript
// Line 1729-1741 - DANGEROUS: for await without try-catch
for await (const item of profiles) {
  let salaryOld = await this.salaryOldRepo.find({
    where: { profileId: item.id },
    order: { commandDateAffect: "ASC", order: "ASC" },
  });

  salaryOld.forEach((item: any, i) => {
    item.order = i + 1;
  });
  num = num + 1;
  console.log(num);
  await this.salaryOldRepo.save(salaryOld);  // If this fails, entire operation crashes
}
```

**Recommended Fix:**

```typescript
// For deleteSalary() - Use Promise.all with error handling
try {
  await Promise.all(
    salaryList.map(async (p, i) => {
      p.order = i + 1;
      await this.salaryRepo.save(p);
    })
  );
} catch (error) {
  console.error('Error updating salary order:', error);
  // Optionally throw a more specific error or handle gracefully
  throw new HttpError(HttpStatus.INTERNAL_SERVER_ERROR, 'Failed to update salary order');
}

// For changeSortEditGenAll() - Add error handling per iteration
try {
  const profiles = await this.profileRepo.find();
  let num = 1;
  
  for await (const item of profiles) {
    try {
      let salaryOld = await this.salaryOldRepo.find({
        where: { profileId: item.id },
        order: { commandDateAffect: "ASC", order: "ASC" },
      });

      salaryOld.forEach((item: any, i) => {
        item.order = i + 1;
      });
      
      await this.salaryOldRepo.save(salaryOld);
      num = num + 1;
      console.log(num);
    } catch (error) {
      console.error(`Error processing profile ${item.id}:`, error);
      // Continue with next profile instead of crashing
      continue;
    }
  }

  return new HttpSuccess();
} catch (error) {
  console.error('Error in changeSortEditGenAll:', error);
  throw new HttpError(HttpStatus.INTERNAL_SERVER_ERROR, 'Failed to process profiles');
}
```

---

### 2. **ProfileSalaryController** - Unhandled forEach Async Operations

**File & Location:** [ProfileSalaryController.ts](src/controllers/ProfileSalaryController.ts) - Methods: `deleteSalary()`, `Registry()`, `RegistryEmployee()`

**Problem Type:** 1. Unhandled Exception / 2. Missing Error Handle

**Root Cause:**
Multiple critical methods use `forEach()` with async database operations. When database operations fail within these callbacks, the Promise rejection is unhandled and can crash the service.

**Affected Code Locations:**
- Line 1115-1118: `salaryList.forEach(async (p, i) => { ... })` in `deleteSalary()`
- Line 362-373: Complex async operations in `Registry()` without error handling
- Line 383-395: Complex async operations in `RegistryEmployee()` without error handling
- Line 412-427: `record.map(async (r) => { ... })` with `Promise.all()` but no error handling
- Line 463-477: Similar pattern in `getSalaryPositionUser()`
- Line 497-512: Similar pattern in `getSalary()`

**Code Examples:**

```typescript
// Line 1115-1118 - CRITICAL: async forEach without error handling
salaryList.forEach(async (p, i) => {
  p.order = i + 1;
  await this.salaryRepo.save(p);  // Unhandled rejection
});
```

```typescript
// Line 412-427 - Promise.all without error handling
const result = await Promise.all(
  record.map(async (r) => {
    let _command = null;
    if (r.commandId) {
      _command = await this.commandRepository.findOne({
        where: { id: r.commandId },
        relations: ["commandType"],
      });
    }
    return {
      ...r,
      commandType: _command && _command?.commandType ? _command?.commandType.code : null,
    };
  }),
);
```

**Recommended Fix:**

```typescript
// For deleteSalary() - Proper error handling
try {
  await Promise.all(
    salaryList.map(async (p, i) => {
      p.order = i + 1;
      await this.salaryRepo.save(p);
    })
  );
} catch (error) {
  console.error('Error updating salary order:', error);
  throw new HttpError(HttpStatus.INTERNAL_SERVER_ERROR, 'Failed to update salary order');
}

// For Promise.all operations - Add error boundary
try {
  const result = await Promise.all(
    record.map(async (r) => {
      try {
        let _command = null;
        if (r.commandId) {
          _command = await this.commandRepository.findOne({
            where: { id: r.commandId },
            relations: ["commandType"],
          });
        }
        return {
          ...r,
          commandType: _command && _command?.commandType ? _command?.commandType.code : null,
        };
      } catch (error) {
        console.error(`Error loading command for salary ${r.id}:`, error);
        return {
          ...r,
          commandType: null,
        };
      }
    }),
  );
  return new HttpSuccess(result);
} catch (error) {
  console.error('Error processing salary records:', error);
  throw new HttpError(HttpStatus.INTERNAL_SERVER_ERROR, 'Failed to load salary data');
}

// For Registry() - Add comprehensive error handling
try {
  await this.registryRepo.clear();
  
  const allRegis = await AppDataSource.getRepository(viewRegistryOfficer)
    .createQueryBuilder("registryOfficer")
    .getMany();
  
  const profileIds = new Set((await this.profileRepo.find()).map((p) => p.id));
  
  const mapData = allRegis
    .filter((x) => profileIds.has(x.profileId))
    .map((x) => ({
      ...x,
      isProbation: Boolean(x.isProbation),
      isLeave: Boolean(x.isLeave),
      isRetirement: Boolean(x.isRetirement),
      Educations: x.Educations ? JSON.stringify(x.Educations) : "",
    }));
  
  if (mapData.length > 0) {
    // Save in batches to avoid overwhelming the database
    const batchSize = 100;
    for (let i = 0; i < mapData.length; i += batchSize) {
      const batch = mapData.slice(i, i + batchSize);
      await this.registryRepo.save(batch);
    }
  }
  
  return new HttpSuccess();
} catch (error) {
  console.error('Error in Registry cronjob:', error);
  throw new HttpError(HttpStatus.INTERNAL_SERVER_ERROR, 'Failed to sync registry data');
}
```

---

### 3. **ProfileSalaryController** - Raw SQL Queries Without Error Handling

**File & Location:** [ProfileSalaryController.ts](src/controllers/ProfileSalaryController.ts) - Multiple methods using `AppDataSource.query()`

**Problem Type:** 2. Missing Error Handle

**Root Cause:**
Multiple stored procedure calls (`CALL GetProfile...()`) are executed without try-catch blocks. If these stored procedures fail or the database is unavailable, the errors will propagate unhandled.

**Affected Code Locations:**
- Line 76-79: `CALL GetProfileSalaryPosition(?, ?)` in `cronjobTenurePositionOfficer()`
- Line 126-129: Similar in `cronjobTenurePositionEmployee()`
- Line 176-179: `CALL GetProfileSalaryLevel(?, ?)` in `cronjobTenureLevelOfficer()`
- Line 236-239: Similar in `cronjobTenureLevelEmployee()`
- Line 317-320: `CALL GetProfileSalaryExecutive(?, ?)` in `cronjobTenureExecutivePositionOfficer()`
- Line 588-591, 622-625, 662-665: Multiple calls in `getPositionTenureUser()`
- Line 722-725, 760-763, 803-806: Multiple calls in `getPositionTenure()`

**Code Examples:**

```typescript
// Line 76-79 - No error handling for stored procedure call
const position = await AppDataSource.query("CALL GetProfileSalaryPosition(?, ?)", [
  x.id,
  _currentDate,
]);
```

**Recommended Fix:**

```typescript
// Wrap all database query calls in try-catch
try {
  const position = await AppDataSource.query("CALL GetProfileSalaryPosition(?, ?)", [
    x.id,
    _currentDate,
  ]);
  
  const _position = position.length > 0 ? position[0] : [];
  const mapPosition =
    _position.length > 1
      ? _position.slice(1).map((curr: any, index: number) => ({
          days_diff: curr.days_diff,
          positionName: _position[index]?.positionName,
        }))
      : [];
  
  const calDayDiff = mapPosition
    .filter((curr: any) => curr.positionName == x.position)
    .reduce(
      (acc: any, curr: any) => {
        acc.days_diff += Number(curr.days_diff) || 0;
        acc.positionName = curr.positionName;
        return acc;
      },
      { days_diff: 0, positionName: null },
    );
  
  const { year, month, day } = calculateTenure(calDayDiff.days_diff);
  const mapData: any = {
    profileId: x.id,
    positionName: calDayDiff.positionName,
    days_diff: calDayDiff.days_diff,
    Years: year,
    Months: month,
    Days: day,
  };
  data.push(mapData);
} catch (error) {
  console.error(`Error processing position tenure for profile ${x.id}:`, error);
  // Add default/error entry or skip this profile
  const mapData: any = {
    profileId: x.id,
    positionName: null,
    days_diff: 0,
    Years: 0,
    Months: 0,
    Days: 0,
    error: true,
  };
  data.push(mapData);
}
```

---

### 4. **ProfileTrainingController** - Multiple Database Operations Without Error Handling

**File & Location:** [ProfileTrainingController.ts](src/controllers/ProfileTrainingController.ts) - Methods: `deleteAllTraining()`, `deleteById()`

**Problem Type:** 2. Missing Error Handle

**Root Cause:**
Multiple sequential delete operations without transaction or error handling. If intermediate operations fail, the database can be left in an inconsistent state.

**Affected Code Locations:**
- Line 238-259: `deleteAllTraining()` - Multiple delete operations without transaction
- Line 274-339: `deleteById()` - Multiple delete operations without transaction

**Code Examples:**

```typescript
// Line 238-259 - No error handling or transaction
const trainings = await this.trainingRepo.find({
  select: { id: true },
  where: { developmentId: reqBody.developmentId },
});
if (trainings.length > 0) {
  const trainingIds = trainings.map((x) => x.id);
  await this.trainingHistoryRepo.delete({
    profileTrainingId: In(trainingIds),
  });
  await this.trainingRepo.delete({
    developmentId: reqBody.developmentId,
  });
}

await this.developmentHistoryRepo.delete({
  kpiDevelopmentId: reqBody.developmentId,
});
await this.developmentRepo.delete({ 
  kpiDevelopmentId: reqBody.developmentId 
});
```

**Recommended Fix:**

```typescript
@Post("delete-all")
public async deleteAllTraining(
  @Body() reqBody: { developmentId: string },
  @Request() req: RequestWithUser
) {
  const queryRunner = AppDataSource.createQueryRunner();
  await queryRunner.connect();
  await queryRunner.startTransaction();

  try {
    const trainings = await queryRunner.manager.find(ProfileTraining, {
      select: { id: true },
      where: { developmentId: reqBody.developmentId },
    });
    
    if (trainings.length > 0) {
      const trainingIds = trainings.map((x) => x.id);
      
      await queryRunner.manager.delete(ProfileTrainingHistory, {
        profileTrainingId: In(trainingIds),
      });
      
      await queryRunner.manager.delete(ProfileTraining, {
        developmentId: reqBody.developmentId,
      });
    }

    await queryRunner.manager.delete(ProfileDevelopmentHistory, {
      kpiDevelopmentId: reqBody.developmentId,
    });
    
    await queryRunner.manager.delete(ProfileDevelopment, { 
      kpiDevelopmentId: reqBody.developmentId 
    });

    await queryRunner.commitTransaction();
    return new HttpSuccess();
  } catch (error) {
    await queryRunner.rollbackTransaction();
    console.error('Error deleting training data:', error);
    throw new HttpError(
      HttpStatus.INTERNAL_SERVER_ERROR,
      'Failed to delete training data'
    );
  } finally {
    await queryRunner.release();
  }
}

// Similar fix for deleteById()
@Post("delete-byId")
public async deleteById(
  @Body() reqBody: {
    type: string;
    profileId: string;
    developmentId: string;
  },
  @Request() req: RequestWithUser
) {
  const queryRunner = AppDataSource.createQueryRunner();
  await queryRunner.connect();
  await queryRunner.startTransaction();

  try {
    const type = reqBody.type?.trim().toUpperCase();
    
    // 1. validate profile
    if (type === "OFFICER") {
      const profile = await queryRunner.manager.findOne(Profile, {
        where: { id: reqBody.profileId }
      });
      if (!profile) {
        throw new HttpError(HttpStatus.BAD_REQUEST, "ไม่พบ profile ดังกล่าว");
      }
    } else {
      const profile = await queryRunner.manager.findOne(ProfileEmployee, {
        where: { id: reqBody.profileId }
      });
      if (!profile) {
        throw new HttpError(HttpStatus.BAD_REQUEST, "ไม่พบ profile ดังกล่าว");
      }
    }

    const profileField = type === "OFFICER" ? "profileId" : "profileEmployeeId";

    // 2. Find and delete ProfileTraining
    const trainings = await queryRunner.manager.find(ProfileTraining, {
      select: { id: true },
      where: {
        developmentId: reqBody.developmentId,
        [profileField]: reqBody.profileId,
      },
    });

    if (trainings.length > 0) {
      const trainingIds = trainings.map(x => x.id);

      await queryRunner.manager.delete(ProfileTrainingHistory, {
        profileTrainingId: In(trainingIds),
      });

      await queryRunner.manager.delete(ProfileTraining, {
        id: In(trainingIds),
      });
    }

    // 3. Find and delete ProfileDevelopment
    const developments = await queryRunner.manager.find(ProfileDevelopment, {
      select: { id: true },
      where: {
        kpiDevelopmentId: reqBody.developmentId,
        [profileField]: reqBody.profileId,
      },
    });

    if (developments.length > 0) {
      const devIds = developments.map(x => x.id);

      await queryRunner.manager.delete(ProfileDevelopmentHistory, {
        profileDevelopmentId: In(devIds),
      });

      await queryRunner.manager.delete(ProfileDevelopment, {
        id: In(devIds),
      });
    }

    await queryRunner.commitTransaction();
    return new HttpSuccess();
  } catch (error) {
    await queryRunner.rollbackTransaction();
    console.error('Error deleting by ID:', error);
    if (error instanceof HttpError) {
      throw error;
    }
    throw new HttpError(
      HttpStatus.INTERNAL_SERVER_ERROR,
      'Failed to delete data'
    );
  } finally {
    await queryRunner.release();
  }
}
```

---

### 5. **ProfileSalaryEmployeeController** - forEach Async Operations Without Error Handling

**File & Location:** [ProfileSalaryEmployeeController.ts](src/controllers/ProfileSalaryEmployeeController.ts) - Method: `deleteSalaryEmployee()`

**Problem Type:** 1. Unhandled Exception / 2. Missing Error Handle

**Root Cause:**
Similar to ProfileSalaryController, uses `forEach()` with async operations without proper error handling.

**Affected Code Locations:**
- Line 608-611: `salaryList.forEach(async (p, i) => { ... })` in `deleteSalaryEmployee()`

**Code Example:**

```typescript
// Line 608-611 - DANGEROUS
salaryList.forEach(async (p, i) => {
  p.order = i + 1;
  await this.salaryRepo.save(p);  // Unhandled rejection
});
```

**Recommended Fix:**

```typescript
try {
  await Promise.all(
    salaryList.map(async (p, i) => {
      p.order = i + 1;
      await this.salaryRepo.save(p);
    })
  );
} catch (error) {
  console.error('Error updating salary order:', error);
  throw new HttpError(HttpStatus.INTERNAL_SERVER_ERROR, 'Failed to update salary order');
}
```

---

### 6. **ProfileSalaryEmployeeTempController** - forEach Async Operations Without Error Handling

**File & Location:** [ProfileSalaryEmployeeTempController.ts](src/controllers/ProfileSalaryEmployeeTempController.ts) - Method: `deleteSalaryEmployee()`

**Problem Type:** 1. Unhandled Exception / 2. Missing Error Handle

**Root Cause:**
Same pattern as above - `forEach()` with async operations.

**Affected Code Locations:**
- Line 202-205: `salaryList.forEach(async (p, i) => { ... })` in `deleteSalaryEmployee()`

**Recommended Fix:** Same as above - use `Promise.all()` with error handling.

---

### 7. **ProfileSalaryTempController** - confirmDoneSalary() Transaction Handling Issues

**File & Location:** [ProfileSalaryTempController.ts](src/controllers/ProfileSalaryTempController.ts) - Method: `confirmDoneSalary()`

**Problem Type:** 2. Missing Error Handle

**Root Cause:**
While this method uses transactions, there are several potential issues:
1. Line 1686: Empty `catch` block that swallows all errors
2. Line 1493-1497: Error is re-thrown without proper logging or context
3. Multiple complex operations within transaction that could fail

**Affected Code Locations:**
- Line 1493-1498: `catch` block re-throws error without logging
- Line 1685: Empty `catch` block in `returnEdit()`

**Code Examples:**

```typescript
// Line 1493-1498 - Insufficient error handling
} catch (error) {
  await queryRunner.rollbackTransaction();
  throw error;  // No logging, no context
} finally {
  await queryRunner.release();
}
```

**Recommended Fix:**

```typescript
} catch (error) {
  await queryRunner.rollbackTransaction();
  console.error('Error in confirmDoneSalary:', {
    profileId: body.profileId,
    type: body.type,
    error: error instanceof Error ? error.message : error,
    stack: error instanceof Error ? error.stack : undefined,
  });
  
  // Provide more specific error message
  if (error instanceof HttpError) {
    throw error;
  }
  
  throw new HttpError(
    HttpStatus.INTERNAL_SERVER_ERROR,
    'Failed to confirm salary data. Please try again.'
  );
} finally {
  await queryRunner.release();
}

// For returnEdit() - Proper error handling
try {
  if (profile) {
    profile.statusCheckEdit = "PENDING";
    await this.profileRepo.save(profile);
  } else if (profileEmployee) {
    profileEmployee.statusCheckEdit = "PENDING";
    await this.profileEmployeeRepo.save(profileEmployee);
  }

  const history: PositionSalaryEditHistory = Object.assign(
    new PositionSalaryEditHistory(),
    body,
  );
  
  if (profile) {
    history.profileId = profileId;
  } else if (profileEmployee) {
    history.profileEmployeeId = profileId;
  }
  
  history.returnedDate = new Date();
  history.examinerName = req.user.name;
  history.createdFullName = req.user.name;
  history.lastUpdateFullName = req.user.name;
  
  await this.positionSalaryEditHistoryRepo.save(history);

  return new HttpSuccess();
} catch (error) {
  console.error('Error in returnEdit:', error);
  throw new HttpError(
    HttpStatus.INTERNAL_SERVER_ERROR,
    'Failed to process return edit request'
  );
}
```

---

### 8. **ProfileSalaryTempController** - Bulk Operations Without Error Handling

**File & Location:** [ProfileSalaryTempController.ts](src/controllers/ProfileSalaryTempController.ts) - Methods: `listSalary()`, `confirmDoneSalary()`

**Problem Type:** 2. Missing Error Handle

**Root Cause:**
Bulk insert operations without error handling for individual records. If one record fails, the entire operation may fail or data may be partially inserted.

**Affected Code Locations:**
- Line 1058-1061: `salaryOld.forEach()` without error handling
- Line 1098-1101: Similar pattern
- Line 1425-1431: Bulk insert without error handling

**Code Example:**

```typescript
// Line 1425-1431 - Bulk insert without error handling
if (salaryRows.length) {
  await queryRunner.manager.insert(
    ProfileSalary,
    salaryRows.map(({ id, ...data }) => ({
      ...data,
      ...metaCreated,
    })),
  );
}
```

**Recommended Fix:**

```typescript
// Implement batch processing with error handling
if (salaryRows.length) {
  const batchSize = 100; // Process in batches
  for (let i = 0; i < salaryRows.length; i += batchSize) {
    const batch = salaryRows.slice(i, i + batchSize);
    
    try {
      await queryRunner.manager.insert(
        ProfileSalary,
        batch.map(({ id, ...data }) => ({
          ...data,
          ...metaCreated,
        }))
      );
    } catch (error) {
      console.error(`Error inserting salary batch ${i / batchSize + 1}:`, error);
      // Log which records failed
      const failedIds = batch.map(b => b.id);
      console.error('Failed record IDs:', failedIds);
      throw new HttpError(
        HttpStatus.INTERNAL_SERVER_ERROR,
        `Failed to insert salary records (batch ${i / batchSize + 1})`
      );
    }
  }
}
```

---

### 9. **ProvinceController** - Try-Catch With Generic Error Handling

**File & Location:** [ProvinceController.ts](src/controllers/ProvinceController.ts) - Method: `Delete()`

**Problem Type:** 2. Missing Error Handle

**Root Cause:**
While there is a try-catch block, it catches all errors without logging or differentiation. This makes debugging difficult and may mask underlying issues.

**Affected Code Locations:**
- Line 168-175: Generic catch block

**Code Example:**

```typescript
// Line 168-175 - Generic error handling
let result: any;
try {
  result = await this.provinceRepository.delete({ id: id });
} catch {
  throw new HttpError(
    HttpStatusCode.NOT_FOUND,
    "ไม่สามารถลบได้เนื่องจากมีการใช้งานข้อมูลจังหวัดนี้อยู่",
  );
}
```

**Recommended Fix:**

```typescript
let result: any;
try {
  result = await this.provinceRepository.delete({ id: id });
} catch (error) {
  console.error('Error deleting province:', {
    id,
    error: error instanceof Error ? error.message : error,
    stack: error instanceof Error ? error.stack : undefined,
  });
  
  // Check for foreign key constraint error
  if (error instanceof Error && error.message.includes('foreign key constraint')) {
    throw new HttpError(
      HttpStatusCode.CONFLICT, // Use 409 instead of 404
      "ไม่สามารถลบได้เนื่องจากมีการใช้งานข้อมูลจังหวัดนี้อยู่",
    );
  }
  
  throw new HttpError(
    HttpStatusCode.INTERNAL_SERVER_ERROR,
    "เกิดข้อผิดพลาดในการลบข้อมูลจังหวัด",
  );
}
```

---

## Summary Statistics

**Total Critical Issues Found:** 9

**Breakdown by Type:**
- **Unhandled Exception (forEach with async):** 6 instances
- **Missing Error Handling (DB operations):** 8 instances
- **Transaction Issues:** 2 instances
- **Generic Error Handling:** 1 instance

**Controllers with Issues:**
1. ProfileSalaryTempController - 4 critical issues
2. ProfileSalaryController - 3 critical issues
3. ProfileSalaryEmployeeController - 1 critical issue
4. ProfileSalaryEmployeeTempController - 1 critical issue
5. ProfileTrainingController - 2 critical issues
6. ProvinceController - 1 minor issue

**Risk Level: HIGH**

---

## Priority Recommendations

### Immediate Actions Required:

1. **Replace all `forEach()` with async operations** - Use `Promise.all()` or `for...of` loops with proper error handling
2. **Add error boundaries** around all database operations
3. **Implement proper logging** for all errors
4. **Use transactions** for multi-step database operations
5. **Add circuit breakers** for external dependencies (database, stored procedures)

### Graceful Recovery Strategies:

1. **Implement request-level error boundaries** - Catch errors at the controller level and return appropriate HTTP responses
2. **Add database operation timeouts** - Prevent indefinite hangs
3. **Implement retry logic** for transient database errors
4. **Add health checks** - Monitor database connectivity
5. **Use connection pooling** with proper error handling

### Long-term Improvements:

1. **Implement a centralized error handling middleware**
2. **Add structured logging** (e.g., Winston, Pino)
3. **Implement request tracing** for debugging
4. **Add metrics/monitoring** for error rates
5. **Implement graceful shutdown** procedures

---

## Testing Recommendations

1. **Test database failure scenarios** - Disconnect database during operations
2. **Test with large datasets** - Ensure forEach operations don't cause memory issues
3. **Test transaction rollback** - Verify data consistency on errors
4. **Test concurrent requests** - Ensure race conditions don't cause crashes
5. **Test stored procedure failures** - Simulate SP errors
