Data Model
Overview
The Membership data model is designed from first principles, informed by the Cash360 entity structure but not constrained by it. The model supports multi-tenancy, extensibility via JSONB custom attributes, full audit trails, and a clean separation between authentication, member profiles, financial operations, and operational resources.
All entities carry an idEntity foreign key for tenant isolation. All timestamps use TIMESTAMP WITH TIME ZONE. All monetary values use DECIMAL(19,4) to avoid floating-point precision errors. Every entity includes @Version for optimistic locking (a lesson from the Cash360 audit where this was missing on all generated entities).
Core Entities
Organization / Entity
The tenant entity representing a sports club, fitness studio, or franchise group. Supports parent-child hierarchies for umbrella organizations and franchise networks.
| Field | Type | Description |
|---|---|---|
id |
BIGINT (PK) |
Primary key |
idParentEntity |
BIGINT (FK, nullable) |
Parent organization for franchise/umbrella |
name |
VARCHAR(255) |
Organization name |
legalName |
VARCHAR(255) |
Legal entity name |
typeCd |
VARCHAR(50) |
CLUB, STUDIO, FRANCHISE, UMBRELLA, MUNICIPAL |
statusCd |
VARCHAR(50) |
ACTIVE, SUSPENDED, TERMINATED |
address |
VARCHAR(500) |
Street address |
zip |
VARCHAR(20) |
Postal code |
city |
VARCHAR(100) |
City |
countryCode |
VARCHAR(3) |
ISO 3166-1 alpha-2 |
phone |
VARCHAR(50) |
Contact phone |
email |
VARCHAR(255) |
Contact email |
website |
VARCHAR(500) |
Website URL |
taxId |
VARCHAR(50) |
Tax identification number |
timezone |
VARCHAR(50) |
IANA timezone (e.g., Europe/Berlin) |
locale |
VARCHAR(10) |
Default locale (e.g., de-DE) |
currencyCode |
VARCHAR(3) |
ISO 4217 (e.g., EUR) |
logoUrl |
VARCHAR(500) |
Branding logo |
primaryColor |
VARCHAR(7) |
Brand color (#hex) |
customAttributes |
JSONB |
Extensible custom fields |
settings |
JSONB |
Entity-level configuration (password policy, features, etc.) |
version |
INTEGER |
Optimistic locking |
createdAt |
TIMESTAMPTZ |
Audit |
updatedAt |
TIMESTAMPTZ |
Audit |
User (Authentication)
Separated from the member profile following the Cash360 MbLogin / MbConsumer pattern. A User represents an authentication identity. One user can be linked to a member profile (consumer app) or exist as a standalone admin user.
| Field | Type | Description |
|---|---|---|
id |
BIGINT (PK) |
Primary key |
idEntity |
BIGINT (FK) |
Tenant |
idMember |
BIGINT (FK, nullable) |
Linked member profile (null for admin-only users) |
email |
VARCHAR(255) |
Login email (unique per entity) |
passwordHash |
VARCHAR(255) |
bcrypt hash |
statusCd |
VARCHAR(50) |
ACTIVE, LOCKED, PENDING_VERIFICATION, DEACTIVATED |
roleCd |
VARCHAR(50) |
MEMBER, TRAINER, ADMIN, GROUP_ADMIN, SYSTEM_ADMIN |
emailVerified |
BOOLEAN |
Email verification status |
verificationToken |
VARCHAR(255) |
Email verification token |
verificationExpiry |
TIMESTAMPTZ |
Token expiry |
passwordResetToken |
VARCHAR(255) |
Password reset token |
passwordResetExpiry |
TIMESTAMPTZ |
Token expiry |
refreshTokenHash |
VARCHAR(255) |
Hashed refresh token |
failedLoginAttempts |
INTEGER |
Brute-force counter |
lockedUntil |
TIMESTAMPTZ |
Account lockout expiry |
lastLoginAt |
TIMESTAMPTZ |
Last successful login |
mfaEnabled |
BOOLEAN |
Multi-factor authentication |
mfaSecret |
VARCHAR(255) |
TOTP secret (encrypted) |
locale |
VARCHAR(10) |
User's preferred locale |
version |
INTEGER |
Optimistic locking |
createdAt |
TIMESTAMPTZ |
Audit |
updatedAt |
TIMESTAMPTZ |
Audit |
Member
The member profile containing personal data, address, and emergency contacts. Corresponds to MbConsumer / CsrConsumer in Cash360 but with a cleaner structure.
| Field | Type | Description |
|---|---|---|
id |
BIGINT (PK) |
Primary key |
idEntity |
BIGINT (FK) |
Tenant |
memberNumber |
VARCHAR(50) |
Human-readable member number |
typeCd |
VARCHAR(50) |
PERSON, COMPANY |
statusCd |
VARCHAR(50) |
ACTIVE, INACTIVE, SUSPENDED, LEFT |
salutation |
VARCHAR(20) |
Mr, Mrs, etc. |
firstName |
VARCHAR(100) |
First name |
lastName |
VARCHAR(100) |
Last name |
companyName |
VARCHAR(255) |
Company name (if typeCd=COMPANY) |
gender |
VARCHAR(20) |
MALE, FEMALE, DIVERSE, UNSPECIFIED |
dateOfBirth |
DATE |
Birthday |
email |
VARCHAR(255) |
Contact email |
phone |
VARCHAR(50) |
Phone number |
mobilePhone |
VARCHAR(50) |
Mobile phone |
street |
VARCHAR(255) |
Street address |
zip |
VARCHAR(20) |
Postal code |
city |
VARCHAR(100) |
City |
countryCode |
VARCHAR(3) |
ISO country code |
photoUrl |
VARCHAR(500) |
Profile photo |
notes |
TEXT |
Internal notes |
emergencyContactName |
VARCHAR(200) |
Emergency contact full name |
emergencyContactPhone |
VARCHAR(50) |
Emergency contact phone |
emergencyContactRelation |
VARCHAR(50) |
Relationship (parent, spouse, etc.) |
idResponsiblePerson |
BIGINT (FK, nullable) |
Parent/guardian for minors |
isMinor |
BOOLEAN |
Auto-calculated from dateOfBirth |
joinDate |
DATE |
Membership start date |
exitDate |
DATE (nullable) |
Membership end date |
exitReason |
VARCHAR(255) |
Reason for leaving |
tags |
JSONB |
Flexible tagging (e.g., ["VIP", "competitor"]) |
customAttributes |
JSONB |
Extensible custom fields |
version |
INTEGER |
Optimistic locking |
createdAt |
TIMESTAMPTZ |
Audit |
updatedAt |
TIMESTAMPTZ |
Audit |
MembershipTemplate
Defines available membership plans that members can purchase. Corresponds to CsrContractPreDefined in Cash360.
| Field | Type | Description |
|---|---|---|
id |
BIGINT (PK) |
Primary key |
idEntity |
BIGINT (FK) |
Tenant |
name |
VARCHAR(255) |
Template name (e.g., "Adult Annual") |
description |
TEXT |
Public description |
typeCd |
VARCHAR(50) |
SUBSCRIPTION, PREPAID, TRIAL, DAY_PASS |
statusCd |
VARCHAR(50) |
ACTIVE, ARCHIVED, DRAFT |
price |
DECIMAL(19,4) |
Recurring price |
currencyCode |
VARCHAR(3) |
EUR, CHF, etc. |
billingIntervalMonths |
INTEGER |
1=monthly, 3=quarterly, 12=annual |
setupFee |
DECIMAL(19,4) |
One-time registration fee |
minimumTermMonths |
INTEGER |
Minimum commitment period |
noticePeriodMonths |
INTEGER |
Cancellation notice period |
autoRenew |
BOOLEAN |
Auto-renewal after minimum term |
renewalTermMonths |
INTEGER |
Renewal period length |
vatRate |
DECIMAL(5,2) |
VAT percentage |
vatIncluded |
BOOLEAN |
Price includes VAT |
maxMembers |
INTEGER (nullable) |
Capacity limit |
ageMinimum |
INTEGER (nullable) |
Minimum age |
ageMaximum |
INTEGER (nullable) |
Maximum age |
accessRules |
JSONB |
Which resources/times are included |
sortOrder |
INTEGER |
Display order |
visibleOnHomepage |
BOOLEAN |
Show on public pages |
version |
INTEGER |
Optimistic locking |
createdAt |
TIMESTAMPTZ |
Audit |
updatedAt |
TIMESTAMPTZ |
Audit |
Contract
An active contract between a member and the organization, created from a MembershipTemplate. Corresponds to CsrContract2Product in Cash360.
| Field | Type | Description |
|---|---|---|
id |
BIGINT (PK) |
Primary key |
idEntity |
BIGINT (FK) |
Tenant |
idMember |
BIGINT (FK) |
Member |
idMembershipTemplate |
BIGINT (FK) |
Source template (snapshot at time of purchase) |
contractNumber |
VARCHAR(50) |
Human-readable contract number |
statusCd |
VARCHAR(50) |
ACTIVE, CANCELLED, EXPIRED, SUSPENDED, PENDING |
startDate |
DATE |
Contract start |
endDate |
DATE (nullable) |
Contract end (null = open-ended with auto-renew) |
cancellationDate |
DATE (nullable) |
When cancellation was requested |
cancellationEffectiveDate |
DATE (nullable) |
When cancellation takes effect |
cancellationReason |
VARCHAR(500) |
Reason for cancellation |
price |
DECIMAL(19,4) |
Agreed price (copied from template at purchase) |
currencyCode |
VARCHAR(3) |
Currency |
billingIntervalMonths |
INTEGER |
Billing cycle |
nextBillingDate |
DATE |
Next billing date |
signatureData |
TEXT |
eIDAS-compliant signature (base64) |
signedAt |
TIMESTAMPTZ |
Signature timestamp |
customAttributes |
JSONB |
Contract-specific overrides |
version |
INTEGER |
Optimistic locking |
createdAt |
TIMESTAMPTZ |
Audit |
updatedAt |
TIMESTAMPTZ |
Audit |
Product / Service
Offerings beyond memberships: courses, personal training sessions, merchandise, facility rentals. Corresponds to PpProduct in Cash360.
| Field | Type | Description |
|---|---|---|
id |
BIGINT (PK) |
Primary key |
idEntity |
BIGINT (FK) |
Tenant |
name |
VARCHAR(255) |
Product name |
description |
TEXT |
Description |
categoryCd |
VARCHAR(50) |
COURSE, PERSONAL_TRAINING, MERCHANDISE, RENTAL, ADD_ON |
typeCd |
VARCHAR(50) |
ONE_TIME, RECURRING, PER_SESSION |
statusCd |
VARCHAR(50) |
ACTIVE, ARCHIVED, DRAFT |
price |
DECIMAL(19,4) |
Price |
currencyCode |
VARCHAR(3) |
Currency |
vatRate |
DECIMAL(5,2) |
VAT percentage |
vatIncluded |
BOOLEAN |
Price includes VAT |
maxCapacity |
INTEGER (nullable) |
Capacity limit (for courses) |
durationMinutes |
INTEGER (nullable) |
Session duration |
sortOrder |
INTEGER |
Display order |
imageUrl |
VARCHAR(500) |
Product image |
customAttributes |
JSONB |
Extensible fields |
version |
INTEGER |
Optimistic locking |
createdAt |
TIMESTAMPTZ |
Audit |
updatedAt |
TIMESTAMPTZ |
Audit |
Transaction
Payment records for billing, adjustments, and refunds. Generated by the billing engine and handed off to Cash360 for processing.
| Field | Type | Description |
|---|---|---|
id |
BIGINT (PK) |
Primary key |
idEntity |
BIGINT (FK) |
Tenant |
idMember |
BIGINT (FK) |
Member |
idContract |
BIGINT (FK, nullable) |
Related contract |
idProduct |
BIGINT (FK, nullable) |
Related product |
externalTransactionId |
VARCHAR(255) |
Cash360 transaction ID |
typeCd |
VARCHAR(50) |
MEMBERSHIP_FEE, SETUP_FEE, PRODUCT_PURCHASE, ADJUSTMENT, REFUND |
statusCd |
VARCHAR(50) |
PENDING, SUBMITTED, PROCESSED, FAILED, CANCELLED |
amount |
DECIMAL(19,4) |
Amount (positive=charge, negative=refund) |
currencyCode |
VARCHAR(3) |
Currency |
vatAmount |
DECIMAL(19,4) |
VAT portion |
description |
VARCHAR(500) |
Human-readable description |
billingPeriodStart |
DATE |
Billing period start |
billingPeriodEnd |
DATE |
Billing period end |
dueDate |
DATE |
Payment due date |
paidDate |
DATE (nullable) |
When payment was received |
paymentMethod |
VARCHAR(50) |
SEPA_DIRECT_DEBIT, BANK_TRANSFER, CASH, CARD |
failureReason |
VARCHAR(500) |
Reason for failure (from Cash360 feedback) |
version |
INTEGER |
Optimistic locking |
createdAt |
TIMESTAMPTZ |
Audit |
updatedAt |
TIMESTAMPTZ |
Audit |
BankAccount
Member bank accounts for SEPA direct debit. Corresponds to CsrBankAccount in Cash360.
| Field | Type | Description |
|---|---|---|
id |
BIGINT (PK) |
Primary key |
idEntity |
BIGINT (FK) |
Tenant |
idMember |
BIGINT (FK) |
Member |
accountHolder |
VARCHAR(255) |
Account holder name |
iban |
VARCHAR(34) |
IBAN (validated) |
bic |
VARCHAR(11) |
BIC/SWIFT code |
bankName |
VARCHAR(255) |
Bank name (auto-resolved from IBAN) |
mandateReference |
VARCHAR(35) |
SEPA mandate reference |
mandateSignDate |
DATE |
Mandate signature date |
mandateTypeCd |
VARCHAR(20) |
CORE, B2B |
isDefault |
BOOLEAN |
Default payment account |
statusCd |
VARCHAR(50) |
ACTIVE, REVOKED, EXPIRED |
version |
INTEGER |
Optimistic locking |
createdAt |
TIMESTAMPTZ |
Audit |
updatedAt |
TIMESTAMPTZ |
Audit |
Resource
Bookable resources: rooms, areas, equipment, and personnel. Derived from the Resource Management specification.
| Field | Type | Description |
|---|---|---|
id |
BIGINT (PK) |
Primary key |
idEntity |
BIGINT (FK) |
Tenant |
name |
VARCHAR(255) |
Resource name |
typeCd |
VARCHAR(50) |
ROOM, AREA, EQUIPMENT, PERSONNEL |
statusCd |
VARCHAR(50) |
AVAILABLE, MAINTENANCE, OUT_OF_SERVICE |
description |
TEXT |
Description |
capacity |
INTEGER (nullable) |
Max capacity (for rooms/areas) |
location |
VARCHAR(255) |
Physical location within facility |
imageUrl |
VARCHAR(500) |
Photo |
hourlyRate |
DECIMAL(19,4) (nullable) |
Rental cost per hour |
equipmentDetails |
JSONB |
Equipment-specific: quantity, condition, serial numbers |
personnelDetails |
JSONB |
Personnel-specific: qualifications, certifications, availability |
hardwareIntegration |
JSONB |
IoT config: KNX addresses, MQTT topics, sensor IDs |
maintenanceSchedule |
JSONB |
Recurring maintenance windows |
customAttributes |
JSONB |
Extensible fields |
version |
INTEGER |
Optimistic locking |
createdAt |
TIMESTAMPTZ |
Audit |
updatedAt |
TIMESTAMPTZ |
Audit |
ResourceBooking
Calendar-based booking of resources with conflict detection.
| Field | Type | Description |
|---|---|---|
id |
BIGINT (PK) |
Primary key |
idEntity |
BIGINT (FK) |
Tenant |
idResource |
BIGINT (FK) |
Booked resource |
idBookedBy |
BIGINT (FK) |
User who created the booking |
idEvent |
BIGINT (FK, nullable) |
Linked event |
idCourse |
BIGINT (FK, nullable) |
Linked course |
title |
VARCHAR(255) |
Booking title |
startTime |
TIMESTAMPTZ |
Start time |
endTime |
TIMESTAMPTZ |
End time |
recurrenceRule |
VARCHAR(255) |
iCalendar RRULE (for recurring bookings) |
statusCd |
VARCHAR(50) |
CONFIRMED, TENTATIVE, CANCELLED |
notes |
TEXT |
Notes |
version |
INTEGER |
Optimistic locking |
createdAt |
TIMESTAMPTZ |
Audit |
updatedAt |
TIMESTAMPTZ |
Audit |
Event
Tournaments, competitions, workshops, and social gatherings.
| Field | Type | Description |
|---|---|---|
id |
BIGINT (PK) |
Primary key |
idEntity |
BIGINT (FK) |
Tenant |
name |
VARCHAR(255) |
Event name |
typeCd |
VARCHAR(50) |
TOURNAMENT, WORKSHOP, SOCIAL, COMPETITION, CAMP |
statusCd |
VARCHAR(50) |
DRAFT, PUBLISHED, ONGOING, COMPLETED, CANCELLED |
description |
TEXT |
Event description |
startDate |
TIMESTAMPTZ |
Start date/time |
endDate |
TIMESTAMPTZ |
End date/time |
location |
VARCHAR(500) |
Event location |
maxParticipants |
INTEGER (nullable) |
Capacity |
registrationDeadline |
TIMESTAMPTZ (nullable) |
Registration cutoff |
entryFee |
DECIMAL(19,4) (nullable) |
Entry fee |
currencyCode |
VARCHAR(3) |
Currency |
imageUrl |
VARCHAR(500) |
Event image |
details |
JSONB |
Event-type-specific data (brackets, schedules, sponsors) |
version |
INTEGER |
Optimistic locking |
createdAt |
TIMESTAMPTZ |
Audit |
updatedAt |
TIMESTAMPTZ |
Audit |
Course
Recurring classes and training sessions with instructor assignment.
| Field | Type | Description |
|---|---|---|
id |
BIGINT (PK) |
Primary key |
idEntity |
BIGINT (FK) |
Tenant |
idInstructor |
BIGINT (FK) |
Trainer/instructor (references Member or User) |
idResource |
BIGINT (FK, nullable) |
Assigned room/area |
name |
VARCHAR(255) |
Course name |
description |
TEXT |
Description |
categoryCd |
VARCHAR(50) |
FITNESS, YOGA, MARTIAL_ARTS, DANCE, TEAM_SPORT, OTHER |
statusCd |
VARCHAR(50) |
ACTIVE, PAUSED, ARCHIVED |
maxParticipants |
INTEGER |
Capacity |
durationMinutes |
INTEGER |
Session duration |
recurrenceRule |
VARCHAR(255) |
iCalendar RRULE |
startDate |
DATE |
Course start date |
endDate |
DATE (nullable) |
Course end date |
level |
VARCHAR(50) |
BEGINNER, INTERMEDIATE, ADVANCED, ALL_LEVELS |
version |
INTEGER |
Optimistic locking |
createdAt |
TIMESTAMPTZ |
Audit |
updatedAt |
TIMESTAMPTZ |
Audit |
Communication
Messages, notifications, and template-based communication.
| Field | Type | Description |
|---|---|---|
id |
BIGINT (PK) |
Primary key |
idEntity |
BIGINT (FK) |
Tenant |
idSender |
BIGINT (FK) |
Sending user |
channelCd |
VARCHAR(50) |
EMAIL, PUSH, SMS, IN_APP |
typeCd |
VARCHAR(50) |
NOTIFICATION, REMINDER, MARKETING, SYSTEM |
statusCd |
VARCHAR(50) |
DRAFT, QUEUED, SENT, FAILED, BOUNCED |
subject |
VARCHAR(500) |
Message subject |
body |
TEXT |
Message body (HTML for email, plain for push/SMS) |
idTemplate |
BIGINT (FK, nullable) |
Source template |
recipientFilter |
JSONB |
Audience filter criteria |
scheduledAt |
TIMESTAMPTZ (nullable) |
Scheduled delivery time |
sentAt |
TIMESTAMPTZ (nullable) |
Actual delivery time |
metadata |
JSONB |
Delivery metadata (bounce info, open tracking) |
version |
INTEGER |
Optimistic locking |
createdAt |
TIMESTAMPTZ |
Audit |
updatedAt |
TIMESTAMPTZ |
Audit |
Device
Check-in terminals, door controllers, and IoT devices.
| Field | Type | Description |
|---|---|---|
id |
BIGINT (PK) |
Primary key |
idEntity |
BIGINT (FK) |
Tenant |
name |
VARCHAR(255) |
Device name |
typeCd |
VARCHAR(50) |
CHECK_IN_TERMINAL, DOOR_LOCK, SENSOR, SHELLY, DISPLAY |
statusCd |
VARCHAR(50) |
ONLINE, OFFLINE, MAINTENANCE |
serialNumber |
VARCHAR(100) |
Hardware serial |
location |
VARCHAR(255) |
Physical location |
ipAddress |
VARCHAR(45) |
Network address |
protocol |
VARCHAR(50) |
MQTT, KNX, BACNET, HTTP, NFC |
configuration |
JSONB |
Device-specific config |
lastHeartbeat |
TIMESTAMPTZ |
Last seen online |
firmwareVersion |
VARCHAR(50) |
Firmware version |
version |
INTEGER |
Optimistic locking |
createdAt |
TIMESTAMPTZ |
Audit |
updatedAt |
TIMESTAMPTZ |
Audit |
Document
Files attached to members, entities, or contracts (contracts, medical certificates, photos).
| Field | Type | Description |
|---|---|---|
id |
BIGINT (PK) |
Primary key |
idEntity |
BIGINT (FK) |
Tenant |
idMember |
BIGINT (FK, nullable) |
Associated member |
idContract |
BIGINT (FK, nullable) |
Associated contract |
name |
VARCHAR(255) |
Document name |
typeCd |
VARCHAR(50) |
CONTRACT, MEDICAL, PHOTO, INVOICE, RECEIPT, OTHER |
mimeType |
VARCHAR(100) |
MIME type |
fileSize |
BIGINT |
Size in bytes |
storagePath |
VARCHAR(500) |
Object storage path |
checksumSha256 |
VARCHAR(64) |
Integrity hash |
version |
INTEGER |
Optimistic locking |
createdAt |
TIMESTAMPTZ |
Audit |
updatedAt |
TIMESTAMPTZ |
Audit |
DataImport
CSV import jobs with column mapping templates.
| Field | Type | Description |
|---|---|---|
id |
BIGINT (PK) |
Primary key |
idEntity |
BIGINT (FK) |
Tenant |
idUser |
BIGINT (FK) |
User who initiated import |
name |
VARCHAR(255) |
Import job name |
targetTypeCd |
VARCHAR(50) |
MEMBERS, SERVICES, TRANSACTIONS |
modeCd |
VARCHAR(50) |
NEW_ONLY, NEW_AND_UPDATE, UPDATE_ONLY |
statusCd |
VARCHAR(50) |
UPLOADED, MAPPING, VALIDATING, EXECUTING, COMPLETED, FAILED |
fileName |
VARCHAR(255) |
Original file name |
storagePath |
VARCHAR(500) |
Storage path of uploaded CSV |
columnMapping |
JSONB |
Column mapping (CSV header -> entity field) |
mappingTemplateId |
BIGINT (FK, nullable) |
Saved mapping template |
totalRows |
INTEGER |
Total rows in file |
processedRows |
INTEGER |
Rows processed so far |
successRows |
INTEGER |
Successfully imported |
errorRows |
INTEGER |
Rows with errors |
errorLog |
JSONB |
Per-row error details |
version |
INTEGER |
Optimistic locking |
createdAt |
TIMESTAMPTZ |
Audit |
updatedAt |
TIMESTAMPTZ |
Audit |
CheckIn
Access log recording member check-ins via QR code, NFC, or manual entry.
| Field | Type | Description |
|---|---|---|
id |
BIGINT (PK) |
Primary key |
idEntity |
BIGINT (FK) |
Tenant |
idMember |
BIGINT (FK) |
Member |
idDevice |
BIGINT (FK, nullable) |
Device used for check-in |
idResource |
BIGINT (FK, nullable) |
Resource accessed |
methodCd |
VARCHAR(50) |
QR_CODE, NFC, MANUAL, BIOMETRIC |
statusCd |
VARCHAR(50) |
GRANTED, DENIED, OVERRIDE |
denialReason |
VARCHAR(255) |
Reason if denied (expired contract, suspended, etc.) |
checkinTime |
TIMESTAMPTZ |
Check-in timestamp |
checkoutTime |
TIMESTAMPTZ (nullable) |
Check-out timestamp |
createdAt |
TIMESTAMPTZ |
Audit |
Lead
Prospective member or B2B customer tracked through the sales pipeline. Used by both club-level sales (trial-to-member conversion) and vendor-level sales (customer acquisition).
| Field | Type | Description |
|---|---|---|
id |
BIGINT (PK) |
Primary key |
idEntity |
BIGINT (FK) |
Tenant |
source |
VARCHAR(50) |
WEBSITE, REFERRAL, WALK_IN, CAMPAIGN, IMPORT, PARTNER, EVENT |
statusCd |
VARCHAR(50) |
NEW, CONTACTED, QUALIFIED, PROPOSAL, WON, LOST |
firstName |
VARCHAR(100) |
First name |
lastName |
VARCHAR(100) |
Last name |
email |
VARCHAR(255) |
Contact email |
phone |
VARCHAR(50) |
Contact phone |
companyName |
VARCHAR(255) |
Company name (for B2B leads) |
idAssignedTo |
BIGINT (FK, nullable) |
Assigned sales user |
expectedValue |
DECIMAL(19,4) (nullable) |
Expected deal value |
expectedCloseDate |
DATE (nullable) |
Expected conversion date |
lostReason |
VARCHAR(500) (nullable) |
Reason for loss (if statusCd=LOST) |
notes |
TEXT |
Internal notes |
tags |
JSONB |
Flexible tagging |
customAttributes |
JSONB |
Extensible custom fields |
version |
INTEGER |
Optimistic locking |
createdAt |
TIMESTAMPTZ |
Audit |
updatedAt |
TIMESTAMPTZ |
Audit |
Deal
A sales opportunity linked to a lead, tracking expected value, probability, and pipeline stage through to closure.
| Field | Type | Description |
|---|---|---|
id |
BIGINT (PK) |
Primary key |
idEntity |
BIGINT (FK) |
Tenant |
idLead |
BIGINT (FK) |
Source lead |
idMember |
BIGINT (FK, nullable) |
Linked member (after conversion) |
name |
VARCHAR(255) |
Deal name |
pipelineStageCd |
VARCHAR(50) |
Configurable per entity (e.g., INITIAL, DEMO, TRIAL, NEGOTIATION, CLOSED) |
value |
DECIMAL(19,4) |
Deal value |
currencyCode |
VARCHAR(3) |
ISO 4217 |
probabilityPercent |
INTEGER |
Win probability (0-100) |
expectedCloseDate |
DATE |
Expected close date |
statusCd |
VARCHAR(50) |
OPEN, WON, LOST |
lostReason |
VARCHAR(500) (nullable) |
Reason for loss |
notes |
TEXT |
Internal notes |
version |
INTEGER |
Optimistic locking |
createdAt |
TIMESTAMPTZ |
Audit |
updatedAt |
TIMESTAMPTZ |
Audit |
Activity
Sales and support activity log entry tracking interactions with leads, deals, or members.
| Field | Type | Description |
|---|---|---|
id |
BIGINT (PK) |
Primary key |
idEntity |
BIGINT (FK) |
Tenant |
idLead |
BIGINT (FK, nullable) |
Related lead |
idDeal |
BIGINT (FK, nullable) |
Related deal |
idMember |
BIGINT (FK, nullable) |
Related member |
idUser |
BIGINT (FK) |
User who performed the activity |
typeCd |
VARCHAR(50) |
CALL, EMAIL, MEETING, NOTE, TASK, DEMO, FOLLOW_UP |
subject |
VARCHAR(500) |
Activity subject/title |
description |
TEXT |
Detailed description |
activityDate |
TIMESTAMPTZ |
When the activity occurred |
durationMinutes |
INTEGER (nullable) |
Duration (for calls, meetings) |
completed |
BOOLEAN |
Whether the activity is completed |
nextFollowUpDate |
DATE (nullable) |
Scheduled follow-up date |
version |
INTEGER |
Optimistic locking |
createdAt |
TIMESTAMPTZ |
Audit |
updatedAt |
TIMESTAMPTZ |
Audit |
Ticket
Support ticket for tracking member inquiries, issues, and operational requests through resolution.
| Field | Type | Description |
|---|---|---|
id |
BIGINT (PK) |
Primary key |
idEntity |
BIGINT (FK) |
Tenant |
ticketNumber |
VARCHAR(50) |
Human-readable ticket number (auto-generated) |
idMember |
BIGINT (FK, nullable) |
Reporting member (null for internal tickets) |
idCreatedBy |
BIGINT (FK) |
User who created the ticket |
idAssignedTo |
BIGINT (FK, nullable) |
Assigned support agent |
categoryCd |
VARCHAR(50) |
BILLING, ACCESS, TECHNICAL, FEEDBACK, COMPLAINT, CONTRACT, GENERAL |
priorityCd |
VARCHAR(50) |
LOW, NORMAL, HIGH, URGENT |
statusCd |
VARCHAR(50) |
OPEN, IN_PROGRESS, WAITING, RESOLVED, CLOSED |
subject |
VARCHAR(500) |
Ticket subject |
description |
TEXT |
Ticket body/description |
channelCd |
VARCHAR(50) |
EMAIL, IN_APP, WEB_FORM, PHONE, WHATSAPP |
slaResponseDeadline |
TIMESTAMPTZ (nullable) |
SLA: first response due by |
slaResolutionDeadline |
TIMESTAMPTZ (nullable) |
SLA: resolution due by |
firstResponseAt |
TIMESTAMPTZ (nullable) |
Actual first response time |
resolvedAt |
TIMESTAMPTZ (nullable) |
When ticket was resolved |
satisfactionScore |
INTEGER (nullable) |
CSAT rating (1-5) |
tags |
JSONB |
Flexible tagging (e.g., ["recurring", "escalated"]) |
version |
INTEGER |
Optimistic locking |
createdAt |
TIMESTAMPTZ |
Audit |
updatedAt |
TIMESTAMPTZ |
Audit |
TicketComment
A comment on a support ticket, either public (visible to member) or internal (staff-only).
| Field | Type | Description |
|---|---|---|
id |
BIGINT (PK) |
Primary key |
idTicket |
BIGINT (FK) |
Parent ticket |
idUser |
BIGINT (FK) |
Comment author |
body |
TEXT |
Comment text (HTML supported) |
isInternal |
BOOLEAN |
True = staff-only note, false = visible to member |
attachments |
JSONB |
Array of attachment references [{name, storagePath, mimeType, size}] |
version |
INTEGER |
Optimistic locking |
createdAt |
TIMESTAMPTZ |
Audit |
updatedAt |
TIMESTAMPTZ |
Audit |
KnowledgeBaseArticle
Help center article for member self-service and agent reference.
| Field | Type | Description |
|---|---|---|
id |
BIGINT (PK) |
Primary key |
idEntity |
BIGINT (FK) |
Tenant (null for platform-wide articles) |
title |
VARCHAR(500) |
Article title |
slug |
VARCHAR(255) |
URL-friendly slug |
body |
TEXT |
Article body (HTML) |
categoryCd |
VARCHAR(50) |
BILLING, ACCESS, ACCOUNT, TECHNICAL, GETTING_STARTED, FAQ |
statusCd |
VARCHAR(50) |
DRAFT, PUBLISHED, ARCHIVED |
viewCount |
INTEGER |
Number of views |
helpfulCount |
INTEGER |
"Was this helpful?" positive votes |
notHelpfulCount |
INTEGER |
"Was this helpful?" negative votes |
tags |
JSONB |
Flexible tagging |
version |
INTEGER |
Optimistic locking |
createdAt |
TIMESTAMPTZ |
Audit |
updatedAt |
TIMESTAMPTZ |
Audit |
AccountingEntry
General ledger journal entry for bookkeeping and DATEV export.
| Field | Type | Description |
|---|---|---|
id |
BIGINT (PK) |
Primary key |
idEntity |
BIGINT (FK) |
Tenant |
entryNumber |
VARCHAR(50) |
Sequential journal entry number |
entryDate |
DATE |
Posting date |
accountCode |
VARCHAR(20) |
Chart of accounts code (e.g., "8400" for revenue) |
contraAccountCode |
VARCHAR(20) |
Contra account code (double-entry) |
debitAmount |
DECIMAL(19,4) |
Debit amount |
creditAmount |
DECIMAL(19,4) |
Credit amount |
currencyCode |
VARCHAR(3) |
ISO 4217 |
description |
VARCHAR(500) |
Posting description |
idCostCenter |
BIGINT (FK, nullable) |
Cost center allocation |
idTransaction |
BIGINT (FK, nullable) |
Link to billing transaction (for auto-postings) |
postingTypeCd |
VARCHAR(50) |
AUTOMATIC, MANUAL, ADJUSTMENT |
statusCd |
VARCHAR(50) |
DRAFT, POSTED, VOID |
periodYear |
INTEGER |
Fiscal year |
periodMonth |
INTEGER |
Fiscal month (1-12) |
datevExported |
BOOLEAN |
Whether this entry has been included in a DATEV export |
datevExportDate |
TIMESTAMPTZ (nullable) |
When exported to DATEV |
version |
INTEGER |
Optimistic locking |
createdAt |
TIMESTAMPTZ |
Audit |
updatedAt |
TIMESTAMPTZ |
Audit |
CostCenter
Organizational cost center for financial reporting and budget management.
| Field | Type | Description |
|---|---|---|
id |
BIGINT (PK) |
Primary key |
idEntity |
BIGINT (FK) |
Tenant |
code |
VARCHAR(20) |
Cost center code |
name |
VARCHAR(255) |
Cost center name |
description |
TEXT |
Description |
budget |
DECIMAL(19,4) (nullable) |
Annual budget |
currencyCode |
VARCHAR(3) |
ISO 4217 |
statusCd |
VARCHAR(50) |
ACTIVE, ARCHIVED |
version |
INTEGER |
Optimistic locking |
createdAt |
TIMESTAMPTZ |
Audit |
updatedAt |
TIMESTAMPTZ |
Audit |
ChartOfAccount
Account definition in the general ledger chart of accounts (SKR03/SKR04 compatible).
| Field | Type | Description |
|---|---|---|
id |
BIGINT (PK) |
Primary key |
idEntity |
BIGINT (FK) |
Tenant |
accountCode |
VARCHAR(20) |
Account number (e.g., "1200", "8400") |
accountName |
VARCHAR(255) |
Account name |
accountTypeCd |
VARCHAR(50) |
ASSET, LIABILITY, EQUITY, REVENUE, EXPENSE |
parentAccountCode |
VARCHAR(20) (nullable) |
Parent account for hierarchy |
datevAccountCode |
VARCHAR(20) (nullable) |
DATEV-mapped account code (if different) |
statusCd |
VARCHAR(50) |
ACTIVE, ARCHIVED |
version |
INTEGER |
Optimistic locking |
createdAt |
TIMESTAMPTZ |
Audit |
updatedAt |
TIMESTAMPTZ |
Audit |
AccessZone
Hierarchical physical zone for access control management (building > floor > room > locker).
| Field | Type | Description |
|---|---|---|
id |
BIGINT (PK) |
Primary key |
idEntity |
BIGINT (FK) |
Tenant |
name |
VARCHAR(255) |
Zone name |
typeCd |
VARCHAR(50) |
BUILDING, FLOOR, ROOM, LOCKER_AREA, OUTDOOR, PARKING |
idParentZone |
BIGINT (FK, nullable) |
Parent zone (self-referencing hierarchy) |
capacity |
INTEGER (nullable) |
Maximum occupancy |
currentOccupancy |
INTEGER |
Real-time occupancy count |
description |
TEXT |
Description |
statusCd |
VARCHAR(50) |
ACTIVE, MAINTENANCE, CLOSED |
version |
INTEGER |
Optimistic locking |
createdAt |
TIMESTAMPTZ |
Audit |
updatedAt |
TIMESTAMPTZ |
Audit |
AccessRule
Defines which membership templates grant access to which zones, with time and day restrictions.
| Field | Type | Description |
|---|---|---|
id |
BIGINT (PK) |
Primary key |
idEntity |
BIGINT (FK) |
Tenant |
idAccessZone |
BIGINT (FK) |
Target zone |
idMembershipTemplate |
BIGINT (FK, nullable) |
Applicable membership template (null = all templates) |
ruleName |
VARCHAR(255) |
Human-readable rule name |
timeRestriction |
JSONB |
Allowed times: {"mon": ["06:00-22:00"], "sat": ["08:00-18:00"]} |
antiPassback |
BOOLEAN |
Require exit before re-entry |
priorityOrder |
INTEGER |
Rule evaluation priority (lower = higher priority) |
statusCd |
VARCHAR(50) |
ACTIVE, DISABLED |
version |
INTEGER |
Optimistic locking |
createdAt |
TIMESTAMPTZ |
Audit |
updatedAt |
TIMESTAMPTZ |
Audit |
Credential
Physical or digital access credential issued to a member for check-in and door access.
| Field | Type | Description |
|---|---|---|
id |
BIGINT (PK) |
Primary key |
idEntity |
BIGINT (FK) |
Tenant |
idMember |
BIGINT (FK) |
Credential holder |
typeCd |
VARCHAR(50) |
NFC_CARD, QR_CODE, BLE, BIOMETRIC_FINGERPRINT, BIOMETRIC_FACE, PIN |
credentialIdentifier |
VARCHAR(500) |
Encrypted credential data (card UID, QR token, BLE key) |
statusCd |
VARCHAR(50) |
ACTIVE, SUSPENDED, REVOKED, EXPIRED |
validFrom |
TIMESTAMPTZ |
Credential valid from |
validUntil |
TIMESTAMPTZ (nullable) |
Credential expiry (null = no expiry) |
fallbackOrder |
INTEGER |
Priority in fallback chain (lower = preferred) |
lastUsedAt |
TIMESTAMPTZ (nullable) |
Last successful use |
issuedBy |
BIGINT (FK) |
User who issued the credential |
version |
INTEGER |
Optimistic locking |
createdAt |
TIMESTAMPTZ |
Audit |
updatedAt |
TIMESTAMPTZ |
Audit |
DashboardConfig
User-specific dashboard layout configuration with role-appropriate defaults.
| Field | Type | Description |
|---|---|---|
id |
BIGINT (PK) |
Primary key |
idEntity |
BIGINT (FK, nullable) |
Tenant (null for vendor-level dashboards) |
idUser |
BIGINT (FK) |
Dashboard owner |
name |
VARCHAR(255) |
Dashboard name |
roleCd |
VARCHAR(50) |
Target role (for default dashboards) |
layout |
JSONB |
Widget positions, sizes, data sources, filters |
isDefault |
BOOLEAN |
Whether this is the default for the role |
version |
INTEGER |
Optimistic locking |
createdAt |
TIMESTAMPTZ |
Audit |
updatedAt |
TIMESTAMPTZ |
Audit |
AuditLog
Immutable audit trail for compliance and debugging.
| Field | Type | Description |
|---|---|---|
id |
BIGINT (PK) |
Primary key |
idEntity |
BIGINT (FK) |
Tenant |
idUser |
BIGINT (FK) |
User who performed the action |
action |
VARCHAR(50) |
CREATE, UPDATE, DELETE, LOGIN, EXPORT, etc. |
entityType |
VARCHAR(100) |
Target entity class name |
entityId |
BIGINT |
Target entity ID |
oldValue |
JSONB |
Previous state (for updates) |
newValue |
JSONB |
New state (for creates/updates) |
ipAddress |
VARCHAR(45) |
Client IP |
userAgent |
VARCHAR(500) |
Client user agent |
createdAt |
TIMESTAMPTZ |
Timestamp (immutable, no updatedAt) |
Entity-Relationship Diagram
Migration Mapping from Cash360
The following table maps Cash360 entities to the new Membership data model. This mapping guides data migration (see Chapter 15).
| Cash360 Entity | Cash360 Table | Membership Entity | Key Differences |
|---|---|---|---|
MbLogin |
mb_login |
User | Added: refresh token, MFA, brute-force protection, password reset flow |
MbConsumer |
mb_consumer |
Member | Added: emergency contacts, responsible person, tags, member number |
CsrConsumer |
csr_consumer |
Member (backend enrichment) | JSONB custom attributes preserved; bank details moved to BankAccount |
CsrContractPreDefined |
csr_contract_pre_defined |
MembershipTemplate | Added: age restrictions, access rules, homepage visibility, setup fee |
CsrContract2Product |
csr_contract_2_product |
Contract | Added: cancellation workflow, eIDAS signature, next billing date |
CsrBankAccount |
csr_bank_account |
BankAccount | Added: mandate type (CORE/B2B), status tracking, default flag |
PmTransaction |
pm_transaction |
Transaction | Simplified: only membership-relevant fields; Cash360 handles payment processing |
PpProduct |
pp_product |
Product | Added: categories, capacity, duration; removed: complex pricing tiers |
McEntity |
mc_entity |
Entity | Added: white-label (logo, color), timezone, locale, JSONB settings |
McUser |
mc_user |
User (admin roles) | Merged with MbLogin into unified User entity with role-based access |
EbSepaExport |
eb_sepa_export |
(handled by Cash360) | SEPA export generation stays in Cash360; Membership only submits transactions |
New entities (no Cash360 equivalent — net new):
| Membership Entity | Module | Purpose |
|---|---|---|
| Lead | membership-crm | Sales lead tracking |
| Deal | membership-crm | Pipeline deal management |
| Activity | membership-crm | Sales/support activity log |
| Ticket | membership-support | Support ticket management |
| TicketComment | membership-support | Ticket conversation thread |
| KnowledgeBaseArticle | membership-support | Self-service help center |
| AccountingEntry | membership-accounting | General ledger journal entries |
| CostCenter | membership-accounting | Financial cost centers |
| ChartOfAccount | membership-accounting | Ledger account definitions |
| AccessZone | membership-checkin | Physical access zones |
| AccessRule | membership-checkin | Zone access policies |
| Credential | membership-checkin | Member access credentials |
| DashboardConfig | membership-core | User dashboard layouts |
Multi-Tenancy Strategy
Every business entity in the database carries an idEntity column that references the Organization table. This is enforced at three levels:
-
Database level: All queries include a
WHERE id_entity = ?clause. ATenantInterceptorin Hibernate automatically injects the tenant filter, preventing cross-tenant data leakage even if application code omits the filter. -
Application level: The
TenantContextis resolved from the JWT token at request entry and propagated via aThreadLocal(or Reactor context for reactive flows). Every repository method receives the tenant ID implicitly. -
API level: Tenant resolution happens during authentication. A user can only access data belonging to their assigned entity. Group admins and franchise admins can access child entities through explicit parent-child traversal, never through a blanket "all data" query.
For large enterprise deployments, the architecture supports schema-per-tenant isolation as an upgrade path, where each tenant gets a dedicated PostgreSQL schema. This is configured per deployment, not per tenant.
JSONB Custom Attributes
Following the Cash360 pattern, several entities include a customAttributes column of type JSONB. This allows tenants to define custom fields without schema changes.
Structure convention:
{
"fields": [
{
"key": "jersey_size",
"label": "Jersey Size",
"type": "SELECT",
"options": ["XS", "S", "M", "L", "XL", "XXL"],
"required": false,
"value": "L"
},
{
"key": "medical_note",
"label": "Medical Note",
"type": "TEXT",
"required": false,
"value": "Asthma - carries inhaler"
}
]
}
Supported field types: TEXT, NUMBER, DATE, BOOLEAN, SELECT, MULTI_SELECT, URL, EMAIL, PHONE.
Custom attribute definitions are stored in the Entity settings JSONB column as a schema that client applications render dynamically. This keeps the database schema stable while allowing per-tenant customization.