AbstractRechnungDO.ktThe shared data model foundation for all invoice types in ProjectForge. It extends DefaultBaseDO (the framework base entity with auditing fields like created, lastUpdate, deleted) and implements IRechnung (shared invoice interface) and DisplayNameCapable (for UI rendering). It uses JPA @MappedSuperclass so its fields are inherited by concrete entity tables without creating a separate table. All properties are declared open to support Hibernate's lazy-loading proxy mechanism.
DefaultBaseDO <- AbstractRechnungDO <- RechnungDO
<- EingangsrechnungDO
IRechnung, DisplayNameCapable (interfaces implemented by AbstractRechnungDO)
| Property | DB Column | Type | Constraints / Annotations |
|---|---|---|---|
datum | — | LocalDate? | @Column(nullable = false), @GenericField (Hibernate Search) |
betreff | — | String? | @Column(length = 4000), @FullTextField |
bemerkung | — | String? | @Column(length = 4000), @FullTextField |
besonderheiten | — | String? | @Column(length = 4000), @FullTextField |
faelligkeit | — | LocalDate? | @GenericField |
bezahlDatum | bezahl_datum | LocalDate? | @GenericField |
zahlBetrag | zahl_betrag | BigDecimal? | scale = 2, precision = 12; actual gross amount paid |
currency | — | String? | @Column(length = 10), @FullTextField |
| Property | Type | Annotations |
|---|---|---|
konto | KontoDO? | @ManyToOne(fetch = LAZY), @JoinColumn(name = "konto_id"), @IndexedEmbedded(includeDepth = 1), @JsonSerialize(using = IdOnlySerializer::class), @IndexingDependency(reindexOnUpdate = SHALLOW) |
The DATEV account associated with this invoice. For outgoing invoices, this defaults to the account assigned to the customer or project. For incoming invoices, it represents the creditor's chart of accounts entry.
| Property | Type | Description |
|---|---|---|
discountPercent | BigDecimal? | Early payment discount percentage (e.g., 2 for 2%) |
discountMaturity | LocalDate? | Date until which the discount applies |
open val year: Int
get() = datum?.year ?: 0
Derived from datum. Indexed via @GenericField + @IndexingDependency(derivedFrom = ...) to enable Hibernate Search queries by year without storing it in the database.
| Property | Type | Description |
|---|---|---|
zahlungsZielInTagen | Int? | Days between invoice date and due date |
discountZahlungsZielInTagen | Int? | Days between invoice date and discount maturity |
Both are computed by recalculate() using PFDateTime.daysBetween(). They are @Transient (not persisted).
uiStatus / uiStatusAsXmlUser interface state is persisted as XML in uiStatusAsXml (@Column(length = 10000)) and lazily deserialized to a RechnungUIStatus instance via XmlObjectReader. The getter caches the deserialized object. Annotated with @NoHistory to exclude from history tracking.
info (RechnungInfo)@Transient @CandHIgnore lateinit var info: RechnungInfo
The calculated invoice summary (net/gross totals, VAT breakdown, cost assignments). Initialized by RechnungCalculator and cached by AbstractRechnungCache. Marked @CandHIgnore to prevent history/audit processing. The isInfoInitialized check prevents re-initialization.
ensuredInfoval ensuredInfo: RechnungInfo
get() {
if (!isInfoInitialized) { RechnungCalculator.calculate(this) }
return info
}
Guaranteed accessor — if info hasn't been calculated yet (e.g., after deserialization or when the cache didn't pre-populate it), it forces calculation on demand.
| Annotation | Usage |
|---|---|
@GenericField | Date and numeric fields — exact filtering, not analyzed full-text |
@FullTextField | Text fields (betreff, bemerkung, besonderheiten, currency) — analyzed full-text search |
@IndexedEmbedded(includeDepth = 1) | konto — nested entity indexing with limited depth |
@IndexingDependency(reindexOnUpdate = SHALLOW) | konto — only reindex when the konto itself changes (not its nested children) |
@IndexingDependency(derivedFrom = ...) | year — transparently derived from datum field |
| Method | Description |
|---|---|
abstractPositionen: List<AbstractRechnungsPositionDO>? | Returns the collection of position/line items for this invoice |
isValid: Boolean | True if the invoice is issued, not canceled, not deleted, and not only planned |
addPositionWithoutCheck(position) | Adds a position to the internal collection without validation |
setAbstractRechnung(position) | Sets the back-reference from position to this invoice |
ensureAndGetPositionen() | Lazily initializes and returns the positions collection |
recalculate()Computes zahlungsZielInTagen and discountZahlungsZielInTagen from datum, faelligkeit, and discountMaturity. If datum is null, sets both to null.
addPosition(position: AbstractRechnungsPositionDO)Adds a new line item to the invoice with automatic number assignment:
setAbstractRechnung()addPositionWithoutCheck()hasKostZuweisungen(): BooleanReturns true if any position on this invoice has cost assignments (kostZuweisungen).
getAbstractPosition(idx: Int): AbstractRechnungsPositionDO?Returns the position at the given 0-based index, or null if out of bounds.
open — this is critical for Hibernate's CGLIB proxy mechanism and Kotlin-JPA interop. Commit 8675a1dbe identified this as a major performance issue where non-open Kotlin properties prevented lazy loading, causing thousands of unnecessary SQL querieskonto relationship uses @JsonSerialize(using = IdOnlySerializer::class) to avoid deep serialization of nested entities in REST responsesuiStatusAsXml is annotated with @NoHistory because UI state changes shouldn't be tracked in the audit loginfo is @CandHIgnore because it's a computed cache value that may not be initialized yet@PropertyInfo annotations provide i18n keys and type metadata for auto-generated UI forms868d6abb7 2025 -> 2026
5d8d7db72 WIP: currency in incoming invoices.
8c2bf04f0 WIP: currency conversion with Claude Code.
16b87b4ce WIP: Import of creditor invoices
175d72c14 WIP: Import of creditor invoices
4d4c6b449 Currency field added to invoices (incoming and outgoing)
aa20b7708 NewGroupSelectPanel fixed (used in wizard). (Eingangs)RechnungsPositionDO: @get:JoinColumn -> mappedBy.
1e0e7225b KostzuweisungsExport of invoices ignores now invalid invoices and 0.00 positions.
63081666f Source file headers: 2024-> 2025.
b3782c8a8 Migration stuff in progress...
b810d1c78 Migration stuff in progress... (all tests of all packages: OK).
ba2479571 Migration stuff in progress...
b47c21af6 Refactored caching and calculations with invoices (not yet finished)
ccb7ca64d Migration stuff in progress...
67a66479e Migration stuff in progress...
4c04cfd65 MAJOR-CHANGE! Migration of integer id's to Long id's (including fk's etc.)
ad9ac6b63 Migration stuff in progress...
f5c09f87f Migration stuff in progress...
c04fb0d51 Migration stuff in progress...
06828f490 Migration stuff in progress...
b6092df09 Copyright 2023 -> 2024
ab45d51fa Copyright 2001-2022 -> 2001-2023.
6307a3dbe *RechnungPagesRest: show kost including tooltip.
2dc192b7a AG grid: auto-format of currency, Formatter works now also with AG grid (rating, dates, timestamps). Currency rendering by client (Formatter.jsx) added.
68402a26b Eingangsrechnungsliste: User discount date and due date.
a286007ee Mass update of creditor invoices: handling of discount implemented.
5f7ef41b8 Copyright 2021 -> 2022
a7fceb731 Some compiler warnings fixed.
cd27dd997 package xstream -> xmlstream. (should be replaced by xstream later).
ceb63e8a1 Source code header: (C) 2001-2021.
07cb34acb IRechnung, IRechnungsPosition introduced. DTOs fro Eingangsrechnung{sPosition}DO added.
552603f75 Rename setter methods in RechnungDO to avoid conflicts with Jackson
92112cdb1 Add InvoicePositionComponent
5064afcec LocalDate Bridge for hibernate search fixed.
b209e00ba PFDay.from -> from, fromOrNow, fromOrNull, PFDateTime.from -> from, fromOrNow, fromOrNull
7c1c48782 Replacing more instance of java.sql.Date with java.time.LocalDate Replacing instances of java sql.Timestamp with java.util.Date
78b436d9e Replace instances of java.util.date and java.sql.Date with java.time.LocalDate
7c79f1922 Copyright of source header -> 2020.
13cfb0330 WIP: Migration to PFDate and PFDateTime.
24019a0f5 Eliminate DateHolder occurences
1dc951c03 Remove instances of DayHolder
e4cdd8d4b Revert "More Holder replacements"
f604a1afe More Holder replacements
19b97fcc3 DateBridge: encoding = EncodingType.STRING for all DateBridges in all DO's.
fbe4381e7 DateBridge: string encoding removed again.
76c5c6955 WIP: MultiFieldQueryParser
8675a1dbe Found big performance issue: Declared all Kotlin JPA entities and their properties as open. Lazy loading wasn't supported by Hibernate and results in thousands of JPA queries...
d36fe6ad9 Heavy WIP: refactoring
a55ef0c14 Heavy WIP: refactoring of (Eingangs)Rechnung*. Found a big performance killer!!!!!
812b5b751 Heavy WIP: refactoring of (Eingangs)Rechnung*. Found a big performance killer!!!!!
39cdd224c Hibernate search: DateBridge...
339e017b4 ElementRegistry: @PropertyInfo is now also supported for getter methods of read-only vals.
f2b55f6aa RechnungsPositionDO.java - RechnungsPositionDO.kt EingangsrechnungsPositionDO.java -> EingangsrechnungsPositionDO.kt AbstractRechnungsPositionDO.java -> AbstractRechnungsPositionDO.kt
050a5ccd7 @NoHistory needed as field annotation: @field:NoHistory.
05244ff19 CopyRight of all Kotlin file-header updated or created.
f55615812 Kotlin migration of {Abstract|Eingangs}RechnungDO...
39a2bd24f AbstractRechnungDO: More open attributes
bd9803e81 AbstractRechnungDO: Add open to previously protected attributes.
103e4f592 AbstractRechnungDO.java -> AbstractRechnungDO.kt