Here’s a compact OOP variant in Harbour/xBase style (single class, no helpers beyond the class). It mirrors the earlier logic: room exists? → conflict check → return a small result hash. Includes a tiny demo at the end.
One class, simple methods, explicit open/close per alias.
Returns small hashes (success/code/message) so you can hb_jsonEncode() when needed.
Swap paths/aliases to your environment; date parsing depends on your SET DATE/SET EPOCH.
/* OOP Demo — Room availability check */
CLASS TRoomAvailability
DATA cAdrPath
DATA cBegPath
DATA cAdrAlias
DATA cBegAlias
METHOD New( cAdr, cBeg, cAdrAlias, cBegAlias ) CONSTRUCTOR
METHOD CheckFree( cZiNr, dFrom, dTo, nBuch )
METHOD ExistsRoom( cZiNr )
METHOD FindConflicts( cZiNr, dFrom, dTo, nBuch )
METHOD Open( cPath, cAlias, lShared )
METHOD Close( cAlias )
ENDCLASS
METHOD New( cAdr, cBeg, cAdrAlias, cBegAlias ) CLASS TRoomAvailability
::cAdrPath := IIF( HB_ISCHAR( cAdr ), cAdr, "" )
::cBegPath := IIF( HB_ISCHAR( cBeg ), cBeg, "" )
::cAdrAlias := IIF( HB_ISCHAR( cAdrAlias ) .AND. ! Empty( cAdrAlias ), cAdrAlias, "ADR" )
::cBegAlias := IIF( HB_ISCHAR( cBegAlias ) .AND. ! Empty( cBegAlias ), cBegAlias, "BEG" )
RETURN Self
METHOD Open( cPath, cAlias, lShared ) CLASS TRoomAvailability
LOCAL nSel
IF Empty( cPath ) .OR. Empty( cAlias )
RETURN .F.
ENDIF
nSel := Select( cAlias )
IF nSel > 0
( cAlias )->( dbCloseArea() )
ENDIF
IF lShared == .T.
USE ( cPath ) NEW ALIAS ( cAlias ) SHARED
ELSE
USE ( cPath ) NEW ALIAS ( cAlias )
ENDIF
RETURN ! NetErr()
METHOD Close( cAlias ) CLASS TRoomAvailability
IF ! Empty( cAlias ) .AND. Select( cAlias ) > 0
( cAlias )->( dbCloseArea() )
ENDIF
RETURN NIL
METHOD ExistsRoom( cZiNr ) CLASS TRoomAvailability
IF ! ::Open( ::cAdrPath, ::cAdrAlias, .F. )
RETURN {=> "success"=>.F., "code"=>"DB_OPEN_FAILED", "message"=>"ADR open failed" }
ENDIF
( ::cAdrAlias )->( dbSetOrder( "a_zinr" ) )
SET SOFTSEEK OFF
( ::cAdrAlias )->( dbSeek( cZiNr ) )
IF ( ::cAdrAlias )->( Found() )
::Close( ::cAdrAlias )
RETURN {=> "success"=>.T. }
ENDIF
::Close( ::cAdrAlias )
RETURN {=> "success"=>.F., "code"=>"INVALID_ROOM", "message"=>"Invalid room number" }
METHOD FindConflicts( cZiNr, dFrom, dTo, nBuch ) CLASS TRoomAvailability
LOCAL cMsg
IF ! ::Open( ::cBegPath, ::cBegAlias, .T. )
RETURN {=> "success"=>.F., "code"=>"DB_OPEN_FAILED", "message"=>"BEG open failed" }
ENDIF
( ::cBegAlias )->( dbSetOrder( "b_abreise" ) )
( ::cBegAlias )->( ordScope( 0, PADR( ALLTRIM( cZiNr ), 5 ) ) )
( ::cBegAlias )->( ordScope( 1, PADR( ALLTRIM( cZiNr ), 5 ) ) )
( ::cBegAlias )->( dbGoTop() )
DO WHILE ! ( ::cBegAlias )->( Eof() )
IF ( ( ::cBegAlias )->anreise < dTo ) .AND. ( ( ::cBegAlias )->abreise > dFrom ) .AND. ;
( nBuch == 0 .OR. ( ::cBegAlias )->buchungsnr <> nBuch )
cMsg := "Entered " + DToC( dFrom ) + " to " + DToC( dTo ) + ;
" conflicts with " + DToC( ( ::cBegAlias )->anreise ) + " to " + DToC( ( ::cBegAlias )->abreise ) + ;
" (" + ( ::cBegAlias )->planname + ")"
::Close( ::cBegAlias )
RETURN {=> "success"=>.F., "code"=>"OCCUPIED", "message"=>cMsg }
ENDIF
( ::cBegAlias )->( dbSkip() )
ENDDO
::Close( ::cBegAlias )
RETURN {=> "success"=>.T. }
METHOD CheckFree( cZiNr, dFrom, dTo, nBuch ) CLASS TRoomAvailability
LOCAL r
IF Empty( cZiNr ) .OR. Empty( dFrom ) .OR. Empty( dTo )
RETURN {=> "success"=>.F., "code"=>"INVALID_INPUT", "message"=>"Room, arrival and departure are required" }
ENDIF
r := ::ExistsRoom( cZiNr )
IF ! r[ "success" ]
RETURN r
ENDIF
r := ::FindConflicts( cZiNr, dFrom, dTo, nBuch )
IF ! r[ "success" ]
RETURN r
ENDIF
RETURN {=> "success"=>.T., "code"=>"OK", "message"=>"Room is free", ;
"data"=>{=> "zi_nr"=>cZiNr, "from"=>DToC( dFrom ), "to"=>DToC( dTo ) } }
/* --- Tiny demo --- */
FUNCTION Demo_OOP()
LOCAL o := TRoomAvailability():New( ;
"x:\xwhdaten\DATAWIN\adr.dbf", ;
"x:\xwhdaten\DATAWIN\belegung.dbf" )
LOCAL h := o:CheckFree( "0101", CToD( "07.08.2025" ), CToD( "15.08.2025" ), 0 )
? hb_jsonEncode( h )
RETURN NIL