This commit is contained in:
Jonas Linter
2025-11-19 19:35:36 +01:00
parent e4bd64a9e4
commit 3819b2bc95

View File

@@ -883,131 +883,6 @@ class ConversionService:
return stats return stats
async def _find_matching_entities(
self,
advertising_campagne: str,
hotel_id: str | None,
reservation_date: Any,
guest_first_name: str | None = None,
guest_last_name: str | None = None,
guest_email: str | None = None,
advertising_partner: str | None = None,
session: AsyncSession | None = None,
) -> dict[str, Any]:
"""Find matching Reservation, Customer, and HashedCustomer.
Uses two strategies with separate matching paths:
1. ID-based matching (fbclid/gclid/md5_unique_id) - returns Reservation + Customer + HashedCustomer
2. Guest detail matching (email/name) - returns Customer + HashedCustomer directly (no Reservation needed)
Args:
advertising_campagne: Truncated tracking ID from conversion XML
hotel_id: Hotel ID for additional filtering
reservation_date: Reservation date for additional filtering
guest_first_name: Guest first name (hashed) for matching
guest_last_name: Guest last name (hashed) for matching
guest_email: Guest email (hashed) for matching
advertising_partner: Partner info (matches utm_medium for additional filtering)
session: AsyncSession to use. If None, uses self.session.
Returns:
Dictionary with 'reservation', 'customer', 'hashed_customer', and 'match_type' keys.
match_type is either 'id' (high confidence) or 'guest_details' (lower confidence)
For guest_details matches, 'reservation' will be None.
"""
if session is None:
session = self.session
result = {
"reservation": None,
"customer": None,
"hashed_customer": None,
"match_type": None, # "id" or "guest_details"
}
# Strategy 1: Try to match by advertising data (fbclid/gclid/md5_unique_id) - ID-based, high confidence
if advertising_campagne:
matched_reservation = await self._match_by_advertising(
advertising_campagne,
hotel_id,
guest_first_name,
guest_last_name,
guest_email,
advertising_partner,
session,
)
if matched_reservation:
result["reservation"] = matched_reservation
result["match_type"] = "id" # Matched by ID
_LOGGER.info(
"Matched conversion by advertising ID data (advertisingCampagne=%s, hotel=%s)",
advertising_campagne,
hotel_id,
)
else:
_LOGGER.debug(
"No match found by advertising ID data (advertisingCampagne=%s), "
"falling back to guest details matching",
advertising_campagne,
)
# Strategy 2: If no ID-based match, try email/name-based matching - guest details, lower confidence
# For guest detail matches, match directly with HashedCustomer (skip Reservation table)
if not result["reservation"] and (
guest_email or guest_first_name or guest_last_name
):
matched_hashed_customer = await self._match_by_guest_details_hashed(
guest_first_name, guest_last_name, guest_email, session
)
if matched_hashed_customer:
result["hashed_customer"] = matched_hashed_customer
result["match_type"] = "guest_details" # Matched by guest details only
# Get the customer if it exists
if matched_hashed_customer.customer_id:
customer_query = select(Customer).where(
Customer.id == matched_hashed_customer.customer_id
)
customer_result = await session.execute(customer_query)
result["customer"] = customer_result.scalar_one_or_none()
_LOGGER.info(
"Matched conversion by guest details to hashed_customer (name=%s %s, email=%s)",
guest_first_name,
guest_last_name,
guest_email,
)
else:
_LOGGER.debug(
"No match found by guest details (name=%s %s, email=%s)",
guest_first_name,
guest_last_name,
guest_email,
)
# If we found a reservation (ID-based match), get its customer and hashed_customer
if result["reservation"]:
if result["reservation"].customer_id:
customer_query = select(Customer).where(
Customer.id == result["reservation"].customer_id
)
customer_result = await session.execute(customer_query)
result["customer"] = customer_result.scalar_one_or_none()
# Get hashed customer
if result["customer"]:
hashed_query = select(HashedCustomer).where(
HashedCustomer.customer_id == result["customer"].id
)
hashed_result = await session.execute(hashed_query)
result["hashed_customer"] = hashed_result.scalar_one_or_none()
return result
async def _match_by_advertising( async def _match_by_advertising(
self, self,
advertising_campagne: str, advertising_campagne: str,