Skip to content
Draft
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
7 changes: 0 additions & 7 deletions speid/helpers/callback_helper.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import base64
import os
from typing import Optional

from celery import Celery

Expand All @@ -20,18 +19,12 @@ def auth_header(username: str, password: str) -> dict:
def set_status_transaction(
speid_id: str,
state: str,
curp: Optional[str] = None,
rfc: Optional[str] = None,
nombre_beneficiario: Optional[str] = None,
) -> None:
queue.send_task(
SEND_STATUS_TRANSACTION_TASK,
kwargs=dict(
speid_id=speid_id,
state=state,
rfc=rfc,
curp=curp,
nombre_beneficiario=nombre_beneficiario,
),
)

Expand Down
6 changes: 2 additions & 4 deletions speid/models/transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from speid.processors import stpmex_client
from speid.types import Estado, EventType, TipoTransaccion

from ..helpers.callback_helper import set_status_transaction
from .account import Account
from .base import BaseModel
from .events import Event
Expand Down Expand Up @@ -131,12 +132,9 @@ class Transaction(Document, BaseModel):
}

def set_state(self, state: Estado):
from ..tasks.transactions import send_transaction_status

send_transaction_status.apply_async((str(self.id), state))
self.estado = state

self.events.append(Event(type=EventType.completed))
set_status_transaction(self.speid_id, state)

def confirm_callback_transaction(self):
response = ''
Expand Down
78 changes: 1 addition & 77 deletions speid/tasks/transactions.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,11 @@
from typing import List

import cep
import pytz
from cep.exc import CepError, MaxRequestError
from mongoengine import DoesNotExist
from stpmex.business_days import current_cdmx_time_zone

from speid.helpers import callback_helper
from speid.models import Account, Event, Transaction
from speid.models import Event, Transaction
from speid.tasks import celery
from speid.types import Estado, EventType

CURP_LENGTH = 18
RFC_LENGTH = 13
STP_BANK_CODE = 90646
GET_RFC_TASK_MAX_RETRIES = 7 # reintentos
GET_RFC_TASK_DELAY = 4 # Segundos


@celery.task
def retry_incoming_transactions(speid_ids: List[str]) -> None:
Expand Down Expand Up @@ -56,68 +45,3 @@ def process_outgoing_transactions(self, transactions: list):
Event(type=event_type, metadata=str('Reversed by recon task'))
)
transaction.save()


@celery.task(bind=True, max_retries=GET_RFC_TASK_MAX_RETRIES)
def send_transaction_status(self, transaction_id: str, state: str) -> None:
try:
transaction = Transaction.objects.get(id=transaction_id)
except DoesNotExist:
return

account = Account.objects.get(cuenta=transaction.cuenta_ordenante)
rfc = None
curp = None
nombre_beneficiario = None

if account.is_restricted:
cdmx_tz = current_cdmx_time_zone(transaction.created_at)

created_at_utc = transaction.created_at.replace(tzinfo=pytz.utc)
transaction_local_time = created_at_utc.astimezone(
pytz.timezone(cdmx_tz)
)

rfc_curp = None

try:
transferencia = cep.Transferencia.validar(
fecha=transaction_local_time.date(),
clave_rastreo=transaction.clave_rastreo,
emisor=str(STP_BANK_CODE),
receptor=transaction.institucion_beneficiaria,
cuenta=transaction.cuenta_beneficiario,
monto=transaction.monto / 100,
)
assert transferencia is not None
except MaxRequestError:
rfc_curp = 'max retries'
except CepError:
self.retry(countdown=GET_RFC_TASK_DELAY)
except AssertionError:
rfc_curp = None
else:
rfc_curp = str(transferencia.beneficiario.rfc)
nombre_beneficiario = transferencia.beneficiario.nombre

if len(rfc_curp) == CURP_LENGTH:
curp = rfc_curp
transaction.rfc_curp_beneficiario = rfc_curp
transaction.save()
elif len(rfc_curp) == RFC_LENGTH:
rfc = rfc_curp
transaction.rfc_curp_beneficiario = rfc_curp
transaction.save()
else:
rfc_curp = None
curp = None
rfc = None

