#885: TradingPartner.kt

projectforge-business/src/main/kotlin/org/projectforge/business/dvelop/TradingPartner.kt

Type: Kotlin class — D-velop domain model

Purpose: Represents a trading partner (vendor, customer, or both) in the D-velop platform. This is the primary entity transferred between ProjectForge and D-velop during synchronization.

Source path: projectforge-business/src/main/kotlin/org/projectforge/business/dvelop/TradingPartner.kt

201 lines · 103 code · 72 comments · 26 blank

The central domain model for D-velop integration. A trading partner represents a business entity (company or person) that ProjectForge exchanges invoices with — suppliers (vendors), customers, or entities that are both. This class maps directly to D-velop's REST API JSON structure for trading partners and includes all fields needed for address management (bill-to, ship-to, and physical addresses), contact info, organizational assignment, and a custom field mechanism that bridges ProjectForge's DATEV account number to D-velop's custom fields API.

Enum Value Wrappers

D-velop uses enum-like value objects rather than plain strings. ProjectForge wraps these as inner classes:

Inner ClassEnum ValuesJSON Structure
TypeVENDOR, CUSTOMER, PARTNER{ "value": "VENDOR" }
ContactTypeCOMPANY, PRIVATE{ "value": "COMPANY" }
ActiveTRUE, FALSE{ "value": "TRUE" }
Organizationid, number?, name?{ "id": "...", "number": null, "name": null }

Organization implements equals() and hashCode() based solely on id for set-based deduplication.

Companion Object

companion object {
    var datevKontoFieldId: String? = null
}

The datevKontoFieldId is a globally cached ID of the D-velop custom field definition for "datevKonto". It is set once during D-velop bootstrapping (by discovering the custom field definitions) and then used by the customFields property to serialize/deserialize the DATEV account number through D-velop's custom field mechanism.

Core Properties

PropertyTypeRequired?Description
idString?Unique identifier assigned by D-velop after creation
numberString?RequiredBusiness partner number (external identifier)
nameString?Read-onlyCalculated by D-velop from company or firstName+lastName
shortNameString?Short identifier for list displays
contactTypeContactType?RequiredCOMPANY or PRIVATE
firstNameString?First name (for PRIVATE contacts)
lastNameString?If PRIVATELast name (required if contactType=PRIVATE)
companyString?If COMPANYCompany name (required if contactType=COMPANY)
typeType?RequiredVENDOR, CUSTOMER, or PARTNER
activeActive?RequiredWhether the partner is active
organizationOrganization?RequiredThe D-velop organization this partner belongs to

Address Properties (Physical Address)

PropertyType
streetString?
zipString?
cityString?
regionString?
countryString?
postBoxZipString?
postBoxString?
addressAdditionalString?

Billing Address Properties (billTo*)

PropertyTypeDescription
billToStreetString?Billing street address
billToZipString?Billing postal code
billToCityString?Billing city
billToRegionString?Billing region/state
billToCountryString?Billing country
billToAddressAdditionalString?Additional billing address lines
isBillToAddressEmptyBoolean (computed)True if all billTo fields are blank

Shipping Address Properties (shipTo*)

PropertyType
shipToStreetString?
shipToZipString?
shipToCityString?
shipToRegionString?
shipToCountryString?
shipToAddressAdditionalString?

Contact & Metadata

PropertyTypeDescription
phoneNumberString?Phone number
faxNumberString?Fax number
eMailString?Email address
websiteString?Website URL
remarksString?Free-text notes
responsibleString?User ID of the responsible person
relationSinceString?Start date of business relation (format: "2022-05-11 00:00:00")
relationUntilString?End date of business relation
importCodeString?Leading ID in the external system (used during migration)

Custom Fields Mechanism

The customFields property with custom getter/setter is the bridge between ProjectForge's datevKonto field and D-velop's generic custom field API:

Getter

get() {
    if (datevKonto == null || datevKontoFieldId == null) return null
    return mapOf(datevKontoFieldId!! to CustomField(
        configID = datevKontoFieldId!!,
        name = "datevKonto",
        value = datevKonto
    ))
}

If a DATEV account number is set and the datevKontoFieldId is known, it creates a single-entry map with the custom field wrapper.

Setter

set(value) {
    value?.values?.find { it.name == "datevKonto" }?.value?.let {
        datevKonto = it as Int?
    }
    if (value == null) { datevKonto = null }
}

When deserializing D-velop JSON responses, finds the "datevKonto" custom field and extracts its integer value.

Architecture Notes

Git History

868d6abb7 2025 -> 2026
63081666f Source file headers: 2024-> 2025.
b6092df09 Copyright 2023 -> 2024
510974f0f D-velop: test fixed.
adc61b60b Nothing real
bf2e51373 WIP D.velop
be750e77e WIP D.velop
9ebb99c88 WIP D.velop
cb2915283 WIP D.velop
aec5b4be0 Nothing real.
cb6d633f9 WIP D-velop
6a4ee1932 WIP D-velop
50926baa8 WIP D-velop
912400e42 WIP D-velop
c2418da24 dvelop package moved from projectforge-business to projectforge-rest.
f993107db Initial revision. Spring boot 2.7.6->2.7.7