class Entry < ApplicationRecord require 'csv' belongs_to :user validates :date, presence: true validates :hours, numericality: { only_integer: true, greater_than_or_equal_to: 0 }, unless: -> { start_time.present? && end_time.nil? } validates :minutes, numericality: { only_integer: true, greater_than_or_equal_to: 0, less_than: 60 }, unless: -> { start_time.present? && end_time.nil? } before_save :normalize_time PRAKTIKUMSTYPEN = %w[propädeutikum fachspezifikum] ENTRY_ARTEN = %w[Praktikum Selbsterfahrung Supervision Fortbildung Semesterkosten] validates :praktikums_typ, inclusion: { in: PRAKTIKUMSTYPEN } validates :entry_art, inclusion: { in: ENTRY_ARTEN } validates :distance_km, numericality: { only_integer: true, greater_than_or_equal_to: 0 } after_initialize :set_default_date, if: :new_record? def set_default_date self.date ||= Date.today end def kilometer_pauschale return 0 unless distance_km.present? distance_km * 0.42 end def self.total_kilometer_cost_by_year(user) yearly_distances = where(user: user) .where.not(distance_km: nil) .group(Arel.sql("EXTRACT(YEAR FROM date)")) .order(Arel.sql("EXTRACT(YEAR FROM date) DESC")) .sum(:distance_km) yearly_distances.each_with_object({}) do |(year, km_sum), result| rate = MileageRate.find_by(year: year.to_i)&.rate_per_km || 0.42 result[year.to_i] = (km_sum * rate).round(2) end end def self.total_fortbildungskosten_by_year(user) where(user: user) .where.not(kosten: nil) .where( Entry.arel_table[:entry_art].eq('Fortbildung') .or(Entry.arel_table[:zaehlt_als_fortbildung].eq(true)) ) .group(Arel.sql("DATE_PART('year', date)")) .order(Arel.sql("DATE_PART('year', date) DESC")) .pluck(Arel.sql("DATE_PART('year', date)::int"), Arel.sql("SUM(kosten)")) .to_h end def self.total_selbsterfahrungskosten_by_year(user) where(user: user, entry_art: 'Selbsterfahrung') .where.not(kosten: nil) .group(Arel.sql("DATE_PART('year', date)")) .order(Arel.sql("DATE_PART('year', date) DESC")) .pluck(Arel.sql("DATE_PART('year', date)::int"), Arel.sql("SUM(kosten)")) .to_h end def self.total_selbsterfahrungskosten_by_year(user) where(user: user, entry_art: 'Selbsterfahrung') .where.not(kosten: nil) .group(Arel.sql("DATE_PART('year', date)")) .order(Arel.sql("DATE_PART('year', date) DESC")) .pluck(Arel.sql("DATE_PART('year', date)::int"), Arel.sql("SUM(kosten)")) .to_h end def self.total_supervision_by_year(user) where(user: user, entry_art: 'Supervision') .where.not(kosten: nil) .group(Arel.sql("DATE_PART('year', date)")) .order(Arel.sql("DATE_PART('year', date) DESC")) .pluck(Arel.sql("DATE_PART('year', date)::int"), Arel.sql("SUM(kosten)")) .to_h end def self.total_semesterkosten_by_year(user) where(user: user) .where.not(kosten: nil) .group(Arel.sql("DATE_PART('year', date)")) .order(Arel.sql("DATE_PART('year', date) DESC")) .pluck(Arel.sql("DATE_PART('year', date)::int"), Arel.sql("SUM(kosten)")) .to_h end def jahr date.year end def total_minutes hours * 60 + minutes end def self.to_csv CSV.generate(headers: true, col_sep: ";") do |csv| csv << %w[Datum Stunden Minuten Typ Art Kilometer User] all.each do |entry| csv << [ entry.date, entry.hours, entry.minutes, entry.praktikums_typ, entry.entry_art, entry.distance_km, entry.beschreibung, entry.kosten, entry.user.email ] end end end def total_minutes_including_break return nil unless start_time && end_time minutes = ((end_time - start_time) / 60).to_i minutes -= lunch_break_minutes.to_i minutes = 0 if minutes < 0 minutes end def hours_and_minutes mins = total_minutes_including_break return [0, 0] unless mins [mins / 60, mins % 60] end private def normalize_time self.minutes ||= 0 self.hours ||= 0 self.hours += minutes / 60 self.minutes = minutes % 60 end end