# Firms Controller Documentation

**File**: `/controllers/firms.php`  
**Purpose**: Manages company/firm directory with hierarchical filtering by country, region, and department  
**Last Updated**: December 20, 2024  
**Total Functions**: 9  
**Lines of Code**: 368

---

## 📋 Overview

The Firms Controller is a directory management system that provides hierarchical navigation through firms based on geographic and business department filtering. It handles:
- Company/firm directory management
- Geographic filtering (country → region → department)
- Firm branch management
- Product catalog browsing
- Package offerings display
- File attachment handling
- AJAX-based filtering interface
- Multi-level navigation system

### Primary Functions
- [x] Hierarchical firm filtering (country/region/department)
- [x] Company detail views
- [x] Product catalog integration
- [x] Branch location management
- [x] Package offerings display
- [x] File attachment management
- [x] AJAX navigation interface
- [x] Multi-language support (Arabic/English)

### Related Controllers
- [propertiesController.php](propertiesController.md) - Property management
- [ads.php](ads.md) - Advertisement management

---

## 🗄️ Database Tables

### Primary Tables (Direct Operations)
| Table Name | Purpose | Key Columns |
|------------|---------|-------------|
| **firms** | Company master data | id, name, logo, departid |
| **firmbranches** | Company branch locations | id, firmid, regionid |
| **firmproducts** | Company product catalog | id, firmid, name, image |
| **firmfiles** | Company file attachments | id, firmid, filename, filepath |
| **packagefirms** | Company package offerings | id, firmid, packageid |

### Reference Tables
| Table Name | Purpose | Key Columns |
|------------|---------|-------------|
| **country** | Country master data | id, name, hide |
| **region** | Regional divisions | id, name, countryid, hide |
| **depart** | Business departments | id, name, image |
| **package** | Package definitions | id, name, image |

---

## 🔑 Key Functions

### 1. **index() / Default Action** - Hierarchical Firm Directory
**Location**: Line 33  
**Purpose**: Display firms filtered by geographic and department hierarchy

**Function Signature**:
```php
public function index()
// Triggered when: no action specified or main directory access
```

**Process Flow**:
1. Parse incoming filter parameters (country, region, department)
2. Apply hierarchical filtering logic
3. Execute appropriate SQL query based on filter combination
4. Load supporting data (countries, regions, departments)
5. Display via filter.html and departments.html templates

**Filter Combinations**:
- All filters: Show specific firms in region/department
- Country + Region: Show departments in region
- Country only: Show departments in country
- No filters: Show all departments

**Features**:
- Geographic drill-down navigation
- Department-based filtering
- Dynamic template assignment
- Multi-language support

---

### 2. **getregions()** - AJAX Region Loading
**Location**: Line 97  
**Purpose**: Load regions for selected country via AJAX

**Function Signature**:
```php
public function getregions()
// Triggered via: AJAX POST with countryId
```

**Process Flow**:
1. Receive country ID from POST
2. Query regions for specified country
3. Build HTML option list for dropdown
4. Query departments available in country
5. Generate department display cards
6. Return JSON response with both datasets

**Response Format**:
```json
{
  "result1": "<option>...</option>",  // Region dropdown options
  "result2": "<li><div>...</div></li>" // Department cards HTML
}
```

---

### 3. **getdepts()** - AJAX Department Loading
**Location**: Line 133  
**Purpose**: Load departments for selected country/region combination

**Function Signature**:
```php
public function getdepts()
// Triggered via: AJAX POST with countryId, regionId
```

**Process Flow**:
1. Receive country and region IDs
2. Query departments with firms in specified region
3. Build dropdown options and display cards
4. Include department images and firm count links
5. Return formatted JSON response

---

### 4. **getfirms()** - AJAX Firm Loading
**Location**: Line 169  
**Purpose**: Load firms for selected department/region combination

**Function Signature**:
```php
public function getfirms()
// Triggered via: AJAX POST with deptId, regionId
```

**Process Flow**:
1. Query firms matching department and region
2. Generate firm display cards with logos
3. Include links to firm detail pages
4. Return HTML-formatted firm listings

---

### 5. **showcompanies()** - Company Listing Page
**Location**: Line 202  
**Purpose**: Display paginated company listings based on filter criteria

**Function Signature**:
```php
public function showcompanies()
// Triggered when: accessing firms by department/region
```

**Process Flow**:
1. Parse filter parameters from URL
2. Execute complex filtering logic
3. Load supporting reference data
4. Display via firms.html template

**Filter Logic**:
```php
if ($regionId != -1 && $countryId != -1) {
    // Specific region and department
} elseif ($regionId == -1 && $countryId != -1) {
    // Country and department only
} elseif ($countryId == -1 && $regionId != -1) {
    // Region and department only
} else {
    // Department only
}
```

---

### 6. **companydetail()** - Company Detail View
**Location**: Line 258  
**Purpose**: Display comprehensive company information

**Function Signature**:
```php
public function companydetail()
// Triggered when: accessing individual company details
```

**Process Flow**:
1. Load firm master data
2. Retrieve all branch locations
3. Load product catalog
4. Get package offerings with details
5. Load file attachments
6. Process package metadata
7. Display via company.html template

**Data Enhancement**:
```php
foreach ($firmPackages as $firmPackage) {
    $package = R::load('package', $firmPackage->packageid);
    $firmPackage->packName = $package->name;
    $firmPackage->packPhoto = $package->image;
}
```

---

### 7. **productdetail()** - Product Detail View
**Location**: Line 284  
**Purpose**: Display individual product information

**Function Signature**:
```php
public function productdetail()
// Triggered when: accessing product details from firm catalog
```

**Process Flow**:
1. Load product data by ID
2. Preserve search context
3. Display via product.html template

---

### 8. **getcomps()** - Advanced AJAX Company Filtering
**Location**: Line 298  
**Purpose**: Handle complex AJAX filtering with multiple parameters

**Function Signature**:
```php
public function getcomps()
// Triggered via: AJAX POST with complex filter parameters
```

**Process Flow**:
1. Process multiple filter parameters
2. Apply same filtering logic as showcompanies()
3. Handle special cases (country changes)
4. Return JSON with options and display HTML

---

## 🔄 Workflows

### Workflow 1: Hierarchical Firm Discovery
```
┌─────────────────────────────────────────────────────────────┐
│                START: Access Firms Directory                │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  1. Load Initial View                                       │
│     - Display all countries                                 │
│     - Show all departments                                  │
│     - Set filter state to "show all"                       │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  2. User Selects Country                                    │
│     AJAX call: getregions()                                 │
│       │                                                     │
│       ├─→ Load regions for country                         │
│       │                                                     │
│       ├─→ Load departments in country                      │
│       │                                                     │
│       └─→ Update dropdowns and display                     │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  3. User Selects Region                                     │
│     AJAX call: getdepts()                                   │
│       │                                                     │
│       ├─→ Load departments in region                       │
│       │                                                     │
│       └─→ Update department display                        │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  4. User Selects Department                                 │
│     AJAX call: getfirms()                                   │
│       │                                                     │
│       ├─→ Load firms in department/region                  │
│       │                                                     │
│       └─→ Display firm cards with logos                    │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  5. User Clicks Firm Details                                │
│     Navigate to: companydetail()                            │
│       │                                                     │
│       ├─→ Load firm master data                            │
│       │                                                     │
│       ├─→ Load branches, products, packages                │
│       │                                                     │
│       └─→ Display comprehensive firm profile               │
└─────────────────────────────────────────────────────────────┘
```

---

### Workflow 2: Direct Firm Access
```
┌─────────────────────────────────────────────────────────────┐
│            START: Direct URL or Link Access                 │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  1. Parse URL Parameters                                    │
│     - Extract department, country, region IDs              │
│     - Validate parameter combinations                       │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  2. Execute Appropriate Query                               │
│     IF all parameters present:                              │
│       │                                                     │
│       ├─→ Show firms in specific dept/region               │
│       │                                                     │
│     ELSE IF region missing:                                 │
│       │                                                     │
│       ├─→ Show firms in dept/country                       │
│       │                                                     │
│     ELSE:                                                   │
│       │                                                     │
│       └─→ Show all firms in department                     │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  3. Load Supporting Data                                    │
│     - Load countries for filter dropdown                    │
│     - Load regions if country specified                     │
│     - Prepare navigation breadcrumbs                        │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  4. Display Results                                         │
│     - Show firm listings with logos                         │
│     - Enable filtering interface                            │
│     - Provide detail page links                             │
└─────────────────────────────────────────────────────────────┘
```

---

## 🌐 URL Routes & Actions

| Route Pattern | Function Called | Description |
|---------------|----------------|-------------|
| `/firms/` or `/firms/index` | `index()` | Main directory page |
| `/firms/showcompanies/{dept},{country},{region}` | `showcompanies()` | Filtered company listings |
| `/firms/companydetail/{id}` | `companydetail()` | Individual firm details |
| `/firms/productdetail/{productid}` | `productdetail()` | Product detail page |

### AJAX Endpoints
| Endpoint | Function Called | Purpose |
|----------|----------------|---------|
| POST `/firms/getregions` | `getregions()` | Load regions for country |
| POST `/firms/getdepts` | `getdepts()` | Load departments for region |
| POST `/firms/getfirms` | `getfirms()` | Load firms for filters |
| POST `/firms/getcomps` | `getcomps()` | Advanced firm filtering |

