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
2 changes: 2 additions & 0 deletions pythonpro/domain/checkout_domain.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from pythonpro.domain import user_domain
from pythonpro.domain.hotzapp_domain import verify_purchase, send_purchase_notification
from pythonpro.email_marketing import facade as email_marketing_facade
from pythonpro.memberkit import facade as memberkit_facade

__all__ = ['contact_info_listener', 'user_factory', 'payment_handler_task', 'payment_change_handler']

Expand Down Expand Up @@ -56,6 +57,7 @@ def payment_handler_task(payment_id):
else:
status = payment.status()
if status == django_pagarme_facade.PAID:
memberkit_facade.create_new_subscription(payment, 'Criação como resposta de pagamento no Pagarme')
user = payment.user
if payment.payment_method == django_pagarme_facade.BOLETO:
email_marketing_facade.remove_tags.delay(user.email, user.id, f'{slug}-boleto', f'{slug}-refused')
Expand Down
9 changes: 9 additions & 0 deletions pythonpro/domain/tests/test_checkout/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
import pytest
from django_pagarme import facade
from django_pagarme.models import PagarmeFormConfig, PagarmeItemConfig
from model_bakery import baker

# Workaround since module beginning with number can't be imported in regular way
from pythonpro.memberkit.models import SubscriptionType, PaymentItemConfigToSubscriptionType

migration_module = import_module('pythonpro.checkout.migrations.0001_payment_setup')
webdev_migration_module = import_module('pythonpro.checkout.migrations.0002_webdev_setup')
Expand Down Expand Up @@ -60,6 +62,13 @@ def execute_migration(db, pytestconfig):
webinar_migration_module.setup_payment_configs_function(PagarmeFormConfig, PagarmeItemConfig)
webserie_migration_module.setup_payment_configs_function(PagarmeFormConfig, PagarmeItemConfig)
thiago_avelino_migration_module.setup_payment_configs_function(PagarmeFormConfig, PagarmeItemConfig)
subscription_type = baker.make(SubscriptionType)
PaymentItemConfigToSubscriptionType.objects.bulk_create(
[
PaymentItemConfigToSubscriptionType(payment_item=config, subscription_type=subscription_type)
for config in PagarmeItemConfig.objects.all()
]
)


@pytest.fixture(autouse=True)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,7 @@ def remove_tags_mock(mocker):


def test_payment_tag_removed_after_payment(resp_logged_user, active_product_item, remove_tags_mock, logged_user,
tag_as_mock,
mocker):
tag_as_mock, mocker):
payment = django_pagarme_facade.find_payment_by_transaction(TRANSACTION_ID)
baker.make(django_pagarme_facade.PagarmeNotification, status=django_pagarme_facade.PAID, payment=payment)
promote_mock = mocker.patch('pythonpro.domain.checkout_domain._promote')
Expand Down
24 changes: 24 additions & 0 deletions pythonpro/domain/tests/test_checkout/test_payment_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from model_bakery import baker

from pythonpro.domain import checkout_domain
from pythonpro.memberkit.models import SubscriptionType, Subscription, PaymentItemConfigToSubscriptionType


