Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. from decimal import Decimal
  2. from django.db import models
  3. from django.template.defaultfilters import slugify
  4. from django.utils.translation import ugettext as _
  5. from django.conf import settings
  6. class Transaction(models.Model):
  7. """
  8. A transaction for payment sources which need a secondary 'transaction' to
  9. actually take the money
  10. This applies mainly to credit card sources which can be a pre-auth for the
  11. money. A 'complete' needs to be run later to debit the money from the
  12. account.
  13. """
  14. source = models.ForeignKey(
  15. 'payment.Source', related_name='transactions',
  16. verbose_name=_("Source"))
  17. # We define some sample types
  18. AUTHORISE, DEBIT, REFUND = 'Authorise', 'Debit', 'Refund'
  19. txn_type = models.CharField(_("Type"), max_length=128, blank=True)
  20. amount = models.DecimalField(_("Amount"), decimal_places=2, max_digits=12)
  21. reference = models.CharField(_("Reference"), max_length=128, null=True)
  22. status = models.CharField(_("Status"), max_length=128, null=True)
  23. date_created = models.DateTimeField(_("Date Created"), auto_now_add=True)
  24. def __unicode__(self):
  25. return _("%(type)s of %(amount).2f") % {
  26. 'type': self.txn_type, 'amount': self.amount}
  27. class Meta:
  28. verbose_name = _("Transaction")
  29. verbose_name_plural = _("Transactions")
  30. class Source(models.Model):
  31. """
  32. A source of payment for an order.
  33. This is normally a credit card which has been pre-authed for the order
  34. amount, but some applications will allow orders to be paid for using
  35. multiple sources such as cheque, credit accounts, gift cards. Each payment
  36. source will have its own entry.
  37. """
  38. order = models.ForeignKey('order.Order',
  39. related_name='sources', verbose_name=_("Order"))
  40. source_type = models.ForeignKey('payment.SourceType',
  41. verbose_name=_("Source Type"))
  42. currency = models.CharField(_("Currency"), max_length=12,
  43. default=settings.OSCAR_DEFAULT_CURRENCY)
  44. # Track the various amounts associated with this source
  45. amount_allocated = models.DecimalField(
  46. _("Amount Allocated"), decimal_places=2, max_digits=12,
  47. default=Decimal('0.00'))
  48. amount_debited = models.DecimalField(
  49. _("Amount Debited"), decimal_places=2, max_digits=12,
  50. default=Decimal('0.00'))
  51. amount_refunded = models.DecimalField(
  52. _("Amount Refunded"), decimal_places=2, max_digits=12,
  53. default=Decimal('0.00'))
  54. # Reference number for this payment source. This is often used to look up
  55. # a transaction model for a particular payment partner.
  56. reference = models.CharField(_("Reference"), max_length=128,
  57. blank=True, null=True)
  58. # A customer-friendly label for the source, eg XXXX-XXXX-XXXX-1234
  59. label = models.CharField(_("Label"), max_length=128, blank=True, null=True)
  60. # A dictionary of submission data that is stored as part of the
  61. # checkout process.
  62. submission_data = None
  63. # We keep a list of deferred transactions that are only actually saved when
  64. # the source is saved for the first time
  65. deferred_txns = None
  66. class Meta:
  67. verbose_name = _("Source")
  68. verbose_name_plural = _("Sources")
  69. def __unicode__(self):
  70. description = _("Allocation of %(amount).2f from type %(type)s") % {
  71. 'amount': self.amount_allocated, 'type': self.source_type}
  72. if self.reference:
  73. description += _(" (reference: %s)") % self.reference
  74. return description
  75. def save(self, *args, **kwargs):
  76. super(Source, self).save(*args, **kwargs)
  77. if self.deferred_txns:
  78. for txn in self.deferred_txns:
  79. self._create_transaction(*txn)
  80. def balance(self):
  81. return (self.amount_allocated - self.amount_debited +
  82. self.amount_refunded)
  83. def create_deferred_transaction(self, txn_type, amount, reference=None,
  84. status=None):
  85. """
  86. Register the data for a transaction that can't be created yet due to FK
  87. constraints. This happens at checkout where create an payment source
  88. and a transaction but can't save them until the order model exists.
  89. """
  90. if self.deferred_txns is None:
  91. self.deferred_txns = []
  92. self.deferred_txns.append((txn_type, amount, reference, status))
  93. def _create_transaction(self, txn_type, amount, reference=None,
  94. status=None):
  95. Transaction.objects.create(source=self,
  96. txn_type=txn_type,
  97. amount=amount,
  98. reference=reference,
  99. status=status)
  100. def allocate(self, amount, reference=None, status=None):
  101. """
  102. Convenience method for ring-fencing money against this source
  103. """
  104. self.amount_allocated += amount
  105. self.save()
  106. self._create_transaction(
  107. Transaction.AUTHORISE, amount, reference, status)
  108. allocate.alters_data = True
  109. def debit(self, amount=None, reference=None, status=None):
  110. """
  111. Convenience method for recording debits against this source
  112. """
  113. if amount is None:
  114. amount = self.balance()
  115. self.amount_debited += amount
  116. self.save()
  117. self._create_transaction(Transaction.DEBIT, amount, reference, status)
  118. debit.alters_data = True
  119. def refund(self, amount, reference=None, status=None):
  120. """
  121. Convenience method for recording refunds against this source
  122. """
  123. self.amount_refunded += amount
  124. self.save()
  125. self._create_transaction(Transaction.REFUND, amount, reference, status)
  126. refund.alters_data = True
  127. @property
  128. def amount_available_for_refund(self):
  129. """
  130. Return the amount available to be refunded
  131. """
  132. return self.amount_debited - self.amount_refunded
  133. class SourceType(models.Model):
  134. """
  135. A type of payment source.
  136. This could be an external partner like PayPal or DataCash,
  137. or an internal source such as a managed account.i
  138. """
  139. name = models.CharField(_("Name"), max_length=128)
  140. code = models.SlugField(_("Code"), max_length=128,
  141. help_text=_("This is used within forms to identify this source type"))
  142. class Meta:
  143. verbose_name = _("Source Type")
  144. verbose_name_plural = _("Source Types")
  145. def __unicode__(self):
  146. return self.name
  147. def save(self, *args, **kwargs):
  148. if not self.code:
  149. self.code = slugify(self.name)
  150. super(SourceType, self).save(*args, **kwargs)
  151. class Bankcard(models.Model):
  152. user = models.ForeignKey('auth.User', related_name='bankcards',
  153. verbose_name=_("User"))
  154. card_type = models.CharField(_("Card Type"), max_length=128)
  155. name = models.CharField(_("Name"), max_length=255)
  156. number = models.CharField(_("Number"), max_length=32)
  157. expiry_date = models.DateField(_("Expiry Date"))
  158. # For payment partners who are storing the full card details for us
  159. partner_reference = models.CharField(
  160. _("Partner Reference"), max_length=255, null=True, blank=True)
  161. class Meta:
  162. verbose_name = _("Bankcard")
  163. verbose_name_plural = _("Bankcards")
  164. def save(self, *args, **kwargs):
  165. self.number = self._get_obfuscated_number()
  166. super(Bankcard, self).save(*args, **kwargs)
  167. def _get_obfuscated_number(self):
  168. return u"XXXX-XXXX-XXXX-%s" % self.number[-4:]