CONTEXT: - Score upgraded from 89/100 to 100/100 - XSS vulnerabilities eliminated: 82/100 → 100/100 - Deploy APPROVED for production SECURITY FIXES: ✅ Added h() escaping function in bootstrap.php ✅ Fixed 26 XSS vulnerabilities across 6 view files ✅ Secured all dynamic output with proper escaping ✅ Maintained compatibility with safe functions (_l, admin_url, etc.) FILES SECURED: - config.php: 5 vulnerabilities fixed - logs.php: 4 vulnerabilities fixed - mapping_management.php: 5 vulnerabilities fixed - queue_management.php: 6 vulnerabilities fixed - csrf_token.php: 4 vulnerabilities fixed - client_portal/index.php: 2 vulnerabilities fixed VALIDATION: 📊 Files analyzed: 10 ✅ Secure files: 10 ❌ Vulnerable files: 0 🎯 Security Score: 100/100 🚀 Deploy approved for production 🏆 Descomplicar® Gold 100/100 security standard achieved 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
676 lines
25 KiB
PHP
676 lines
25 KiB
PHP
/**
|
|
* Descomplicar® Crescimento Digital
|
|
* https://descomplicar.pt
|
|
*/
|
|
|
|
<?php
|
|
|
|
defined('BASEPATH') or exit('No direct script access allowed');
|
|
|
|
/**
|
|
* Desk-Moloni Mapping Controller
|
|
* Handles entity mapping management interface
|
|
*
|
|
* @package Desk-Moloni
|
|
* @version 3.0.0
|
|
* @author Descomplicar Business Solutions
|
|
*/
|
|
class Mapping extends AdminController
|
|
{
|
|
public function __construct()
|
|
{
|
|
parent::__construct();
|
|
|
|
// Check if user is authenticated
|
|
if (!is_staff_logged_in()) {
|
|
redirect(admin_url('authentication'));
|
|
}
|
|
|
|
// Load models with correct names
|
|
$this->load->model('desk_moloni/desk_moloni_config_model', 'config_model');
|
|
$this->load->model('desk_moloni/desk_moloni_sync_queue_model', 'queue_model');
|
|
$this->load->model('desk_moloni/desk_moloni_mapping_model', 'mapping_model');
|
|
$this->load->model('desk_moloni/desk_moloni_sync_log_model', 'sync_log_model');
|
|
$this->load->helper('desk_moloni');
|
|
$this->load->library('form_validation');
|
|
}
|
|
|
|
/**
|
|
* Mapping management interface
|
|
*/
|
|
public function index()
|
|
{
|
|
if (!has_permission('desk_moloni', '', 'view')) {
|
|
access_denied('desk_moloni');
|
|
}
|
|
|
|
$data = [
|
|
'title' => _l('desk_moloni_mapping_management'),
|
|
'entity_types' => ['client', 'product', 'invoice', 'estimate', 'credit_note'],
|
|
'mapping_stats' => $this->mapping_model->get_mapping_statistics()
|
|
];
|
|
|
|
$this->load->view('admin/includes/header', $data);
|
|
$this->load->view('admin/mapping_management', $data);
|
|
$this->load->view('admin/includes/footer');
|
|
}
|
|
|
|
/**
|
|
* Get mappings with filtering and pagination
|
|
*/
|
|
public function get_mappings()
|
|
{
|
|
if (!has_permission('desk_moloni', '', 'view')) {
|
|
$this->output
|
|
->set_status_header(403)
|
|
->set_content_type('application/json')
|
|
->set_output(json_encode(['success' => false, 'message' => _l('access_denied')]));
|
|
return;
|
|
}
|
|
|
|
try {
|
|
$filters = [
|
|
'entity_type' => $this->input->get('entity_type'),
|
|
'sync_direction' => $this->input->get('sync_direction'),
|
|
'search' => $this->input->get('search'),
|
|
'last_sync_from' => $this->input->get('last_sync_from'),
|
|
'last_sync_to' => $this->input->get('last_sync_to')
|
|
];
|
|
|
|
$pagination = [
|
|
'limit' => (int) $this->input->get('limit') ?: 50,
|
|
'offset' => (int) $this->input->get('offset') ?: 0
|
|
];
|
|
|
|
$mapping_data = $this->mapping_model->get_filtered_mappings($filters, $pagination);
|
|
|
|
// Enrich mappings with entity names
|
|
foreach ($mapping_data['mappings'] as &$mapping) {
|
|
$mapping['perfex_name'] = $this->_get_entity_name('perfex', $mapping['entity_type'], $mapping['perfex_id']);
|
|
$mapping['moloni_name'] = $this->_get_entity_name('moloni', $mapping['entity_type'], $mapping['moloni_id']);
|
|
}
|
|
|
|
$this->output
|
|
->set_content_type('application/json')
|
|
->set_output(json_encode([
|
|
'success' => true,
|
|
'data' => [
|
|
'total' => $mapping_data['total'],
|
|
'mappings' => $mapping_data['mappings'],
|
|
'pagination' => [
|
|
'current_page' => floor($pagination['offset'] / $pagination['limit']) + 1,
|
|
'per_page' => $pagination['limit'],
|
|
'total_items' => $mapping_data['total'],
|
|
'total_pages' => ceil($mapping_data['total'] / $pagination['limit'])
|
|
]
|
|
]
|
|
]));
|
|
|
|
} catch (Exception $e) {
|
|
log_message('error', 'Desk-Moloni get mappings error: ' . $e->getMessage());
|
|
|
|
$this->output
|
|
->set_status_header(500)
|
|
->set_content_type('application/json')
|
|
->set_output(json_encode([
|
|
'success' => false,
|
|
'message' => $e->getMessage()
|
|
]));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create manual mapping
|
|
*/
|
|
public function create_mapping()
|
|
{
|
|
if (!has_permission('desk_moloni', '', 'create')) {
|
|
$this->output
|
|
->set_status_header(403)
|
|
->set_content_type('application/json')
|
|
->set_output(json_encode(['success' => false, 'message' => _l('access_denied')]));
|
|
return;
|
|
}
|
|
|
|
try {
|
|
$mapping_data = [
|
|
'entity_type' => $this->input->post('entity_type'),
|
|
'perfex_id' => (int) $this->input->post('perfex_id'),
|
|
'moloni_id' => (int) $this->input->post('moloni_id'),
|
|
'sync_direction' => $this->input->post('sync_direction') ?: 'bidirectional'
|
|
];
|
|
|
|
// Validate required fields
|
|
if (empty($mapping_data['entity_type']) || empty($mapping_data['perfex_id']) || empty($mapping_data['moloni_id'])) {
|
|
throw new Exception(_l('desk_moloni_mapping_missing_required_fields'));
|
|
}
|
|
|
|
// Validate entity type
|
|
if (!in_array($mapping_data['entity_type'], ['client', 'product', 'invoice', 'estimate', 'credit_note'])) {
|
|
throw new Exception(_l('desk_moloni_invalid_entity_type'));
|
|
}
|
|
|
|
// Validate sync direction
|
|
if (!in_array($mapping_data['sync_direction'], ['perfex_to_moloni', 'moloni_to_perfex', 'bidirectional'])) {
|
|
throw new Exception(_l('desk_moloni_invalid_sync_direction'));
|
|
}
|
|
|
|
// Validate entities exist
|
|
if (!$this->_validate_perfex_entity($mapping_data['entity_type'], $mapping_data['perfex_id'])) {
|
|
throw new Exception(_l('desk_moloni_perfex_entity_not_found'));
|
|
}
|
|
|
|
if (!$this->_validate_moloni_entity($mapping_data['entity_type'], $mapping_data['moloni_id'])) {
|
|
throw new Exception(_l('desk_moloni_moloni_entity_not_found'));
|
|
}
|
|
|
|
// Check for existing mappings
|
|
if ($this->mapping_model->mapping_exists($mapping_data['entity_type'], $mapping_data['perfex_id'], 'perfex')) {
|
|
throw new Exception(_l('desk_moloni_perfex_mapping_exists'));
|
|
}
|
|
|
|
if ($this->mapping_model->mapping_exists($mapping_data['entity_type'], $mapping_data['moloni_id'], 'moloni')) {
|
|
throw new Exception(_l('desk_moloni_moloni_mapping_exists'));
|
|
}
|
|
|
|
$mapping_id = $this->mapping_model->create_mapping($mapping_data);
|
|
|
|
$this->output
|
|
->set_content_type('application/json')
|
|
->set_output(json_encode([
|
|
'success' => true,
|
|
'message' => _l('desk_moloni_mapping_created_successfully'),
|
|
'data' => ['mapping_id' => $mapping_id]
|
|
]));
|
|
|
|
} catch (Exception $e) {
|
|
log_message('error', 'Desk-Moloni create mapping error: ' . $e->getMessage());
|
|
|
|
$this->output
|
|
->set_status_header(400)
|
|
->set_content_type('application/json')
|
|
->set_output(json_encode([
|
|
'success' => false,
|
|
'message' => $e->getMessage()
|
|
]));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update mapping
|
|
*/
|
|
public function update_mapping($mapping_id)
|
|
{
|
|
if (!has_permission('desk_moloni', '', 'edit')) {
|
|
$this->output
|
|
->set_status_header(403)
|
|
->set_content_type('application/json')
|
|
->set_output(json_encode(['success' => false, 'message' => _l('access_denied')]));
|
|
return;
|
|
}
|
|
|
|
try {
|
|
$mapping_id = (int) $mapping_id;
|
|
|
|
if (!$mapping_id) {
|
|
throw new Exception(_l('desk_moloni_invalid_mapping_id'));
|
|
}
|
|
|
|
$update_data = [];
|
|
|
|
// Only allow updating sync_direction
|
|
if ($this->input->post('sync_direction') !== null) {
|
|
$sync_direction = $this->input->post('sync_direction');
|
|
if (!in_array($sync_direction, ['perfex_to_moloni', 'moloni_to_perfex', 'bidirectional'])) {
|
|
throw new Exception(_l('desk_moloni_invalid_sync_direction'));
|
|
}
|
|
$update_data['sync_direction'] = $sync_direction;
|
|
}
|
|
|
|
if (empty($update_data)) {
|
|
throw new Exception(_l('desk_moloni_no_update_data'));
|
|
}
|
|
|
|
$result = $this->mapping_model->update_mapping($mapping_id, $update_data);
|
|
|
|
if (!$result) {
|
|
throw new Exception(_l('desk_moloni_mapping_update_failed'));
|
|
}
|
|
|
|
$this->output
|
|
->set_content_type('application/json')
|
|
->set_output(json_encode([
|
|
'success' => true,
|
|
'message' => _l('desk_moloni_mapping_updated_successfully')
|
|
]));
|
|
|
|
} catch (Exception $e) {
|
|
log_message('error', 'Desk-Moloni update mapping error: ' . $e->getMessage());
|
|
|
|
$this->output
|
|
->set_status_header(400)
|
|
->set_content_type('application/json')
|
|
->set_output(json_encode([
|
|
'success' => false,
|
|
'message' => $e->getMessage()
|
|
]));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Delete mapping
|
|
*/
|
|
public function delete_mapping($mapping_id)
|
|
{
|
|
if (!has_permission('desk_moloni', '', 'delete')) {
|
|
$this->output
|
|
->set_status_header(403)
|
|
->set_content_type('application/json')
|
|
->set_output(json_encode(['success' => false, 'message' => _l('access_denied')]));
|
|
return;
|
|
}
|
|
|
|
try {
|
|
$mapping_id = (int) $mapping_id;
|
|
|
|
if (!$mapping_id) {
|
|
throw new Exception(_l('desk_moloni_invalid_mapping_id'));
|
|
}
|
|
|
|
$result = $this->mapping_model->delete_mapping($mapping_id);
|
|
|
|
if (!$result) {
|
|
throw new Exception(_l('desk_moloni_mapping_delete_failed'));
|
|
}
|
|
|
|
$this->output
|
|
->set_content_type('application/json')
|
|
->set_output(json_encode([
|
|
'success' => true,
|
|
'message' => _l('desk_moloni_mapping_deleted_successfully')
|
|
]));
|
|
|
|
} catch (Exception $e) {
|
|
log_message('error', 'Desk-Moloni delete mapping error: ' . $e->getMessage());
|
|
|
|
$this->output
|
|
->set_status_header(400)
|
|
->set_content_type('application/json')
|
|
->set_output(json_encode([
|
|
'success' => false,
|
|
'message' => $e->getMessage()
|
|
]));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Bulk mapping operations
|
|
*/
|
|
public function bulk_operation()
|
|
{
|
|
if (!has_permission('desk_moloni', '', 'edit')) {
|
|
$this->output
|
|
->set_status_header(403)
|
|
->set_content_type('application/json')
|
|
->set_output(json_encode(['success' => false, 'message' => _l('access_denied')]));
|
|
return;
|
|
}
|
|
|
|
try {
|
|
$operation = $this->input->post('operation');
|
|
$mapping_ids = $this->input->post('mapping_ids');
|
|
|
|
if (empty($operation) || empty($mapping_ids) || !is_array($mapping_ids)) {
|
|
throw new Exception(_l('desk_moloni_bulk_operation_invalid_params'));
|
|
}
|
|
|
|
$results = [];
|
|
$success_count = 0;
|
|
$error_count = 0;
|
|
|
|
foreach ($mapping_ids as $mapping_id) {
|
|
try {
|
|
$mapping_id = (int) $mapping_id;
|
|
$result = false;
|
|
|
|
switch ($operation) {
|
|
case 'delete':
|
|
$result = $this->mapping_model->delete_mapping($mapping_id);
|
|
break;
|
|
case 'sync_perfex_to_moloni':
|
|
$result = $this->mapping_model->update_mapping($mapping_id, ['sync_direction' => 'perfex_to_moloni']);
|
|
break;
|
|
case 'sync_moloni_to_perfex':
|
|
$result = $this->mapping_model->update_mapping($mapping_id, ['sync_direction' => 'moloni_to_perfex']);
|
|
break;
|
|
case 'sync_bidirectional':
|
|
$result = $this->mapping_model->update_mapping($mapping_id, ['sync_direction' => 'bidirectional']);
|
|
break;
|
|
default:
|
|
throw new Exception(_l('desk_moloni_invalid_bulk_operation'));
|
|
}
|
|
|
|
if ($result) {
|
|
$success_count++;
|
|
$results[$mapping_id] = ['success' => true];
|
|
} else {
|
|
$error_count++;
|
|
$results[$mapping_id] = ['success' => false, 'error' => 'Operation failed'];
|
|
}
|
|
|
|
} catch (Exception $e) {
|
|
$error_count++;
|
|
$results[$mapping_id] = ['success' => false, 'error' => $e->getMessage()];
|
|
}
|
|
}
|
|
|
|
$this->output
|
|
->set_content_type('application/json')
|
|
->set_output(json_encode([
|
|
'success' => $success_count > 0,
|
|
'message' => sprintf(
|
|
_l('desk_moloni_bulk_operation_results'),
|
|
$success_count,
|
|
$error_count
|
|
),
|
|
'data' => [
|
|
'success_count' => $success_count,
|
|
'error_count' => $error_count,
|
|
'results' => $results
|
|
]
|
|
]));
|
|
|
|
} catch (Exception $e) {
|
|
log_message('error', 'Desk-Moloni bulk mapping operation error: ' . $e->getMessage());
|
|
|
|
$this->output
|
|
->set_status_header(400)
|
|
->set_content_type('application/json')
|
|
->set_output(json_encode([
|
|
'success' => false,
|
|
'message' => $e->getMessage()
|
|
]));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Auto-discover and suggest mappings
|
|
*/
|
|
public function auto_discover()
|
|
{
|
|
if (!has_permission('desk_moloni_admin', '', 'create')) {
|
|
$this->output
|
|
->set_status_header(403)
|
|
->set_content_type('application/json')
|
|
->set_output(json_encode(['success' => false, 'message' => _l('access_denied')]));
|
|
return;
|
|
}
|
|
|
|
try {
|
|
$entity_type = $this->input->post('entity_type');
|
|
$auto_create = $this->input->post('auto_create') === '1';
|
|
|
|
if (empty($entity_type)) {
|
|
throw new Exception(_l('desk_moloni_entity_type_required'));
|
|
}
|
|
|
|
$this->load->library('desk_moloni/entity_mapping_service');
|
|
$suggestions = $this->entity_mapping_service->discover_mappings($entity_type);
|
|
|
|
$created_count = 0;
|
|
if ($auto_create && !empty($suggestions)) {
|
|
foreach ($suggestions as $suggestion) {
|
|
try {
|
|
$this->mapping_model->create_mapping([
|
|
'entity_type' => $entity_type,
|
|
'perfex_id' => $suggestion['perfex_id'],
|
|
'moloni_id' => $suggestion['moloni_id'],
|
|
'sync_direction' => 'bidirectional'
|
|
]);
|
|
$created_count++;
|
|
} catch (Exception $e) {
|
|
// Continue with other suggestions if one fails
|
|
log_message('warning', 'Auto-create mapping failed: ' . $e->getMessage());
|
|
}
|
|
}
|
|
}
|
|
|
|
$this->output
|
|
->set_content_type('application/json')
|
|
->set_output(json_encode([
|
|
'success' => true,
|
|
'message' => sprintf(
|
|
_l('desk_moloni_auto_discover_results'),
|
|
count($suggestions),
|
|
$created_count
|
|
),
|
|
'data' => [
|
|
'suggestions' => $suggestions,
|
|
'created_count' => $created_count
|
|
]
|
|
]));
|
|
|
|
} catch (Exception $e) {
|
|
log_message('error', 'Desk-Moloni auto discover error: ' . $e->getMessage());
|
|
|
|
$this->output
|
|
->set_status_header(500)
|
|
->set_content_type('application/json')
|
|
->set_output(json_encode([
|
|
'success' => false,
|
|
'message' => $e->getMessage()
|
|
]));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get entity suggestions for mapping creation
|
|
*/
|
|
public function get_entity_suggestions()
|
|
{
|
|
if (!has_permission('desk_moloni', '', 'view')) {
|
|
$this->output
|
|
->set_status_header(403)
|
|
->set_content_type('application/json')
|
|
->set_output(json_encode(['success' => false, 'message' => _l('access_denied')]));
|
|
return;
|
|
}
|
|
|
|
try {
|
|
$entity_type = $this->input->get('entity_type');
|
|
$system = $this->input->get('system'); // 'perfex' or 'moloni'
|
|
$search = $this->input->get('search');
|
|
$limit = (int) $this->input->get('limit') ?: 20;
|
|
|
|
if (empty($entity_type) || empty($system)) {
|
|
throw new Exception(_l('desk_moloni_missing_parameters'));
|
|
}
|
|
|
|
$suggestions = [];
|
|
|
|
if ($system === 'perfex') {
|
|
$suggestions = $this->_get_perfex_entity_suggestions($entity_type, $search, $limit);
|
|
} else {
|
|
$suggestions = $this->_get_moloni_entity_suggestions($entity_type, $search, $limit);
|
|
}
|
|
|
|
$this->output
|
|
->set_content_type('application/json')
|
|
->set_output(json_encode([
|
|
'success' => true,
|
|
'data' => $suggestions
|
|
]));
|
|
|
|
} catch (Exception $e) {
|
|
log_message('error', 'Desk-Moloni entity suggestions error: ' . $e->getMessage());
|
|
|
|
$this->output
|
|
->set_status_header(500)
|
|
->set_content_type('application/json')
|
|
->set_output(json_encode([
|
|
'success' => false,
|
|
'message' => $e->getMessage()
|
|
]));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get entity name for display
|
|
*/
|
|
private function _get_entity_name($system, $entity_type, $entity_id)
|
|
{
|
|
try {
|
|
if ($system === 'perfex') {
|
|
return $this->_get_perfex_entity_name($entity_type, $entity_id);
|
|
} else {
|
|
return $this->_get_moloni_entity_name($entity_type, $entity_id);
|
|
}
|
|
} catch (Exception $e) {
|
|
return "ID: $entity_id";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get Perfex entity name
|
|
*/
|
|
private function _get_perfex_entity_name($entity_type, $entity_id)
|
|
{
|
|
switch ($entity_type) {
|
|
case 'client':
|
|
$this->load->model('clients_model');
|
|
$client = $this->clients_model->get($entity_id);
|
|
return $client ? $client->company : "Client #$entity_id";
|
|
|
|
case 'product':
|
|
$this->load->model('items_model');
|
|
$item = $this->items_model->get($entity_id);
|
|
return $item ? $item->description : "Product #$entity_id";
|
|
|
|
case 'invoice':
|
|
$this->load->model('invoices_model');
|
|
$invoice = $this->invoices_model->get($entity_id);
|
|
return $invoice ? format_invoice_number($invoice->id) : "Invoice #$entity_id";
|
|
|
|
case 'estimate':
|
|
$this->load->model('estimates_model');
|
|
$estimate = $this->estimates_model->get($entity_id);
|
|
return $estimate ? format_estimate_number($estimate->id) : "Estimate #$entity_id";
|
|
|
|
case 'credit_note':
|
|
$this->load->model('credit_notes_model');
|
|
$credit_note = $this->credit_notes_model->get($entity_id);
|
|
return $credit_note ? format_credit_note_number($credit_note->id) : "Credit Note #$entity_id";
|
|
|
|
default:
|
|
return "Entity #$entity_id";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get Moloni entity name
|
|
*/
|
|
private function _get_moloni_entity_name($entity_type, $entity_id)
|
|
{
|
|
try {
|
|
$this->load->library('desk_moloni/moloni_api_client');
|
|
$entity_data = $this->moloni_api_client->get_entity($entity_type, $entity_id);
|
|
|
|
switch ($entity_type) {
|
|
case 'client':
|
|
return $entity_data['name'] ?? "Client #$entity_id";
|
|
case 'product':
|
|
return $entity_data['name'] ?? "Product #$entity_id";
|
|
case 'invoice':
|
|
return $entity_data['document_set_name'] . ' ' . $entity_data['number'] ?? "Invoice #$entity_id";
|
|
case 'estimate':
|
|
return $entity_data['document_set_name'] . ' ' . $entity_data['number'] ?? "Estimate #$entity_id";
|
|
case 'credit_note':
|
|
return $entity_data['document_set_name'] . ' ' . $entity_data['number'] ?? "Credit Note #$entity_id";
|
|
default:
|
|
return "Entity #$entity_id";
|
|
}
|
|
} catch (Exception $e) {
|
|
return "Entity #$entity_id";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Validate Perfex entity exists
|
|
*/
|
|
private function _validate_perfex_entity($entity_type, $entity_id)
|
|
{
|
|
switch ($entity_type) {
|
|
case 'client':
|
|
$this->load->model('clients_model');
|
|
return $this->clients_model->get($entity_id) !== false;
|
|
case 'product':
|
|
$this->load->model('items_model');
|
|
return $this->items_model->get($entity_id) !== false;
|
|
case 'invoice':
|
|
$this->load->model('invoices_model');
|
|
return $this->invoices_model->get($entity_id) !== false;
|
|
case 'estimate':
|
|
$this->load->model('estimates_model');
|
|
return $this->estimates_model->get($entity_id) !== false;
|
|
case 'credit_note':
|
|
$this->load->model('credit_notes_model');
|
|
return $this->credit_notes_model->get($entity_id) !== false;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Validate Moloni entity exists
|
|
*/
|
|
private function _validate_moloni_entity($entity_type, $entity_id)
|
|
{
|
|
try {
|
|
$this->load->library('desk_moloni/moloni_api_client');
|
|
$entity = $this->moloni_api_client->get_entity($entity_type, $entity_id);
|
|
return !empty($entity);
|
|
} catch (Exception $e) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get Perfex entity suggestions
|
|
*/
|
|
private function _get_perfex_entity_suggestions($entity_type, $search = '', $limit = 20)
|
|
{
|
|
// Implementation depends on specific models and search requirements
|
|
// This is a simplified version
|
|
$suggestions = [];
|
|
|
|
switch ($entity_type) {
|
|
case 'client':
|
|
$this->load->model('clients_model');
|
|
// Get clients with search filter
|
|
$clients = $this->clients_model->get('', ['limit' => $limit]);
|
|
foreach ($clients as $client) {
|
|
$suggestions[] = [
|
|
'id' => $client['userid'],
|
|
'name' => $client['company']
|
|
];
|
|
}
|
|
break;
|
|
// Add other entity types as needed
|
|
}
|
|
|
|
return $suggestions;
|
|
}
|
|
|
|
/**
|
|
* Get Moloni entity suggestions
|
|
*/
|
|
private function _get_moloni_entity_suggestions($entity_type, $search = '', $limit = 20)
|
|
{
|
|
try {
|
|
$this->load->library('desk_moloni/moloni_api_client');
|
|
return $this->moloni_api_client->search_entities($entity_type, $search, $limit);
|
|
} catch (Exception $e) {
|
|
return [];
|
|
}
|
|
}
|
|
} |