Created
January 30, 2019 19:58
-
-
Save williln/171542625d92f4944f79a90051ea3a21 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import re | |
from django.conf import settings | |
from django.contrib.auth.models import User | |
from django.contrib.sites.models import Site | |
from django.core.mail import send_mail | |
from django.db import models | |
from django.template.loader import render_to_string | |
from django.utils.crypto import salted_hmac | |
from django.utils.translation import ugettext_lazy as _ | |
from nanog.constants import MBR_TYPE_STANDARD | |
from service_store.constants import ( | |
XACT_TYPES, XACT_REFUND, XACT_PURCHASE, | |
XACT_STATES, XACT_INCOMPLETE, XACT_REFUNDED, | |
XACT_DISPOSITIONS, XACT_NONE, | |
XACT_KINDS, XACT_KIND_INFO, ) | |
class Transaction(models.Model): | |
""" | |
Description: Represents a single transaction in the store | |
""" | |
xact_type = models.CharField(_('type'), | |
max_length=20, | |
default=XACT_PURCHASE, | |
choices=XACT_TYPES, | |
help_text=_('Type of transaction -- purchase, refund, etc.'), ) | |
state = models.CharField(_('state'), | |
max_length=20, | |
default=XACT_INCOMPLETE, | |
choices=XACT_STATES, | |
help_text=_('Current state of this transaction -- incomplete, complete, canceled, etc.'), ) | |
disposition = models.CharField(_('disposition'), | |
max_length=20, | |
default=XACT_NONE, | |
choices=XACT_DISPOSITIONS, | |
help_text=_('The ultimate disposition of the transaction -- none, successfully completed, unsuccessful, or abnormal (partially completed but something was strange).'), ) | |
explanation = models.CharField(_('explanation'), | |
max_length=384, | |
help_text=_('explanation for the disposition -- declined, amount mismatch, etc.'), | |
blank=True, | |
default='', ) | |
membership_type = models.CharField(_('Kind of membership'), | |
help_text=_('is a student or regular membership'), | |
default=MBR_TYPE_STANDARD, | |
max_length=16, ) | |
membership_years = models.IntegerField(_('Number of years'), | |
help_text=_('How many years\' worth of membership?'), | |
default=1, ) | |
user = models.ForeignKey(User) | |
description = models.CharField(_('description'), | |
max_length=384, | |
help_text=_('description of transaction'), ) | |
amount_billed = models.FloatField(_('amount billed'), | |
help_text=_('transaction amount that should be paid'), | |
default=0.00, ) | |
amount_paid = models.FloatField(_('amount paid'), | |
help_text=_('transaction amount paid (or refunded) thru merchant account'), | |
default=0.00, ) | |
cc_transaction_id = models.CharField(_('CC processor transaction id'), | |
null=True, | |
blank=True, | |
max_length=30, | |
help_text=_('CC processor transaction id for transaction'), ) | |
authorization = models.CharField(_('authorization code'), | |
null=True, | |
blank=True, | |
max_length=120, | |
help_text=_('authorization code for completed transaction'), ) | |
created = models.DateTimeField(_('created'), | |
auto_now_add=True, | |
help_text=_('Date transaction was created'), ) | |
completed = models.DateTimeField(_('completed'), | |
auto_now=True, | |
help_text=_('Date transaction was completed'), ) | |
last_updated = models.DateTimeField(_('last_updated'), | |
auto_now=True, | |
help_text=_('Date transaction was updated'),) | |
related_transaction = models.ForeignKey('Transaction', | |
null=True, | |
blank=True, ) | |
notes = models.CharField(_('notes'), max_length=200, | |
help_text=_('notes'), blank=True, null=True, ) | |
class Meta: | |
ordering = ['-completed', ] | |
verbose_name_plural = 'Membership Transactions' | |
verbose_name = 'Membership Transaction' | |
def __unicode__(self): | |
return u'%d (%s)' % (self.id, self.state, ) | |
@property | |
def token(self): | |
"""Generates a token that hashes the current state of the transaction.""" | |
key_salt = "store.confirm.token" | |
value = '%s.%s.%s.%s' % ( | |
self.pk, | |
self.state, | |
self.user.pk, | |
self.amount_billed) | |
token = salted_hmac(key_salt, value).hexdigest()[::2] | |
return token | |
@property | |
def as_dict(self): | |
return self.__dict__.copy() | |
@property | |
def is_refundable(self): | |
"""Determines if a transaction is refundable - must not be | |
a refund already; must be a purchase type; must be for an | |
amount > 0.00""" | |
return (self.state != XACT_REFUNDED and | |
self.xact_type == XACT_PURCHASE and | |
self.amount_paid > 0.00) | |
def send_receipt(self): | |
"""Generate a receipt and send to the accountholder via email.""" | |
site = Site.objects.get_current() | |
ctx_dict = {'transaction': self, | |
'contact_email': settings.RECEIPT_HELP_ADDRESS, | |
'site': site} | |
subject = render_to_string('store/email_receipt_subject.txt', | |
ctx_dict) | |
# Email subject *must not* contain newlines | |
subject = ''.join(subject.splitlines()) | |
message = render_to_string('store/email_receipt.txt', | |
ctx_dict) | |
send_mail(subject, message, settings.RECEIPT_EMAIL_FROM, (self.user.email, )) | |
class TransactionLog(models.Model): | |
""" | |
Description: Represents a log / history of a Transaction | |
""" | |
transaction = models.ForeignKey('Transaction') | |
message = models.CharField(_('message'), max_length=500, | |
help_text=_('log message'), ) | |
user = models.ForeignKey(User) | |
timestamp = models.DateTimeField(auto_now=True) | |
kind = models.CharField(_('kind'), | |
max_length=10, | |
help_text=_('Log message kind'), | |
choices=XACT_KINDS, | |
default=XACT_KIND_INFO, ) | |
class LegacyAMSTransaction(models.Model): | |
""" | |
Description: stuff from AMS transactions | |
""" | |
""" | |
"TRANSNO","EMAIL","TYPE","JOINED","ADJUSTMENT","TOTAL","RECEIVED","COMMENT1","COMMENT2" | |
""" | |
transno = models.IntegerField(_('AMS transaction number'), | |
blank=True, | |
null=True) | |
email = models.CharField(_('Email address'), | |
max_length=128, | |
blank=True, | |
null=True) | |
# ^^ matched to vv | |
user = models.ForeignKey(User, null=True, ) | |
purchase_type = models.CharField(_('Purchase type'), | |
max_length=32, | |
blank=True, | |
null=True) | |
# date joined vvv | |
created = models.DateTimeField(_('created'), | |
auto_now=True, | |
help_text=_('Date transaction was created'), ) | |
# amount received | |
amount = models.FloatField(_('amount'), | |
help_text=_('transaction amount'), ) | |
authorization = models.CharField(_('authorization code'), | |
null=True, | |
blank=True, | |
max_length=120, | |
help_text=_('authorization code for completed transaction'), ) | |
description = models.CharField(_('description'), | |
max_length=1024, | |
help_text=_('description of transaction'), ) | |
def __init__(self, *args, **kwargs): | |
super(LegacyAMSTransaction, self).__init__(*args, **kwargs) | |
# if the email is provided, then look up a user that matches | |
if kwargs.get('email'): | |
user = User.objects.filter(email=kwargs.get('email')).first() | |
if user: | |
self.user = user | |
def __unicode__(self): | |
return u'%s %s (%s)' % (self.transno, self.email, self.purchase_type, ) | |
@staticmethod | |
def authorization_from_comment(comment): | |
"""Grabs the transaction number from the comment from AMS' log""" | |
regex = re.match(r".*uthorization number (.*?)$", comment) | |
ret = None | |
if regex: | |
try: | |
ret = regex.group(1) | |
except Exception: | |
pass | |
return ret |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment