# employee-import-error-file Specification

## Purpose
Defines the annotated error file returned after an employee import with failed rows: its contents and formatting, the download endpoint and its access rules, cleanup of old files, and re-importing a fixed file.

## Requirements

### Requirement: Import Response Error File URL
The employee import response SHALL keep all its existing fields, including `errors[]`, and SHALL add `error_file_url`. When `errors` is empty, `error_file_url` SHALL be `null`. Otherwise it SHALL be the absolute URL of the error file download endpoint for that import. If building the error file fails, the import result SHALL still be returned, with `error_file_url` = `null`, and the failure SHALL be logged.

#### Scenario: Import without errors
- **WHEN** every row of an import is created successfully
- **THEN** the response has `errors` = `[]` and `error_file_url` = `null`

#### Scenario: Import with errors
- **WHEN** at least one row of an import fails
- **THEN** the response has the same `errors[]` as before
- **AND** `error_file_url` is a non-null URL to `GET /api/v1/admin/employees/import-errors/{id}`

### Requirement: Error File Rows
The error file SHALL be built from the first sheet of the uploaded file. It SHALL keep the header row (row 1) and exactly the rows listed in `errors[]`, in their original order. Successfully imported rows and blank rows SHALL be removed.

#### Scenario: One duplicate row and one valid row
- **WHEN** a file has a header and 2 data rows, Excel row 2 fails with `code_duplicated` and Excel row 3 is imported
- **THEN** the error file contains the header and exactly one data row with the values of the original row 2

### Requirement: Error File Structure and Formatting
The error file SHALL keep the column order, header texts and cell formatting of the uploaded sheet. The cells under the "Mã NV" and "SĐT" headers SHALL be stored as text with text number format, so that values with leading zeros, such as `00123`, are kept as typed in the upload. The error file SHALL always be written as `.xlsx`, whatever the uploaded format (`.xlsx`, `.xls` or `.csv`). When the upload is a CSV, its values SHALL be read as text so that leading zeros are not lost.

#### Scenario: Leading zeros kept
- **WHEN** a failed row has "Mã NV" entered as the text `00123`
- **THEN** the error file's "Mã NV" cell for that row is the text `00123`

#### Scenario: CSV upload
- **WHEN** the uploaded file is a `.csv` and a row fails
- **THEN** the error file is a valid `.xlsx` file containing that row

### Requirement: Error File Annotation Columns
The error file SHALL add two columns directly after the last non-empty header cell of row 1: "Dòng gốc" and "Lý do lỗi". For each row, "Dòng gốc" SHALL hold the `row` of its error entry, and "Lý do lỗi" SHALL hold the messages of its `reasons[]`, joined with a line break (`\n`), with wrap text enabled.

#### Scenario: Original row and reason
- **WHEN** Excel row 2 fails only with `code_duplicated`
- **THEN** its "Dòng gốc" cell is `2` and its "Lý do lỗi" cell is "Mã NV đã tồn tại"

#### Scenario: Row with four reasons
- **WHEN** a row fails with four reasons
- **THEN** its "Lý do lỗi" cell contains the four messages on four lines

### Requirement: Error Cell Highlighting
For each reason of a failed row, the cell in that row whose header matches the reason's column SHALL get a light red fill (`#FDE2E1`) and a comment holding that reason's message. When several reasons point to the same cell, the comment SHALL contain all of their messages. Header matching SHALL use the same slugged heading key as the importer, so that spacing and case differences in the header do not prevent the match. When a reason's column is `null` or matches no header, no data cell SHALL be filled for it. The "Lý do lỗi" cell of every failed row SHALL be filled. The import response SHALL NOT change because of this matching.

#### Scenario: Four reasons on four columns
- **WHEN** a row fails with reasons on "Email", "SĐT", "Mã NV" and "Vị trí HĐ"
- **THEN** those four cells of that row are filled light red and have comments

#### Scenario: Unknown error
- **WHEN** a row fails only with `unknown_error`
- **THEN** no data cell of that row is filled
- **AND** only its "Lý do lỗi" cell is filled, with the text "Lỗi không xác định"

### Requirement: Error File Download Endpoint
The system SHALL provide `GET /api/v1/admin/employees/import-errors/{id}` under the admin API authentication. It SHALL return the error file as an `.xlsx` attachment named `HRM_Employees_Import_Errors_<YYYY_MM_DD_HHmmss>.xlsx`, where the timestamp is the time of the import. It SHALL be allowed only for:
- the admin who ran the import; or
- an admin who has the `employees_info` permission and can access the `company_mode` used for the import.

Any other authenticated admin SHALL get 403. Without authentication, the request SHALL get 401. An unknown id, or a file created more than 24 hours ago, SHALL get 404.

#### Scenario: Not logged in
- **WHEN** the error file URL is requested without a bearer token
- **THEN** the response is 401

#### Scenario: Owner downloads
- **WHEN** the admin who ran the import requests the URL within 24 hours
- **THEN** the response is the `.xlsx` file named `HRM_Employees_Import_Errors_<YYYY_MM_DD_HHmmss>.xlsx`

#### Scenario: Admin of another company
- **WHEN** an admin with `employees_info` but without access to the import's `company_mode` requests the URL
- **THEN** the response is 403

#### Scenario: Expired
- **WHEN** the URL is requested more than 24 hours after the import
- **THEN** the response is 404

### Requirement: Error File Cleanup
A scheduled task SHALL run at least daily and delete error files, with their metadata, that were created more than 24 hours ago.

#### Scenario: Old file removed
- **WHEN** the cleanup runs and an error file was created 25 hours ago
- **THEN** the file and its metadata are deleted

### Requirement: Re-importing an Error File
The employee import SHALL accept an error file after the user fixes it. The "Dòng gốc" and "Lý do lỗi" columns SHALL be ignored, including by the blank-row detection. Cell fills and comments SHALL NOT affect the values that are read. No "wrong template" error SHALL be raised because of these columns.

#### Scenario: Fixed error file imported again
- **WHEN** a user fixes the rows of an error file and imports it
- **THEN** the fixed rows are created
- **AND** no error is reported because of the "Dòng gốc" or "Lý do lỗi" columns

#### Scenario: Row with only annotation values
- **WHEN** a row of a re-imported error file has all data cells cleared but still has "Dòng gốc" and "Lý do lỗi" values
- **THEN** the row is treated as blank and skipped without an error
