# BlacksoilController Documentation ## Overview The `BlacksoilController` is responsible for handling the Business Rule Engine (BRE) and co-lending integrations for the **Blacksoil** vendor. It acts as the orchestrator for ingesting loan decision batches (via UI file upload or background S3 processing), parsing lead data, calling the external BRE decision API, storing the results, and providing a UI to visualize these decisions. ## Architecture & Data Flow The typical flow for processing loan decisions through this controller is as follows: 1. **Ingestion**: - A user uploads an Excel/CSV file via `importLoanDecision` (Admin UI). - Alternatively, a background job triggers `startLoanDecisionFileProcess` which downloads a file from S3. 2. **Parsing & Evaluation (`evaluate`)**: - The file is parsed row by row using the `Maatwebsite\Excel` package. - Each row is passed through `categorize()` to split the data into **Demographic** data and **Decision** data. - The decision payload is strictly type-cast using `castDecisionTypes()`. - The lead is saved to the database (`vendor_loans_data`). 3. **API Integration (`callDecisionApi`)**: - The standardized decision payload is dispatched to the external Blacksoil BRE API via GuzzleHTTP. 4. **Storage (`vendor_bre_decision`)**: - The response from the BRE API (including pass/fail rules) is recorded in the `vendor_bre_decision` table. 5. **Visualization**: - The `viewDecisionResults` and `viewDecisionDetail` methods serve the UI for administrators to review the BRE outcomes. --- ## Key Methods ### Core Processing #### `evaluate($localFilePath = null)` The primary entry point for processing batches. - Accepts either an uploaded file from the request or a local file path (for background S3 jobs). - Validates the file and initializes the Excel reader. - Iterates through the rows, extracts demographic/decision data, saves the lead, calls the external API, and logs the results. - Returns a JSON summary of the batch processing (total rows, success count, failures). #### `startLoanDecisionFileProcess($file_path)` Used for background processing. - Connects to the AWS S3 disk, retrieves the file specified by `$file_path`, and saves it to the local `/tmp` directory. - Calls `$this->evaluate($tempPath)` to process the file offline, bypassing standard HTTP request validation. #### `importLoanDecision(Request $request)` The UI-facing endpoint for file uploads. - Validates the incoming request file. - Delegates to the `evaluate()` method to process the upload. ### Database Operations #### `createLead(array $demographic, array $row, $batchId, $rowNumber)` Inserts the core demographic data into the `vendor_loans_data` table and returns the newly created `vendor_loan_id`. #### `saveCoApplicants($vendorLoanId, $applicationId, array $row)` Extracts co-applicant data (up to 3 co-applicants) from the row and inserts them into the `vendor_loans_co_applicants` table. #### `saveDecisionData($vendorLoanId, $applicationId, $batchId, array $decision)` Saves the raw mapped decision payload into `vendor_loans_decision_data` for historical record-keeping. ### API Integration #### `callDecisionApi(array $payload)` Handles the HTTP communication with the external Blacksoil Decision API. - Uses GuzzleHTTP to POST the JSON payload. - Returns a tuple: `[boolean $apiOk, int $httpStatus, array|string $apiResponse, string $errorMsg]`. - Catches and gracefully handles connection timeouts and HTTP exceptions. ### UI & Visualization #### `viewDecisionResults(Request $request)` Serves the paginated list view of decisions. - Joins `vendor_loans_data` with `vendor_bre_decision`. - Passes the result to the `admin.co-lending.decision-results` blade view. #### `viewDecisionDetail($id)` Serves the deep-dive detail view for a specific decision. - Fetches the row from `vendor_bre_decision` and joins `vendor_loans_data` to retrieve the `customer_name` and `gender`. - Passes the payload to `admin.co-lending.decision-detail` to render the collapsible policy rule evaluations. ### Data Mapping & Type Casting The controller enforces strict mapping to prevent invalid data from corrupting the BRE evaluations. - **`categorize(array $row)`**: Splits the flat Excel row into a `$demographic` array and a `$decision` array. - **`demographicKeys()`**: Defines the accepted keys for lead demographics (e.g., `application_id`, `customer_name`, address details). - **`decisionKeys()`**: Defines the strict keys expected by the BRE API. - **`decisionTypes()`**: A dictionary mapping decision keys to strict PHP types (`int`, `float`, `bool`, `string`). - **`castDecisionTypes(array $decision)`**: Iterates over the payload and applies the `decisionTypes()`. Critical for ensuring that string labels (like `"TRUE"`) don't accidentally get cast to `0` by upstream numerical type-hints. --- ## Logging The controller heavily utilizes the custom `BlacksoilBreLogger` facade for granular event tracking. **Logged Events:** - `evaluate.received`: When a batch is received. - `row.db_failed`: If database insertion fails for a specific row. - `row.api_request` / `row.api_response`: Full request payloads and HTTP statuses for the external BRE calls. - `api.exception`: Network-level failures. All logs are serialized and written to the dedicated Blacksoil logging channel to aid in debugging vendor integration issues.