# Si no se pudo obtener el RFC o CURP de ninguna fuente se reintenta
# en 5 segundos
if not rfc_curp and self.request.retries < GET_RFC_TASK_MAX_RETRIES:
self.retry(countdown=GET_RFC_TASK_DELAY)

callback_helper.set_status_transaction(
transaction.speid_id, state, curp, rfc, nombre_beneficiario
)
225 changes: 0 additions & 225 deletions tests/tasks/test_transactions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,14 @@
from unittest.mock import patch

import pytest
from celery.exceptions import Retry
from cep.exc import CepError, MaxRequestError

from speid.models import Transaction
from speid.tasks.transactions import (
GET_RFC_TASK_MAX_RETRIES,
process_outgoing_transactions,
retry_incoming_transactions,
send_transaction_status,
)
from speid.types import Estado, EventType
from speid.validations import SpeidTransaction
from tests.conftest import SEND_STATUS_TRANSACTION_TASK


@pytest.fixture
Expand Down Expand Up @@ -160,223 +155,3 @@ def test_outgoing_transaction_retry_core(
) as method_mock:
retry_incoming_transactions(speid_ids=[speid_id])
method_mock.assert_called_once()


@patch('celery.Celery.send_task')
@pytest.mark.vcr
def test_send_transaction_restricted_accounts_retry_task(
mock_send_task, outcome_transaction, moral_account, orden_pago
):

outcome_transaction.institucion_beneficiaria = '40012'
outcome_transaction.clave_rastreo = 'CUENCA954881386502'
outcome_transaction.cuenta_ordenante = '646180157016683211'
outcome_transaction.cuenta_beneficiario = '012180015839965374'
outcome_transaction.created_at = dt.datetime(2022, 4, 12, 20, 31)
outcome_transaction.save()

moral_account.cuenta = '646180157016683211'
moral_account.is_restricted = True
moral_account.save()

with pytest.raises(Retry):
send_transaction_status(outcome_transaction.id, Estado.rejected)

mock_send_task.assert_not_called()


@patch('celery.Celery.send_task')
@pytest.mark.vcr
def test_send_transaction_restricted_accounts_info_from_cep(
mock_send_task, outcome_transaction, moral_account, orden_pago
):

outcome_transaction.institucion_beneficiaria = '40012'
outcome_transaction.clave_rastreo = orden_pago['ordenPago']['claveRastreo']
outcome_transaction.cuenta_beneficiario = '012180015025335996'
outcome_transaction.created_at = dt.datetime(2022, 4, 6, 23, 19)
outcome_transaction.save()

moral_account.is_restricted = True
moral_account.save()

send_transaction_status(outcome_transaction.id, Estado.succeeded)

mock_send_task.assert_called_with(
SEND_STATUS_TRANSACTION_TASK,
kwargs=dict(
speid_id=outcome_transaction.speid_id,
state=Estado.succeeded.value,
rfc='MAVM901122UK9',
curp=None,
nombre_beneficiario='MANUEL ALEJANDRO MARTINEZ VIQUEZ',
),
)


@patch('celery.Celery.send_task')
@pytest.mark.vcr
def test_send_transaction_restricted_accounts_curp_from_cep(
mock_send_task, outcome_transaction, moral_account, orden_pago
):

outcome_transaction.institucion_beneficiaria = '40012'
outcome_transaction.clave_rastreo = 'CUENCA22026847429'
outcome_transaction.cuenta_beneficiario = '012180015328558878'
outcome_transaction.created_at = dt.datetime(2022, 4, 20, 2, 33)
outcome_transaction.monto = 1
outcome_transaction.save()

moral_account.is_restricted = True
moral_account.save()

send_transaction_status(outcome_transaction.id, Estado.succeeded)

mock_send_task.assert_called_with(
SEND_STATUS_TRANSACTION_TASK,
kwargs=dict(
speid_id=outcome_transaction.speid_id,
state=Estado.succeeded.value,
rfc=None,
curp='AOVM910106HCSPRL05',
nombre_beneficiario='MIGUEL ACOSTA VENTURA',
),
)


@patch('celery.Celery.send_task')
def test_send_transaction_restricted_accounts_retry_task_on_cep_error(
mock_send_task, outcome_transaction, moral_account
):
moral_account.is_restricted = True
moral_account.save()

