You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
48 lines
2.3 KiB
48 lines
2.3 KiB
require "date"
|
|
require "bigdecimal"
|
|
require "securerandom"
|
|
|
|
class PracticeTransferExport
|
|
class Invalid < StandardError; end
|
|
MAX_ENTRIES = 500
|
|
|
|
def initialize(user:, from:, to:, descriptions: false)
|
|
@user, @from, @to, @descriptions = user, Date.iso8601(from.to_s), Date.iso8601(to.to_s), descriptions
|
|
raise Invalid, "Der Zeitraum ist ungültig." if @from > @to || !(1900..2200).cover?(@from.year) || !(1900..2200).cover?(@to.year)
|
|
rescue Date::Error
|
|
raise Invalid, "Bitte Beginn und Ende des Zeitraums angeben."
|
|
end
|
|
|
|
def call
|
|
entries = @user.entries.where(date: @from..@to).order(:id).limit(MAX_ENTRIES + 1).to_a
|
|
raise Invalid, "Höchstens 500 Einträge je Export. Bitte den Zeitraum verkleinern." if entries.length > MAX_ENTRIES
|
|
namespace = @user.with_lock do
|
|
@user.update!(practice_transfer_id: SecureRandom.uuid) if @user.practice_transfer_id.blank?
|
|
@user.practice_transfer_id
|
|
end
|
|
rates = MileageRate.where(year: (@from.year..@to.year)).pluck(:year, :rate_per_km).to_h
|
|
skipped = 0
|
|
rows = entries.flat_map do |entry|
|
|
if entry.start_time.present? && entry.end_time.nil?
|
|
skipped += 1
|
|
next []
|
|
end
|
|
hours, minutes = entry.hours_and_minutes
|
|
total_minutes = hours * 60 + minutes
|
|
common = { source_id: entry.id.to_s, date: entry.date.iso8601, education: entry.praktikums_typ,
|
|
activity: entry.entry_art, duration_minutes: total_minutes,
|
|
description: @descriptions ? entry.beschreibung.to_s.first(4_000) : nil,
|
|
cost_cents: entry.kosten ? (entry.kosten.to_d * 100).round.to_i : nil }
|
|
result = []
|
|
if entry.distance_km.to_i.positive?
|
|
rate = rates[entry.date.year] || BigDecimal("0.42") # Existing source application fallback, not a tax determination.
|
|
result << common.merge(type: "mileage", kilometers: entry.distance_km, rate_cents: (rate.to_d * 100).round.to_i,
|
|
rate_origin: rates.key?(entry.date.year) ? "configured" : "source_default")
|
|
end
|
|
result << common.merge(type: "development") if total_minutes.positive?
|
|
result
|
|
end
|
|
{ format: "pcm-professional-transfer-v1", source_namespace: namespace, exported_at: Time.current.iso8601,
|
|
from: @from.iso8601, to: @to.iso8601, skipped_running: skipped, records: rows }
|
|
end
|
|
end
|