diff --git a/src/memberdb/actions.py b/src/memberdb/actions.py index e7c0f99e31994bb92511b69b194d99e2cd9e91bd..be51331c05519a27e32ecc3a5315760e22257432 100644 --- a/src/memberdb/actions.py +++ b/src/memberdb/actions.py @@ -124,7 +124,24 @@ def _(description): wrapped_action.short_description = description return wrapped_action -def refresh_dispense_payment(modeladmin, request, queryset): +def sync_to_cokelog(modeladmin, request, queryset): + """ get your free portable blegs here """ + """ copy the payment information from Membership records into the cokelog by calling dispense """ + + # first, make sure the payment information is synced _from_ the cokelog + num_changed_from = refresh_dispense_payment(modeladmin, request, queryset, notify=False) + + # get the memberships which are marked as paid, we're only concerned about whichever ones + # don't involve dispense already (what this does: online, cash; what this could do: eft, card) + set_paid = queryset.filter(date_paid__isnull=False, cokelog_updated__exact=False).exclude(payment_method__exact='dispense') + set_has_accounts = set_paid.filter(member__has_account__exact=True) + set_no_accounts = set_paid.filter(member__has_account__exact=False) + + + + + +def refresh_dispense_payment(modeladmin, request, queryset, notify=True): """ update paid status from cokelog, for Membership model """ num_changed = 0 membership_list = list(queryset) @@ -135,9 +152,11 @@ def refresh_dispense_payment(modeladmin, request, queryset): ms.save() num_changed += 1 - if num_changed > 0: - messages.success(request, "Updated %d records of %d total" % (num_changed, len(membership_list))) - else: - messages.warning(request, "No records updated") + if notify == True: + if num_changed > 0: + messages.success(request, "Updated %d records of %d total" % (num_changed, len(membership_list))) + else: + messages.warning(request, "No records updated") + return num_changed -refresh_dispense_payment.short_description = "Update payment status from cokelog" \ No newline at end of file +refresh_dispense_payment.short_description = "Update payment status from cokelog" diff --git a/src/memberdb/admin.py b/src/memberdb/admin.py index 9d3da34b0a51ad69132db77c93d7a204a4935bc5..4f56003a5db43ddb5cdf214e80b22878abcc1306 100644 --- a/src/memberdb/admin.py +++ b/src/memberdb/admin.py @@ -73,7 +73,7 @@ class IAMemberAdmin(ReadOnlyModelAdmin): class MembershipInline(admin.TabularInline): model = Membership - readonly_fields = ['member', 'date_submitted'] + readonly_fields = ['member', 'date_submitted', 'cokelog_updated'] radio_fields = {'payment_method': admin.VERTICAL, 'membership_type': admin.VERTICAL} extra = 0 fk_name = 'member' @@ -114,7 +114,7 @@ class MembershipAdmin(admin.ModelAdmin): list_display = ['membership_info', 'membership_type', 'payment_method', 'approved', 'date_submitted', 'member_actions'] list_display_links = None list_filter = ['approved', 'payment_method', 'membership_type', 'member__is_student', 'member__is_guild', 'member__has_account'] - readonly_fields = ['date_submitted'] + readonly_fields = ['date_submitted', 'cokelog_updated'] radio_fields = {'payment_method': admin.VERTICAL, 'membership_type': admin.VERTICAL} actions = [refresh_dispense_payment] diff --git a/src/memberdb/management/commands/lockaccounts.py b/src/memberdb/management/commands/unpaid-accounts.py similarity index 76% rename from src/memberdb/management/commands/lockaccounts.py rename to src/memberdb/management/commands/unpaid-accounts.py index d6dcdbe54bd38d3f920d99f9e7be472793d11a5a..ef583974f596df8c4a98de8190a5cec169b076a2 100644 --- a/src/memberdb/management/commands/lockaccounts.py +++ b/src/memberdb/management/commands/unpaid-accounts.py @@ -2,10 +2,10 @@ from django.core.management.base import BaseCommand, CommandError from datetime import datetime class Command(BaseCommand): - help = 'Locks unpaid accounts for the current year' + help = 'Process unpaid accounts for the current year' def add_arguments(self, parser): parser.add_argument('year', nargs='?', type=int, default=datetime.now().year) def handle(self, *args, **options): - self.stdout.write(self.style.SUCCESS('Not implemented: locking accounts for year %d' % options['year'])) \ No newline at end of file + self.stdout.write(self.style.SUCCESS('Not implemented: locking accounts for year %d' % options['year'])) diff --git a/src/memberdb/models.py b/src/memberdb/models.py index e0642d7578b9e481d121a40e91fed3e7b42bde85..55e7ada0eea203452ba395e2a08342d164fcd891 100644 --- a/src/memberdb/models.py +++ b/src/memberdb/models.py @@ -88,7 +88,16 @@ def get_membership_choices(is_renew=None, get_prices=True): return choices +def get_dispense_items(): + """ returns the dispense items corresponding to membership types """ + items = [] + for key, val in MEMBERSHIP_TYPES: + if val['dispense'] != '': + items += [val['dispense']] + return items + def get_membership_type(member): + """ finds the appropriate membership type for the given member """ best = 'non_student' is_fresh = member.memberships.all().count() == 0 for i, t in MEMBERSHIP_TYPES: @@ -134,14 +143,6 @@ ID_TYPES = [ ('Other', 'Other ID'), ] -ACCOUNT_STATUS = [ - 'enabled', - 'disabled', - 'no account' - ] - - - class IncAssocMember (models.Model): """ Member record for data we are legally required to keep under Incorporations Act (and make available to members upon request) @@ -226,6 +227,8 @@ class Membership (models.Model): date_paid = models.DateTimeField ('Date of payment', blank=True, null=True) date_approved = models.DateTimeField ('Date approved', blank=True, null=True) + cokelog_updated = models.BooleanField ('Payment has been sent to cokelog via dispense', default=False, editable=False) + def __str__ (self): return "Member [%s] (%s) renewed membership on %s" % (self.member.username, self.member.display_name, self.date_submitted.strftime("%Y-%m-%d")) diff --git a/src/squarepay/cokelog.py b/src/squarepay/cokelog.py index 0921dae57df52ef57bb019cf58a4d5869e558e06..6e96250e492689958ad52db71bb2a9a19433ffd4 100644 --- a/src/squarepay/cokelog.py +++ b/src/squarepay/cokelog.py @@ -11,14 +11,28 @@ if COKELOG is None: ALL_REGEX = r"^(?P<date>[A-Za-z]{3}\s+\d+\s[\d:]{8})\s(\w+)\sodispense2:\sdispense '([^']+)' \((?P<item>(coke|pseudo|snack|door):(\d{1,3}))\) for (?P<for>\w+) by (?P<by>\w+) \[cost\s+(\d+), balance\s+(\d+)\]$" MEMBERSHIP_REGEX = r"^(?P<date>[A-Za-z]{3}\s+\d+\s[\d:]{8})\s(\w+)\sodispense2:\sdispense '(membership [^']+)' \((?P<item>(pseudo):(\d{1,3}))\) for (?P<for>\w+) by (?P<by>\w+) \[cost\s+(\d+), balance\s+(\d+)\]$" +MEMBERSHIP_REFUND_REGEX = r"^(?P<date>[A-Za-z]{3}\s+\d+\s[\d:]{8})\s(\w+)\sodispense2:\srefund '(membership [^']+)' \((?P<item>(pseudo):(\d{1,3}))\) to (?P<for>\w+) by (?P<by>\w+) \[cost\s+(\d+), balance\s+(\d+)\]$" class CokeLog: + """ + parse and provide some search functionality for the cokelog + """ regex = ALL_REGEX - # dictionary (keyed by username) of lists of dispense records (by-user, and date) + """ dictionary (keyed by username) of lists of dispense records (by-user, and date) + { "username" : { + 'item': 'pseudo:9', + 'by': 'frekk', + 'date': datetime.now() + } + } + """ dispenses = {} + filename = COKELOG file = None + + # track the file offset so we don't have to parse the whole thing from the beginning last_offset = 0 def __init__(self, **kwargs): @@ -110,14 +124,15 @@ def try_update_from_dispense(membership): if ms_disp is not None: if ms_disp['item'] != membership.get_dispense_item(): - log.warn("user '%s': paid incorrect item '%s', not '%s' in dispense." % ( + log.warn("user '%s': paid incorrect item '%s', not '%s' in dispense. Updating." % ( membership.member.username, ms_disp['item'], membership.get_dispense_item() )) - else: + if membership.date_paid != ms_disp['date'] or membership.payment_method != 'dispense': + # only update the record if the details have changed, this preserves the last modified time membership.date_paid = ms_disp['date'] membership.payment_method = 'dispense' - log.debug("user '%s': paid in cokelog" % membership.member.username) - return True + log.debug("user '%s': paid in cokelog" % membership.member.username) + return True else: log.info("user '%s': no paid membership in cokelog" % membership.member.username)