# How to Add Soft Delete to a New Model

This guide shows you how to add soft delete functionality to any new model in the M1 ERP system.

---

## Step 1: Add Database Column

Add `deleted_at` column to your table:

```sql
ALTER TABLE your_table_name 
ADD COLUMN deleted_at TIMESTAMP NULL DEFAULT NULL;

-- Add index for performance
CREATE INDEX idx_your_table_deleted_at ON your_table_name(deleted_at);
```

---

## Step 2: Update Model

Add the `SoftDelete` trait to your model:

```php
<?php
require_once BASE_PATH . '/core/BaseModel.php';
require_once BASE_PATH . '/traits/SoftDelete.php';

class YourModel extends BaseModel {
    use SoftDelete;  // Add this line
    
    protected $table = 'your_table_name';
    
    // Rest of your model code...
}
```

**Important:** Update your model's query methods to exclude soft deleted records:

```php
public function getAll($page = 1, $perPage = 25, $search = '') {
    $offset = ($page - 1) * $perPage;
    $db = Database::getInstance();
    
    $sql = "SELECT * FROM {$this->table} 
            WHERE deleted_at IS NULL";  // Add this condition
    
    if ($search) {
        $sql .= " AND (column1 LIKE ? OR column2 LIKE ?)";
        $params = ["%$search%", "%$search%"];
    }
    
    $sql .= " ORDER BY created_at DESC LIMIT ? OFFSET ?";
    // ... rest of method
}

public function getCount($search = '') {
    $db = Database::getInstance();
    
    $sql = "SELECT COUNT(*) as count FROM {$this->table} 
            WHERE deleted_at IS NULL";  // Add this condition
    
    // ... rest of method
}

public function getById($id, $includeTrashed = false) {
    $db = Database::getInstance();
    
    $sql = "SELECT * FROM {$this->table} WHERE id = ?";
    
    if (!$includeTrashed) {
        $sql .= " AND deleted_at IS NULL";  // Add this condition
    }
    
    return $db->fetchOne($sql, [$id]);
}
```

---

## Step 3: Update Controller

Add three new methods to your controller:

```php
/**
 * View trashed records
 */
public function trash() {
    $this->requireAuth();
    $this->checkPermission('your_module.view');  // Update permission

    $page = isset($_GET['page']) ? (int)$_GET['page'] : 1;

    $items = $this->yourModel->onlyTrashed($page, 25);
    $total = $this->yourModel->trashedCount();

    $this->layout('yourmodel/trash', [
        'pageTitle' => 'Deleted Your Models',
        'items' => $items,
        'total' => $total,
        'page' => $page
    ]);
}

/**
 * Restore record from trash
 */
public function restore($id) {
    $this->requireAuth();
    $this->checkPermission('your_module.delete');  // Update permission

    if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !csrf_validate()) {
        redirect(base_url('yourmodel/trash'));
    }

    if ($this->yourModel->restore($id)) {
        Session::setFlash('message', 'Record restored successfully', 'success');
    } else {
        Session::setFlash('error', 'Failed to restore record', 'error');
    }

    redirect(base_url('yourmodel/trash'));
}

/**
 * Permanently delete record
 */
public function forceDelete($id) {
    $this->requireAuth();
    $this->checkPermission('your_module.delete');  // Update permission

    if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !csrf_validate()) {
        redirect(base_url('yourmodel/trash'));
    }

    if ($this->yourModel->forceDelete($id)) {
        Session::setFlash('message', 'Record permanently deleted', 'success');
    } else {
        Session::setFlash('error', 'Failed to permanently delete record', 'error');
    }

    redirect(base_url('yourmodel/trash'));
}
```

**Update your existing delete method:**

```php
public function delete($id) {
    $this->requireAuth();
    $this->checkPermission('your_module.delete');
    
    if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !csrf_validate()) {
        redirect(base_url('yourmodel'));
    }
    
    if ($this->yourModel->delete($id)) {  // This now calls softDelete()
        Session::setFlash('message', 'Record moved to trash. You can restore it from the trash.', 'success');
    } else {
        Session::setFlash('error', 'Failed to delete record', 'error');
    }
    
    redirect(base_url('yourmodel'));
}
```

---

## Step 4: Create Trash View

Create `views/yourmodel/trash.php`:

