From 8c0909453517dbb6f3e1b7b8e9f97de4fd415f3b Mon Sep 17 00:00:00 2001 From: Jonas Linter <{email_address}> Date: Wed, 3 Dec 2025 12:12:37 +0100 Subject: [PATCH] Fixed removing hashed_customer --- src/alpine_bits_python/conversion_service.py | 57 +++++++++----------- src/alpine_bits_python/schemas.py | 3 +- 2 files changed, 25 insertions(+), 35 deletions(-) diff --git a/src/alpine_bits_python/conversion_service.py b/src/alpine_bits_python/conversion_service.py index 3049721..869a6f7 100644 --- a/src/alpine_bits_python/conversion_service.py +++ b/src/alpine_bits_python/conversion_service.py @@ -471,7 +471,6 @@ class ConversionService: "total_daily_sales": 0, "matched_to_reservation": 0, "matched_to_customer": 0, - "matched_to_hashed_customer": 0, "unmatched": 0, "errors": 0, } @@ -1471,41 +1470,40 @@ class ConversionService: session: AsyncSession for database queries """ - # Get all ConversionGuests that have ANY customer link - # This includes: - # 1. Guests matched via guest-details (hashed_customer_id is not null) - # 2. Guests matched via ID-based matching (customer_id is not null via conversion) + # Collect every guest/customer pair derived from conversions. result = await session.execute( - select(ConversionGuest).where( - ConversionGuest.hashed_customer_id.isnot(None) + select(Conversion.guest_id, Conversion.customer_id).where( + Conversion.guest_id.isnot(None), Conversion.customer_id.isnot(None) ) ) - matched_guests = result.scalars().all() + guest_customer_rows = result.all() - if not matched_guests: + if not guest_customer_rows: _LOGGER.debug("Phase 3d: No matched guests to check for regularity") return + # Deduplicate by guest_id to avoid recalculating when multiple conversions share the same guest. + guest_to_customer: dict[int, int] = {} + for guest_id, customer_id in guest_customer_rows: + if guest_id is None or customer_id is None: + continue + if guest_id not in guest_to_customer: + guest_to_customer[guest_id] = customer_id + elif guest_to_customer[guest_id] != customer_id: + _LOGGER.warning( + "Guest %s linked to multiple customers (%s, %s); keeping first match", + guest_id, + guest_to_customer[guest_id], + customer_id, + ) + _LOGGER.debug( - "Phase 3d: Checking regularity for %d matched guests", len(matched_guests) + "Phase 3d: Checking regularity for %d matched guests", + len(guest_to_customer), ) - for conversion_guest in matched_guests: - if not conversion_guest.hashed_customer_id: - continue - - # Get the customer ID from the hashed_customer - hashed_customer_result = await session.execute( - select(Customer).where( - Customer.id == conversion_guest.hashed_customer_id - ) - ) - hashed_customer = hashed_customer_result.scalar_one_or_none() - - if hashed_customer and hashed_customer.id: - await self._check_if_guest_is_regular( - conversion_guest.guest_id, hashed_customer.id, session - ) + for guest_id, customer_id in guest_to_customer.items(): + await self._check_if_guest_is_regular(guest_id, customer_id, session) async def _match_conversions_from_db_sequential( self, pms_reservation_ids: list[str], stats: dict[str, int] @@ -1721,7 +1719,6 @@ class ConversionService: # Guest detail matching is deferred to Phase 3b/3c matched_reservation = None matched_customer = None - matched_hashed_customer = None if conversion.advertising_campagne: matched_reservation = await self._match_by_advertising( @@ -1755,10 +1752,6 @@ class ConversionService: conversion.directly_attributable = True conversion.guest_matched = False - # Update conversion_guest with hashed_customer reference if matched - if conversion_guest and matched_hashed_customer: - conversion_guest.hashed_customer_id = matched_hashed_customer.id - conversion.updated_at = datetime.now() # Update stats if provided @@ -1767,8 +1760,6 @@ class ConversionService: stats["matched_to_reservation"] += 1 elif matched_customer: stats["matched_to_customer"] += 1 - elif matched_hashed_customer: - stats["matched_to_hashed_customer"] += 1 else: stats["unmatched"] += 1 diff --git a/src/alpine_bits_python/schemas.py b/src/alpine_bits_python/schemas.py index 638c546..1ce1f05 100644 --- a/src/alpine_bits_python/schemas.py +++ b/src/alpine_bits_python/schemas.py @@ -562,7 +562,6 @@ class ConversionData(BaseModel): # Foreign key references (nullable - matched after creation) reservation_id: int | None = Field(None, gt=0) customer_id: int | None = Field(None, gt=0) - hashed_customer_id: int | None = Field(None, gt=0) # Required reservation metadata from PMS hotel_id: str = Field(..., min_length=1, max_length=50) @@ -591,7 +590,7 @@ class ConversionData(BaseModel): @field_validator( "pms_reservation_id", "guest_id", "reservation_id", "customer_id", - "hashed_customer_id", mode="before" + mode="before" ) @classmethod def convert_int_fields(cls, v: Any) -> int | None: