Skip to content

Commit

Permalink
Merge branch 'main' into naziz1/SONIC-740/get-customer-id-if-not-passed
Browse files Browse the repository at this point in the history
  • Loading branch information
Muhammad Noyan Aziz authored and Muhammad Noyan Aziz committed Nov 18, 2024
2 parents 39ab1f4 + d91676d commit 864a45f
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 19 deletions.
20 changes: 20 additions & 0 deletions commerce_coordinator/apps/commercetools/catalog_info/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from commercetools.platform.models import Attribute as CTAttribute
from commercetools.platform.models import CentPrecisionMoney as CTCentPrecisionMoney
from commercetools.platform.models import HighPrecisionMoney as CTHighPrecisionMoney
from commercetools.platform.models import LineItem as CTLineItem
from commercetools.platform.models import LocalizedString as CTLocalizedString
from commercetools.platform.models import MoneyType as CTMoneyType
from commercetools.platform.models import Price as CTPrice
Expand Down Expand Up @@ -32,6 +33,25 @@ def ls(string_dict: LSLike) -> CTLocalizedString:
return string_dict


def get_line_item_attribute(in_line_item, in_attribute_name): # pragma no cover
"""Utility to get line item's attribute's value."""
attribute_value = None
for attribute in in_line_item.variant.attributes:
if attribute.name == in_attribute_name and hasattr(attribute, 'value'):
if isinstance(attribute.value, dict):
attribute_value = attribute.value.get('label', None)
elif isinstance(attribute.value, str):
attribute_value = attribute.value
break

return attribute_value


def get_course_mode_from_ct_order(line_item: CTLineItem) -> str:
course_mode = get_line_item_attribute(line_item, 'mode')
return course_mode or 'verified'


def ls_eq(a: LSLike, b: LSLike) -> bool:
if a is None or b is None:
return False
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import json
from collections import defaultdict

from commerce_coordinator.apps.commercetools.management.commands._ct_api_client_command import (
CommercetoolsAPIClientCommand
)

""" Those intending to use this script, please take into consideration that currently
the script doesn't cater for the returns (refunds) and hence
it will show the duplicates even if the user has returned the product.
"""


class Command(CommercetoolsAPIClientCommand):
help = "Find duplicate purchases in commercetools"

def handle(self, *args, **options):

order_state = "Complete"
# TODO: Change for your use case
last_modified_at = "2024-10-23T00:00:00"
limit = 500
offset = 0

orders_result = self.ct_api_client.base_client.orders.query(
where=[
f'orderState="{order_state}"',
f'lastModifiedAt>"{last_modified_at}"',
],
sort=["completedAt desc", "lastModifiedAt desc"],
limit=limit,
offset=offset,
)

orders = orders_result.results

while orders_result.offset + orders_result.limit < orders_result.total:
orders_result = self.ct_api_client.base_client.orders.query(
where=[
f'orderState="{order_state}"',
'lastModifiedAt>"2024-10-23T00:00:00"',
],
sort=["completedAt desc", "lastModifiedAt desc"],
limit=orders_result.limit,
offset=orders_result.offset + orders_result.limit,
)
orders.extend(orders_result.results)

user_orders = defaultdict(lambda: defaultdict(list))

for order in orders:
user_email = order.customer_email
line_items = order.line_items

for item in line_items:
sku = item.variant.sku
user_orders[user_email][sku].append(order.id)

duplicate_purchases = {}
for user_id, sku_orders in user_orders.items():
duplicates = {
sku: order_ids
for sku, order_ids in sku_orders.items()
if len(order_ids) > 1
}
if duplicates:
duplicate_purchases[user_id] = duplicates