### Required Parameters

**Company Listings** (`showcompanies`):
- `id` - Comma-separated: `{deptId},{countryId},{regionId}`
- Use `-1` for "all" in any position

**Company Details** (`companydetail`):
- `id` - Firm ID

**Product Details** (`productdetail`):
- `productid` - Product ID
- `search` - Optional search context

---

## 🔒 Security & Permissions

### Input Sanitization
```php
$companyId = filter_input(INPUT_GET, 'id');
$countryId = filter_input(INPUT_POST, 'countryId');
// All inputs filtered through filter_input()
```

### File Access Control
- Images served from `/upload/` directories
- File paths validated before access
- No direct file system access from user input

### SQL Injection Prevention
- Uses RedBeanPHP ORM for all database operations
- Parameterized queries throughout
- No raw SQL with user input

---

## 📊 Performance Considerations

### Database Optimization Tips
1. **Indexes Required**:
   - `firmbranches(regionid, firmid)`
   - `firms(departid)`
   - `firmproducts(firmid)`
   - `region(countryid, hide)`

2. **Query Optimization**:
   - Complex JOINs in filtering queries
   - DISTINCT clauses for unique results
   - Consider materialized views for complex filters

3. **Memory Management**:
   - Large datasets for major departments
   - Image loading optimization needed
   - AJAX pagination for large firm lists

### Known Performance Issues
```sql
-- This query can be slow with many firms
SELECT distinct f.* from firms f
JOIN depart d ON d.id = f.departid
JOIN firmbranches b ON b.firmid = f.id
WHERE b.regionid = ? and f.departid = ?

-- Solution: Add composite index
CREATE INDEX idx_firm_branch_region ON firmbranches(regionid, firmid);
CREATE INDEX idx_firm_department ON firms(departid);
```

---

## 🐛 Common Issues & Troubleshooting

### 1. **Missing Firm Images**
**Issue**: Firms display without logos or broken image links  
**Cause**: Missing files in `/upload/firms/` directory

**Debug**:
```php
// Check file existence before display
if (file_exists("../upload/firms/{$firm->logo}")) {
    echo $firm->logo;
} else {
    echo "default-logo.png";
}
```

### 2. **AJAX Filtering Not Working**
**Issue**: Dropdown changes don't trigger region/department updates  
**Cause**: JavaScript errors or missing POST parameters

**Debug**:
```javascript
// Check browser console for errors
// Verify POST data format
console.log("Sending:", {countryId: selectedValue});
```

### 3. **Hierarchical Filtering Logic Errors**
**Issue**: Wrong firms displayed for filter combination  
**Cause**: Complex conditional logic in filtering functions

**Debug**:
```sql
-- Test filter queries directly
SELECT DISTINCT f.* FROM firms f
JOIN firmbranches b ON b.firmid = f.id
WHERE b.regionid = 5 AND f.departid = 3;

-- Check for missing relationships
SELECT f.id, f.name, COUNT(b.id) as branch_count
FROM firms f
LEFT JOIN firmbranches b ON b.firmid = f.id
GROUP BY f.id
HAVING branch_count = 0;
```

### 4. **Slow Page Loading**
**Issue**: Directory pages load slowly with large datasets  
**Cause**: Complex queries without proper indexing

**Solution**:
- Add database indexes on JOIN columns
- Implement pagination for large result sets
- Cache department/region data
- Optimize image loading with lazy loading

---

## 🧪 Testing Scenarios

### Test Case 1: Hierarchical Navigation
```
1. Access main firms directory
2. Select country from dropdown
3. Verify regions load correctly
4. Select region, verify departments update
5. Select department, verify firms display
6. Check all filter combinations work
```

### Test Case 2: Direct URL Access
```
1. Access URL: /firms/showcompanies/5,2,3
2. Verify correct firms display
3. Test with -1 parameters for "all"
4. Verify breadcrumb navigation works
```

### Test Case 3: Company Detail Page
```
1. Click firm from listings
2. Verify all sections load:
   - Company information
   - Branch locations
   - Product catalog
   - Package offerings
   - File attachments
3. Test product detail links
```

### Test Case 4: AJAX Functionality
```
1. Test each AJAX endpoint individually
2. Verify JSON response format
3. Check error handling for invalid IDs
4. Test concurrent AJAX requests
```

---

## 📚 Related Documentation

- [CLAUDE.md](/Applications/AMPPS/www/erp19/CLAUDE.md) - PHP 8.2 migration guide
- [propertiesController.md](propertiesController.md) - Property management
- [ads.md](ads.md) - Advertisement management

---

**Documented By**: AI Assistant  
**Review Status**: ✅ Complete  
**Next Review**: When major changes occur