Skip to content

Instantly share code, notes, and snippets.

@antikytheraton
Created November 21, 2018 03:19
Show Gist options
  • Save antikytheraton/fb9448b1e29c3a08068e5c4810b7d3f0 to your computer and use it in GitHub Desktop.
Save antikytheraton/fb9448b1e29c3a08068e5c4810b7d3f0 to your computer and use it in GitHub Desktop.
TODO

TODO LIST

Logica para codigos de invitacion

  • Funcionalidad para generar codigos de invitacion - ya esta
  • Una persona puede invitar a varias personas a unirse
  • Por cada seis personas que se haya invitado, se sumaran 365 dias a tu suscripcion
  • Una persona que uso un codigo de invitacion para crear su cuenta, recibe 15 dias a sus suscripcion

Estructura de la aplicacion

  • La logica de aplicacion de codigos esta en

  • src/apps/codes

    • models.py: Coneccion a la tabla de la base de datos donde se almacena la informacion de aplicacion de codigos de invitacion
    • serializers.py: Logica para serializar datos y volverlo compatibles con el modelo de la base de datos y el objeto JSON que se retornara al frontend
    • urls.py: Direcciones de los servicios de codigos
    • views.py: Logica del servicio de codigos
  • src/apps/subscriptions

    • models.py: Modelo de base de datos
    • serializers.py: serializador y calculo de dias restantes de tu suscripcion
    • urls.py: Direccion de los servicios de suscripcion
    • views.py: Servicio que devuelve datos de la suscripcion
  • src/apps/payments

    • views.py: logica de cobros

Descripcion codigo InvitationCode

src/apps/codes/models.py

class InvitationCode(models.Model):
    '''
    Modelo de la tabla de codigos (No es necesario modificarla)
    '''
    # ID del profile de usuario
    profile = models.OneToOneField(Profile, on_delete=models.CASCADE, null=True)

    # Codigo unico por profile
    code = models.CharField(max_length=8, unique=True, db_index=True)
    
    # Aqui se lleva la cuenta de cuantas veces han usado tu codigo de invitacion
    application_count = models.IntegerField(default=0)
    
    # Este campo no lo vamos a usar (es de la vieja logica de negocios)
    credits = models.IntegerField(default=0)
    
    # Este campo se cambia a True si usaste el codigo de invitacion de alguien
    invitation = models.BooleanField(default=False)
    
    # Este campo se cambia a True si se te añadieron tus 15 dias por haber sido invitado
    invitation_used = models.BooleanField(default=False)

src/apps/codes/views.py

class InvitationCodeView(APIView):
    permission_classes = (IsAuthenticated,)

    def get_remaining_days(self, user, data):
        '''
        Esta funcion devuelve los dias restantes de tu suscripcion
        '''
        remaining_days = get_object_or_404(Subscription, 
                Q(profile__single=user) |
                Q(profile__couple__id_person_one=user) |
                Q(profile__couple__id_person_two=user)
            )
        days_serializer = SubscriptionSerializer(remaining_days)
        data['days_remaining'] = days_serializer.data.get('days_remaining')
        return data

    def get(self, request):
        '''Servicio para devolver tu codigo de invitacion'''
        try:
            code = InvitationCode.objects.get(
                Q(profile__single__person_id=request.user.person_id) |
                Q(profile__couple__id_person_one__person_id=request.user.person_id) |
                Q(profile__couple__id_person_two__person_id=request.user.person_id)
            )
        except InvitationCode.DoesNotExist:
            code = None
        if code:
            serializer = InvitationCodeSerializer(code)
            data = self.get_remaining_days(request.user, serializer.data)
            return Response(data)
        data = dict(
            profile=get_profile(request.user.person_id).id,
            code=code_generator(length=8, chars=string.ascii_uppercase + string.digits),
            application_count=0,
            credits=0
        )
        serializer = InvitationCodeSerializer(data=data)
        if serializer.is_valid():
            serializer.save()
            data = self.get_remaining_days(request.user, serializer.data)
            return Response(data, status=status.HTTP_200_OK)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    def apply_code(self, invitation_code, person_id):
        '''
        Funcion para aplicacion de codigo
        (Parte de la nueva logica de negocio va aqui)
        '''
        try:
            his_code = get_object_or_404(InvitationCode, code=invitation_code)
            my_code = get_object_or_404(InvitationCode, profile=get_profile(person_id).id)
            if my_code.credits == 0 and my_code.application_count == 0:
                his_code.credits += 1
                his_code.application_count += 1
                his_code.save()
                my_code.credits += 1
                my_code.invitation = True
                my_code.save()
            return my_code
        except InvitationCode.DoesNotExist:
            raise Http404

    @error_handler
    def post(self, request):
        '''Servicio de aplicacion de codigo'''
        invitation_code = request.data['code']
        my_code = self.apply_code(invitation_code, request.user.person_id)
        serializer = InvitationCodeSerializer(my_code)
        return Response(serializer.data)


