bzzzzzz/django-yandex-cash-register

View on GitHub
yandex_cash_register/models.py

Summary

Maintainability
A
0 mins
Test Coverage
# coding=utf-8
from __future__ import absolute_import, unicode_literals

import uuid

from django.conf import settings
from django.core.urlresolvers import reverse
from django.db import models
from django.utils.encoding import python_2_unicode_compatible
from django.utils.timezone import now
from django.utils.translation import ugettext_lazy as _

from . import conf
from .forms import PaymentForm, FinalPaymentStateForm
from .signals import payment_process, payment_success, payment_fail


@python_2_unicode_compatible
class Payment(models.Model):
    STATE_CREATED = 'created'
    STATE_PROCESSED = 'processed'
    STATE_SUCCESS = 'success'
    STATE_FAIL = 'fail'
    STATE_CHOICES = (
        (STATE_CREATED, _('Created')),
        (STATE_PROCESSED, _('Processed')),
        (STATE_SUCCESS, _('Succeed')),
        (STATE_FAIL, _('Failed')),
    )

    CURRENCY_RUB = 643
    CURRENCY_TEST = 10643

    CURRENCY_CHOICES = (
        (CURRENCY_RUB, _('Rouble')),
        (CURRENCY_TEST, _('Test currency')),
    )

    user = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True,
                             verbose_name=_('User'))
    order_id = models.CharField(_('Order ID'), max_length=50, unique=True,
                                editable=False, db_index=True)
    customer_id = models.UUIDField(_('Customer ID'), unique=True,
                                   default=uuid.uuid4, editable=False)
    state = models.CharField(_('State'), max_length=16, choices=STATE_CHOICES,
                             default=STATE_CREATED, editable=False)

    payment_type = models.CharField(_('Payment method'), max_length=2,
                                    choices=conf.BASE_PAYMENT_TYPE_CHOICES,
                                    editable=False, blank=True)
    invoice_id = models.CharField(_('Invoice ID'), max_length=64,
                                  blank=True, editable=False)
    order_sum = models.DecimalField(_('Order sum'), max_digits=15,
                                    decimal_places=2, editable=False)
    shop_sum = models.DecimalField(_('Received sum'), max_digits=15,
                                   decimal_places=2, null=True,
                                   help_text=_('Order sum - Yandex.Kassa fee'),
                                   editable=False)

    order_currency = models.PositiveIntegerField(
        _('Order currency'), default=CURRENCY_RUB, choices=CURRENCY_CHOICES,
        editable=False)
    shop_currency = models.PositiveIntegerField(
        _('Payment currency'), null=True, default=CURRENCY_RUB,
        choices=CURRENCY_CHOICES)
    payer_code = models.CharField(_('Payer code'), max_length=33,
                                  blank=True, editable=False)

    cps_email = models.EmailField(_('Payer e-mail'), blank=True,
                                  editable=False)
    cps_phone = models.CharField(_('Payer phone'), max_length=15,
                                 blank=True, editable=False)

    created = models.DateTimeField(_('Created at'), auto_now_add=True)
    performed = models.DateTimeField(_('Started at'), null=True)
    completed = models.DateTimeField(_('Completed at'), null=True)

    def __str__(self):
        return _('Payment #%(payment)s') % {'payment': self.order_id}

    class Meta:
        ordering = ('-created',)
        verbose_name = _('payment')
        verbose_name_plural = _('payments')

    @property
    def is_payed(self):
        return self.state == self.STATE_SUCCESS

    @property
    def is_started(self):
        return self.state != self.STATE_CREATED

    @property
    def is_completed(self):
        return self.state in (self.STATE_SUCCESS, self.STATE_FAIL)

    def process(self):
        send_signal = False
        if self.state == self.STATE_CREATED:
            send_signal = True
        elif self.state != self.STATE_PROCESSED:
            raise RuntimeError(
                'Cannot set state to "Processing" when current state '
                'is {}'.format(self.state))

        self.performed = now()
        self.state = self.STATE_PROCESSED
        self.save()

        if send_signal:
            payment_process.send(sender=self)

    def complete(self):
        if self.state == self.STATE_FAIL and self.performed is None:
            raise RuntimeError(
                'Cannot set state to "Success" when current state '
                'is {}'.format(self.state))
        if self.state not in (self.STATE_PROCESSED, self.STATE_FAIL):
            raise RuntimeError(
                'Cannot set state to "Success" when current state '
                'is {}'.format(self.state))

        self.completed = now()
        self.state = self.STATE_SUCCESS
        self.save()

        payment_success.send(sender=self)

    def fail(self):
        if self.state in (self.STATE_SUCCESS, self.STATE_FAIL):
            raise RuntimeError('Cannot set state to "Fail" when current '
                               'state is {}'.format(self.state))

        self.completed = now()
        self.state = self.STATE_FAIL
        self.save()

        payment_fail.send(sender=self)

    def form(self):
        initial = {
            'orderNumber': self.order_id,
            'sum': self.order_sum,
            'customerNumber': self.customer_id,
            'cps_email': self.cps_email,
            'cps_phone': self.cps_phone,
            'paymentType': self.payment_type,
        }
        if conf.SUCCESS_URL is None:
            url = reverse('yandex_cash_register:money_payment_finish')
            initial['shopSuccessURL'] = \
                '{}{}?cr_action={}&cr_order_number={}'.format(
                    conf.SHOP_DOMAIN, url,
                    FinalPaymentStateForm.ACTION_CONFIRM, self.order_id
                )
            initial['shopFailURL'] = \
                '{}{}?cr_action={}&cr_order_number={}'.format(
                    conf.SHOP_DOMAIN, url, FinalPaymentStateForm.ACTION_FAIL,
                    self.order_id
                )
        return PaymentForm(initial=initial)