chore: add spec-kit and standardize signatures
- 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>
This commit is contained in:
894
src/includes/models/class-encounter.php
Normal file
894
src/includes/models/class-encounter.php
Normal file
@@ -0,0 +1,894 @@
|
||||
/**
|
||||
* Descomplicar® Crescimento Digital
|
||||
* https://descomplicar.pt
|
||||
*/
|
||||
|
||||
<?php
|
||||
/**
|
||||
* Encounter Model
|
||||
*
|
||||
* Handles medical encounter operations and patient consultation data
|
||||
*
|
||||
* @package KiviCare_API
|
||||
* @subpackage Models
|
||||
* @version 1.0.0
|
||||
* @author Descomplicar® <dev@descomplicar.pt>
|
||||
* @link https://descomplicar.pt
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
namespace KiviCare_API\Models;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class Encounter
|
||||
*
|
||||
* Model for handling patient encounters (medical consultations)
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class Encounter {
|
||||
|
||||
/**
|
||||
* Database table name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private static $table_name = 'kc_patient_encounters';
|
||||
|
||||
/**
|
||||
* Encounter ID
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* Encounter data
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $data = array();
|
||||
|
||||
/**
|
||||
* Required fields for encounter creation
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $required_fields = array(
|
||||
'encounter_date',
|
||||
'clinic_id',
|
||||
'doctor_id',
|
||||
'patient_id'
|
||||
);
|
||||
|
||||
/**
|
||||
* Valid encounter statuses
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $valid_statuses = array(
|
||||
0 => 'draft',
|
||||
1 => 'completed',
|
||||
2 => 'cancelled'
|
||||
);
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param int|array $encounter_id_or_data Encounter ID or data array
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function __construct( $encounter_id_or_data = null ) {
|
||||
if ( is_numeric( $encounter_id_or_data ) ) {
|
||||
$this->id = (int) $encounter_id_or_data;
|
||||
$this->load_data();
|
||||
} elseif ( is_array( $encounter_id_or_data ) ) {
|
||||
$this->data = $encounter_id_or_data;
|
||||
$this->id = isset( $this->data['id'] ) ? (int) $this->data['id'] : null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load encounter data from database
|
||||
*
|
||||
* @return bool True on success, false on failure
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private function load_data() {
|
||||
if ( ! $this->id ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$encounter_data = self::get_encounter_full_data( $this->id );
|
||||
|
||||
if ( $encounter_data ) {
|
||||
$this->data = $encounter_data;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new encounter
|
||||
*
|
||||
* @param array $encounter_data Encounter data
|
||||
* @return int|WP_Error Encounter ID on success, WP_Error on failure
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static function create( $encounter_data ) {
|
||||
// Validate required fields
|
||||
$validation = self::validate_encounter_data( $encounter_data );
|
||||
if ( is_wp_error( $validation ) ) {
|
||||
return $validation;
|
||||
}
|
||||
|
||||
global $wpdb;
|
||||
$table = $wpdb->prefix . self::$table_name;
|
||||
|
||||
// Prepare data for insertion
|
||||
$insert_data = array(
|
||||
'encounter_date' => sanitize_text_field( $encounter_data['encounter_date'] ),
|
||||
'clinic_id' => (int) $encounter_data['clinic_id'],
|
||||
'doctor_id' => (int) $encounter_data['doctor_id'],
|
||||
'patient_id' => (int) $encounter_data['patient_id'],
|
||||
'appointment_id' => isset( $encounter_data['appointment_id'] ) ? (int) $encounter_data['appointment_id'] : null,
|
||||
'description' => isset( $encounter_data['description'] ) ? sanitize_textarea_field( $encounter_data['description'] ) : '',
|
||||
'status' => isset( $encounter_data['status'] ) ? (int) $encounter_data['status'] : 0,
|
||||
'added_by' => get_current_user_id(),
|
||||
'created_at' => current_time( 'mysql' ),
|
||||
'template_id' => isset( $encounter_data['template_id'] ) ? (int) $encounter_data['template_id'] : null
|
||||
);
|
||||
|
||||
$result = $wpdb->insert( $table, $insert_data );
|
||||
|
||||
if ( $result === false ) {
|
||||
return new \WP_Error(
|
||||
'encounter_creation_failed',
|
||||
'Failed to create encounter: ' . $wpdb->last_error,
|
||||
array( 'status' => 500 )
|
||||
);
|
||||
}
|
||||
|
||||
$encounter_id = $wpdb->insert_id;
|
||||
|
||||
// Create medical history entries if provided
|
||||
if ( ! empty( $encounter_data['medical_history'] ) && is_array( $encounter_data['medical_history'] ) ) {
|
||||
self::create_medical_history_entries( $encounter_id, $encounter_data['medical_history'] );
|
||||
}
|
||||
|
||||
return $encounter_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update encounter data
|
||||
*
|
||||
* @param int $encounter_id Encounter ID
|
||||
* @param array $encounter_data Updated encounter data
|
||||
* @return bool|WP_Error True on success, WP_Error on failure
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static function update( $encounter_id, $encounter_data ) {
|
||||
if ( ! self::exists( $encounter_id ) ) {
|
||||
return new \WP_Error(
|
||||
'encounter_not_found',
|
||||
'Encounter not found',
|
||||
array( 'status' => 404 )
|
||||
);
|
||||
}
|
||||
|
||||
global $wpdb;
|
||||
$table = $wpdb->prefix . self::$table_name;
|
||||
|
||||
// Prepare update data
|
||||
$update_data = array();
|
||||
$allowed_fields = array(
|
||||
'encounter_date', 'clinic_id', 'doctor_id', 'patient_id',
|
||||
'appointment_id', 'description', 'status', 'template_id'
|
||||
);
|
||||
|
||||
foreach ( $allowed_fields as $field ) {
|
||||
if ( isset( $encounter_data[ $field ] ) ) {
|
||||
$value = $encounter_data[ $field ];
|
||||
|
||||
switch ( $field ) {
|
||||
case 'clinic_id':
|
||||
case 'doctor_id':
|
||||
case 'patient_id':
|
||||
case 'appointment_id':
|
||||
case 'status':
|
||||
case 'template_id':
|
||||
$update_data[ $field ] = (int) $value;
|
||||
break;
|
||||
case 'description':
|
||||
$update_data[ $field ] = sanitize_textarea_field( $value );
|
||||
break;
|
||||
default:
|
||||
$update_data[ $field ] = sanitize_text_field( $value );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( empty( $update_data ) ) {
|
||||
return new \WP_Error(
|
||||
'no_data_to_update',
|
||||
'No valid data provided for update',
|
||||
array( 'status' => 400 )
|
||||
);
|
||||
}
|
||||
|
||||
$result = $wpdb->update(
|
||||
$table,
|
||||
$update_data,
|
||||
array( 'id' => $encounter_id ),
|
||||
null,
|
||||
array( '%d' )
|
||||
);
|
||||
|
||||
if ( $result === false ) {
|
||||
return new \WP_Error(
|
||||
'encounter_update_failed',
|
||||
'Failed to update encounter: ' . $wpdb->last_error,
|
||||
array( 'status' => 500 )
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an encounter
|
||||
*
|
||||
* @param int $encounter_id Encounter ID
|
||||
* @return bool|WP_Error True on success, WP_Error on failure
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static function delete( $encounter_id ) {
|
||||
if ( ! self::exists( $encounter_id ) ) {
|
||||
return new \WP_Error(
|
||||
'encounter_not_found',
|
||||
'Encounter not found',
|
||||
array( 'status' => 404 )
|
||||
);
|
||||
}
|
||||
|
||||
// Check if encounter can be deleted (has prescriptions or bills)
|
||||
if ( self::has_dependencies( $encounter_id ) ) {
|
||||
return new \WP_Error(
|
||||
'encounter_has_dependencies',
|
||||
'Cannot delete encounter with associated prescriptions or bills',
|
||||
array( 'status' => 409 )
|
||||
);
|
||||
}
|
||||
|
||||
global $wpdb;
|
||||
|
||||
// Delete medical history entries first
|
||||
$wpdb->delete(
|
||||
"{$wpdb->prefix}kc_medical_history",
|
||||
array( 'encounter_id' => $encounter_id ),
|
||||
array( '%d' )
|
||||
);
|
||||
|
||||
// Delete encounter
|
||||
$table = $wpdb->prefix . self::$table_name;
|
||||
$result = $wpdb->delete(
|
||||
$table,
|
||||
array( 'id' => $encounter_id ),
|
||||
array( '%d' )
|
||||
);
|
||||
|
||||
if ( $result === false ) {
|
||||
return new \WP_Error(
|
||||
'encounter_deletion_failed',
|
||||
'Failed to delete encounter: ' . $wpdb->last_error,
|
||||
array( 'status' => 500 )
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get encounter by ID
|
||||
*
|
||||
* @param int $encounter_id Encounter ID
|
||||
* @return array|null Encounter data or null if not found
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static function get_by_id( $encounter_id ) {
|
||||
return self::get_encounter_full_data( $encounter_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all encounters with optional filtering
|
||||
*
|
||||
* @param array $args Query arguments
|
||||
* @return array Array of encounter data
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static function get_all( $args = array() ) {
|
||||
global $wpdb;
|
||||
$table = $wpdb->prefix . self::$table_name;
|
||||
|
||||
$defaults = array(
|
||||
'clinic_id' => null,
|
||||
'doctor_id' => null,
|
||||
'patient_id' => null,
|
||||
'status' => null,
|
||||
'date_from' => null,
|
||||
'date_to' => null,
|
||||
'search' => '',
|
||||
'limit' => 50,
|
||||
'offset' => 0,
|
||||
'orderby' => 'encounter_date',
|
||||
'order' => 'DESC'
|
||||
);
|
||||
|
||||
$args = wp_parse_args( $args, $defaults );
|
||||
|
||||
$where_clauses = array( '1=1' );
|
||||
$where_values = array();
|
||||
|
||||
// Clinic filter
|
||||
if ( ! is_null( $args['clinic_id'] ) ) {
|
||||
$where_clauses[] = 'e.clinic_id = %d';
|
||||
$where_values[] = $args['clinic_id'];
|
||||
}
|
||||
|
||||
// Doctor filter
|
||||
if ( ! is_null( $args['doctor_id'] ) ) {
|
||||
$where_clauses[] = 'e.doctor_id = %d';
|
||||
$where_values[] = $args['doctor_id'];
|
||||
}
|
||||
|
||||
// Patient filter
|
||||
if ( ! is_null( $args['patient_id'] ) ) {
|
||||
$where_clauses[] = 'e.patient_id = %d';
|
||||
$where_values[] = $args['patient_id'];
|
||||
}
|
||||
|
||||
// Status filter
|
||||
if ( ! is_null( $args['status'] ) ) {
|
||||
$where_clauses[] = 'e.status = %d';
|
||||
$where_values[] = $args['status'];
|
||||
}
|
||||
|
||||
// Date range filters
|
||||
if ( ! is_null( $args['date_from'] ) ) {
|
||||
$where_clauses[] = 'e.encounter_date >= %s';
|
||||
$where_values[] = $args['date_from'];
|
||||
}
|
||||
|
||||
if ( ! is_null( $args['date_to'] ) ) {
|
||||
$where_clauses[] = 'e.encounter_date <= %s';
|
||||
$where_values[] = $args['date_to'];
|
||||
}
|
||||
|
||||
// Search filter
|
||||
if ( ! empty( $args['search'] ) ) {
|
||||
$where_clauses[] = '(p.first_name LIKE %s OR p.last_name LIKE %s OR d.first_name LIKE %s OR d.last_name LIKE %s OR e.description LIKE %s)';
|
||||
$search_term = '%' . $wpdb->esc_like( $args['search'] ) . '%';
|
||||
$where_values = array_merge( $where_values, array_fill( 0, 5, $search_term ) );
|
||||
}
|
||||
|
||||
$where_sql = implode( ' AND ', $where_clauses );
|
||||
|
||||
// Build query
|
||||
$query = "SELECT e.*,
|
||||
c.name as clinic_name,
|
||||
CONCAT(p.first_name, ' ', p.last_name) as patient_name,
|
||||
p.user_email as patient_email,
|
||||
CONCAT(d.first_name, ' ', d.last_name) as doctor_name,
|
||||
d.user_email as doctor_email,
|
||||
CONCAT(ab.first_name, ' ', ab.last_name) as added_by_name
|
||||
FROM {$table} e
|
||||
LEFT JOIN {$wpdb->prefix}kc_clinics c ON e.clinic_id = c.id
|
||||
LEFT JOIN {$wpdb->prefix}users p ON e.patient_id = p.ID
|
||||
LEFT JOIN {$wpdb->prefix}users d ON e.doctor_id = d.ID
|
||||
LEFT JOIN {$wpdb->prefix}users ab ON e.added_by = ab.ID
|
||||
WHERE {$where_sql}";
|
||||
|
||||
$query .= sprintf( ' ORDER BY e.%s %s',
|
||||
sanitize_sql_orderby( $args['orderby'] ),
|
||||
sanitize_sql_orderby( $args['order'] )
|
||||
);
|
||||
|
||||
$query .= $wpdb->prepare( ' LIMIT %d OFFSET %d', $args['limit'], $args['offset'] );
|
||||
|
||||
if ( ! empty( $where_values ) ) {
|
||||
$query = $wpdb->prepare( $query, $where_values );
|
||||
}
|
||||
|
||||
$encounters = $wpdb->get_results( $query, ARRAY_A );
|
||||
|
||||
return array_map( array( self::class, 'format_encounter_data' ), $encounters );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get encounter full data with related entities
|
||||
*
|
||||
* @param int $encounter_id Encounter ID
|
||||
* @return array|null Full encounter data or null if not found
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static function get_encounter_full_data( $encounter_id ) {
|
||||
global $wpdb;
|
||||
$table = $wpdb->prefix . self::$table_name;
|
||||
|
||||
$encounter = $wpdb->get_row(
|
||||
$wpdb->prepare(
|
||||
"SELECT e.*,
|
||||
c.name as clinic_name, c.address as clinic_address,
|
||||
CONCAT(p.first_name, ' ', p.last_name) as patient_name,
|
||||
p.user_email as patient_email,
|
||||
CONCAT(d.first_name, ' ', d.last_name) as doctor_name,
|
||||
d.user_email as doctor_email,
|
||||
CONCAT(ab.first_name, ' ', ab.last_name) as added_by_name
|
||||
FROM {$table} e
|
||||
LEFT JOIN {$wpdb->prefix}kc_clinics c ON e.clinic_id = c.id
|
||||
LEFT JOIN {$wpdb->prefix}users p ON e.patient_id = p.ID
|
||||
LEFT JOIN {$wpdb->prefix}users d ON e.doctor_id = d.ID
|
||||
LEFT JOIN {$wpdb->prefix}users ab ON e.added_by = ab.ID
|
||||
WHERE e.id = %d",
|
||||
$encounter_id
|
||||
),
|
||||
ARRAY_A
|
||||
);
|
||||
|
||||
if ( ! $encounter ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return self::format_encounter_data( $encounter );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get encounter prescriptions
|
||||
*
|
||||
* @param int $encounter_id Encounter ID
|
||||
* @return array Array of prescriptions
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static function get_prescriptions( $encounter_id ) {
|
||||
global $wpdb;
|
||||
|
||||
$prescriptions = $wpdb->get_results(
|
||||
$wpdb->prepare(
|
||||
"SELECT p.*,
|
||||
CONCAT(ab.first_name, ' ', ab.last_name) as added_by_name
|
||||
FROM {$wpdb->prefix}kc_prescription p
|
||||
LEFT JOIN {$wpdb->prefix}users ab ON p.added_by = ab.ID
|
||||
WHERE p.encounter_id = %d
|
||||
ORDER BY p.created_at ASC",
|
||||
$encounter_id
|
||||
),
|
||||
ARRAY_A
|
||||
);
|
||||
|
||||
return array_map( function( $prescription ) {
|
||||
return array(
|
||||
'id' => (int) $prescription['id'],
|
||||
'name' => $prescription['name'],
|
||||
'frequency' => $prescription['frequency'],
|
||||
'duration' => $prescription['duration'],
|
||||
'instruction' => $prescription['instruction'],
|
||||
'patient_id' => (int) $prescription['patient_id'],
|
||||
'added_by' => (int) $prescription['added_by'],
|
||||
'added_by_name' => $prescription['added_by_name'],
|
||||
'created_at' => $prescription['created_at'],
|
||||
'is_from_template' => (bool) $prescription['is_from_template']
|
||||
);
|
||||
}, $prescriptions );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get encounter medical history
|
||||
*
|
||||
* @param int $encounter_id Encounter ID
|
||||
* @return array Array of medical history entries
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static function get_medical_history( $encounter_id ) {
|
||||
global $wpdb;
|
||||
|
||||
$history = $wpdb->get_results(
|
||||
$wpdb->prepare(
|
||||
"SELECT mh.*,
|
||||
CONCAT(ab.first_name, ' ', ab.last_name) as added_by_name
|
||||
FROM {$wpdb->prefix}kc_medical_history mh
|
||||
LEFT JOIN {$wpdb->prefix}users ab ON mh.added_by = ab.ID
|
||||
WHERE mh.encounter_id = %d
|
||||
ORDER BY mh.created_at ASC",
|
||||
$encounter_id
|
||||
),
|
||||
ARRAY_A
|
||||
);
|
||||
|
||||
return array_map( function( $entry ) {
|
||||
return array(
|
||||
'id' => (int) $entry['id'],
|
||||
'type' => $entry['type'],
|
||||
'title' => $entry['title'],
|
||||
'patient_id' => (int) $entry['patient_id'],
|
||||
'added_by' => (int) $entry['added_by'],
|
||||
'added_by_name' => $entry['added_by_name'],
|
||||
'created_at' => $entry['created_at']
|
||||
);
|
||||
}, $history );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get encounter bills
|
||||
*
|
||||
* @param int $encounter_id Encounter ID
|
||||
* @return array Array of bills
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static function get_bills( $encounter_id ) {
|
||||
global $wpdb;
|
||||
|
||||
$bills = $wpdb->get_results(
|
||||
$wpdb->prepare(
|
||||
"SELECT * FROM {$wpdb->prefix}kc_bills
|
||||
WHERE encounter_id = %d
|
||||
ORDER BY created_at DESC",
|
||||
$encounter_id
|
||||
),
|
||||
ARRAY_A
|
||||
);
|
||||
|
||||
return array_map( function( $bill ) {
|
||||
return array(
|
||||
'id' => (int) $bill['id'],
|
||||
'title' => $bill['title'],
|
||||
'total_amount' => (float) $bill['total_amount'],
|
||||
'discount' => (float) $bill['discount'],
|
||||
'actual_amount' => (float) $bill['actual_amount'],
|
||||
'status' => (int) $bill['status'],
|
||||
'payment_status' => $bill['payment_status'],
|
||||
'created_at' => $bill['created_at'],
|
||||
'clinic_id' => (int) $bill['clinic_id'],
|
||||
'appointment_id' => (int) $bill['appointment_id']
|
||||
);
|
||||
}, $bills );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add prescription to encounter
|
||||
*
|
||||
* @param int $encounter_id Encounter ID
|
||||
* @param array $prescription_data Prescription data
|
||||
* @return int|WP_Error Prescription ID on success, WP_Error on failure
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static function add_prescription( $encounter_id, $prescription_data ) {
|
||||
if ( ! self::exists( $encounter_id ) ) {
|
||||
return new \WP_Error(
|
||||
'encounter_not_found',
|
||||
'Encounter not found',
|
||||
array( 'status' => 404 )
|
||||
);
|
||||
}
|
||||
|
||||
$encounter = self::get_by_id( $encounter_id );
|
||||
|
||||
return Prescription::create( array_merge( $prescription_data, array(
|
||||
'encounter_id' => $encounter_id,
|
||||
'patient_id' => $encounter['patient']['id']
|
||||
) ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if encounter exists
|
||||
*
|
||||
* @param int $encounter_id Encounter ID
|
||||
* @return bool True if exists, false otherwise
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static function exists( $encounter_id ) {
|
||||
global $wpdb;
|
||||
$table = $wpdb->prefix . self::$table_name;
|
||||
|
||||
$count = $wpdb->get_var(
|
||||
$wpdb->prepare(
|
||||
"SELECT COUNT(*) FROM {$table} WHERE id = %d",
|
||||
$encounter_id
|
||||
)
|
||||
);
|
||||
|
||||
return (int) $count > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if encounter has dependencies (prescriptions, bills, etc.)
|
||||
*
|
||||
* @param int $encounter_id Encounter ID
|
||||
* @return bool True if has dependencies, false otherwise
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private static function has_dependencies( $encounter_id ) {
|
||||
global $wpdb;
|
||||
|
||||
// Check prescriptions
|
||||
$prescriptions_count = $wpdb->get_var(
|
||||
$wpdb->prepare(
|
||||
"SELECT COUNT(*) FROM {$wpdb->prefix}kc_prescription WHERE encounter_id = %d",
|
||||
$encounter_id
|
||||
)
|
||||
);
|
||||
|
||||
if ( (int) $prescriptions_count > 0 ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check bills
|
||||
$bills_count = $wpdb->get_var(
|
||||
$wpdb->prepare(
|
||||
"SELECT COUNT(*) FROM {$wpdb->prefix}kc_bills WHERE encounter_id = %d",
|
||||
$encounter_id
|
||||
)
|
||||
);
|
||||
|
||||
return (int) $bills_count > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate encounter data
|
||||
*
|
||||
* @param array $encounter_data Encounter data to validate
|
||||
* @return bool|WP_Error True if valid, WP_Error if invalid
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private static function validate_encounter_data( $encounter_data ) {
|
||||
$errors = array();
|
||||
|
||||
// Check required fields
|
||||
foreach ( self::$required_fields as $field ) {
|
||||
if ( empty( $encounter_data[ $field ] ) ) {
|
||||
$errors[] = "Field '{$field}' is required";
|
||||
}
|
||||
}
|
||||
|
||||
// Validate date format
|
||||
if ( ! empty( $encounter_data['encounter_date'] ) ) {
|
||||
$date = \DateTime::createFromFormat( 'Y-m-d', $encounter_data['encounter_date'] );
|
||||
if ( ! $date || $date->format( 'Y-m-d' ) !== $encounter_data['encounter_date'] ) {
|
||||
$errors[] = 'Invalid encounter date format. Use YYYY-MM-DD';
|
||||
}
|
||||
}
|
||||
|
||||
// Validate entities exist
|
||||
if ( ! empty( $encounter_data['clinic_id'] ) &&
|
||||
! Clinic::exists( $encounter_data['clinic_id'] ) ) {
|
||||
$errors[] = 'Clinic not found';
|
||||
}
|
||||
|
||||
if ( ! empty( $encounter_data['doctor_id'] ) &&
|
||||
! Doctor::exists( $encounter_data['doctor_id'] ) ) {
|
||||
$errors[] = 'Doctor not found';
|
||||
}
|
||||
|
||||
if ( ! empty( $encounter_data['patient_id'] ) &&
|
||||
! Patient::exists( $encounter_data['patient_id'] ) ) {
|
||||
$errors[] = 'Patient not found';
|
||||
}
|
||||
|
||||
// Validate appointment exists if provided
|
||||
if ( ! empty( $encounter_data['appointment_id'] ) &&
|
||||
! Appointment::exists( $encounter_data['appointment_id'] ) ) {
|
||||
$errors[] = 'Appointment not found';
|
||||
}
|
||||
|
||||
// Validate status
|
||||
if ( isset( $encounter_data['status'] ) &&
|
||||
! array_key_exists( $encounter_data['status'], self::$valid_statuses ) ) {
|
||||
$errors[] = 'Invalid status value';
|
||||
}
|
||||
|
||||
if ( ! empty( $errors ) ) {
|
||||
return new \WP_Error(
|
||||
'encounter_validation_failed',
|
||||
'Encounter validation failed',
|
||||
array(
|
||||
'status' => 400,
|
||||
'errors' => $errors
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format encounter data for API response
|
||||
*
|
||||
* @param array $encounter_data Raw encounter data
|
||||
* @return array Formatted encounter data
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private static function format_encounter_data( $encounter_data ) {
|
||||
if ( ! $encounter_data ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$formatted = array(
|
||||
'id' => (int) $encounter_data['id'],
|
||||
'encounter_date' => $encounter_data['encounter_date'],
|
||||
'description' => $encounter_data['description'],
|
||||
'status' => (int) $encounter_data['status'],
|
||||
'status_text' => self::$valid_statuses[ $encounter_data['status'] ] ?? 'unknown',
|
||||
'created_at' => $encounter_data['created_at'],
|
||||
'clinic' => array(
|
||||
'id' => (int) $encounter_data['clinic_id'],
|
||||
'name' => $encounter_data['clinic_name'] ?? '',
|
||||
'address' => $encounter_data['clinic_address'] ?? ''
|
||||
),
|
||||
'patient' => array(
|
||||
'id' => (int) $encounter_data['patient_id'],
|
||||
'name' => $encounter_data['patient_name'] ?? '',
|
||||
'email' => $encounter_data['patient_email'] ?? ''
|
||||
),
|
||||
'doctor' => array(
|
||||
'id' => (int) $encounter_data['doctor_id'],
|
||||
'name' => $encounter_data['doctor_name'] ?? '',
|
||||
'email' => $encounter_data['doctor_email'] ?? ''
|
||||
),
|
||||
'appointment_id' => isset( $encounter_data['appointment_id'] ) ? (int) $encounter_data['appointment_id'] : null,
|
||||
'template_id' => isset( $encounter_data['template_id'] ) ? (int) $encounter_data['template_id'] : null,
|
||||
'added_by' => array(
|
||||
'id' => (int) $encounter_data['added_by'],
|
||||
'name' => $encounter_data['added_by_name'] ?? ''
|
||||
)
|
||||
);
|
||||
|
||||
return $formatted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create medical history entries for encounter
|
||||
*
|
||||
* @param int $encounter_id Encounter ID
|
||||
* @param array $history_entries Array of history entries
|
||||
* @return bool True on success
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private static function create_medical_history_entries( $encounter_id, $history_entries ) {
|
||||
global $wpdb;
|
||||
|
||||
$encounter = self::get_by_id( $encounter_id );
|
||||
if ( ! $encounter ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ( $history_entries as $entry ) {
|
||||
$wpdb->insert(
|
||||
"{$wpdb->prefix}kc_medical_history",
|
||||
array(
|
||||
'encounter_id' => $encounter_id,
|
||||
'patient_id' => $encounter['patient']['id'],
|
||||
'type' => sanitize_text_field( $entry['type'] ),
|
||||
'title' => sanitize_text_field( $entry['title'] ),
|
||||
'added_by' => get_current_user_id(),
|
||||
'created_at' => current_time( 'mysql' )
|
||||
),
|
||||
array( '%d', '%d', '%s', '%s', '%d', '%s' )
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get encounter statistics
|
||||
*
|
||||
* @param array $filters Optional filters
|
||||
* @return array Encounter statistics
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static function get_statistics( $filters = array() ) {
|
||||
global $wpdb;
|
||||
$table = $wpdb->prefix . self::$table_name;
|
||||
|
||||
$where_clauses = array( '1=1' );
|
||||
$where_values = array();
|
||||
|
||||
if ( ! empty( $filters['clinic_id'] ) ) {
|
||||
$where_clauses[] = 'clinic_id = %d';
|
||||
$where_values[] = $filters['clinic_id'];
|
||||
}
|
||||
|
||||
if ( ! empty( $filters['doctor_id'] ) ) {
|
||||
$where_clauses[] = 'doctor_id = %d';
|
||||
$where_values[] = $filters['doctor_id'];
|
||||
}
|
||||
|
||||
if ( ! empty( $filters['patient_id'] ) ) {
|
||||
$where_clauses[] = 'patient_id = %d';
|
||||
$where_values[] = $filters['patient_id'];
|
||||
}
|
||||
|
||||
$where_sql = implode( ' AND ', $where_clauses );
|
||||
|
||||
$stats = array(
|
||||
'total_encounters' => 0,
|
||||
'draft_encounters' => 0,
|
||||
'completed_encounters' => 0,
|
||||
'cancelled_encounters' => 0,
|
||||
'encounters_today' => 0,
|
||||
'encounters_this_week' => 0,
|
||||
'encounters_this_month' => 0,
|
||||
'avg_encounters_per_day' => 0
|
||||
);
|
||||
|
||||
// Total encounters
|
||||
$query = "SELECT COUNT(*) FROM {$table} WHERE {$where_sql}";
|
||||
if ( ! empty( $where_values ) ) {
|
||||
$query = $wpdb->prepare( $query, $where_values );
|
||||
}
|
||||
$stats['total_encounters'] = (int) $wpdb->get_var( $query );
|
||||
|
||||
// Encounters by status
|
||||
foreach ( self::$valid_statuses as $status_id => $status_name ) {
|
||||
$status_where = $where_clauses;
|
||||
$status_where[] = 'status = %d';
|
||||
$status_values = array_merge( $where_values, array( $status_id ) );
|
||||
|
||||
$query = $wpdb->prepare(
|
||||
"SELECT COUNT(*) FROM {$table} WHERE " . implode( ' AND ', $status_where ),
|
||||
$status_values
|
||||
);
|
||||
|
||||
$stats[ $status_name . '_encounters' ] = (int) $wpdb->get_var( $query );
|
||||
}
|
||||
|
||||
// Encounters today
|
||||
$today_where = array_merge( $where_clauses, array( 'encounter_date = CURDATE()' ) );
|
||||
$query = "SELECT COUNT(*) FROM {$table} WHERE " . implode( ' AND ', $today_where );
|
||||
if ( ! empty( $where_values ) ) {
|
||||
$query = $wpdb->prepare( $query, $where_values );
|
||||
}
|
||||
$stats['encounters_today'] = (int) $wpdb->get_var( $query );
|
||||
|
||||
// Encounters this week
|
||||
$week_where = array_merge( $where_clauses, array(
|
||||
'WEEK(encounter_date) = WEEK(CURDATE())',
|
||||
'YEAR(encounter_date) = YEAR(CURDATE())'
|
||||
) );
|
||||
$query = "SELECT COUNT(*) FROM {$table} WHERE " . implode( ' AND ', $week_where );
|
||||
if ( ! empty( $where_values ) ) {
|
||||
$query = $wpdb->prepare( $query, $where_values );
|
||||
}
|
||||
$stats['encounters_this_week'] = (int) $wpdb->get_var( $query );
|
||||
|
||||
// Encounters this month
|
||||
$month_where = array_merge( $where_clauses, array(
|
||||
'MONTH(encounter_date) = MONTH(CURDATE())',
|
||||
'YEAR(encounter_date) = YEAR(CURDATE())'
|
||||
) );
|
||||
$query = "SELECT COUNT(*) FROM {$table} WHERE " . implode( ' AND ', $month_where );
|
||||
if ( ! empty( $where_values ) ) {
|
||||
$query = $wpdb->prepare( $query, $where_values );
|
||||
}
|
||||
$stats['encounters_this_month'] = (int) $wpdb->get_var( $query );
|
||||
|
||||
// Calculate average encounters per day (last 30 days)
|
||||
if ( $stats['total_encounters'] > 0 ) {
|
||||
$days_active = $wpdb->get_var(
|
||||
"SELECT DATEDIFF(MAX(encounter_date), MIN(encounter_date)) + 1
|
||||
FROM {$table}
|
||||
WHERE encounter_date >= DATE_SUB(CURDATE(), INTERVAL 30 DAY)"
|
||||
);
|
||||
|
||||
if ( $days_active > 0 ) {
|
||||
$stats['avg_encounters_per_day'] = round( $stats['encounters_this_month'] / min( $days_active, 30 ), 2 );
|
||||
}
|
||||
}
|
||||
|
||||
return $stats;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user