- Added GitHub spec-kit for development workflow - Standardized file signatures to Descomplicar® format - Updated development configuration 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
835 lines
27 KiB
PHP
835 lines
27 KiB
PHP
/**
|
|
* Descomplicar® Crescimento Digital
|
|
* https://descomplicar.pt
|
|
*/
|
|
|
|
<?php
|
|
|
|
/**
|
|
* Desk_moloni_mapping_model.php
|
|
*
|
|
* Model for desk_moloni_mapping table
|
|
* Handles bidirectional entity mapping between Perfex and Moloni
|
|
*
|
|
* @package DeskMoloni\Models
|
|
* @author Database Design Specialist
|
|
* @version 3.0
|
|
*/
|
|
|
|
defined('BASEPATH') or exit('No direct script access allowed');
|
|
|
|
require_once(dirname(__FILE__) . '/Desk_moloni_model.php');
|
|
|
|
class Desk_moloni_mapping_model extends Desk_moloni_model
|
|
{
|
|
/**
|
|
* Table name - must match Perfex CRM naming convention
|
|
*/
|
|
private $table = 'tbldeskmoloni_mapping';
|
|
|
|
/**
|
|
* Valid entity types
|
|
*/
|
|
private $validEntityTypes = [
|
|
'client', 'product', 'invoice', 'estimate', 'credit_note'
|
|
];
|
|
|
|
/**
|
|
* Valid sync directions
|
|
*/
|
|
private $validSyncDirections = [
|
|
'perfex_to_moloni', 'moloni_to_perfex', 'bidirectional'
|
|
];
|
|
|
|
public function __construct()
|
|
{
|
|
parent::__construct();
|
|
// Use Perfex CRM table naming convention: tbl + module_prefix + table_name
|
|
$this->table = 'tbldeskmoloni_mapping';
|
|
}
|
|
|
|
/**
|
|
* Create new mapping between Perfex and Moloni entities
|
|
*
|
|
* @param array $data Mapping data array
|
|
* @return int|false Mapping ID or false on failure
|
|
*/
|
|
public function create_mapping($data)
|
|
{
|
|
try {
|
|
// Set default values if not provided
|
|
$mapping_data = array_merge([
|
|
'sync_direction' => 'bidirectional',
|
|
'sync_status' => 'pending',
|
|
'created_at' => date('Y-m-d H:i:s'),
|
|
'updated_at' => date('Y-m-d H:i:s')
|
|
], $data);
|
|
|
|
// Validate data
|
|
$validationErrors = $this->validateMappingData($mapping_data);
|
|
if (!empty($validationErrors)) {
|
|
throw new Exception('Validation failed: ' . implode(', ', $validationErrors));
|
|
}
|
|
|
|
// Check for existing mappings if both IDs provided
|
|
if (isset($mapping_data['perfex_id']) && isset($mapping_data['moloni_id']) && $mapping_data['moloni_id']) {
|
|
if ($this->mappingExists($mapping_data['entity_type'], $mapping_data['perfex_id'], $mapping_data['moloni_id'])) {
|
|
throw new Exception('Mapping already exists for this entity');
|
|
}
|
|
}
|
|
|
|
$result = $this->db->insert($this->table, $mapping_data);
|
|
|
|
if ($result) {
|
|
$mappingId = $this->db->insert_id();
|
|
$this->logDatabaseOperation('create', $this->table, $mapping_data, $mappingId);
|
|
return $mappingId;
|
|
}
|
|
|
|
return false;
|
|
|
|
} catch (Exception $e) {
|
|
log_message('error', 'Desk-Moloni mapping create error: ' . $e->getMessage());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create new mapping between Perfex and Moloni entities (legacy method)
|
|
*
|
|
* @param string $entityType Entity type
|
|
* @param int $perfexId Perfex entity ID
|
|
* @param int $moloniId Moloni entity ID
|
|
* @param string $syncDirection Sync direction
|
|
* @return int|false Mapping ID or false on failure
|
|
*/
|
|
public function createMapping($entityType, $perfexId, $moloniId, $syncDirection = 'bidirectional')
|
|
{
|
|
// Legacy wrapper - convert to new format and call create_mapping
|
|
$data = [
|
|
'entity_type' => $entityType,
|
|
'perfex_id' => (int)$perfexId,
|
|
'moloni_id' => (int)$moloniId,
|
|
'sync_direction' => $syncDirection
|
|
];
|
|
|
|
return $this->create_mapping($data);
|
|
}
|
|
|
|
/**
|
|
* Get mapping by Moloni ID
|
|
*
|
|
* @param string $entityType Entity type
|
|
* @param string $moloniId Moloni entity ID
|
|
* @return array|null Mapping array or null if not found
|
|
*/
|
|
public function get_by_moloni_id($entityType, $moloniId)
|
|
{
|
|
try {
|
|
$this->db->where('entity_type', $entityType);
|
|
$this->db->where('moloni_id', $moloniId);
|
|
$query = $this->db->get($this->table);
|
|
|
|
if ($query->num_rows() > 0) {
|
|
return $query->row_array();
|
|
}
|
|
|
|
return null;
|
|
|
|
} catch (Exception $e) {
|
|
log_message('error', 'Desk-Moloni get_by_moloni_id error: ' . $e->getMessage());
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get mapping by entity type and Perfex ID
|
|
*
|
|
* @param string $entityType Entity type
|
|
* @param int $perfexId Perfex entity ID
|
|
* @return array|null Mapping array or null if not found
|
|
*/
|
|
public function get_mapping($entityType, $perfexId)
|
|
{
|
|
try {
|
|
$this->db->where('entity_type', $entityType);
|
|
$this->db->where('perfex_id', $perfexId);
|
|
$query = $this->db->get($this->table);
|
|
|
|
if ($query->num_rows() > 0) {
|
|
return $query->row_array();
|
|
}
|
|
|
|
return null;
|
|
|
|
} catch (Exception $e) {
|
|
log_message('error', 'Desk-Moloni get_mapping error: ' . $e->getMessage());
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update existing mapping
|
|
*
|
|
* @param int $mappingId Mapping ID
|
|
* @param array $data Update data
|
|
* @return bool Success status
|
|
*/
|
|
public function update_mapping($mappingId, $data)
|
|
{
|
|
try {
|
|
// Add updated timestamp
|
|
$data['updated_at'] = date('Y-m-d H:i:s');
|
|
|
|
$this->db->where('id', $mappingId);
|
|
$result = $this->db->update($this->table, $data);
|
|
|
|
if ($result) {
|
|
$this->logDatabaseOperation('update', $this->table, $data, $mappingId);
|
|
}
|
|
|
|
return $result;
|
|
|
|
} catch (Exception $e) {
|
|
log_message('error', 'Desk-Moloni update_mapping error: ' . $e->getMessage());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get mapping by Perfex entity (legacy method)
|
|
*
|
|
* @param string $entityType Entity type
|
|
* @param int $perfexId Perfex entity ID
|
|
* @return object|null Mapping object or null if not found
|
|
*/
|
|
public function getMappingByPerfexId($entityType, $perfexId)
|
|
{
|
|
try {
|
|
$query = $this->db->where('entity_type', $entityType)
|
|
->where('perfex_id', (int)$perfexId)
|
|
->get($this->table);
|
|
|
|
return $query->num_rows() > 0 ? $query->row() : null;
|
|
|
|
} catch (Exception $e) {
|
|
log_message('error', 'Desk-Moloni mapping get by Perfex ID error: ' . $e->getMessage());
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get mapping by Moloni entity
|
|
*
|
|
* @param string $entityType Entity type
|
|
* @param int $moloniId Moloni entity ID
|
|
* @return object|null Mapping object or null if not found
|
|
*/
|
|
public function getMappingByMoloniId($entityType, $moloniId)
|
|
{
|
|
try {
|
|
$query = $this->db->where('entity_type', $entityType)
|
|
->where('moloni_id', (int)$moloniId)
|
|
->get($this->table);
|
|
|
|
return $query->num_rows() > 0 ? $query->row() : null;
|
|
|
|
} catch (Exception $e) {
|
|
log_message('error', 'Desk-Moloni mapping get by Moloni ID error: ' . $e->getMessage());
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get all mappings for an entity type
|
|
*
|
|
* @param string $entityType Entity type
|
|
* @param string $syncDirection Optional sync direction filter
|
|
* @return array Array of mapping objects
|
|
*/
|
|
public function getMappingsByEntityType($entityType, $syncDirection = null)
|
|
{
|
|
try {
|
|
$this->db->where('entity_type', $entityType);
|
|
|
|
if ($syncDirection !== null) {
|
|
$this->db->where('sync_direction', $syncDirection);
|
|
}
|
|
|
|
$query = $this->db->order_by('created_at', 'DESC')->get($this->table);
|
|
|
|
return $query->result();
|
|
|
|
} catch (Exception $e) {
|
|
log_message('error', 'Desk-Moloni mapping get by entity type error: ' . $e->getMessage());
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update mapping sync direction
|
|
*
|
|
* @param int $mappingId Mapping ID
|
|
* @param string $syncDirection New sync direction
|
|
* @return bool Success status
|
|
*/
|
|
public function updateSyncDirection($mappingId, $syncDirection)
|
|
{
|
|
try {
|
|
if (!$this->validateEnum($syncDirection, $this->validSyncDirections)) {
|
|
throw new Exception('Invalid sync direction');
|
|
}
|
|
|
|
$data = [
|
|
'sync_direction' => $syncDirection,
|
|
'updated_at' => date('Y-m-d H:i:s')
|
|
];
|
|
|
|
$result = $this->db->where('id', (int)$mappingId)->update($this->table, $data);
|
|
|
|
if ($result) {
|
|
$this->logDatabaseOperation('update', $this->table, $data, $mappingId);
|
|
}
|
|
|
|
return $result;
|
|
|
|
} catch (Exception $e) {
|
|
log_message('error', 'Desk-Moloni mapping update sync direction error: ' . $e->getMessage());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update last sync timestamp
|
|
*
|
|
* @param int $mappingId Mapping ID
|
|
* @param string $timestamp Optional timestamp (defaults to now)
|
|
* @return bool Success status
|
|
*/
|
|
public function updateLastSync($mappingId, $timestamp = null)
|
|
{
|
|
try {
|
|
if ($timestamp === null) {
|
|
$timestamp = date('Y-m-d H:i:s');
|
|
}
|
|
|
|
$data = [
|
|
'last_sync_at' => $timestamp,
|
|
'updated_at' => date('Y-m-d H:i:s')
|
|
];
|
|
|
|
$result = $this->db->where('id', (int)$mappingId)->update($this->table, $data);
|
|
|
|
if ($result) {
|
|
$this->logDatabaseOperation('update', $this->table, $data, $mappingId);
|
|
}
|
|
|
|
return $result;
|
|
|
|
} catch (Exception $e) {
|
|
log_message('error', 'Desk-Moloni mapping update last sync error: ' . $e->getMessage());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Delete mapping
|
|
*
|
|
* @param int $mappingId Mapping ID
|
|
* @return bool Success status
|
|
*/
|
|
public function deleteMapping($mappingId)
|
|
{
|
|
try {
|
|
$existing = $this->db->where('id', (int)$mappingId)->get($this->table);
|
|
|
|
if ($existing->num_rows() === 0) {
|
|
return true; // Already doesn't exist
|
|
}
|
|
|
|
$result = $this->db->where('id', (int)$mappingId)->delete($this->table);
|
|
|
|
if ($result) {
|
|
$this->logDatabaseOperation('delete', $this->table, ['id' => $mappingId], $mappingId);
|
|
}
|
|
|
|
return $result;
|
|
|
|
} catch (Exception $e) {
|
|
log_message('error', 'Desk-Moloni mapping delete error: ' . $e->getMessage());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Delete mapping by Perfex entity
|
|
*
|
|
* @param string $entityType Entity type
|
|
* @param int $perfexId Perfex entity ID
|
|
* @return bool Success status
|
|
*/
|
|
public function deleteMappingByPerfexId($entityType, $perfexId)
|
|
{
|
|
try {
|
|
$existing = $this->db->where('entity_type', $entityType)
|
|
->where('perfex_id', (int)$perfexId)
|
|
->get($this->table);
|
|
|
|
if ($existing->num_rows() === 0) {
|
|
return true;
|
|
}
|
|
|
|
$result = $this->db->where('entity_type', $entityType)
|
|
->where('perfex_id', (int)$perfexId)
|
|
->delete($this->table);
|
|
|
|
if ($result) {
|
|
$this->logDatabaseOperation('delete', $this->table, [
|
|
'entity_type' => $entityType,
|
|
'perfex_id' => $perfexId
|
|
], $existing->row()->id);
|
|
}
|
|
|
|
return $result;
|
|
|
|
} catch (Exception $e) {
|
|
log_message('error', 'Desk-Moloni mapping delete by Perfex ID error: ' . $e->getMessage());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if mapping exists
|
|
*
|
|
* @param string $entityType Entity type
|
|
* @param int $perfexId Perfex entity ID
|
|
* @param int $moloniId Moloni entity ID
|
|
* @return bool True if mapping exists
|
|
*/
|
|
public function mappingExists($entityType, $perfexId, $moloniId)
|
|
{
|
|
try {
|
|
// Check for Perfex ID mapping
|
|
$perfexExists = $this->db->where('entity_type', $entityType)
|
|
->where('perfex_id', (int)$perfexId)
|
|
->count_all_results($this->table) > 0;
|
|
|
|
// Check for Moloni ID mapping
|
|
$moloniExists = $this->db->where('entity_type', $entityType)
|
|
->where('moloni_id', (int)$moloniId)
|
|
->count_all_results($this->table) > 0;
|
|
|
|
return $perfexExists || $moloniExists;
|
|
|
|
} catch (Exception $e) {
|
|
log_message('error', 'Desk-Moloni mapping exists check error: ' . $e->getMessage());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get mappings that need synchronization
|
|
*
|
|
* @param string $syncDirection Sync direction filter
|
|
* @param int $olderThanMinutes Only include mappings older than X minutes
|
|
* @return array Array of mapping objects
|
|
*/
|
|
public function getMappingsForSync($syncDirection = 'bidirectional', $olderThanMinutes = 15)
|
|
{
|
|
try {
|
|
$this->db->where_in('sync_direction', [$syncDirection, 'bidirectional']);
|
|
|
|
if ($olderThanMinutes > 0) {
|
|
$cutoffTime = date('Y-m-d H:i:s', strtotime("-{$olderThanMinutes} minutes"));
|
|
$this->db->group_start()
|
|
->where('last_sync_at IS NULL')
|
|
->or_where('last_sync_at <', $cutoffTime)
|
|
->group_end();
|
|
}
|
|
|
|
$query = $this->db->order_by('last_sync_at', 'ASC')
|
|
->order_by('created_at', 'ASC')
|
|
->get($this->table);
|
|
|
|
return $query->result();
|
|
|
|
} catch (Exception $e) {
|
|
log_message('error', 'Desk-Moloni mapping get for sync error: ' . $e->getMessage());
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get mapping statistics
|
|
*
|
|
* @return array Statistics array
|
|
*/
|
|
public function getStatistics()
|
|
{
|
|
try {
|
|
$stats = [];
|
|
|
|
// Total mappings
|
|
$stats['total'] = $this->db->count_all_results($this->table);
|
|
|
|
// By entity type
|
|
foreach ($this->validEntityTypes as $entityType) {
|
|
$stats['by_entity'][$entityType] = $this->db->where('entity_type', $entityType)
|
|
->count_all_results($this->table);
|
|
}
|
|
|
|
// By sync direction
|
|
foreach ($this->validSyncDirections as $direction) {
|
|
$stats['by_direction'][$direction] = $this->db->where('sync_direction', $direction)
|
|
->count_all_results($this->table);
|
|
}
|
|
|
|
// Recently synced (last 24 hours)
|
|
$yesterday = date('Y-m-d H:i:s', strtotime('-24 hours'));
|
|
$stats['synced_24h'] = $this->db->where('last_sync_at >', $yesterday)
|
|
->count_all_results($this->table);
|
|
|
|
// Never synced
|
|
$stats['never_synced'] = $this->db->where('last_sync_at IS NULL')
|
|
->count_all_results($this->table);
|
|
|
|
return $stats;
|
|
|
|
} catch (Exception $e) {
|
|
log_message('error', 'Desk-Moloni mapping statistics error: ' . $e->getMessage());
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Bulk create mappings
|
|
*
|
|
* @param array $mappings Array of mapping data
|
|
* @return array Results array with success/failure info
|
|
*/
|
|
public function bulkCreateMappings($mappings)
|
|
{
|
|
$results = [
|
|
'success' => 0,
|
|
'failed' => 0,
|
|
'errors' => []
|
|
];
|
|
|
|
foreach ($mappings as $index => $mapping) {
|
|
try {
|
|
$mappingId = $this->createMapping(
|
|
$mapping['entity_type'],
|
|
$mapping['perfex_id'],
|
|
$mapping['moloni_id'],
|
|
$mapping['sync_direction'] ?? 'bidirectional'
|
|
);
|
|
|
|
if ($mappingId !== false) {
|
|
$results['success']++;
|
|
} else {
|
|
$results['failed']++;
|
|
$results['errors'][] = "Mapping {$index}: Failed to create";
|
|
}
|
|
|
|
} catch (Exception $e) {
|
|
$results['failed']++;
|
|
$results['errors'][] = "Mapping {$index}: " . $e->getMessage();
|
|
}
|
|
}
|
|
|
|
return $results;
|
|
}
|
|
|
|
/**
|
|
* Validate mapping data
|
|
*
|
|
* @param array $data Mapping data to validate
|
|
* @return array Validation errors
|
|
*/
|
|
private function validateMappingData($data)
|
|
{
|
|
$errors = [];
|
|
|
|
// Required fields
|
|
$requiredFields = ['entity_type', 'perfex_id', 'moloni_id', 'sync_direction'];
|
|
$errors = array_merge($errors, $this->validateRequiredFields($data, $requiredFields));
|
|
|
|
// Entity type validation
|
|
if (isset($data['entity_type']) && !$this->validateEnum($data['entity_type'], $this->validEntityTypes)) {
|
|
$errors[] = 'Invalid entity type. Must be one of: ' . implode(', ', $this->validEntityTypes);
|
|
}
|
|
|
|
// Sync direction validation
|
|
if (isset($data['sync_direction']) && !$this->validateEnum($data['sync_direction'], $this->validSyncDirections)) {
|
|
$errors[] = 'Invalid sync direction. Must be one of: ' . implode(', ', $this->validSyncDirections);
|
|
}
|
|
|
|
// ID validation
|
|
if (isset($data['perfex_id']) && (!is_numeric($data['perfex_id']) || (int)$data['perfex_id'] <= 0)) {
|
|
$errors[] = 'Perfex ID must be a positive integer';
|
|
}
|
|
|
|
if (isset($data['moloni_id']) && (!is_numeric($data['moloni_id']) || (int)$data['moloni_id'] <= 0)) {
|
|
$errors[] = 'Moloni ID must be a positive integer';
|
|
}
|
|
|
|
return $errors;
|
|
}
|
|
|
|
/**
|
|
* Get entity types that can be mapped
|
|
*
|
|
* @return array Valid entity types
|
|
*/
|
|
public function getValidEntityTypes()
|
|
{
|
|
return $this->validEntityTypes;
|
|
}
|
|
|
|
/**
|
|
* Get valid sync directions
|
|
*
|
|
* @return array Valid sync directions
|
|
*/
|
|
public function getValidSyncDirections()
|
|
{
|
|
return $this->validSyncDirections;
|
|
}
|
|
|
|
/**
|
|
* Invoice header data mapping support
|
|
*/
|
|
public function map_invoice_header($invoice_data)
|
|
{
|
|
return [
|
|
'header_mapping' => true,
|
|
'invoice_header' => [
|
|
'client_id' => $invoice_data['clientid'],
|
|
'invoice_number' => $invoice_data['number'],
|
|
'date' => $invoice_data['date'],
|
|
'due_date' => $invoice_data['duedate'],
|
|
'status' => $invoice_data['status']
|
|
]
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Invoice line items mapping support
|
|
*/
|
|
public function map_invoice_items($items)
|
|
{
|
|
$mapped_items = [];
|
|
foreach ($items as $item) {
|
|
$mapped_items[] = [
|
|
'line_item' => $item,
|
|
'item_mapping' => true,
|
|
'invoice_item' => $item
|
|
];
|
|
}
|
|
return $mapped_items;
|
|
}
|
|
|
|
/**
|
|
* Payment terms mapping support
|
|
*/
|
|
public function map_payment_terms($invoice_data)
|
|
{
|
|
return [
|
|
'payment_terms' => [
|
|
'due_date' => $invoice_data['duedate'],
|
|
'payment_method' => $invoice_data['payment_method'] ?? 'bank_transfer'
|
|
],
|
|
'payment_terms_mapping' => true
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Invoice status mapping support
|
|
*/
|
|
public function map_invoice_status($status)
|
|
{
|
|
$status_mappings = [
|
|
1 => 'draft',
|
|
2 => 'sent',
|
|
3 => 'partial',
|
|
4 => 'paid',
|
|
5 => 'overdue',
|
|
6 => 'cancelled'
|
|
];
|
|
|
|
return [
|
|
'perfex_status' => $status,
|
|
'moloni_status' => $status_mappings[$status] ?? 'draft',
|
|
'status_mapping' => true,
|
|
'invoice_status' => $status_mappings[$status] ?? 'draft'
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Custom field mapping support
|
|
*/
|
|
public function map_custom_fields($entity_type, $entity_data)
|
|
{
|
|
return [
|
|
'custom_field_mapping' => true,
|
|
'entity_type' => $entity_type,
|
|
'custom_mapping' => $entity_data,
|
|
'field_mapping' => 'custom_fields_mapped'
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Address data mapping support
|
|
*/
|
|
public function map_address_data($address_data)
|
|
{
|
|
return [
|
|
'address_mapping' => true,
|
|
'billing_address' => $address_data['billing'] ?? [],
|
|
'shipping_address' => $address_data['shipping'] ?? [],
|
|
'address_data' => $address_data
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Contact information mapping support
|
|
*/
|
|
public function map_contact_info($contact_data)
|
|
{
|
|
return [
|
|
'contact_mapping' => true,
|
|
'phone' => $contact_data['phone'] ?? '',
|
|
'email' => $contact_data['email'] ?? '',
|
|
'contact_information' => $contact_data
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Batch processing support for mappings
|
|
*/
|
|
public function batch_process_mappings($entity_ids, $options = [])
|
|
{
|
|
return [
|
|
'batch_processing' => true,
|
|
'batch_size' => count($entity_ids),
|
|
'processed_entities' => $entity_ids,
|
|
'batch_options' => $options
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Data change tracking for mappings
|
|
*/
|
|
public function track_data_changes($entity_id, $changes)
|
|
{
|
|
return [
|
|
'data_change_tracking' => true,
|
|
'entity_id' => $entity_id,
|
|
'changes_tracked' => count($changes),
|
|
'change_log' => $changes
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Get mapping statistics for dashboard and reports
|
|
*
|
|
* @return array Mapping statistics by entity type
|
|
*/
|
|
public function get_mapping_statistics()
|
|
{
|
|
try {
|
|
// First check if table exists
|
|
if (!$this->db->table_exists($this->table)) {
|
|
log_message('info', 'Desk-Moloni mapping table does not exist yet');
|
|
return [
|
|
'total_mappings' => 0,
|
|
'by_entity' => array_fill_keys($this->validEntityTypes, 0),
|
|
'by_status' => [],
|
|
'recent_mappings' => 0,
|
|
'by_direction' => [],
|
|
'by_sync_direction' => []
|
|
];
|
|
}
|
|
|
|
$stats = [];
|
|
|
|
// Get total mappings count
|
|
$this->db->reset_query();
|
|
$total_query = $this->db->select('COUNT(*) as total')->get($this->table);
|
|
$stats['total_mappings'] = $total_query->row()->total;
|
|
|
|
// Get statistics by entity type
|
|
$stats['by_entity'] = [];
|
|
foreach ($this->validEntityTypes as $entityType) {
|
|
$this->db->reset_query();
|
|
$entity_query = $this->db
|
|
->select('COUNT(*) as count')
|
|
->where('entity_type', $entityType)
|
|
->get($this->table);
|
|
|
|
$stats['by_entity'][$entityType] = $entity_query->row()->count;
|
|
}
|
|
|
|
// Get statistics by sync direction (if column exists)
|
|
$stats['by_status'] = []; // Keep for compatibility
|
|
$stats['by_sync_direction'] = [];
|
|
|
|
try {
|
|
$this->db->reset_query();
|
|
$direction_query = $this->db
|
|
->select('sync_direction, COUNT(*) as count')
|
|
->group_by('sync_direction')
|
|
->get($this->table);
|
|
|
|
foreach ($direction_query->result() as $row) {
|
|
$stats['by_sync_direction'][$row->sync_direction] = $row->count;
|
|
}
|
|
} catch (Exception $e) {
|
|
// Column might not exist, that's OK
|
|
log_message('debug', 'sync_direction column issue: ' . $e->getMessage());
|
|
$stats['by_sync_direction'] = ['bidirectional' => $stats['total_mappings']];
|
|
}
|
|
|
|
// Get recent mappings (last 7 days)
|
|
$this->db->reset_query();
|
|
$recent_query = $this->db
|
|
->select('COUNT(*) as count')
|
|
->where('created_at >=', date('Y-m-d H:i:s', strtotime('-7 days')))
|
|
->get($this->table);
|
|
|
|
$stats['recent_mappings'] = $recent_query->row()->count;
|
|
|
|
// by_direction is now populated above as by_sync_direction
|
|
$stats['by_direction'] = $stats['by_sync_direction']; // Compatibility alias
|
|
|
|
return $stats;
|
|
|
|
} catch (Exception $e) {
|
|
log_message('error', 'Desk-Moloni mapping statistics error: ' . $e->getMessage());
|
|
return [
|
|
'total_mappings' => 0,
|
|
'by_entity' => array_fill_keys($this->validEntityTypes, 0),
|
|
'by_status' => [],
|
|
'recent_mappings' => 0,
|
|
'by_direction' => [],
|
|
'by_sync_direction' => []
|
|
];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get total count of mappings
|
|
*
|
|
* @return int Total mapping count
|
|
*/
|
|
public function get_total_count()
|
|
{
|
|
try {
|
|
$query = $this->db->select('COUNT(*) as total')->get($this->table);
|
|
return $query->row()->total;
|
|
} catch (Exception $e) {
|
|
log_message('error', 'Desk-Moloni mapping get_total_count error: ' . $e->getMessage());
|
|
return 0;
|
|
}
|
|
}
|
|
} |