@pytest.fixture
Expand Down Expand Up @@ -31,6 +32,9 @@ def test_pagarme_payment_paid_boleto(db, tag_as_mock, remove_tags_mock, promote_
payment = baker.make(PagarmePayment, payment_method=facade.BOLETO, user=logged_user)
baker.make(PagarmeNotification, status=facade.PAID, payment=payment)
config = baker.make(PagarmeItemConfig, payments=[payment])
subscription_type = baker.make(SubscriptionType)
PaymentItemConfigToSubscriptionType.objects.create(payment_item=config, subscription_type=subscription_type)

checkout_domain.payment_handler_task(payment.id)
assert tag_as_mock.called is False
remove_tags_mock.assert_called_once_with(
Expand All @@ -40,11 +44,31 @@ def test_pagarme_payment_paid_boleto(db, tag_as_mock, remove_tags_mock, promote_
send_purchase_notification_mock.asser_called_once_with(payment.id)


def test_subscription_creation(db, tag_as_mock, remove_tags_mock, promote_user_mock, logged_user,
send_purchase_notification_mock):
payment = baker.make(PagarmePayment, payment_method=facade.BOLETO, user=logged_user)
baker.make(PagarmeNotification, status=facade.PAID, payment=payment)
config = baker.make(PagarmeItemConfig, payments=[payment])
subscription_type = baker.make(SubscriptionType)
PaymentItemConfigToSubscriptionType.objects.create(payment_item=config, subscription_type=subscription_type)
checkout_domain.payment_handler_task(payment.id)
subscription = Subscription.objects.first()
assert subscription is not None
assert subscription.subscriber_id == payment.user_id
assert subscription.status == Subscription.Status.INACTIVE
assert subscription.payment_id == payment.id
assert list(subscription.subscription_types.all()) == [subscription_type]
assert subscription.responsible is None
assert subscription.observation == 'Criação como resposta de pagamento no Pagarme'


def test_pagarme_payment_paid_credit_card(db, tag_as_mock, remove_tags_mock, promote_user_mock, logged_user,
send_purchase_notification_mock):
payment = baker.make(PagarmePayment, payment_method=facade.CREDIT_CARD, user=logged_user)
baker.make(PagarmeNotification, status=facade.PAID, payment=payment)
config = baker.make(PagarmeItemConfig, payments=[payment])
subscription_type = baker.make(SubscriptionType)
PaymentItemConfigToSubscriptionType.objects.create(payment_item=config, subscription_type=subscription_type)
checkout_domain.payment_handler_task(payment.id)
assert tag_as_mock.called is False
remove_tags_mock.assert_called_once_with(
Expand Down
14 changes: 13 additions & 1 deletion pythonpro/memberkit/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

# Register your models here.
from pythonpro.memberkit import facade
from pythonpro.memberkit.models import SubscriptionType, PaymentItemConfigToSubscriptionType
from pythonpro.memberkit.models import SubscriptionType, PaymentItemConfigToSubscriptionType, Subscription


class PaymentItemConfigInline(admin.TabularInline):
Expand Down Expand Up @@ -45,3 +45,15 @@ def has_delete_permission(self, request, obj=None):
@admin.register(PagarmeItemConfig)
class NewPagarmeItemConfigAdmin(PagarmeItemConfigAdmin):
inlines = [PaymentItemConfigInline]


@admin.register(Subscription)
class SubscriptionAdmin(admin.ModelAdmin):
fields = ['subscriber', 'subscription_types', 'observation']
list_display = ['subscriber', 'responsible', 'status', 'created_at', 'updated_at']
autocomplete_fields = ['subscriber']
search_fields = ['subscriber__email']
list_filter = ['status', 'subscription_types']

def has_delete_permission(self, request, obj=None):
return False
17 changes: 16 additions & 1 deletion pythonpro/memberkit/facade.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,26 @@
from typing import List

from pythonpro.memberkit import api
from pythonpro.memberkit.models import SubscriptionType
from pythonpro.memberkit.models import SubscriptionType, Subscription


def synchronize_subscription_types() -> List[SubscriptionType]:
return [
SubscriptionType.objects.update_or_create(id=dct['id'], name=dct['name'])[0]
for dct in api.list_membership_levels()
]


def create_new_subscription(payment, observation: str = '') -> Subscription:
subscription_types = [item.subscription_type_relation.subscription_type for item in payment.items.all()]
if len(subscription_types) == 0:
raise ValueError(f"Payment {payment} doesn't have subscription types")

subscription = Subscription.objects.create(
status=Subscription.Status.INACTIVE,
payment=payment,
subscriber=payment.user,
observation=observation
)
subscription.subscription_types.set(subscription_types)
return subscription
38 changes: 38 additions & 0 deletions pythonpro/memberkit/migrations/0003_subscription.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Generated by Django 3.2.4 on 2021-06-05 20:15

import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
('django_pagarme', '0004_pagarme_item_config_available_until'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('memberkit', '0002_create_relationship_with_payment_config_item'),
]

operations = [
migrations.CreateModel(
name='Subscription',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('status', models.CharField(choices=[('A', 'Ativo'), ('I', 'Inativo')], max_length=1)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('observation', models.TextField(blank=True, default='', verbose_name='Observação')),
('payment', models.OneToOneField(null=True, on_delete=django.db.models.deletion.DO_NOTHING,
to='django_pagarme.pagarmepayment')),
('responsible', models.ForeignKey(null=True, on_delete=django.db.models.deletion.DO_NOTHING,
related_name='created_subscriptions', to=settings.AUTH_USER_MODEL)),
('subscriber', models.ForeignKey(null=True, on_delete=django.db.models.deletion.DO_NOTHING,
related_name='subscriptions', to=settings.AUTH_USER_MODEL)),
('subscription_types',
models.ManyToManyField(related_name='subscriptions', to='memberkit.SubscriptionType')),
],
options={
'verbose_name': 'Assinatura',
'verbose_name_plural': 'Assinaturas',
},
),
]
26 changes: 21 additions & 5 deletions pythonpro/memberkit/models.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from django.contrib.auth import get_user_model
from django.db import models


Expand All @@ -22,8 +23,23 @@ class PaymentItemConfigToSubscriptionType(models.Model):
def __str__(self):
return f'{self.payment_item} -> {self.subscription_type}'

# class Subscription(models.Model):
# created_at = models.DateTimeField(auto_now_add=True)
# updated_at = models.DateTimeField(auto_now_add=True)
# payment = models.OneToOneField('PagarmePayment', on_delete=models.DO_NOTHING, )
# subscription_type = models.OneToOneField(SubscriptionType, null=False, on_delete=models.DO_NOTHING)

class Subscription(models.Model):
class Meta:
verbose_name = 'Assinatura'
verbose_name_plural = 'Assinaturas'

class Status(models.TextChoices):
ACTIVE = 'A', 'Ativo'
INACTIVE = 'I', 'Inativo'

status = models.CharField(max_length=1, choices=Status.choices)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
payment = models.OneToOneField('django_pagarme.PagarmePayment', on_delete=models.DO_NOTHING, null=True)
subscription_types = models.ManyToManyField(SubscriptionType, related_name='subscriptions')
subscriber = models.ForeignKey(get_user_model(), on_delete=models.DO_NOTHING, null=True,
related_name='subscriptions')
responsible = models.ForeignKey(get_user_model(), on_delete=models.DO_NOTHING, null=True,
related_name='created_subscriptions')
observation = models.TextField(verbose_name='Observação', blank=True, default='')