Time Record Schema
Felder
Time Record Schema
Schema for validating time record entities
userId
User ID who recorded this time entry
date
Date of the time entry (ISO date format)
startTime
Start time for exact time tracking (ISO datetime)
endTime
End time for exact time tracking (ISO datetime)
durationMinutes
Total duration in minutes
breakMinutes
Break time in minutes
breakStartTime
When the break started (ISO datetime) — legacy, first break
breaks
Multiple break entries with start time and duration
startTime
Break start time (ISO datetime)
durationMinutes
Break duration in minutes
description
Work description
internalNote
Internal note not visible to customer
serviceTypeId
Service type UUID — primary classification for billing parameters
serviceNature
Snapshot von service_type.serviceNature zum Buchungszeitpunkt (SUPPORT = im Full-Service enthalten, ORDER_BASED = auftragsbezogen). Server-gesetzt (protected), als direktes Feld in der Analytics-Auswertung gruppierbar. Null = nicht klassifiziert.
SUPPORTORDER_BASEDnullentryType
Derived from serviceType.category (legacy compatibility)
WORKTRAVELREMOTEON_SITEPHONEADMINCONSULTINGMAINTENANCESUPPORTCUSTOMnulllocation
Location where work was performed (defaults from serviceType.defaultLocation)
OFFICECUSTOMER_SITEREMOTETRAVELnullpartyId
Company UUID — always required (own company or customer)
partyAddressId
Specific customer site (party.addresses[]._id) the work was performed at
isInternal
If true, not visible to customer and not billable
referenceType
Type of linked entity (Auftrag)
TICKETPROJECTTASKCONTRACTPHONE_CALLCONTACT_PERSONASSETnullreferenceId
UUID of linked entity (Auftrag)
linkedProjectId
Linked project UUID (max. one; primary reference when set)
linkedTicketId
Linked ticket UUID (max. one; must belong to linkedProjectId when both set)
linkedTaskId
Linked task UUID (max. one; must belong to linkedTicketId/linkedProjectId when set)
linkedContactPersonIds
Linked contact person (PERSON party) UUIDs — informational, never billing-relevant while a work-item link exists
linkedAssetIds
Linked asset UUIDs — informational, never billing-relevant while a work-item link exists
contractId
Associated contract UUID (can be set alongside referenceType/Id)
positionId
ADR 0310 / 0552: project_position this booking is billed against. Carries the position for referenceType=PROJECT (direct bookings, e.g. off a project appointment via calendar_event.positionId) AND referenceType=TICKET (bookings on a project sub-ticket, ticket.projectId). TASK bookings ignore this field — they inherit their position from task.positionId. Feeds the position Ist (consumedMinutes/consumedAmount). Mandatory (422) when the effective project bills standalone and has at least one non-CANCELLED position, unless the tenant opted out via settings.timeEntry.requireProjectPosition=false (ADR 0631).
bookedFromTicketId
ADR 0631: PURELY INFORMATIONAL origin marker — the ticket whose Zeiterfassung tab this booking was made from, when the booking targets a POSITION OF THE TICKET'S PROJECT (referenceType=TASK on a project task). Such a record has no other trace of the ticket: its anchor is the project task, so the ticket time list cannot find it and the user sees their own booking vanish. This field is what the ticket tab filters on to show it back, read-only. It deliberately does NOT participate in derivePrimaryReference, validateLinkHierarchy, billing, coverage or any rollup — linking the ticket via the typed linkedTicketId instead would flip the anchor from TASK to TICKET and move the booking out of the position rollup into the central Leistungserfassung. No backfill: records written before this field simply lack it and stay invisible in that block.
assetId
Asset the work was performed on (inherited from the linked ticket)
coverageMode
How this record is handled financially against a contract
INCLUDEDCONTINGENTCONTRACT_RATEBILLABLEnullcoverageContractId
Contract that covers this record (resolver result)
coverageAssignmentId
Contract assignment whose coverage rule applied
coverageContingentId
Contingent the work was deducted from (CONTINGENT mode)
coverageContractRatePerHour
Frozen contract special rate snapshot (CONTRACT_RATE mode)
coverageRateSource
Frozen rate-source snapshot for revision safety
CONTRACT_RATECONTINGENTINCLUDEDSTANDARDnullcoverageResolverVersion
Resolver logic version active when coverage was resolved
coverageResolvedAt
Timestamp the coverage was resolved
coverageAmbiguous
Manual clarification needed. Set when the asset could not be determined unambiguously, and (ADR 0948) when the technician forced a mixed booking onto the contract but the covered assets point at more than one contract/contingent — the resolver then picks one (INCLUDED before CONTINGENT before CONTRACT_RATE, then slot order) and flags the record.
coverageOverride
ADR 0948 — the technician's manual decision, overriding the automatic all-or-nothing rule in BOTH directions and surviving a re-resolution. `null` (default) = automatic: the booking goes onto the contract only when EVERY linked asset is covered. `CONTRACT` = put it on the contract anyway, `BILLABLE` = keep it off the contract. Protected on PATCH — the flip goes through POST /time-records/:id/coverage, which re-resolves the coverage and moves the contingent in the same step.
CONTRACTBILLABLEnullcoverageMixedAssets
ADR 0948 — at the time of resolution, at least one linked asset was covered by the contract and at least one was not. Explains why an otherwise contract-covered booking came out BILLABLE; read-only resolver output.
overageBlocked
Contingent overage was blocked by policy — overhang billed regularly
overageSurchargeAmount
Surcharge amount for the contingent overhang (ALLOW_WITH_SURCHARGE)
coveragePartial
This record mixes covered + billable shares. When true, billing invoices only `billableMinutes - coveredMinutes` (ADR 0932 — in every document path, not just the tiles). Two origins: (a) the contingent ran out mid-booking, so the covered part is the remaining balance rounded up to the booking grid and the overhang is billed (ADR 0358/0932) — still produced today; (b) historically, a reference linked several assets and coverage was split across them (ADR 0164) — NO LONGER produced since ADR 0948, which decides per booking all-or-nothing. Existing (b) records keep their value and keep billing net; nothing is backfilled.
coveredMinutes
Sum of per-asset share minutes resolving to INCLUDED/CONTINGENT coverage. Subtracted from billable minutes for coveragePartial records so covered work is not double-charged (`partialCoveredMinutes()`, ADR 0932).
coverageAllocations
Per-asset coverage split snapshot when a reference links multiple assets (ADR 0164). Display/audit only — the actual contingent deduction lives in contingent_ledger / contingent_usages.
assetId
contractId
contingentId
billingMode
INCLUDEDCONTINGENTCONTRACT_RATEBILLABLEconsumedAmount
unit
overageBlocked
sourceType
Origin entity type this time record was booked from. CALENDAR_EVENT marks records booked off a calendar appointment via "Zeit buchen" — the source event is then hidden in the calendar (the time record renders in its place). MESSAGE (ADR 0779) marks a record booked off a sent mail; the CRUD hook writes `messages.bookedAsTimeRecordId` back, like it does for PHONE_CALL/REMOTE_SESSION.
PHONE_CALLREMOTE_SESSIONCALENDAR_EVENTMESSAGEnullsourceId
Origin entity ID this time record was booked from
sourceOccurrenceDate
Local calendar day (YYYY-MM-DD) of the booked OCCURRENCE when `sourceId` points to a recurring calendar_event series (RFC 5545 RECURRENCE-ID, day granularity). A series is one document, so `sourceId` alone cannot tell which occurrence was booked. Readers fall back to `date` when absent (records booked before the field existed) — see ADR 0957.
billableType
Billability classification
FULLNONEPARTIALbillable
Derived: true when billableType !== NONE
billableAmount
Billable minutes when billableType is PARTIAL
billablePercent
Billable percentage when billableType is PARTIAL
billableMinutes
Computed billable minutes (after rounding and inclusive deduction)
nonBillableMinutes
Non-billable minutes
nonBillableReason
Reason why time is not billable
nonBillableReasonId
Id of the picked non_billable_reason — the display text itself stays in nonBillableReason
ratePerHour
Hourly rate in EUR as decimal string
appliedRatePerHour
Snapshot of the hourly rate that was effective when this time record was created. Read-only after insert (ADR 0139 — Snapshot-Pattern analog ADR 0128a). Sourced from serviceType.billing.pricing[] or articlePrices[] depending on priceSource.
appliedRateSource
Provenance of the frozen base rate (ADR 0415): contract = contract.conditions.serviceRates / CONTRACT_RATE, party_override / party_discount = customer conditions (party.serviceTypeOverrides), article = article.articlePrices, service_type = tenant-wide mask price. Server-set alongside appliedRatePerHour; billing uses it to decide whether a BILLING_DATE re-resolve may replace the frozen rate (a contract-driven rate never is). Null on legacy records (treated conservatively: frozen rate kept).
contractparty_overrideparty_discountarticleservice_typenullconsumedAmount
Snapshot of billable amount = durationMinutes/60 * appliedRatePerHour, computed once on insert. Drives task.consumedAmount aggregation for limitMode=amount tasks (ADR 0139).
weclappTimeRecordId
Linked weclapp time-record ID after successful push
weclappSyncState
Sync state of this time-record against weclapp. `deferred` (ADR 0634) = die Buchung gehört zu einem Projekt im Positionsersatz-Modus; sie wird bewusst NICHT einzeln nach weclapp geschrieben, sondern beim Abrechnungsabschluss gesammelt als Auftragsposition gerendert.
pendingsyncederrordeferrednullweclappLastSyncedAt
Last successful weclapp push timestamp
weclappLastSyncError
Last weclapp push error message
weclappBatchTransferredAt
ADR 0634 — wann diese Buchung als Teil der gesammelten Buchungspositionen in den weclapp-Auftrag übertragen wurde (Positionsersatz-Modus). Null = noch nicht übertragen (`deferred`). Adresse der Position ist NICHT hier, sondern projektweit in project.weclappBillingItemIds: bei Gruppierung pro Ticket / eine Position speisen mehrere Buchungen dieselbe Auftragsposition, ein Rückzug einzelner Buchungen ist deshalb gar nicht adressierbar.
weclappOrderItemId
weclapp salesOrderItem id of the TIME position appended to the sales order for this booking (non-service-position time, ADR 0412). Dedupe key for retries and the removal address when the approval is revoked.
weclappTravelOrderItemIds
weclapp salesOrderItem ids of the TRAVEL positions appended to the sales order for this booking (ADR 0412).
isFlatRate
Whether this is a flat-rate billing entry
flatRateAmount
Flat rate amount in EUR as decimal string
assignmentType
Assignment target type
CUSTOMEREMPLOYEEASSETORG_UNITnullassignmentId
Assignment target UUID
orgUnitId
Associated org unit (department/team) UUID
costCenterId
Associated cost center UUID
source
Herkunftsmarker (z. B. TANSS). Entspricht der Wert einem `externalMaster`-Provider, ist der Datensatz ein read-only Spiegel (ADR 0377) und nur vom Sync pflegbar; Bearbeitungsversuche scheitern mit 423. Der Guard wirkt pro Dokument — lokal erfasste Zeitbuchungen tragen kein `source` und bleiben normal bearbeitbar.
externalOwnership
Übernahme einer TANSS-Buchung in die Codemeta-Abrechnung (ADR 0432, für Zeitbuchungen ADR 0911). `"LOCAL"` hebt den Ausschluss über `NOT_EXTERNALLY_BILLED` auf und lässt den TANSS-Sync die Buchung überspringen; `source`/`sourceRef` bleiben als Herkunft stehen. Fehlt das Feld (Altbestand), gilt die Buchung als fremd abgerechnet — kein Backfill nötig. Nur über POST /time-records/:id/reopen-tanss setzbar (in `protectedFields`).
LOCALnullexternalOwnershipTakenAt
Zeitpunkt der Übernahme (ADR 0432 / ADR 0911).
externalOwnershipTakenBy
Interne User-Id, die die Übernahme ausgelöst hat (ADR 0432 / ADR 0911).
externalOwnershipTag
Tag, der bei der Übernahme im Fremdsystem gesetzt wurde. Beim Wiederöffnen einer TANSS-Buchung (ADR 0911) immer `null`: in TANSS ist die Leistung bereits verbucht, dort wird nichts markiert.
tanssReopenedAt
Zeitpunkt, zu dem die aus TANSS als „Fakturiert" importierte Buchung wieder geöffnet wurde (ADR 0911). Nur über POST /time-records/:id/reopen-tanss setzbar.
tanssReopenedBy
Interne User-Id, die die Buchung wieder geöffnet hat (ADR 0911).
tanssReopenReason
Pflicht-Begründung für das Wiederöffnen (ADR 0911). Wird im Bearbeiten-Dialog angezeigt und steht zusätzlich in den Metadaten des History-Eintrags.
status
Unified lifecycle status: DRAFT → SUBMITTED → APPROVED → RECORDED → INVOICED. Branches: REJECTED, CREDITED.
DRAFTSUBMITTEDAPPROVEDREJECTEDRECORDEDINVOICEDCREDITEDapprovedById
User who approved this entry
approvedAt
Timestamp when approved (ISO datetime)
rejectionReason
Reason for rejection if status is REJECTED
budgetHold
Parked over-limit draft (ADR 0223): this entry was recorded against a ticket whose budget cap (Obergrenze) is already reached. It stays a permanent DRAFT, is EXCLUDED from budget.consumedMinutes and from billing, and blocks the ticket from being closed until the cap is raised (which auto-converts it back to a normal draft) or it is deleted. Distinct from a regular status:'DRAFT'.
budgetHoldReason
Why more time than the ticket budget allows was needed (ADR 0886). Captured from the technician in the "Trotzdem speichern" dialog and carried into the budget-increase approval request, where it is the only context the approving lead gets. Set on create through the budget branch; never client-writable afterwards.
budgetHoldEstimatedRemainingMinutes
Technician estimate of how much MORE time the ticket still needs beyond this entry (ADR 0886, minutes). Informational only — the approval request asks for the held minutes, not this estimate — but it tells the lead whether a minimal increase will do. Set on create through the budget branch; never client-writable afterwards.
serviceBlockHold
Parked because of a customer service block (Leistungssperre, ADR 0815). Set server-side on create when the booked party is service-blocked and the referenced ticket has no exemption. The entry stays status DRAFT (never billed, still counts as booked for the daily closing) until it is released — by a user holding party_block who is not the booking user, or automatically when the ticket gets an exemption, moves to an unblocked customer, or the manual block is lifted. Never client-writable.
serviceBlockHoldReason
Block reason captured when the entry was parked (ADR 0815). Server-set.
serviceBlockReleasedAt
When the service-block hold was released (ISO datetime, ADR 0815). Server-set.
serviceBlockReleasedBy
Internal users._id of whoever released the hold — for an automatic release the user whose change triggered it (ticket exemption, ticket moved, block lifted); the trigger itself is in the history metadata (ADR 0815). Server-set.
isUnclear
Employee flagged this entry as unclear/needing clarification at recording time (e.g. unsure how to bill it, missing context). Surfaced to the team lead in the draft overview so it can be resolved before approval. Client-editable by the recorder.
unclearNote
Optional note describing what is unclear about this entry (set alongside isUnclear).
consultationTaskIds
Task UUIDs of Rücksprache (consultation) tasks raised for this entry. Owned by the request-consultation endpoint — never client-editable.
performanceRecordId
Leistungsnachweis this record was booked onto (set when status becomes RECORDED, cleared when the Leistungsnachweis is deleted)
invoiceId
Invoice this record was billed on (set when status becomes INVOICED, cleared when the invoice attempt is rolled back)
closedWithoutInvoice
ADR 0929 — `true` while the Leistungsnachweis this record is booked onto (status RECORDED, `performanceRecordId`) is closed without an invoice. Such a booking is settled, not "nicht fakturiert": the customer draft overview skips it, and its drilldown sends `closedWithoutInvoice__ne=true`. Set only by POST /performance-records/:id/close-without-invoice; removed by …/reopen and by the billing-link revert when the Leistungsnachweis is deleted. Absent otherwise (no backfill). Server-owned (`protectedFields`).
isOvertime
Whether this time entry is overtime (outside regular hours)
isWeekend
Whether this time entry was performed on a weekend or public holiday
isEmergencyService
Whether this time entry was emergency/on-call service
overtimeReason
Reason explaining why overtime/weekend/emergency service was necessary
overtimeScope
Which parts of a booking flagged as overtime/weekend/emergency are requested: `travelBefore` = outbound leg (placed directly before the start), `service` = the service itself, `travelAfter` = return leg (placed directly after the end). The leg minutes come from `travels` (`travelLegMinutes`). Missing/null = service only, the behaviour before travel became requestable — no backfill.
flatRateServiceId
Reference to a flat rate service (Pauschalleistung)
appliedSurcharges
Surcharges applied to this time record
surchargeId
Reference to the surcharge definition ID
name
Surcharge name at time of application
percentChange
Percentage change applied
autoApplied
Whether this surcharge was auto-applied based on time rules
appliedMinutes
Minutes of this booking the surcharge actually covers (ADR 0431). Absent on records booked before the minute-accurate split, and on records resolved without clock times (only all-day rules could apply, so there is no minute share to report).
surchargeSegmentation
Minute-accurate surcharge split of this booking (ADR 0431) — the breakdown behind the blended `ratePerHour`.
basis
MINUTE = split from real clock times. DAY = the booking had no start/end, so only all-day rules were evaluated and `segments` is empty.
MINUTEDAYstackingMode
Stacking mode in force at booking time, frozen — the contract/service-type setting can change later.
ADDITIVEMAXeffectiveFactor
Minute-weighted multiplier on the base rate. `ratePerHour` = base × this, rounded to 2 decimals.
segments
fromTime
Local wall-clock start. "24:00" marks end-of-day.
toTime
minutes
surchargeIds
Surcharges covering this segment. Empty = base rate.
percentTotal
Percent applied after stacking (not necessarily the sum of all hits).
ratePerHour
This segment's own hourly rate, frozen. Stored rather than recomputed from base × factor: the base rate may change, and an amount already shown to a customer must not move retroactively (same freeze rule as `ratePerHour`, ADR 0139/0163). NOTE: summing minutes × these rates can differ from the invoiced amount by cents — the invoice bills `billableMinutes × ratePerHour` (the blended rate), and these are the breakdown shown alongside it.
tripIds
References to trip entities linked to this time record
travels
All trips of this booking (supersedes the single `travel`)
mode
Mode of transportation
CARPUBLIC_TRANSPORTFLIGHTTAXIBIKEWALKdistanceKm
Distance in kilometers
durationMinutes
Travel duration in minutes
isRoundTrip
If true, distanceKm is one-way and will be doubled for billing
distanceBillable
Whether distance/mileage is billable
timeBillable
Whether travel time is billable
ratePerKm
Rate per kilometer in EUR as decimal string
timeBillingMode
How travel time is billed when timeBillable=true
HOURLYFLAT_RATEnulltimeRatePerHour
Hourly rate for travel time in EUR (when timeBillingMode=HOURLY)
timeFlatRateAmount
Flat-rate amount for travel time in EUR (when timeBillingMode=FLAT_RATE)
parkingCost
Parking costs in EUR as decimal string
tollCost
Toll costs in EUR as decimal string
startLocation
Start location (free text)
endLocation
End location (free text)
vehicleInfo
Vehicle information (license plate, description)
vehicleId
Selected fleet vehicle UUID (source of `vehicleInfo`)
billingMode
How the distance is billed: per kilometer or as a flat rate
DISTANCE_BASEDFLAT_RATEnulltravelAllowanceId
Selected travel-allowance band UUID (when billingMode=FLAT_RATE)
flatRateAmount
Flat-rate travel amount in EUR (when billingMode=FLAT_RATE)
travel
Legacy single trip — read-only fallback, superseded by `travels`
mode
Mode of transportation
CARPUBLIC_TRANSPORTFLIGHTTAXIBIKEWALKdistanceKm
Distance in kilometers
durationMinutes
Travel duration in minutes
isRoundTrip
If true, distanceKm is one-way and will be doubled for billing
distanceBillable
Whether distance/mileage is billable
timeBillable
Whether travel time is billable
ratePerKm
Rate per kilometer in EUR as decimal string
timeBillingMode
How travel time is billed when timeBillable=true
HOURLYFLAT_RATEnulltimeRatePerHour
Hourly rate for travel time in EUR (when timeBillingMode=HOURLY)
timeFlatRateAmount
Flat-rate amount for travel time in EUR (when timeBillingMode=FLAT_RATE)
parkingCost
Parking costs in EUR as decimal string
tollCost
Toll costs in EUR as decimal string
startLocation
Start location (free text)
endLocation
End location (free text)
vehicleInfo
Vehicle information (license plate, description)
vehicleId
Selected fleet vehicle UUID (source of `vehicleInfo`)
billingMode
How the distance is billed: per kilometer or as a flat rate
DISTANCE_BASEDFLAT_RATEnulltravelAllowanceId
Selected travel-allowance band UUID (when billingMode=FLAT_RATE)
flatRateAmount
Flat-rate travel amount in EUR (when billingMode=FLAT_RATE)
tags
Time entry tags
Keine Felder passen zum Filter.
Standard-Endpoints
Diese Resource folgt dem generischen CRUD-Vertrag der Plattform. Lesen Sie die Konventionen für Pagination, Idempotenz, Optimistic Locking und Audit. Die wichtigsten Endpoints:
GET /api/v1/time_records— Liste, paginiert + filterbarGET /api/v1/time_records/<id>— Einzelne EntityPOST /api/v1/time_records— AnlegenPATCH /api/v1/time_records/<id>— Teil-UpdateDELETE /api/v1/time_records/<id>— Soft-DeleteGET /api/v1/time_records/<id>/timeline— Audit + Aktivitäten