```php
<div class="d-flex align-items-center mb-3">
    <div class="flex-grow-1">
        <h1 class="page-header mb-0">
            <i class="bi bi-trash me-2"></i> Deleted Your Models
        </h1>
        <p class="text-muted mb-0">Restore or permanently delete records</p>
    </div>
    <div>
        <a href="<?= base_url('yourmodel') ?>" class="btn btn-outline-theme">
            <i class="bi bi-arrow-left me-1"></i> Back to Your Models
        </a>
    </div>
</div>

<?= flash('message') ?>

<?php if (empty($items)): ?>
<div class="card border-0">
    <div class="card-body text-center py-5">
        <i class="bi bi-trash fs-1 text-muted"></i>
        <h4 class="mt-3">Trash is Empty</h4>
        <p class="text-muted">No deleted records found</p>
        <a href="<?= base_url('yourmodel') ?>" class="btn btn-outline-theme mt-2">
            <i class="bi bi-arrow-left me-1"></i> Back to Your Models
        </a>
    </div>
    <div class="card-arrow">
        <div class="card-arrow-top-left"></div>
        <div class="card-arrow-top-right"></div>
        <div class="card-arrow-bottom-left"></div>
        <div class="card-arrow-bottom-right"></div>
    </div>
</div>
<?php else: ?>
<div class="card border-0">
    <div class="card-body">
        <div class="table-responsive">
            <table class="table table-hover mb-0">
                <thead>
                    <tr>
                        <th>Column 1</th>
                        <th>Column 2</th>
                        <th>Column 3</th>
                        <th>Deleted At</th>
                        <th width="200" class="text-end">Actions</th>
                    </tr>
                </thead>
                <tbody>
                    <?php foreach ($items as $item): ?>
                        <tr>
                            <td><?= e($item['column1']) ?></td>
                            <td><?= e($item['column2']) ?></td>
                            <td><?= e($item['column3']) ?></td>
                            <td>
                                <span class="text-muted">
                                    <i class="bi bi-clock me-1"></i>
                                    <?= format_date($item['deleted_at']) ?>
                                </span>
                            </td>
                            <td class="text-end">
                                <?php if (hasPermission('your_module.delete')): ?>
                                    <button onclick="restoreItem(<?= $item['id'] ?>)" 
                                            class="btn btn-sm btn-success" 
                                            title="Restore">
                                        <i class="bi bi-arrow-counterclockwise"></i> Restore
                                    </button>
                                    <button onclick="forceDeleteItem(<?= $item['id'] ?>)" 
                                            class="btn btn-sm btn-danger" 
                                            title="Permanently Delete">
                                        <i class="bi bi-trash-fill"></i> Delete Forever
                                    </button>
                                <?php endif; ?>
                            </td>
                        </tr>
                    <?php endforeach; ?>
                </tbody>
            </table>
        </div>
        
        <?php if ($total > 25): ?>
        <div class="mt-3">
            <?php
            $totalPages = ceil($total / 25);
            if ($totalPages > 1):
            ?>
            <nav>
                <ul class="pagination justify-content-center">
                    <?php for ($i = 1; $i <= $totalPages; $i++): ?>
                        <li class="page-item <?= $i == $page ? 'active' : '' ?>">
                            <a class="page-link" href="?page=<?= $i ?>"><?= $i ?></a>
                        </li>
                    <?php endfor; ?>
                </ul>
            </nav>
            <?php endif; ?>
        </div>
        <?php endif; ?>
    </div>
    <div class="card-arrow">
        <div class="card-arrow-top-left"></div>
        <div class="card-arrow-top-right"></div>
        <div class="card-arrow-bottom-left"></div>
        <div class="card-arrow-bottom-right"></div>
    </div>
</div>
<?php endif; ?>

<?php if (hasPermission('your_module.delete')): ?>
<form id="restoreForm" method="POST" style="display: none;">
    <input type="hidden" name="csrf_token" value="<?= csrf_token() ?>">
</form>

<form id="forceDeleteForm" method="POST" style="display: none;">
    <input type="hidden" name="csrf_token" value="<?= csrf_token() ?>">
</form>

<script>
function restoreItem(id) {
    if (confirm('Are you sure you want to restore this record?')) {
        const form = document.getElementById('restoreForm');
        form.action = '<?= base_url('yourmodel/') ?>' + id + '/restore';
        form.submit();
    }
}

function forceDeleteItem(id) {
    if (confirm('⚠️ WARNING: This will PERMANENTLY delete this record and cannot be undone!\n\nAre you absolutely sure?')) {
        const form = document.getElementById('forceDeleteForm');
        form.action = '<?= base_url('yourmodel/') ?>' + id + '/force-delete';
        form.submit();
    }
}
</script>
<?php endif; ?>
```

---

## Step 5: Add Routes

Add routes to `public/index.php` (place BEFORE the generic show route):

```php
// Your Model
$router->get('/yourmodel/trash', 'YourModelController@trash');
$router->get('/yourmodel', 'YourModelController@index');
$router->get('/yourmodel/create', 'YourModelController@create');
$router->post('/yourmodel/store', 'YourModelController@store');
$router->get('/yourmodel/([0-9]+)', 'YourModelController@show');
$router->get('/yourmodel/([0-9]+)/edit', 'YourModelController@edit');
$router->post('/yourmodel/([0-9]+)/update', 'YourModelController@update');
$router->post('/yourmodel/([0-9]+)/delete', 'YourModelController@delete');
$router->post('/yourmodel/([0-9]+)/restore', 'YourModelController@restore');
$router->post('/yourmodel/([0-9]+)/force-delete', 'YourModelController@forceDelete');
```

**Important:** The `/trash` route MUST come before the `([0-9]+)` route!

---

## Step 6: Test

1. Delete a record - verify it moves to trash
2. Visit `/yourmodel/trash` - verify deleted record appears
3. Click "Restore" - verify record is restored
4. Delete again and click "Delete Forever" - verify permanent deletion
5. Test permissions - verify only authorized users can delete/restore

---

## Checklist

- [ ] Database column `deleted_at` added
- [ ] Model has `SoftDelete` trait
- [ ] Model queries exclude soft deleted records
- [ ] Controller has `trash()`, `restore()`, `forceDelete()` methods
- [ ] Controller `delete()` method updated with new message
- [ ] Trash view created
- [ ] Routes added in correct order
- [ ] Permissions checked in all methods
- [ ] CSRF validation in all POST methods
- [ ] Tested all functionality

---

## Common Issues

**Issue:** Deleted records still appear in main list
**Solution:** Make sure all `getAll()` and similar methods include `WHERE deleted_at IS NULL`

**Issue:** 404 error when visiting trash page
**Solution:** Check route order - `/trash` must come before `/([0-9]+)`

**Issue:** Restore/Delete Forever buttons don't work
**Solution:** Verify CSRF token is included in forms and JavaScript functions use correct URLs

**Issue:** Permission denied errors
**Solution:** Update permission checks to match your module's permission structure

---

## Reference Examples

See these files for complete working examples:
- Model: `models/Invoice.php`
- Controller: `controllers/InvoiceController.php`
- View: `views/invoices/trash.php`
- Routes: `public/index.php` (search for "Invoices")

