Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions drf_lint_baseline.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,11 @@
"hubspot_sync/serializers.py:171:22:ORM001",
"hubspot_sync/serializers.py:183:25:ORM002",
"hubspot_sync/serializers.py:186:33:ORM002",
"hubspot_sync/serializers.py:193:21:ORM002",
"hubspot_sync/serializers.py:312:33:ORM001",
"hubspot_sync/serializers.py:323:36:ORM001",
"hubspot_sync/serializers.py:324:33:ORM001",
"hubspot_sync/serializers.py:335:36:ORM001",
"hubspot_sync/serializers.py:64:22:ORM002",
"hubspot_sync/serializers.py:76:31:ORM002",
"hubspot_sync/serializers.py:80:31:ORM002",
Expand Down
31 changes: 26 additions & 5 deletions hubspot_sync/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -2191,14 +2191,35 @@ def _ensure_hubspot_contact_for_user(
def _sync_cart_add_deal_with_hubspot(
order: Order, contact_id: str, hubspot_client: HubspotApi
) -> SimplePublicObject:
"""Create cart-add deal and line-item objects and associate them in target account."""
"""Create or update cart-add deal and line-item objects and associate them in target account."""
deal_input = _build_target_deal_message(order, hubspot_client)

wait_for_hubspot_rate_limit()
deal = hubspot_client.crm.objects.basic_api.create(
object_type=HubspotObjectType.DEALS.value,
simple_public_object_input_for_create=deal_input,
# Extract unique_app_id from deal input to check for existing deals
unique_app_id = deal_input.properties.get("unique_app_id")

# Use the same dual lookup logic as checkout flow to prevent duplicates
existing_deal_id = _find_target_deal_id_by_dealname(
hubspot_client, deal_input.properties.get("dealname")
)
if not existing_deal_id and unique_app_id:
existing_deal_id = _find_target_deal_id_by_unique_app_id(
hubspot_client, unique_app_id
)

wait_for_hubspot_rate_limit()
if existing_deal_id:
# Update existing deal
deal = hubspot_client.crm.objects.basic_api.update(
object_type=HubspotObjectType.DEALS.value,
object_id=existing_deal_id,
simple_public_object_input=deal_input,
)
else:
# Create new deal
deal = hubspot_client.crm.objects.basic_api.create(
object_type=HubspotObjectType.DEALS.value,
simple_public_object_input_for_create=deal_input,
)

wait_for_hubspot_rate_limit()
hubspot_client.crm.associations.v4.basic_api.create_default(
Expand Down
14 changes: 13 additions & 1 deletion hubspot_sync/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,19 @@ def _get_discount(self, instance):
return self._discount

def get_unique_app_id(self, instance):
"""Get the app_id for the object"""
"""Get the app_id for the object - based on user and purchasable object for consistency"""
# For consistency between cart-add and checkout, base the unique_app_id on
# the user and purchasable object rather than the order ID
first_line = instance.lines.first()
if first_line and first_line.purchased_object:
# Generate consistent ID based on user and purchasable object
user_identifier = instance.purchaser.global_id
object_type = first_line.purchased_content_type.model
object_id = first_line.purchased_object_id
consistent_id = f"{user_identifier}-{object_type}-{object_id}"
return format_app_id(consistent_id)

# Fallback to original behavior for orders without lines
return format_app_id(instance.id)

def get_dealname(self, instance):
Expand Down
30 changes: 28 additions & 2 deletions hubspot_sync/serializers_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,19 @@ def test_serialize_order(settings, hubspot_order, status):
"""Test that OrderToDealSerializer produces the correct serialized data"""
hubspot_order.state = status
serialized_data = OrderToDealSerializer(instance=hubspot_order).data

# Calculate expected unique_app_id based on new logic (user global_id + purchasable object)
first_line = hubspot_order.lines.first()
if first_line and first_line.purchased_object:
user_identifier = hubspot_order.purchaser.global_id
object_type = first_line.purchased_content_type.model
object_id = first_line.purchased_object_id
consistent_id = f"{user_identifier}-{object_type}-{object_id}"
expected_unique_app_id = format_app_id(consistent_id)
else:
# Fallback to original behavior for orders without lines
expected_unique_app_id = format_app_id(hubspot_order.id)

assert serialized_data == {
"dealname": f"MITXONLINE-ORDER-{hubspot_order.id}",
"dealstage": ORDER_STATUS_MAPPING[status],
Expand All @@ -132,7 +145,7 @@ def test_serialize_order(settings, hubspot_order, status):
"coupon_code": None,
"status": hubspot_order.state,
"pipeline": settings.HUBSPOT_PIPELINE_ID,
"unique_app_id": format_app_id(hubspot_order.id),
"unique_app_id": expected_unique_app_id,
}


Expand Down Expand Up @@ -174,6 +187,19 @@ def test_serialize_order_with_coupon( # noqa: PLR0913
)

serialized_data = OrderToDealSerializer(instance=hubspot_order).data

# Calculate expected unique_app_id based on new logic (user global_id + purchasable object)
first_line = hubspot_order.lines.first()
if first_line and first_line.purchased_object:
user_identifier = hubspot_order.purchaser.global_id
object_type = first_line.purchased_content_type.model
object_id = first_line.purchased_object_id
consistent_id = f"{user_identifier}-{object_type}-{object_id}"
expected_unique_app_id = format_app_id(consistent_id)
else:
# Fallback to original behavior for orders without lines
expected_unique_app_id = format_app_id(hubspot_order.id)

assert serialized_data == {
"dealname": f"MITXONLINE-ORDER-{hubspot_order.id}",
"dealstage": ORDER_STATUS_MAPPING[hubspot_order.state],
Expand All @@ -189,7 +215,7 @@ def test_serialize_order_with_coupon( # noqa: PLR0913
"discount_percent": percent_off,
"status": hubspot_order.state,
"pipeline": settings.HUBSPOT_PIPELINE_ID,
"unique_app_id": format_app_id(hubspot_order.id),
"unique_app_id": expected_unique_app_id,
}


Expand Down
Loading