feat(saetze): Satz je Task plus Import-Endpunkt fuer Harvest
Satz je Task Ein Task kann einen eigenen Satz tragen; leer bedeutet weiter "Satz des Budgettopfs" bzw. bei Abruf "Satz des Projekts". Damit lassen sich "remote" und "vor Ort" im selben Projekt abrechnen, ohne dafuer getrennte Projekte anzulegen. Das Budget bleibt in Stunden - gemischte Saetze aendern nichts daran, wann ein Topf voll ist. Der Umsatz wird je Task-Satz aufsummiert: loadProjects gruppiert den Topfverbrauch nach Task-Satz und gibt das Ergebnis an potStats, Abruf rechnet je Task. Mitarbeiter sehen keine Saetze, auch nicht die am Task. Import POST /api/imports/entries (nur Administrator) schreibt Buchungen fuer beliebige Personen - POST /api/entries bucht bewusst nur auf den angemeldeten Benutzer. Der Import laeuft durch dieselben Regeln: Projekt aktiv, Person zugewiesen, Topf passend zum Datum, maximal 24 h am Tag. dry_run schreibt nichts. Jede uebernommene Buchung traegt ihre harvest_id (unique). Ein zweiter Lauf legt nichts doppelt an. DELETE /api/imports/entries entfernt genau die uebernommenen Zeiten; handgetippte bleiben unberuehrt. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
7606df9280
commit
7e7188d476
@@ -48,7 +48,7 @@ export async function loadEntries(settings, { from, to, userIds = null, customer
|
||||
p.name AS project_name, p.code AS project_code, p.type AS project_type, p.billing_unit,
|
||||
p.rate AS project_rate,
|
||||
c.id AS customer_id, c.name AS customer_name, c.short_name AS customer_short, c.color AS customer_color,
|
||||
t.name AS task_name,
|
||||
t.name AS task_name, t.rate AS task_rate,
|
||||
b.label AS pot_label, b.rate AS pot_rate, b.budget_amount AS pot_amount
|
||||
FROM pulse_time_entries e
|
||||
JOIN pulse_users u ON u.id = e.user_id
|
||||
@@ -78,8 +78,14 @@ export async function loadEntries(settings, { from, to, userIds = null, customer
|
||||
let amount = 0;
|
||||
let fixedAmount = 0;
|
||||
// Billable rechnet ueber den Satz des Topfs ab, Abruf ueber den am Projekt.
|
||||
// Traegt der Task einen eigenen Satz (z. B. "vor Ort"), gewinnt dieser.
|
||||
if (r.billable && (r.project_type === 'billable' || r.project_type === 'abruf')) {
|
||||
const rate = hourlyRate(r.project_type === 'abruf' ? null : { rate: r.pot_rate }, project, settings);
|
||||
const rate = hourlyRate(
|
||||
r.project_type === 'abruf' ? null : { rate: r.pot_rate },
|
||||
project,
|
||||
settings,
|
||||
r.task_rate == null ? null : { rate: r.task_rate }
|
||||
);
|
||||
amount = rate ? hours * rate : 0;
|
||||
}
|
||||
if (r.billable && r.project_type === 'fixed' && r.pot_amount && potMinutes.get(r.pot_id)) {
|
||||
|
||||
Reference in New Issue
Block a user