class ValidateCode(APIView):
    permission_classes = (AllowAny,)

    @error_handler
    def post(self, request):
        '''
        Servicio para validar que un codigo exista
        '''
        try:
            code = InvitationCode.objects.get(code=request.data['code'])
            response = Response({'valid': True})
        except InvitationCode.DoesNotExist:
            response = Response({'valid': False})
        return response


class ApplyDiscountView(APIView):
    permission_classes = (IsAuthenticated,)

    @error_handler
    def patch(self, request):
        '''
        Antigua logica de necocio para aplicar descuentos
        Funcionaba asi:
            Por cada tres personas que hayas invitado aliberti, se te anexaban $149 pesos de descuento
        Funcionalidad nueva:
            Por cada seis personas que hayas invitado, se te suman 365 dias a tu tiempo de suscripcion
        '''
        credits = request.data['credits']
        code = get_object_or_404(InvitationCode, profile=get_profile(request.user.person_id).id)
        if code.credits >= credits and credits % 3 == 0:
            code.credits -= credits
            code.save()
            serializer = InvitationCodeSerializer(code)
            return Response(serializer.data)
        raise ApplyDiscountException('The discount is not applicable')


class MakeMembershipAmountCalc(APIView):
    permission_classes = (IsAuthenticated,)

    @error_handler
    def post(self, request):
        '''
        Antiguo servicio que daba el calculo del descuento total acumulado por cada tres personas que hayas invitado
        Este servicio ya no va
        '''
        apply_discount = request.data['apply_discount']
        membership = request.data['membership']
        counter = 0
        applicables = 0
        if membership == '0799':
            total_cost = 799
        elif membership == '1899':
            total_cost = 1899
        if apply_discount:
            invitation_code = InvitationCode.objects.get(
                Q(profile__single=request.user) |
                Q(profile__couple__id_person_one=request.user) |
                Q(profile__couple__id_person_two=request.user)
            )
            if invitation_code.invitation == True and invitation_code.invitation_used == False:
                counter += 50
                invitation_code.invitation_used = True
            if (invitation_code.credits // 3) > total_cost // 149:
                if total_cost == 799:
                    applicables = 5
                elif total_cost == 1899:
                    applicables = 12
            else:
                applicables = (invitation_code.credits // 3)
            credits = applicables * 149
            total_cost -= (counter + credits)
        return Response({"total_cost": total_cost, "aplicable_credits": applicables})

Suscripciones

src/apps/subscriptions/models.py

class Subscription(models.Model):
    '''
    Modelo de la base de datos
    '''
    profile = models.OneToOneField(Profile, on_delete=models.CASCADE)
    application_date = models.DateTimeField(auto_now_add=True)
    expiration_date = models.DateTimeField()
    credits = models.IntegerField(default=0)
    membership_days = models.DateTimeField(null=True)
    membership_application_date = models.DateTimeField(null=True)
    lifetime = models.BooleanField(default=False)


def set_subscription_start(sender, **kwargs):
    '''
    Funcion usada como trigger para setear el inicio de una suscripcion
    Se activa desde src/apps/swinger_profile/models.py linea 128
    '''
    if kwargs['created']:
        new_user = kwargs['instance']
        subscription = Subscription.objects.create(
            profile=new_user,
            expiration_date=free_month()
        )

def current_time():
    '''
    Devuelve la fecha actual
    '''
    return timezone.now()

def free_month():
    '''
    Devuelve la cuenta de un mes gratis
    '''
    return current_time()+datetime.timedelta(days=30)

src/apps/subscriptions/serializers.py

class SubscriptionSerializer(serializers.ModelSerializer):
    '''
    Serializador y calculo de dias restantes
    '''
    days_remaining = serializers.SerializerMethodField()
    class Meta:
        model = Subscription
        fields = ('days_remaining',)


    def get_days_remaining(self, obj):
        '''
        Aqui va la logica del calculo de dias restantes de una suscripcion
        '''
        if obj.lifetime == True:
            return '∞'
        # days = (obj.expiration_date - obj.application_date).days + 1
        days = (obj.expiration_date - current_time()).days
        if obj.membership_days != None:
            membership_days = (obj.membership_days - obj.membership_application_date - current_time()).days
        else:
            membership_days = 0
        total = days + membership_days
        return str(total) if total > 0 else str(0)

Payments

src/apps/payments/views.py

class ChargesAPIView(APIView):
    permission_classes = (IsAuthenticated,)

    @property
    def order_id(self):
        return 'oid_' + str(uuid.uuid4().fields[-1])[:10]

    @openpay_errors
    def post(self, request):
        '''Create a payment'''
        customer_id = request.data.get('customer').get('id')
        card_data = request.data.get('card')
        membership = request.data.get('membership')
        if membership not in ['0799', '1899']:
            return Response(400, status=status.HTTP_400_BAD_REQUEST)
        customer = openpay.Customer.retrieve(customer_id)
        card_obj = get_object_or_404(Card, card__id=card_data.get('source_id'))
        charge = customer.charges.create(
            method=card_data.get('method'),
            source_id=card_data.get('source_id'),
            device_session_id=card_data.get('device_session_id'),
            amount=card_data.get('amount'),
            description=card_data.get('description'),
            order_id=self.order_id
        )
        print(membership)
        charge_data = {
            'card': card_obj.id,
            'charge': charge,
            'membership': membership
        }
        serializer = ChargeSerializer(data=charge_data)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        self.apply_credits(request.user.person_id, membership, request.user.email)
        return Response(serializer.data)

    def apply_credits(self, person_id, membership, email):
        '''
        En esta funcion se realizaba la aplicacion de tiempo comprado dependiendo del paquete elegido
        '''
        profile = get_profile(person_id)
        if membership == '0799':
            self.anual(profile)
            send_membership_mail(email, membership)
        elif membership == '1899':
            self.lifetime(profile)
            send_membership_mail(email, membership)
        else:
            pass

    def anual(self, profile):
        '''
        aplicacion de plan anual
        '''
        subscription = self.get_subscriptions(profile)
        if subscription.membership_days != None:
            remaining_days = (subscription.membership_days - timezone.now()).days
        else:
            remaining_days = 0
        today = timezone.now()
        nextD = today + datetime.timedelta(days=(365 + remaining_days))
        subscription.membership_application_date = today
        subscription.membership_days = nextD
        subscription.save()
        return subscription

    def lifetime(self, profile):
        '''
        aplicacion de plan de por vida (Ya no va, ahora el plan debe ser de aplicacion mensual)
        '''
        subscription = self.get_subscriptions(profile)
        subscription.lifetime = True
        subscription.save()
        return subscription

    def get_subscriptions(self, profile):
        subscription = get_object_or_404(Subscription, profile=profile)
        return subscription
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment