Skip to content

OBV Database Schema

The OBV module uses PostgreSQL 16 accessed via Prisma ORM v6. The schema is defined in nitro-backend/prisma/schema.prisma.

All primary keys are UUID strings generated by @default(uuid()). All models include createdAt and updatedAt timestamps.


erDiagram
Villa {
string id PK
string villaCode UK
string villaName
string address
string city
string country
string zipCode
float latitude
float longitude
int bedrooms
int bathrooms
int maxGuests
float propertySize
float plotSize
int yearBuilt
int renovationYear
PropertyType propertyType
VillaStyle villaStyle
string locationType
string description
string shortDescription
VillaStatus status
boolean isDraft
boolean isActive
string sharePointBasePath
datetime sharePointCreatedAt
json bedroomConfiguration
datetime createdAt
datetime updatedAt
}
Owner {
string id PK
string villaId FK
string clerkUserId
string firstName
string lastName
string email
string phone
OwnerType ownerType
string nationality
string address
string city
string country
CommunicationPreference communicationPreference
datetime createdAt
datetime updatedAt
}
ContractualDetails {
string id PK
string villaId FK
datetime contractStartDate
datetime contractEndDate
ContractType contractType
decimal commissionRate
decimal managementFee
decimal marketingFee
PaymentSchedule paymentSchedule
int minimumStayNights
CancellationPolicy cancellationPolicy
string checkInTime
string checkOutTime
string vatRegistrationNumber
string dbdNumber
datetime createdAt
datetime updatedAt
}
BankDetails {
string id PK
string villaId FK
string accountHolderName
string bankName
string accountNumber
string iban
string swiftCode
string currency
string bankCountry
boolean isVerified
datetime verifiedAt
datetime createdAt
datetime updatedAt
}
OTACredentials {
string id PK
string villaId FK
OTAPlatform platform
string propertyId
string username
string password
string apiKey
boolean isActive
SyncStatus syncStatus
datetime lastSyncAt
datetime createdAt
datetime updatedAt
}
Staff {
string id PK
string villaId FK
string firstName
string lastName
string email
string phone
StaffPosition position
StaffDepartment department
EmploymentType employmentType
datetime startDate
decimal salary
SalaryFrequency salaryFrequency
string currency
boolean hasAccommodation
boolean hasTransport
boolean hasHealthInsurance
boolean isActive
json emergencyContacts
datetime createdAt
datetime updatedAt
}
Photo {
string id PK
string villaId FK
PhotoCategory category
string fileName
string fileUrl
string thumbnailUrl
int fileSize
string mimeType
boolean isMain
int sortOrder
string subfolder
string sharePointFileId
string sharePointUrl
string storageLocation
bytes fileContent
bytes thumbnailContent
datetime createdAt
datetime updatedAt
}
Document {
string id PK
string villaId FK
DocumentType documentType
string fileName
string fileUrl
int fileSize
string mimeType
boolean isActive
string sharePointFileId
string sharePointUrl
string storageLocation
bytes fileContent
datetime validFrom
datetime validUntil
datetime createdAt
datetime updatedAt
}
FacilityChecklist {
string id PK
string villaId FK
FacilityCategory category
string subcategory
string itemName
boolean isAvailable
int quantity
string condition
string notes
string photoUrl
string sharePointUrl
bytes photoData
datetime lastCheckedAt
datetime createdAt
datetime updatedAt
}
OnboardingProgress {
string id PK
string villaId FK
int currentStep
int totalSteps
boolean villaInfoCompleted
boolean ownerDetailsCompleted
boolean contractualDetailsCompleted
boolean bankDetailsCompleted
boolean otaCredentialsCompleted
boolean staffConfigCompleted
boolean facilitiesCompleted
boolean photosUploaded
boolean documentsUploaded
boolean reviewCompleted
OnboardingStatus status
datetime submittedAt
datetime createdAt
datetime updatedAt
}
OnboardingBackup {
string id PK
string userId
string sessionId
string villaId FK
int currentStep
json stepData
datetime lastSaved
boolean autoSaveEnabled
datetime createdAt
datetime updatedAt
}
OnboardingStepProgress {
string id PK
string villaId FK
int stepNumber
string stepName
StepStatus status
boolean isValid
json validationErrors
int[] dependsOnSteps
int version
datetime createdAt
datetime updatedAt
}
StepFieldProgress {
string id PK
string stepProgressId FK
string fieldName
string fieldType
FieldStatus status
boolean isSkipped
json value
boolean isValid
boolean isRequired
datetime createdAt
datetime updatedAt
}
SkippedItem {
string id PK
string villaId FK
SkippedItemType itemType
int stepNumber
string fieldName
SkipCategory skipCategory
string skippedBy
datetime skippedAt
boolean isActive
datetime createdAt
datetime updatedAt
}
OnboardingSession {
string id PK
string villaId FK
string userId
string userEmail
datetime sessionStartedAt
int currentStep
int stepsCompleted
int fieldsCompleted
boolean isCompleted
boolean submittedForReview
datetime createdAt
datetime updatedAt
}
Villa ||--o| Owner : "owner"
Villa ||--o| ContractualDetails : "contractualDetails"
Villa ||--o| BankDetails : "bankDetails"
Villa ||--o{ OTACredentials : "otaCredentials"
Villa ||--o{ Staff : "staff"
Villa ||--o{ Photo : "photos"
Villa ||--o{ Document : "documents"
Villa ||--o{ FacilityChecklist : "facilities"
Villa ||--o| OnboardingProgress : "onboarding"
Villa ||--o{ OnboardingBackup : "onboardingBackups"
Villa ||--o{ OnboardingStepProgress : "stepProgress"
Villa ||--o| OnboardingSession : "onboardingSession"
Villa ||--o{ SkippedItem : "skippedItems"
OnboardingStepProgress ||--o{ StepFieldProgress : "fields"

The central entity. All other models relate back to a villa via villaId.

FieldTypeRequiredDescription
idString (UUID)YesPrimary key
villaCodeStringYesUnique human-readable code (e.g. BALI-001)
villaNameStringYesDisplay name
addressStringYesStreet address
cityStringYesCity
countryStringYesCountry
zipCodeString?NoPostal code
latitudeFloat?NoGPS latitude
longitudeFloat?NoGPS longitude
bedroomsIntYesNumber of bedrooms
bathroomsIntYesNumber of bathrooms
maxGuestsIntYesMaximum guest capacity
propertySizeFloat?NoInterior size (m²)
plotSizeFloat?NoLand plot size (m²)
yearBuiltInt?NoYear of construction
renovationYearInt?NoYear of last renovation
propertyTypePropertyTypeYesEnum: VILLA, APARTMENT, etc.
villaStyleVillaStyle?NoEnum: MODERN, BALINESE, etc.
locationTypeString?Noe.g. Seaview, Beachfront, Mountain
descriptionString?NoLong description
shortDescriptionString?NoShort marketing copy
statusVillaStatusYesDRAFT | ACTIVE | INACTIVE | ARCHIVED
isDraftBooleanYesDefault true during onboarding
isActiveBooleanYesDefault true
propertyEmailString?NoProperty-specific email
propertyWebsiteString?NoProperty website URL
sharePointBasePathString?NoSharePoint folder path (set on first upload)
sharePointCreatedAtDateTime?NoWhen SharePoint folders were created
bedroomConfigurationJson?NoArray of { id, name, bedType } for bedroom folders
googleMapsLinkString?NoGoogle Maps URL
iCalCalendarLinkString?NoiCal feed URL
createdAtDateTimeYesAuto-set on creation
updatedAtDateTimeYesAuto-updated

Indexes: villaCode, status, isActive, (city, country), (propertyType, status), (bedrooms, bathrooms, maxGuests)


One-to-one with Villa. Stores villa owner or company details.

FieldTypeRequiredDescription
idString (UUID)YesPrimary key
villaIdStringYesFK → Villa (unique, cascade delete)
clerkUserIdString?NoClerk user ID for owner portal login
firstNameStringYes
lastNameStringYes
emailStringYes
phoneStringYes
alternativePhoneString?No
nationalityString?No
passportNumberString?No
idNumberString?NoNational ID
addressStringYes
cityStringYes
countryStringYes
zipCodeString?No
preferredLanguageStringYesDefault "en"
communicationPreferenceCommunicationPreferenceYesEMAIL | PHONE | WHATSAPP | SMS
ownerTypeOwnerTypeYesINDIVIDUAL | COMPANY | TRUST | OTHER
companyNameString?NoFor COMPANY owners
companyAddressString?No
companyTaxIdString?No
companyVatString?No
managerNameString?NoCompany manager contact
managerEmailString?No
managerPhoneString?No
notesString?NoInternal notes

Indexes: email, clerkUserId, (villaId, firstName, lastName), nationality


One-to-one with Villa. Management contract terms.

FieldTypeRequiredDescription
idString (UUID)YesPrimary key
villaIdStringYesFK → Villa (unique)
contractStartDateDateTimeYes
contractEndDateDateTime?NoNull for open-ended contracts
contractTypeContractTypeYesEXCLUSIVE | NON_EXCLUSIVE | SEASONAL | LONG_TERM
commissionRateDecimal(5,2)YesILS commission %
managementFeeDecimal(5,2)?No
marketingFeeDecimal(5,2)?No
paymentTermsString?NoFree-text payment terms
paymentSchedulePaymentScheduleYesWEEKLY | BIWEEKLY | MONTHLY | QUARTERLY | ANNUALLY
minimumStayNightsIntYesDefault 1
cancellationPolicyCancellationPolicyYesFLEXIBLE | MODERATE | STRICT | SUPER_STRICT | NON_REFUNDABLE
checkInTimeStringYesDefault "15:00"
checkOutTimeStringYesDefault "11:00"
insuranceProviderString?No
insurancePolicyNumberString?No
insuranceExpiryDateTime?No
specialTermsString?NoAdditional terms
vatRegistrationNumberString?No
vatPaymentTermsString?No
dbdNumberString?NoThai DBD registration number
paymentThroughIPLBooleanYesDefault false
payoutDay1Int?NoFirst payout day of month
payoutDay2Int?NoSecond payout day of month

One-to-one with Villa. Owner bank account for payouts.

FieldTypeRequiredDescription
idString (UUID)YesPrimary key
villaIdStringYesFK → Villa (unique)
accountHolderNameStringYes
bankNameStringYes
accountNumberStringYes
ibanString?NoMax 34 chars
swiftCodeString?NoMax 11 chars
branchCodeString?No
branchNameString?No
branchAddressString?No
currencyStringYesDefault "USD"
bankAddressString?No
bankCountryString?No
accountTypeString?NoDefault "CHECKING"
isVerifiedBooleanYesDefault false
verifiedAtDateTime?No
notesString?No

Many-to-one with Villa. Stores platform login credentials for each OTA.

FieldTypeRequiredDescription
idString (UUID)YesPrimary key
villaIdStringYesFK → Villa
platformOTAPlatformYesSee enum below
propertyIdString?NoPlatform-specific property ID
usernameString?NoLogin username
passwordString?NoLogin password
apiKeyString?No
apiSecretString?No
isActiveBooleanYesDefault true
syncStatusSyncStatusYesPENDING | IN_PROGRESS | SUCCESS | FAILED | PARTIAL
lastSyncAtDateTime?No
accountUrlString?NoAccount dashboard URL
propertyUrlString?NoProperty listing URL
listingUrlString?NoDirect booking link

Unique constraint: (villaId, platform) — one record per platform per villa.


Many-to-one with Villa. Villa personnel records.

FieldTypeRequiredDescription
idString (UUID)YesPrimary key
villaIdStringYesFK → Villa
firstNameStringYes
lastNameStringYes
emailString?No
phoneStringYes
idNumberString?NoNational ID
passportNumberString?No
nationalityString?No
dateOfBirthDateTime?No
nicknameString?No
positionStaffPositionYesSee enum
departmentStaffDepartmentYesSee enum
employmentTypeEmploymentTypeYesFULL_TIME | PART_TIME | CONTRACT | SEASONAL | FREELANCE
startDateDateTimeYes
endDateDateTime?NoNull for current employees
salaryDecimal(10,2)Yes
salaryFrequencySalaryFrequencyYesHOURLY | DAILY | WEEKLY | BIWEEKLY | MONTHLY | ANNUALLY
currencyStringYesDefault "USD"
hasAccommodationBooleanYesDefault false
hasTransportBooleanYesDefault false
hasHealthInsuranceBooleanYesDefault false
hasWorkInsuranceBooleanYesDefault false
foodAllowanceBooleanYesDefault false
serviceChargeDecimal?No
totalIncomeDecimal?No
totalNetIncomeDecimal?No
otherDeductionsDecimal?No
numberOfDaySalaryInt?No
maritalStatusBoolean?No
transportationString?NoTransport description
emergencyContactsJson?NoArray of emergency contacts
isActiveBooleanYesDefault true

Many-to-one with Villa. Supports dual storage (database bytes or SharePoint).

FieldTypeRequiredDescription
idString (UUID)YesPrimary key
villaIdStringYesFK → Villa
categoryPhotoCategoryYesSee enum (19 categories)
fileNameStringYes
fileUrlStringYesPrimary access URL
thumbnailUrlString?No
fileSizeIntYesBytes
mimeTypeStringYes
widthInt?NoPixels
heightInt?NoPixels
captionString?No
altTextString?No
tagsString[]YesDefault []
isMainBooleanYesDefault false
sortOrderIntYesDefault 0
subfolderString?NoFor bedroom subfolders
sharePointFileIdString?NoGraph API item ID
sharePointPathString?NoPath within drive
sharePointUrlString?NoDirect web URL
fileContentBytes?NoBinary content (database storage)
thumbnailContentBytes?NoCompressed thumbnail (database storage)
isCompressedBooleanYesDefault false
originalFileSizeInt?NoSize before compression
storageLocationStringYes"database" | "local" | "sharepoint"

Many-to-one with Villa. Supports dual storage (database bytes or SharePoint).

FieldTypeRequiredDescription
idString (UUID)YesPrimary key
villaIdStringYesFK → Villa
documentTypeDocumentTypeYesSee enum (14 types)
fileNameStringYes
fileUrlStringYesPrimary access URL
fileSizeIntYesBytes
mimeTypeStringYes
descriptionString?No
isActiveBooleanYesDefault true
deletedAtDateTime?NoSoft delete timestamp
sharePointFileIdString?No
sharePointPathString?No
sharePointUrlString?No
validFromDateTime?NoDocument validity start
validUntilDateTime?NoDocument validity end (indexed for expiry queries)
fileContentBytes?NoBinary content (database storage)
storageLocationStringYes"database" | "local" | "sharepoint"

Many-to-one with Villa. One row per facility item.

FieldTypeRequiredDescription
idString (UUID)YesPrimary key
villaIdStringYesFK → Villa
categoryFacilityCategoryYesSee enum
subcategoryStringYesWithin-category grouping
itemNameStringYesSpecific item name
isAvailableBooleanYesDefault false
quantityInt?NoDefault 1
conditionString?NoDefault "good"
notesString?No
specificationsString?NoTechnical details
photoUrlString?NoAPI endpoint or legacy path
sharePointUrlString?NoDirect SharePoint URL
thumbnailUrlString?No
photoDataBytes?NoBinary thumbnail (database storage)
photoMimeTypeString?No
productLinkString?NoSupplier/product URL
lastCheckedAtDateTime?NoDefault now()
checkedByString?NoStaff name who checked

Unique constraint: (villaId, category, subcategory, itemName)


One-to-one with Villa. Tracks which of the 10 steps are complete.

FieldTypeDescription
idStringPrimary key
villaIdStringFK → Villa (unique)
currentStepIntActive step (1–10)
totalStepsIntAlways 10
villaInfoCompletedBooleanStep 1
ownerDetailsCompletedBooleanStep 2
contractualDetailsCompletedBooleanStep 3
bankDetailsCompletedBooleanStep 4
otaCredentialsCompletedBooleanStep 5
documentsUploadedBooleanStep 6
staffConfigCompletedBooleanStep 7
facilitiesCompletedBooleanStep 8
photosUploadedBooleanStep 9
reviewCompletedBooleanStep 10
statusOnboardingStatusNOT_STARTED | IN_PROGRESS | COMPLETED
submittedAtDateTime?When wizard was submitted

Crash-recovery data stored server-side. Created by the frontend’s OnboardingBackupService.

FieldTypeDescription
idStringPrimary key
userIdStringClerk user ID
sessionIdStringBrowser session identifier
villaIdString?FK → Villa (nullable for pre-creation backups)
currentStepIntLast active step
stepDataJsonFull form state snapshot
lastSavedDateTime
autoSaveEnabledBooleanDefault true
userAgentStringBrowser info
versionStringApp version at time of backup

Unique: (userId, sessionId)


Granular per-step tracking (optional, used for detailed analytics).

FieldTypeDescription
villaIdStringFK → Villa
stepNumberInt1–10
stepNameStringHuman-readable step name
statusStepStatusNOT_STARTED | IN_PROGRESS | COMPLETED | SKIPPED | BLOCKED | ERROR
isValidBooleanWhether step passes validation
validationErrorsJson?Error messages
dependsOnStepsInt[]Prerequisite step numbers
estimatedDurationInt?Expected time in seconds
actualDurationInt?Actual time in seconds
versionIntOptimistic locking version

Unique: (villaId, stepNumber)


Tracks individual field completion within a step.

FieldTypeDescription
stepProgressIdStringFK → OnboardingStepProgress
fieldNameStringForm field identifier
fieldLabelString?Display name
fieldTypeStringInput type
statusFieldStatusNOT_STARTED | IN_PROGRESS | COMPLETED | SKIPPED | ERROR
isSkippedBoolean
skipReasonString?
valueJson?Last known value
isValidBoolean
isRequiredBoolean
dependsOnFieldsString[]Prerequisite field names

Unique: (stepProgressId, fieldName)


Audit log of skipped steps/fields during onboarding.

FieldTypeDescription
villaIdStringFK → Villa
itemTypeSkippedItemTypeSTEP | FIELD | SECTION
stepNumberInt?
fieldNameString?
sectionNameString?
skipReasonString?User-provided reason
skipCategorySkipCategoryNOT_APPLICABLE | DATA_UNAVAILABLE | LATER | OPTIONAL | PRIVACY_CONCERNS | OTHER
skippedByStringClerk user ID
isActiveBooleanfalse if item was later completed
unskippedAtDateTime?
unskippedByString?

Session-level analytics tracking per villa.

FieldTypeDescription
villaIdStringFK → Villa (unique)
userIdStringClerk user ID
userEmailString?
currentStepInt
stepsCompletedIntCount
stepsSkippedIntCount
fieldsCompletedIntCount
fieldsSkippedIntCount
totalFieldsIntCount
isCompletedBoolean
submittedForReviewBoolean
totalTimeSpentInt?Seconds
averageStepTimeInt?Seconds

VILLA | APARTMENT | PENTHOUSE | TOWNHOUSE | CHALET | BUNGALOW | ESTATE | HOUSE

MODERN | TRADITIONAL | MEDITERRANEAN | CONTEMPORARY | BALINESE | MINIMALIST | LUXURY | RUSTIC

DRAFT | ACTIVE | INACTIVE | ARCHIVED

INDIVIDUAL | COMPANY | TRUST | OTHER

EMAIL | PHONE | WHATSAPP | SMS

EXCLUSIVE | NON_EXCLUSIVE | SEASONAL | LONG_TERM

WEEKLY | BIWEEKLY | MONTHLY | QUARTERLY | ANNUALLY

FLEXIBLE | MODERATE | STRICT | SUPER_STRICT | NON_REFUNDABLE

BOOKING_COM | AIRBNB | VRBO | EXPEDIA | AGODA | HOTELS_COM | TRIPADVISOR | MARRIOTT_HOMES_VILLAS | HOMEAWAY | FLIPKEY | DIRECT

PENDING | IN_PROGRESS | SUCCESS | FAILED | PARTIAL

VILLA_MANAGER | HOUSEKEEPER | GARDENER | POOL_MAINTENANCE | SECURITY | CHEF | DRIVER | CONCIERGE | MAINTENANCE | OTHER

MANAGEMENT | HOUSEKEEPING | MAINTENANCE | SECURITY | HOSPITALITY | ADMINISTRATION

FULL_TIME | PART_TIME | CONTRACT | SEASONAL | FREELANCE

HOURLY | DAILY | WEEKLY | BIWEEKLY | MONTHLY | ANNUALLY

EXTERIOR_VIEWS | INTERIOR_LIVING_SPACES | BEDROOMS | BATHROOMS | KITCHEN | DINING_AREAS | POOL_OUTDOOR_AREAS | GARDEN_LANDSCAPING | AMENITIES_FACILITIES | VIEWS_SURROUNDINGS | STAFF_AREAS | UTILITY_AREAS | LOGO | FLOOR_PLAN | VIDEOS | DRONE_SHOTS | ENTERTAINMENT | VIRTUAL_TOUR | OTHER

PROPERTY_CONTRACT | INSURANCE_CERTIFICATE | PROPERTY_TITLE | TAX_DOCUMENTS | UTILITY_BILLS | MAINTENANCE_RECORDS | INVENTORY_LIST | HOUSE_RULES | EMERGENCY_CONTACTS | STAFF_CONTRACTS | LICENSES_PERMITS | FLOOR_PLANS | MAINTENANCE_CONTRACTS | OTHER

New (frontend-matching, kebab-case): property_layout_spaces | occupancy_sleeping | bathrooms | kitchen_dining | service_staff | living_spaces | outdoor_facilities | home_office | entertainment_gaming | technology | wellness_spa | accessibility | safety_security | child_friendly

Legacy (kept for backward compatibility): KITCHEN_EQUIPMENT | BATHROOM_AMENITIES | BEDROOM_AMENITIES | LIVING_ROOM | OUTDOOR_FACILITIES | POOL_AREA | ENTERTAINMENT | SAFETY_SECURITY | UTILITIES | ACCESSIBILITY | BUSINESS_FACILITIES | CHILDREN_FACILITIES | PET_FACILITIES | OTHER

NOT_STARTED | IN_PROGRESS | COMPLETED

NOT_STARTED | IN_PROGRESS | COMPLETED | SKIPPED | BLOCKED | ERROR

NOT_STARTED | IN_PROGRESS | COMPLETED | SKIPPED | ERROR

STEP | FIELD | SECTION

NOT_APPLICABLE | DATA_UNAVAILABLE | LATER | OPTIONAL | PRIVACY_CONCERNS | OTHER


MigrationDescription
20240916000000_add_step_progress_versionAdds version column (int, default 0) to OnboardingStepProgress for optimistic locking

Note: Only one migration file is present in the repository. The initial schema was likely created with prisma migrate dev before the migration history was tracked, or earlier migrations are not committed.