# dump to a json file
with open("duplicate_purchases.json", "w") as f:
json.dump(duplicate_purchases, f)
25 changes: 8 additions & 17 deletions commerce_coordinator/apps/commercetools/sub_messages/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
get_edx_product_course_run_key,
is_edx_lms_order
)
from commerce_coordinator.apps.commercetools.catalog_info.utils import (
get_course_mode_from_ct_order,
get_line_item_attribute
)
from commerce_coordinator.apps.commercetools.clients import CommercetoolsAPIClient
from commerce_coordinator.apps.commercetools.constants import EMAIL_NOTIFICATION_CACHE_TTL_SECS
from commerce_coordinator.apps.commercetools.filters import OrderRefundRequested
Expand Down Expand Up @@ -83,7 +87,6 @@ def fulfill_order_placed_message_signal_task(
'order_id': order.id,
'provider_id': None,
'edx_lms_user_id': lms_user_id,
'course_mode': 'verified',
'date_placed': order.last_modified_at.strftime(ISO_8601_FORMAT),
'source_system': source_system,
}
Expand Down Expand Up @@ -112,6 +115,7 @@ def fulfill_order_placed_message_signal_task(
**default_params,
'course_id': get_edx_product_course_run_key(item), # likely not correct
'line_item_id': item.id,
'course_mode': get_course_mode_from_ct_order(item),
'item_quantity': item.quantity,
'line_item_state_id': line_item_state_id,
'message_id': message_id
Expand Down Expand Up @@ -202,19 +206,6 @@ def fulfill_order_returned_signal_task(
):
"""Celery task for an order return (and refunded) message."""

def _get_line_item_attribute(in_line_item, in_attribute_name): # pragma no cover
"""Utility to get line item's attribute's value."""
attribute_value = None
for attribute in in_line_item.variant.attributes:
if attribute.name == in_attribute_name and hasattr(attribute, 'value'):
if isinstance(attribute.value, dict):
attribute_value = attribute.value.get('label', None)
elif isinstance(attribute.value, str):
attribute_value = attribute.value
break

return attribute_value

def _cents_to_dollars(in_amount):
return in_amount.cent_amount / pow(
10, in_amount.fraction_digits
Expand Down Expand Up @@ -299,10 +290,10 @@ def _prepare_segment_event_properties(in_order, return_line_item_return_id):
'name': line_item.name['en-US'],
'price': _cents_to_dollars(line_item.price.value),
'quantity': line_item.quantity,
'category': _get_line_item_attribute(line_item, 'primary-subject-area'),
'category': get_line_item_attribute(line_item, 'primary-subject-area'),
'image_url': line_item.variant.images[0].url if line_item.variant.images else None,
'brand': _get_line_item_attribute(line_item, 'brand-text'),
'url': _get_line_item_attribute(line_item, 'url-course'),
'brand': get_line_item_attribute(line_item, 'brand-text'),
'url': get_line_item_attribute(line_item, 'url-course'),
'lob': 'edX', # TODO: Decision was made to hardcode this value for phase 1.
'product_type': line_item.product_type.obj.name
if hasattr(line_item.product_type.obj, 'name') else None
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,28 @@
""" Commcercetools API Utilities """
from typing import Union
from unittest import TestCase

import ddt
import pytest
from commercetools.platform.models import CentPrecisionMoney, HighPrecisionMoney, Price
from commercetools.platform.models import CentPrecisionMoney, HighPrecisionMoney
from commercetools.platform.models import Order as CTOrder
from commercetools.platform.models import Price
from currencies import Currency

from commerce_coordinator.apps.commercetools.catalog_info.constants import Languages
from commerce_coordinator.apps.commercetools.catalog_info.edx_utils import get_edx_items
from commerce_coordinator.apps.commercetools.catalog_info.utils import (
attribute_dict,
get_course_mode_from_ct_order,
ls,
ls_eq,
price_to_string,
typed_money_add,
typed_money_to_string,
un_ls
)
from commerce_coordinator.apps.core.tests.utils import name_test
from commerce_coordinator.apps.commercetools.tests.conftest import gen_order
from commerce_coordinator.apps.core.tests.utils import name_test, uuid4_str

# <Country>_<Currency>
JAPAN_YEN = "JPY" # 0 fractional digits
Expand All @@ -29,12 +35,25 @@
class LocalizedStringsTests(TestCase):
""" Localized String Utility Tests"""

order: Union[CTOrder, None]

def setUp(self):
self.order = gen_order(uuid4_str())
super().setUp()

def tearDown(self):
self.order = None
super().tearDown()

# ls()
def test_single_unknown_key_ls_creation(self):
string = "test"
result = ls({'ZZ': string})
self.assertEqual(result, {'ZZ': string, Languages.ENGLISH: string, Languages.US_ENGLISH: string})

def test_get_course_mode_from_ct_order(self):
self.assertEqual(get_course_mode_from_ct_order(get_edx_items(self.order)[0]), 'professional')

def test_single_key_ls_creation(self):
string = "test-2"
result = ls({Languages.ENGLISH: string})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@
"name": "brand-text",
"value": "MichiganX"
},
{
"name": "mode",
"value": "professional"
},
{
"name": "date-created",
"value": "2019-08-21T00:02:00.000Z"
Expand Down

0 comments on commit 864a45f

Please sign in to comment.