Only update updated_at timestamps if something actually changes.
This commit is contained in:
@@ -119,6 +119,28 @@ class ConversionService:
|
|||||||
f"session must be AsyncSession or SessionMaker, got {type(session)}"
|
f"session must be AsyncSession or SessionMaker, got {type(session)}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _update_timestamp_if_modified(
|
||||||
|
obj: Conversion | ConversionRoom, session: AsyncSession
|
||||||
|
) -> bool:
|
||||||
|
"""Update the updated_at timestamp only if the object has been modified.
|
||||||
|
|
||||||
|
Uses SQLAlchemy's change tracking to determine if any scalar attributes
|
||||||
|
have changed. Only updates the timestamp if actual changes were detected.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
obj: The ORM object to check and potentially update
|
||||||
|
session: The session managing this object
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True if the object was modified and timestamp was updated, False otherwise
|
||||||
|
|
||||||
|
"""
|
||||||
|
if session.is_modified(obj, include_collections=False):
|
||||||
|
obj.updated_at = datetime.now()
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _parse_required_int(value: str | None, field_name: str) -> int:
|
def _parse_required_int(value: str | None, field_name: str) -> int:
|
||||||
"""Parse an integer attribute that must be present."""
|
"""Parse an integer attribute that must be present."""
|
||||||
@@ -897,26 +919,39 @@ class ConversionService:
|
|||||||
existing_conversion = existing_result.scalar_one_or_none()
|
existing_conversion = existing_result.scalar_one_or_none()
|
||||||
|
|
||||||
if existing_conversion:
|
if existing_conversion:
|
||||||
# Update existing conversion - only update reservation metadata and advertising data
|
# Update existing conversion using Pydantic validation
|
||||||
# Guest info is stored in ConversionGuest table, not here
|
# Guest info is stored in ConversionGuest table, not here
|
||||||
# Don't clear reservation/customer links (matching logic will update if needed)
|
# Preserve reservation/customer links (matching logic will update if needed)
|
||||||
existing_conversion.reservation_number = (
|
conversion_data = ConversionData(
|
||||||
parsed_reservation.reservation_number
|
hotel_id=hotel_id,
|
||||||
|
pms_reservation_id=pms_reservation_id,
|
||||||
|
guest_id=parsed_reservation.guest_id,
|
||||||
|
reservation_number=parsed_reservation.reservation_number,
|
||||||
|
reservation_date=parsed_reservation.reservation_date,
|
||||||
|
creation_time=parsed_reservation.creation_time,
|
||||||
|
reservation_type=parsed_reservation.reservation_type,
|
||||||
|
booking_channel=parsed_reservation.booking_channel,
|
||||||
|
advertising_medium=parsed_reservation.advertising_medium,
|
||||||
|
advertising_partner=parsed_reservation.advertising_partner,
|
||||||
|
advertising_campagne=parsed_reservation.advertising_campagne,
|
||||||
|
# Preserve existing values (managed separately)
|
||||||
|
created_at=existing_conversion.created_at,
|
||||||
|
reservation_id=existing_conversion.reservation_id,
|
||||||
|
customer_id=existing_conversion.customer_id,
|
||||||
|
directly_attributable=existing_conversion.directly_attributable,
|
||||||
|
guest_matched=existing_conversion.guest_matched,
|
||||||
)
|
)
|
||||||
existing_conversion.reservation_date = parsed_reservation.reservation_date
|
|
||||||
existing_conversion.creation_time = parsed_reservation.creation_time
|
# Apply validated data, excluding managed fields
|
||||||
existing_conversion.reservation_type = parsed_reservation.reservation_type
|
validated_dict = conversion_data.model_dump(
|
||||||
existing_conversion.booking_channel = parsed_reservation.booking_channel
|
exclude={'created_at', 'updated_at', 'reservation_id', 'customer_id',
|
||||||
existing_conversion.advertising_medium = (
|
'directly_attributable', 'guest_matched'}
|
||||||
parsed_reservation.advertising_medium
|
|
||||||
)
|
)
|
||||||
existing_conversion.advertising_partner = (
|
for key, value in validated_dict.items():
|
||||||
parsed_reservation.advertising_partner
|
setattr(existing_conversion, key, value)
|
||||||
)
|
|
||||||
existing_conversion.advertising_campagne = (
|
# Only update timestamp if something actually changed
|
||||||
parsed_reservation.advertising_campagne
|
self._update_timestamp_if_modified(existing_conversion, session)
|
||||||
)
|
|
||||||
existing_conversion.updated_at = datetime.now()
|
|
||||||
conversion = existing_conversion
|
conversion = existing_conversion
|
||||||
_LOGGER.debug(
|
_LOGGER.debug(
|
||||||
"Updated conversion %s (pms_id=%s)",
|
"Updated conversion %s (pms_id=%s)",
|
||||||
@@ -998,7 +1033,8 @@ class ConversionService:
|
|||||||
else None
|
else None
|
||||||
)
|
)
|
||||||
existing_room_reservation.total_revenue = room_reservation.total_revenue
|
existing_room_reservation.total_revenue = room_reservation.total_revenue
|
||||||
existing_room_reservation.updated_at = datetime.now()
|
# Only update timestamp if something actually changed
|
||||||
|
self._update_timestamp_if_modified(existing_room_reservation, session)
|
||||||
_LOGGER.debug(
|
_LOGGER.debug(
|
||||||
"Updated room reservation %s (pms_id=%s, room=%s)",
|
"Updated room reservation %s (pms_id=%s, room=%s)",
|
||||||
existing_room_reservation.id,
|
existing_room_reservation.id,
|
||||||
@@ -1358,8 +1394,8 @@ class ConversionService:
|
|||||||
# ID-based matches are always directly attributable
|
# ID-based matches are always directly attributable
|
||||||
conversion.directly_attributable = True
|
conversion.directly_attributable = True
|
||||||
conversion.guest_matched = False
|
conversion.guest_matched = False
|
||||||
|
# Only update timestamp if something actually changed
|
||||||
conversion.updated_at = datetime.now()
|
self._update_timestamp_if_modified(conversion, session)
|
||||||
|
|
||||||
# Update stats if provided
|
# Update stats if provided
|
||||||
if stats is not None:
|
if stats is not None:
|
||||||
@@ -1506,7 +1542,8 @@ class ConversionService:
|
|||||||
elif conversion.reservation_id is None:
|
elif conversion.reservation_id is None:
|
||||||
conversion.directly_attributable = False
|
conversion.directly_attributable = False
|
||||||
|
|
||||||
conversion.updated_at = datetime.now()
|
# Only update timestamp if something actually changed
|
||||||
|
self._update_timestamp_if_modified(conversion, session)
|
||||||
|
|
||||||
return matched_reservation, matched_customer
|
return matched_reservation, matched_customer
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user