12 changed files with 178 additions and 9 deletions
@ -0,0 +1,47 @@ |
|||
class MileageRatesController < ApplicationController |
|||
before_action :authenticate_user! |
|||
# Optional: nur Admins erlauben |
|||
# before_action :require_admin! |
|||
|
|||
before_action :set_mileage_rate, only: [:edit, :update] |
|||
|
|||
def index |
|||
@mileage_rates = MileageRate.order(year: :desc) |
|||
end |
|||
|
|||
def new |
|||
@mileage_rate = MileageRate.new |
|||
end |
|||
|
|||
def create |
|||
@mileage_rate = MileageRate.new(mileage_rate_params) |
|||
if @mileage_rate.save |
|||
redirect_to mileage_rates_path, notice: "Kilometersatz für #{@mileage_rate.year} gespeichert." |
|||
else |
|||
flash.now[:alert] = "Speichern fehlgeschlagen" |
|||
render :new, status: :unprocessable_entity |
|||
end |
|||
end |
|||
|
|||
def edit |
|||
end |
|||
|
|||
def update |
|||
if @mileage_rate.update(mileage_rate_params) |
|||
redirect_to mileage_rates_path, notice: "Kilometersatz für #{@mileage_rate.year} aktualisiert." |
|||
else |
|||
flash.now[:alert] = "Aktualisierung fehlgeschlagen" |
|||
render :edit, status: :unprocessable_entity |
|||
end |
|||
end |
|||
|
|||
private |
|||
|
|||
def set_mileage_rate |
|||
@mileage_rate = MileageRate.find(params[:id]) |
|||
end |
|||
|
|||
def mileage_rate_params |
|||
params.require(:mileage_rate).permit(:year, :rate_per_km) |
|||
end |
|||
end |
|||
@ -1,2 +1,22 @@ |
|||
module EntriesHelper |
|||
# Berechnet Kilometerpauschale für eine Entry anhand der gespeicherten MileageRate |
|||
# entry: muss attribute `distance_km` und `date` haben |
|||
def kilometrergeld_for(entry) |
|||
rate = MileageRate.for_year(entry.date.year) |
|||
if rate |
|||
(entry.distance_km.to_f * rate).round(2) |
|||
else |
|||
# Fallback: z. B. Standardrate 0.42 |
|||
(entry.distance_km.to_f * 0.05).round(2) |
|||
end |
|||
end |
|||
|
|||
# Für Summen: sum_meilen ist Summe der km oder Array von entries |
|||
def total_km_cost_for_year(user, year) |
|||
rate = MileageRate.for_year(year) || 0.42 |
|||
total_km = user.entries |
|||
.where("EXTRACT(YEAR FROM date) = ?", year) |
|||
.sum(:distance_km).to_f |
|||
(total_km * rate).round(2) |
|||
end |
|||
end |
|||
@ -0,0 +1,14 @@ |
|||
class MileageRate < ApplicationRecord |
|||
validates :year, presence: true, uniqueness: true |
|||
validates :rate_per_km, presence: true, numericality: { greater_than_or_equal_to: 0 } |
|||
|
|||
# Gibt die Rate für ein gegebenes Jahr — oder nil |
|||
def self.for_year(year) |
|||
find_by(year: year)&.rate_per_km |
|||
end |
|||
|
|||
# Gibt die Rate für heute bzw. aktuelles Jahr |
|||
def self.current |
|||
for_year(Date.today.year) |
|||
end |
|||
end |
|||
@ -0,0 +1,27 @@ |
|||
<%= form_with(model: mileage_rate, local: true) do |form| %> |
|||
<% if mileage_rate.errors.any? %> |
|||
<div class="alert alert-danger"> |
|||
<h4><%= pluralize(mileage_rate.errors.count, "Fehler") %> verhinderten das Speichern:</h4> |
|||
<ul> |
|||
<% mileage_rate.errors.full_messages.each do |msg| %> |
|||
<li><%= msg %></li> |
|||
<% end %> |
|||
</ul> |
|||
</div> |
|||
<% end %> |
|||
|
|||
<div class="mb-3"> |
|||
<%= form.label :year, "Jahr", class: "form-label" %> |
|||
<%= form.number_field :year, class: "form-control", min: 2000 %> |
|||
</div> |
|||
|
|||
<div class="mb-3"> |
|||
<%= form.label :rate_per_km, "Kilometersatz (€/km)", class: "form-label" %> |
|||
<%= form.number_field :rate_per_km, class: "form-control", step: 0.01, min: 0 %> |
|||
</div> |
|||
|
|||
<div class="mb-3"> |
|||
<%= form.submit class: "btn btn-success" %> |
|||
<%= link_to "Abbrechen", mileage_rates_path, class: "btn btn-secondary ms-2" %> |
|||
</div> |
|||
<% end %> |
|||
@ -0,0 +1,2 @@ |
|||
<h1>Satz für Jahr <%= @mileage_rate.year %> bearbeiten</h1> |
|||
<%= render "form", mileage_rate: @mileage_rate %> |
|||
@ -0,0 +1,24 @@ |
|||
<h1>Kilometersätze je Jahr</h1> |
|||
|
|||
<%= link_to "Neuen Satz anlegen", new_mileage_rate_path, class: "btn btn-primary mb-3" %> |
|||
|
|||
<table class="table table-striped table-bordered"> |
|||
<thead> |
|||
<tr> |
|||
<th>Jahr</th> |
|||
<th>Satz (€/km)</th> |
|||
<th>Aktionen</th> |
|||
</tr> |
|||
</thead> |
|||
<tbody> |
|||
<% @mileage_rates.each do |rate| %> |
|||
<tr> |
|||
<td><%= rate.year %></td> |
|||
<td><%= number_to_currency(rate.rate_per_km, unit: "€", separator: ",", delimiter: ".", precision: 2) %></td> |
|||
<td> |
|||
<%= link_to "Bearbeiten", edit_mileage_rate_path(rate), class: "btn btn-sm btn-outline-secondary" %> |
|||
</td> |
|||
</tr> |
|||
<% end %> |
|||
</tbody> |
|||
</table> |
|||
@ -0,0 +1,2 @@ |
|||
<h1>Neuen Kilometersatz anlegen</h1> |
|||
<%= render "form", mileage_rate: @mileage_rate %> |
|||
@ -0,0 +1,15 @@ |
|||
class CreateMileageRates < ActiveRecord::Migration[7.1] |
|||
def change |
|||
create_table :mileage_rates do |t| |
|||
t.integer :year, null: false, index: { unique: true } |
|||
t.decimal :rate_per_km, precision: 5, scale: 2, null: false |
|||
t.timestamps |
|||
end |
|||
[2024, 2025, 2026].each do |year| |
|||
MileageRate.create_or_find_by(year: year) do |rate| |
|||
rate.rate_per_km = 0.42 |
|||
end |
|||
end |
|||
|
|||
end |
|||
end |
|||
Loading…
Reference in new issue