2014-07-28 17:28:00 +00:00
|
|
|
from django.core.exceptions import ValidationError
|
2014-07-23 16:24:56 +00:00
|
|
|
from django.db import models
|
2014-09-05 14:27:30 +00:00
|
|
|
from django.utils import timezone
|
2014-07-28 17:28:00 +00:00
|
|
|
from django.utils.functional import cached_property
|
2014-07-23 16:24:56 +00:00
|
|
|
from django.utils.translation import ugettext_lazy as _
|
|
|
|
from jsonfield import JSONField
|
|
|
|
|
2014-07-28 17:28:00 +00:00
|
|
|
from orchestra.core import accounts
|
2014-09-04 15:55:43 +00:00
|
|
|
from orchestra.models.queryset import group_by
|
2014-07-28 17:28:00 +00:00
|
|
|
|
2014-07-23 16:24:56 +00:00
|
|
|
from . import settings
|
|
|
|
from .methods import PaymentMethod
|
|
|
|
|
|
|
|
|
2014-09-04 15:55:43 +00:00
|
|
|
class PaymentSourcesQueryset(models.QuerySet):
|
2014-09-05 14:27:30 +00:00
|
|
|
def get_default(self):
|
2014-09-04 15:55:43 +00:00
|
|
|
return self.filter(is_active=True).first()
|
|
|
|
|
|
|
|
|
2014-07-23 16:24:56 +00:00
|
|
|
class PaymentSource(models.Model):
|
|
|
|
account = models.ForeignKey('accounts.Account', verbose_name=_("account"),
|
2014-09-04 15:55:43 +00:00
|
|
|
related_name='paymentsources')
|
2014-07-23 16:24:56 +00:00
|
|
|
method = models.CharField(_("method"), max_length=32,
|
|
|
|
choices=PaymentMethod.get_plugin_choices())
|
|
|
|
data = JSONField(_("data"))
|
2014-07-24 09:53:34 +00:00
|
|
|
is_active = models.BooleanField(_("is active"), default=True)
|
2014-07-28 17:28:00 +00:00
|
|
|
|
2014-09-04 15:55:43 +00:00
|
|
|
objects = PaymentSourcesQueryset.as_manager()
|
|
|
|
|
2014-07-28 17:28:00 +00:00
|
|
|
def __unicode__(self):
|
2014-09-04 15:55:43 +00:00
|
|
|
return "%s (%s)" % (self.label, self.method_class.verbose_name)
|
|
|
|
|
|
|
|
@cached_property
|
|
|
|
def method_class(self):
|
|
|
|
return PaymentMethod.get_plugin(self.method)
|
2014-07-28 17:28:00 +00:00
|
|
|
|
|
|
|
@cached_property
|
|
|
|
def label(self):
|
2014-09-04 15:55:43 +00:00
|
|
|
return self.method_class().get_label(self.data)
|
2014-07-28 17:28:00 +00:00
|
|
|
|
|
|
|
@cached_property
|
|
|
|
def number(self):
|
2014-09-04 15:55:43 +00:00
|
|
|
return self.method_class().get_number(self.data)
|
2014-09-05 14:27:30 +00:00
|
|
|
|
|
|
|
def get_bill_context(self):
|
|
|
|
method = self.method_class()
|
|
|
|
return {
|
|
|
|
'message': method.get_bill_message(self),
|
|
|
|
}
|
|
|
|
|
|
|
|
def get_due_delta(self):
|
|
|
|
return self.method_class().due_delta
|
2014-09-04 15:55:43 +00:00
|
|
|
|
|
|
|
|
|
|
|
class TransactionQuerySet(models.QuerySet):
|
|
|
|
group_by = group_by
|
2014-09-06 10:56:30 +00:00
|
|
|
|
|
|
|
def create(self, **kwargs):
|
|
|
|
source = kwargs.get('source')
|
|
|
|
if source is None or not hasattr(source.method_class, 'process'):
|
|
|
|
# Manual payments don't need processing
|
|
|
|
kwargs['state']=self.model.WAITTING_CONFIRMATION
|
|
|
|
return super(TransactionQuerySet, self).create(**kwargs)
|
2014-07-23 16:24:56 +00:00
|
|
|
|
|
|
|
|
2014-07-30 12:55:33 +00:00
|
|
|
# TODO lock transaction in waiting confirmation
|
2014-07-23 16:24:56 +00:00
|
|
|
class Transaction(models.Model):
|
2014-09-06 10:56:30 +00:00
|
|
|
WAITTING_PROCESSING = 'WAITTING_PROCESSING' # CREATED
|
|
|
|
WAITTING_CONFIRMATION = 'WAITTING_CONFIRMATION' # PROCESSED
|
2014-07-23 16:24:56 +00:00
|
|
|
CONFIRMED = 'CONFIRMED'
|
|
|
|
REJECTED = 'REJECTED'
|
|
|
|
DISCARTED = 'DISCARTED'
|
2014-09-05 14:27:30 +00:00
|
|
|
SECURED = 'SECURED'
|
2014-07-23 16:24:56 +00:00
|
|
|
STATES = (
|
2014-07-24 09:53:34 +00:00
|
|
|
(WAITTING_PROCESSING, _("Waitting processing")),
|
|
|
|
(WAITTING_CONFIRMATION, _("Waitting confirmation")),
|
2014-07-23 16:24:56 +00:00
|
|
|
(CONFIRMED, _("Confirmed")),
|
|
|
|
(REJECTED, _("Rejected")),
|
2014-09-05 14:27:30 +00:00
|
|
|
(SECURED, _("Secured")),
|
2014-07-23 16:24:56 +00:00
|
|
|
(DISCARTED, _("Discarted")),
|
|
|
|
)
|
|
|
|
|
2014-09-04 15:55:43 +00:00
|
|
|
objects = TransactionQuerySet.as_manager()
|
|
|
|
|
2014-07-23 16:24:56 +00:00
|
|
|
bill = models.ForeignKey('bills.bill', verbose_name=_("bill"),
|
|
|
|
related_name='transactions')
|
2014-07-30 12:55:33 +00:00
|
|
|
source = models.ForeignKey(PaymentSource, null=True, blank=True,
|
|
|
|
verbose_name=_("source"), related_name='transactions')
|
2014-09-05 14:27:30 +00:00
|
|
|
process = models.ForeignKey('payments.TransactionProcess', null=True,
|
|
|
|
blank=True, verbose_name=_("process"), related_name='transactions')
|
2014-07-23 16:24:56 +00:00
|
|
|
state = models.CharField(_("state"), max_length=32, choices=STATES,
|
|
|
|
default=WAITTING_PROCESSING)
|
|
|
|
amount = models.DecimalField(_("amount"), max_digits=12, decimal_places=2)
|
|
|
|
currency = models.CharField(max_length=10, default=settings.PAYMENT_CURRENCY)
|
|
|
|
created_on = models.DateTimeField(auto_now_add=True)
|
|
|
|
modified_on = models.DateTimeField(auto_now=True)
|
2014-07-24 09:53:34 +00:00
|
|
|
|
|
|
|
def __unicode__(self):
|
|
|
|
return "Transaction {}".format(self.id)
|
2014-09-04 15:55:43 +00:00
|
|
|
|
|
|
|
@property
|
|
|
|
def account(self):
|
|
|
|
return self.bill.account
|
2014-07-28 17:28:00 +00:00
|
|
|
|
|
|
|
|
2014-09-05 14:27:30 +00:00
|
|
|
class TransactionProcess(models.Model):
|
2014-07-29 20:10:37 +00:00
|
|
|
"""
|
|
|
|
Stores arbitrary data generated by payment methods while processing transactions
|
|
|
|
"""
|
|
|
|
data = JSONField(_("data"), blank=True)
|
|
|
|
file = models.FileField(_("file"), blank=True)
|
|
|
|
created_at = models.DateTimeField(_("created at"), auto_now_add=True)
|
|
|
|
|
2014-09-05 14:27:30 +00:00
|
|
|
class Meta:
|
|
|
|
verbose_name_plural = _("Transaction processes")
|
2014-07-30 12:55:33 +00:00
|
|
|
|
2014-07-29 20:10:37 +00:00
|
|
|
def __unicode__(self):
|
|
|
|
return str(self.id)
|
|
|
|
|
|
|
|
|
2014-07-28 17:28:00 +00:00
|
|
|
accounts.register(PaymentSource)
|
|
|
|
accounts.register(Transaction)
|