Files
KGV-Verein-Manager/includes/Repositories/CostRepository.php
2026-04-13 21:01:07 +02:00

314 lines
9.0 KiB
PHP

<?php
/**
* Cost repository.
*
* @package KGV\VereinManager
*/
namespace KGV\VereinManager\Repositories;
use KGV\VereinManager\Schema;
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
class CostRepository extends AbstractRepository {
/**
* Resolve table name.
*
* @return string
*/
protected function resolve_table() {
return Schema::table( 'cost_entries' );
}
/**
* Get the persistent year table name.
*
* @return string
*/
private function year_table() {
return Schema::table( 'cost_years' );
}
/**
* Get the yearly section rates table name.
*
* @return string
*/
private function rate_table() {
return Schema::table( 'cost_rates' );
}
/**
* Search cost entries.
*
* @param array $args Query arguments.
* @return array
*/
public function search( $args = array() ) {
$search = isset( $args['s'] ) ? sanitize_text_field( wp_unslash( $args['s'] ) ) : '';
$year = isset( $args['year'] ) ? absint( $args['year'] ) : 0;
$orderby = $this->sanitize_orderby( isset( $args['orderby'] ) ? sanitize_key( wp_unslash( $args['orderby'] ) ) : 'name', array( 'entry_year', 'name', 'distribution_type', 'unit_amount', 'total_cost', 'updated_at', 'created_at' ), 'name' );
$order = $this->sanitize_order( isset( $args['order'] ) ? sanitize_key( wp_unslash( $args['order'] ) ) : 'ASC' );
$sql = "SELECT * FROM {$this->table} WHERE 1=1";
$params = array();
if ( $year > 0 ) {
$this->ensure_year( $year );
$sql .= ' AND entry_year = %d';
$params[] = $year;
}
if ( '' !== $search ) {
$like = '%' . $this->wpdb->esc_like( $search ) . '%';
$sql .= ' AND (name LIKE %s OR note LIKE %s)';
$params[] = $like;
$params[] = $like;
}
$sql .= " ORDER BY {$orderby} {$order}, id DESC";
if ( ! empty( $params ) ) {
return $this->wpdb->get_results( $this->wpdb->prepare( $sql, $params ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
}
return $this->wpdb->get_results( $sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery
}
/**
* Save or update a cost entry.
*
* @param array $data Entry data.
* @param int $id Optional ID.
* @return int|false
*/
public function save( $data, $id = 0 ) {
$payload = array(
'entry_year' => absint( $data['entry_year'] ),
'name' => $data['name'],
'distribution_type' => isset( $data['distribution_type'] ) ? $data['distribution_type'] : 'parcel',
'unit_amount' => isset( $data['unit_amount'] ) ? (float) $data['unit_amount'] : 0,
'total_cost' => (float) $data['total_cost'],
'note' => $data['note'],
'updated_at' => $this->now(),
);
$formats = array( '%d', '%s', '%s', '%f', '%f', '%s', '%s' );
$this->ensure_year( $payload['entry_year'] );
if ( $id > 0 ) {
$this->wpdb->update( $this->table, $payload, array( 'id' => $id ), $formats, array( '%d' ) );
return $id;
}
$payload['created_at'] = $this->now();
$this->wpdb->insert( $this->table, $payload, array( '%d', '%s', '%s', '%f', '%f', '%s', '%s', '%s' ) );
return $this->wpdb->insert_id;
}
/**
* Get available years for filters and dropdowns.
*
* @param int $selected_year Optional selected year.
* @return array
*/
public function get_years( $selected_year = 0 ) {
$year_table = $this->year_table();
$years = $this->wpdb->get_col( "SELECT DISTINCT entry_year FROM {$this->table} WHERE entry_year > 0 ORDER BY entry_year DESC" ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery
$saved_years = $this->wpdb->get_col( "SELECT entry_year FROM {$year_table} ORDER BY entry_year DESC" ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery
$current_year = (int) current_time( 'Y' );
$years = array_map( 'absint', array_merge( (array) $years, (array) $saved_years ) );
$years[] = $current_year;
$years[] = $current_year - 1;
$years[] = $current_year + 1;
if ( $selected_year > 0 ) {
$years[] = absint( $selected_year );
}
$years = array_values( array_unique( array_filter( $years ) ) );
rsort( $years, SORT_NUMERIC );
return $years;
}
/**
* Ensure one year exists so it remains selectable without cost entries.
*
* @param int $year Year value.
* @return bool
*/
public function ensure_year( $year ) {
$year = absint( $year );
$year_table = $this->year_table();
if ( $year < 1 || '' === $year_table ) {
return false;
}
$exists = (int) $this->wpdb->get_var( $this->wpdb->prepare( "SELECT COUNT(*) FROM {$year_table} WHERE entry_year = %d", $year ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
if ( $exists > 0 ) {
return true;
}
$result = $this->wpdb->insert(
$year_table,
array(
'entry_year' => $year,
'power_price_per_kwh' => null,
'water_price_per_m3' => null,
'created_at' => $this->now(),
'updated_at' => $this->now(),
),
array( '%d', '%s', '%s', '%s', '%s' )
);
return false !== $result;
}
/**
* Save the configured yearly prices for electricity and water.
*
* @param int $year Year value.
* @param float|null $power_price_per_kwh Price per kWh.
* @param float|null $water_price_per_m3 Price per m³.
* @return bool
*/
public function save_year( $year, $power_price_per_kwh = null, $water_price_per_m3 = null ) {
$year = absint( $year );
$year_table = $this->year_table();
if ( ! $this->ensure_year( $year ) ) {
return false;
}
$result = $this->wpdb->update(
$year_table,
array(
'power_price_per_kwh' => null !== $power_price_per_kwh ? (float) $power_price_per_kwh : null,
'water_price_per_m3' => null !== $water_price_per_m3 ? (float) $water_price_per_m3 : null,
'updated_at' => $this->now(),
),
array( 'entry_year' => $year ),
array( '%f', '%f', '%s' ),
array( '%d' )
);
return false !== $result;
}
/**
* Load one year's saved price details.
*
* @param int $year Selected year.
* @return object|null
*/
public function get_year_details( $year ) {
$year = absint( $year );
$year_table = $this->year_table();
if ( $year < 1 || '' === $year_table ) {
return null;
}
return $this->wpdb->get_row( $this->wpdb->prepare( "SELECT * FROM {$year_table} WHERE entry_year = %d", $year ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
}
/**
* Save section-specific yearly prices.
*
* @param array $data Section pricing data.
* @return bool
*/
public function save_section_prices( $data ) {
$year = absint( $data['entry_year'] );
$section_id = absint( $data['section_id'] );
$rate_table = $this->rate_table();
if ( ! $this->ensure_year( $year ) || $section_id < 1 || '' === $rate_table ) {
return false;
}
$payload = array(
'entry_year' => $year,
'section_id' => $section_id,
'power_price_per_kwh' => null !== $data['power_price_per_kwh'] ? (float) $data['power_price_per_kwh'] : null,
'water_price_per_m3' => null !== $data['water_price_per_m3'] ? (float) $data['water_price_per_m3'] : null,
'updated_at' => $this->now(),
);
$exists = (int) $this->wpdb->get_var( $this->wpdb->prepare( "SELECT COUNT(*) FROM {$rate_table} WHERE entry_year = %d AND section_id = %d", $year, $section_id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
if ( $exists > 0 ) {
$result = $this->wpdb->update(
$rate_table,
array(
'power_price_per_kwh' => $payload['power_price_per_kwh'],
'water_price_per_m3' => $payload['water_price_per_m3'],
'updated_at' => $payload['updated_at'],
),
array(
'entry_year' => $year,
'section_id' => $section_id,
),
array( '%f', '%f', '%s' ),
array( '%d', '%d' )
);
return false !== $result;
}
$payload['created_at'] = $this->now();
$result = $this->wpdb->insert( $rate_table, $payload, array( '%d', '%d', '%f', '%f', '%s', '%s' ) );
return false !== $result;
}
/**
* Get all saved section prices for one year.
*
* @param int $year Selected year.
* @return array
*/
public function get_section_prices( $year ) {
$year = absint( $year );
$rate_table = $this->rate_table();
$sections = Schema::table( 'sections' );
if ( $year < 1 || '' === $rate_table ) {
return array();
}
$sql = "SELECT r.*, s.name AS section_name
FROM {$rate_table} r
LEFT JOIN {$sections} s ON s.id = r.section_id
WHERE r.entry_year = %d
ORDER BY s.name ASC, r.section_id ASC";
return $this->wpdb->get_results( $this->wpdb->prepare( $sql, $year ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
}
/**
* Sum all costs for a year.
*
* @param int $year Selected year.
* @return float
*/
public function get_total_for_year( $year ) {
if ( $year < 1 ) {
return 0.0;
}
$total = $this->wpdb->get_var( $this->wpdb->prepare( "SELECT COALESCE(SUM(total_cost), 0) FROM {$this->table} WHERE entry_year = %d", absint( $year ) ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
return (float) $total;
}
}