with patch('cep.Transferencia.validar', side_effect=CepError):
with pytest.raises(Retry):
send_transaction_status(outcome_transaction.id, Estado.rejected)

mock_send_task.assert_not_called()


@patch('celery.Celery.send_task')
def test_send_transaction_status_does_not_retry_task_on_max_retries(
mock_send_task, outcome_transaction, moral_account
):
moral_account.is_restricted = True
moral_account.save()

with patch('cep.Transferencia.validar', side_effect=MaxRequestError):
send_transaction_status(outcome_transaction.id, Estado.succeeded)

mock_send_task.assert_called_with(
SEND_STATUS_TRANSACTION_TASK,
kwargs=dict(
speid_id=outcome_transaction.speid_id,
state=Estado.succeeded.value,
rfc=None,
curp=None,
nombre_beneficiario=None,
),
)


@patch(
'speid.tasks.transactions.send_transaction_status.request.retries',
GET_RFC_TASK_MAX_RETRIES,
)
@patch('celery.Celery.send_task')
@pytest.mark.vcr
def test_send_transaction_restricted_accounts_send_status_on_last_retry_task(
mock_send_task, outcome_transaction, moral_account
):

outcome_transaction.institucion_beneficiaria = '40012'
outcome_transaction.clave_rastreo = 'CUENCA954881386502'
outcome_transaction.cuenta_ordenante = '646180157016683211'
outcome_transaction.cuenta_beneficiario = '012180015839965374'
outcome_transaction.created_at = dt.datetime(2022, 4, 12, 20, 31)
outcome_transaction.save()

moral_account.cuenta = '646180157016683211'
moral_account.is_restricted = True
moral_account.save()

send_transaction_status(outcome_transaction.id, Estado.succeeded)

mock_send_task.assert_called_with(
SEND_STATUS_TRANSACTION_TASK,
kwargs=dict(
speid_id=outcome_transaction.speid_id,
state=Estado.succeeded.value,
rfc=None,
curp=None,
nombre_beneficiario='MANUEL AVALOS TOVAR',
),
)


@patch('celery.Celery.send_task')
@pytest.mark.vcr
def test_send_transaction_restricted_accounts_cep_not_found(
mock_send_task, outcome_transaction, moral_account
):

outcome_transaction.institucion_beneficiaria = '40012'
outcome_transaction.clave_rastreo = 'CUENCA954881386503'
outcome_transaction.cuenta_ordenante = '646180157016683211'
outcome_transaction.cuenta_beneficiario = '012180015839965344'
outcome_transaction.created_at = dt.datetime(2022, 4, 12, 20, 31)
outcome_transaction.save()

moral_account.cuenta = '646180157016683211'
moral_account.is_restricted = True
moral_account.save()

with pytest.raises(Retry):
send_transaction_status(outcome_transaction.id, Estado.succeeded)

mock_send_task.assert_not_called()


@patch('celery.Celery.send_task')
def test_send_transaction_restricted_transaction_does_not_exist(
mock_send_task,
):
send_transaction_status('624f53b45809fa4d49258a57', Estado.failed)
mock_send_task.assert_not_called()


@patch('celery.Celery.send_task')
def test_send_transaction_not_restricted_accounts(
mock_send_task, outcome_transaction, moral_account
):
send_transaction_status(outcome_transaction.id, Estado.succeeded)

outcome_transaction.reload()
mock_send_task.assert_called_with(
SEND_STATUS_TRANSACTION_TASK,
kwargs=dict(
speid_id=outcome_transaction.speid_id,
state=Estado.succeeded.value,
rfc=None,
curp=None,
nombre_beneficiario=None,
),
)


@patch('celery.Celery.send_task')
def test_send_transaction_not_restricted_accounts_persona_fisica(
mock_send_task, outcome_transaction, physical_account
):
send_transaction_status(outcome_transaction.id, Estado.succeeded)
mock_send_task.assert_called_with(
SEND_STATUS_TRANSACTION_TASK,
kwargs=dict(
speid_id=outcome_transaction.speid_id,
state=Estado.succeeded.value,
rfc=None,
curp=None,
nombre_beneficiario=None,
),
)