Skip to content

Commit

Permalink
Merge branch 'main' into huniafatima/deprecate-edx-sphinx-theme
Browse files Browse the repository at this point in the history
  • Loading branch information
huniafatima-arbi authored Oct 2, 2024
2 parents b8d223c + dadccbd commit da87798
Show file tree
Hide file tree
Showing 10 changed files with 201 additions and 271 deletions.
41 changes: 37 additions & 4 deletions commerce_coordinator/apps/commercetools/clients.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"""

import datetime
import decimal
import logging
from typing import Generic, List, Optional, Tuple, TypeVar, Union

Expand All @@ -19,11 +20,12 @@
from commercetools.platform.models import Order as CTOrder
from commercetools.platform.models import (
OrderAddReturnInfoAction,
OrderSetReturnItemCustomTypeAction,
OrderSetReturnPaymentStateAction,
OrderTransitionLineItemStateAction
)
from commercetools.platform.models import Payment as CTPayment
from commercetools.platform.models import PaymentAddTransactionAction
from commercetools.platform.models import PaymentAddTransactionAction, PaymentSetTransactionCustomTypeAction
from commercetools.platform.models import ProductVariant as CTProductVariant
from commercetools.platform.models import (
ReturnItemDraft,
Expand All @@ -43,6 +45,7 @@
from commerce_coordinator.apps.commercetools.catalog_info.constants import DEFAULT_ORDER_EXPANSION, EdXFieldNames
from commerce_coordinator.apps.commercetools.catalog_info.foundational_types import TwoUCustomTypes
from commerce_coordinator.apps.commercetools.utils import (
find_refund_transaction,
handle_commercetools_error,
translate_stripe_refund_status_to_transaction_status
)
Expand Down Expand Up @@ -372,7 +375,9 @@ def create_return_for_order(self, order_id: str, order_version: int, order_line_

def update_return_payment_state_after_successful_refund(self, order_id: str,
order_version: int,
return_line_item_return_id: str) -> Union[CTOrder, None]:
return_line_item_return_id: str,
payment_intent_id: str,
amount_in_cents: decimal) -> Union[CTOrder, None]:
"""
Update paymentState on the LineItemReturnItem attached to the order.
Updated by the Order ID (UUID)
Expand All @@ -388,17 +393,45 @@ def update_return_payment_state_after_successful_refund(self, order_id: str,
try:
logger.info(f"[CommercetoolsAPIClient] - Updating payment state for return "
f"with id {return_line_item_return_id} to '{ReturnPaymentState.REFUNDED}'.")

return_payment_state_action = OrderSetReturnPaymentStateAction(
return_item_id=return_line_item_return_id,
payment_state=ReturnPaymentState.REFUNDED
)
if not payment_intent_id:
payment_intent_id = ''
logger.info(f'Creating return for order - payment_intent_id: {payment_intent_id}')
payment = self.get_payment_by_key(payment_intent_id)
logger.info(f"Payment found: {payment}")
transaction_id = find_refund_transaction(payment, amount_in_cents)
update_transaction_id_action = OrderSetReturnItemCustomTypeAction(
return_item_id=return_line_item_return_id,
type=CTTypeResourceIdentifier(
key='returnItemCustomType',
),
fields=CTFieldContainer({
'transactionId': transaction_id
})
)
return_transaction_return_item_action = PaymentSetTransactionCustomTypeAction(
transaction_id=transaction_id,
type=CTTypeResourceIdentifier(key='transactionCustomType'),
fields=CTFieldContainer({
'returnItemId': return_line_item_return_id
})
)
logger.info(f"Update return payment state after successful refund - payment_intent_id: {payment_intent_id}")

updated_order = self.base_client.orders.update_by_id(
id=order_id,
version=order_version,
actions=[return_payment_state_action]
actions=[return_payment_state_action, update_transaction_id_action]
)
self.base_client.payments.update_by_id(
id=payment.id,
version=payment.version,
actions=[return_transaction_return_item_action]
)
logger.info("Updated transaction with return item id")
return updated_order
except CommercetoolsError as err:
handle_commercetools_error(err, f"Unable to update ReturnPaymentState of order {order_id}")
Expand Down
5 changes: 4 additions & 1 deletion commerce_coordinator/apps/commercetools/pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ def run_filter(
try:
ct_api_client = CommercetoolsAPIClient()
order = ct_api_client.get_order_by_id(order_id=order_id)

if not is_commercetools_line_item_already_refunded(order, order_line_item_id):
returned_order = ct_api_client.create_return_for_order(
order_id=order.id,
Expand Down Expand Up @@ -273,7 +274,9 @@ def run_filter(
updated_order = ct_api_client.update_return_payment_state_after_successful_refund(
order_id=order.id,
order_version=order.version,
return_line_item_return_id=return_line_item_return_id
return_line_item_return_id=return_line_item_return_id,
payment_intent_id=kwargs['payment_intent_id'],
amount_in_cents=kwargs['amount_in_cents']
)

return {
Expand Down
12 changes: 5 additions & 7 deletions commerce_coordinator/apps/commercetools/sub_messages/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@
from commerce_coordinator.apps.commercetools.utils import (
extract_ct_order_information_for_braze_canvas,
extract_ct_product_information_for_braze_canvas,
send_order_confirmation_email,
send_refund_notification
send_order_confirmation_email
)
from commerce_coordinator.apps.core.constants import ISO_8601_FORMAT
from commerce_coordinator.apps.core.memcache import safe_key
Expand Down Expand Up @@ -280,9 +279,8 @@ def _prepare_segment_event_properties(in_order, return_line_item_return_id):

if 'refund_response' in result and result['refund_response']:
if result['refund_response'] == 'charge_already_refunded':
logger.info(f'[CT-{tag}] payment intent {payment_intent_id} already has refund transaction, '
f'sending Zendesk email, message id: {message_id}')
send_refund_notification(customer, order_id)
logger.info(f'[CT-{tag}] payment intent {payment_intent_id} already has refunded transaction, '
f'sending Slack notification, message id: {message_id}')
else:
logger.debug(f'[CT-{tag}] payment intent {payment_intent_id} refunded. message id: {message_id}')
segment_event_properties = _prepare_segment_event_properties(order, return_line_item_return_id)
Expand Down Expand Up @@ -319,8 +317,8 @@ def _prepare_segment_event_properties(in_order, return_line_item_return_id):
properties=segment_event_properties
)
else: # pragma no cover
logger.info(f'[CT-{tag}] payment intent {payment_intent_id} not refunded. message id: {message_id}')
return send_refund_notification(customer, order_id)
logger.info(f'[CT-{tag}] payment intent {payment_intent_id} not refunded, '
f'sending Slack notification, message id: {message_id}')

logger.info(f'[CT-{tag}] Finished return for order: {order_id}, line item: {return_line_item_return_id}, '
f'message id: {message_id}')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -317,14 +317,12 @@ def test_correct_arguments_passed_valid_stripe_refund(
mock_values.customer_mock.assert_called_once_with(mock_values.customer_id)
_stripe_api_mock.return_value.refund_payment_intent.assert_called_once()

@patch('commerce_coordinator.apps.commercetools.sub_messages.tasks.send_refund_notification')
@patch('commerce_coordinator.apps.commercetools.sub_messages.tasks.get_edx_payment_intent_id')
@patch('commerce_coordinator.apps.commercetools.sub_messages.tasks.OrderRefundRequested.run_filter')
def test_refund_already_charged(
self,
_return_filter_mock: MagicMock,
_mock_payment_intent: MagicMock,
_mock_zendesk: MagicMock
):
"""
Check calling uut with mock_parameters yields call to client with
Expand All @@ -336,4 +334,3 @@ def test_refund_already_charged(
_mock_payment_intent.return_value = 'mock_payment_intent_id'

self.get_uut()(*self.unpack_for_uut(self.mock.example_payload))
_mock_zendesk.assert_called_once()
142 changes: 108 additions & 34 deletions commerce_coordinator/apps/commercetools/tests/test_clients.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,16 @@
Customer,
CustomerDraft,
CustomerPagedQueryResponse,
MoneyType,
Order,
OrderPagedQueryResponse,
ReturnInfo,
ReturnPaymentState,
ReturnShipmentState,
TransactionState,
TransactionType,
Type,
TypedMoney,
TypeDraft
)
from django.test import TestCase
Expand All @@ -23,17 +26,20 @@

from commerce_coordinator.apps.commercetools.catalog_info.constants import EdXFieldNames, TwoUKeys
from commerce_coordinator.apps.commercetools.catalog_info.foundational_types import TwoUCustomTypes
from commerce_coordinator.apps.commercetools.clients import PaginatedResult
from commerce_coordinator.apps.commercetools.clients import CommercetoolsAPIClient, PaginatedResult
from commerce_coordinator.apps.commercetools.tests.conftest import (
APITestingSet,
MonkeyPatch,
gen_example_customer,
gen_line_item_state,
gen_order,
gen_order_history,
gen_payment,
gen_payment_with_multiple_transactions,
gen_retired_customer,
gen_return_item
)
from commerce_coordinator.apps.commercetools.tests.sub_messages.test_tasks import CommercetoolsAPIClientMock
from commerce_coordinator.apps.core.constants import ORDER_HISTORY_PER_SYSTEM_REQ_LIMIT
from commerce_coordinator.apps.core.tests.utils import uuid4_str

Expand Down Expand Up @@ -483,55 +489,46 @@ def test_successful_order_return_payment_state_update(self):

# Mocked expected order recieved after CT SDK call to update the order
mock_response_order = gen_order("mock_order_id")
mock_payment = gen_payment_with_multiple_transactions(TransactionType.CHARGE, 4900, TransactionType.REFUND,
TypedMoney(cent_amount=4900,
currency_code='USD',
type=MoneyType.CENT_PRECISION,
fraction_digits=2))
mock_response_order.version = "3"
mock_response_return_item = gen_return_item("mock_return_item_id", ReturnPaymentState.REFUNDED)
mock_response_return_info = ReturnInfo(items=[mock_response_return_item])
mock_response_order.return_info.append(mock_response_return_info)

with requests_mock.Mocker(real_http=True, case_sensitive=False) as mocker:
mocker.post(
f"{base_url}orders/{mock_response_order.id}",
f"{base_url}orders/mock_order_id",
json=mock_response_order.serialize(),
status_code=200
)
mocker.post(
f"{base_url}payments/{mock_payment.id}",
json=mock_payment.serialize(),
status_code=200
)
mocker.get(
f"{base_url}payments/key={mock_payment.id}",
json=mock_payment.serialize(),
status_code=200
)
mocker.get(
f"{base_url}orders/mock_order_id",
json=mock_response_order.serialize(),
status_code=200
)

result = self.client_set.client.update_return_payment_state_after_successful_refund(
mock_order.id,
mock_order.version,
mock_response_return_item.line_item_id
mock_response_return_item.line_item_id,
mock_payment.id,
10000
)

self.assertEqual(result.return_info[1].items[0].payment_state, ReturnPaymentState.REFUNDED)

def test_update_return_payment_state_exception(self):
base_url = self.client_set.get_base_url_from_client()
mock_error_response: CommercetoolsError = {
"message": "Could not update ReturnPaymentState",
"errors": [
{
"code": "ConcurrentModification",
"message": "Object [mock_order_id] has a "
"different version than expected. Expected: 3 - Actual: 2."
},
],
"response": {},
"correlation_id": "123456"
}

with requests_mock.Mocker(real_http=True, case_sensitive=False) as mocker:
mocker.post(
f"{base_url}orders/mock_order_id",
json=mock_error_response,
status_code=409
)

with self.assertRaises(OpenEdxFilterException):
self.client_set.client.update_return_payment_state_after_successful_refund(
order_id="mock_order_id",
order_version="2",
return_line_item_return_id="mock_return_item_id"
)

def test_create_refund_transaction(self):
base_url = self.client_set.get_base_url_from_client()

Expand Down Expand Up @@ -823,3 +820,80 @@ def test_data_class_doesnt_have_more(self):

self.assertEqual(paginated.has_more(), False)
self.assertEqual(paginated.next_offset(), 10)


class ClientUpdateReturnTests(TestCase):
"""Tests for the update_return_payment_state_after_successful_refund method"""
client_set: APITestingSet

def setUp(self):
super().setUp()
self.mock = CommercetoolsAPIClientMock()
self.client_set = APITestingSet.new_instance()

MonkeyPatch.monkey(
CommercetoolsAPIClient,
{
'__init__': lambda _: None,
'get_order_by_id': self.mock.get_order_by_id,
# 'get_customer_by_id': self.mock.get_customer_by_id,
'get_payment_by_key': self.mock.get_payment_by_key,
'create_return_for_order': self.mock.create_return_for_order,
'create_return_payment_transaction': self.mock.create_return_payment_transaction
}
)

def tearDown(self):
self.mock.payment_mock.side_effect = None
MonkeyPatch.unmonkey(CommercetoolsAPIClient)
super().tearDown()

def test_update_return_payment_state_exception(self):
mock_error_response: CommercetoolsError = CommercetoolsError(
"Could not update ReturnPaymentState", [
{
"code": "ConcurrentModification",
"detailedErrorMessage": "Object [mock_order_id] has a "
"different version than expected. Expected: 3 - Actual: 2."
},
], {}, "123456"
)

def _throw(_payment_id):
raise mock_error_response

self.mock.payment_mock.side_effect = _throw

with self.assertRaises(OpenEdxFilterException):
self.client_set.client.update_return_payment_state_after_successful_refund(
order_id="mock_order_id",
order_version="2",
return_line_item_return_id="mock_return_item_id",
payment_intent_id="1",
amount_in_cents=10000
)

def test_update_return_payment_state_no_payment(self):
mock_error_response: CommercetoolsError = CommercetoolsError(
"Could not update ReturnPaymentState", [
{
"code": "ConcurrentModification",
"detailedErrorMessage": "Object [mock_order_id] has a "
"different version than expected. Expected: 3 - Actual: 2."
},
], {}, "123456"
)

def _throw(_payment_id):
raise mock_error_response

self.mock.payment_mock.side_effect = _throw

with self.assertRaises(OpenEdxFilterException):
self.client_set.client.update_return_payment_state_after_successful_refund(
order_id="mock_order_id",
order_version="2",
return_line_item_return_id="mock_return_item_id",
payment_intent_id=None,
amount_in_cents=10000
)
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,8 @@ def test_pipeline(self, mock_order_return_update):

pipe = UpdateCommercetoolsOrderReturnPaymentStatus("test_pipe", None)
mock_order_return_update.return_value = self.update_order_response
ret = pipe.run_filter(order_data=self.update_order_data, returned_order=self.update_order_data)
ret = pipe.run_filter(order_data=self.update_order_data, returned_order=self.update_order_data,
payment_intent_id="mock_payment_intent_id", amount_in_cents=10000)
result_data = ret['returned_order']
self.assertEqual(result_data, self.update_order_response)
self.assertEqual(result_data.return_info[1].items[0].payment_state, ReturnPaymentState.REFUNDED)
Expand Down
Loading

0 comments on commit da87798

Please sign in to comment.