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)