Index: treasury/finance_core/admin.py
===================================================================
--- treasury/finance_core/admin.py	(revision f468e6dc2e7a343e90f44bdf7dd5281b8e93698c)
+++ treasury/finance_core/admin.py	(revision f468e6dc2e7a343e90f44bdf7dd5281b8e93698c)
@@ -0,0 +1,15 @@
+from django.contrib import admin
+import finance_core.models
+
+class BudgetAreaAdmin(admin.ModelAdmin):
+    pass
+    #fields = [ 'path', 'name', 'comment', 'owner', 'interested', ]
+
+
+class BudgetTermAdmin(admin.ModelAdmin):
+    prepopulated_fields = {"slug": ("name",)}
+
+
+admin.site.register(finance_core.models.BudgetArea, BudgetAreaAdmin)
+admin.site.register(finance_core.models.BudgetTerm, BudgetTermAdmin)
+admin.site.register(finance_core.models.BudgetAreaTerm)
Index: treasury/finance_core/models.py
===================================================================
--- treasury/finance_core/models.py	(revision f468e6dc2e7a343e90f44bdf7dd5281b8e93698c)
+++ treasury/finance_core/models.py	(revision f468e6dc2e7a343e90f44bdf7dd5281b8e93698c)
@@ -0,0 +1,89 @@
+from django.db import models
+import settings
+import treebeard.mp_tree
+
+
+class BudgetArea(treebeard.mp_tree.MP_Node):
+    indent_str = u"\u00A0\u00A0"
+
+    name = models.CharField(max_length=50)
+    comment = models.TextField(blank=True)
+    always = models.BooleanField(blank=True, default=False)
+
+    # Contact / ACL information
+    # If not specified, inherit from parent
+    owner = models.EmailField(help_text = 'Email address of the officer responsible for the area', blank=True) # owner of the budget area
+    interested = models.EmailField(help_text='Email address of parties interested in the area', blank=True) # interested parties (ie, whole committee)
+    use_owner = models.BooleanField(default=False, blank=True)
+
+    def contact_address(self,):
+        address = ''
+        if self.use_owner:
+            address = self.owner or self.interested
+        else:
+            address = self.interested or self.owner
+        return address or self.get_parent().contact_address()
+
+    def mark_used(self, term, comment=""):
+        BudgetAreaTerm.objects.get_or_create(
+            budget_area=self,
+            budget_term=term,
+            defaults={'comment':comment}
+        )
+
+    @classmethod
+    def get_by_path(cls, path):
+        root = BudgetArea.objects.get(name=path[0], depth=1)
+        node = root
+        for name in path[1:]:
+            node = node.get_children().filter(name=name)[0]
+        return node
+
+    def dump_to_html(self):
+        struct = self.dump_bulk()
+        return self.struct_to_html(struct, depth=0)
+
+    def struct_to_html(self, struct, depth=0):
+        def format_data(data):
+            return "<em>"+data['name']+"</em> "+unicode(data)
+        prefix = "\t"*depth
+        html = prefix+"<ul>\n"
+        html = html + "\n".join(
+            [("%(prefix)s\t<li>%(data)s\n%(children)s\t%(prefix)s</li>\n" % {
+                'prefix':prefix,
+                'data':format_data(elem['data']),
+                'children':('children' in elem and self.struct_to_html(elem['children'], depth+1) or '')
+            }) for elem in struct])
+        html = html + prefix + "</ul>\n"
+        return html
+
+    def indented_name(self):
+        return indent_str*self.depth + unicode(self)
+
+    def __unicode__(self,):
+        return u"%s [%s] (%s)" % (self.name, self.contact_address(), self.always, )
+
+
+class BudgetTerm(models.Model):
+    name = models.CharField(max_length=20)
+    slug = models.SlugField(max_length=20)
+    start_date = models.DateField()
+    end_date = models.DateField()
+    submit_deadline = models.DateField()
+
+    def __unicode__(self,):
+        return "%s (%s to %s [due %s])" % (self.name, self.start_date, self.end_date, self.submit_deadline, )
+
+
+class BudgetAreaTerm(models.Model):
+    budget_area = models.ForeignKey(BudgetArea)
+    budget_term = models.ForeignKey(BudgetTerm)
+    comment = models.TextField(blank=True, )
+
+    def __unicode__(self,):
+        return "%s during %s" % (self.budget_area, self.budget_term, )
+
+
+def coerce_full_email(email):
+    if '@' in email: return email
+    else: return email + '@' + settings.DEFAULT_DOMAIN
Index: treasury/finance_core/tests.py
===================================================================
--- treasury/finance_core/tests.py	(revision f468e6dc2e7a343e90f44bdf7dd5281b8e93698c)
+++ treasury/finance_core/tests.py	(revision f468e6dc2e7a343e90f44bdf7dd5281b8e93698c)
@@ -0,0 +1,23 @@
+"""
+This file demonstrates two different styles of tests (one doctest and one
+unittest). These will both pass when you run "manage.py test".
+
+Replace these with more appropriate tests for your application.
+"""
+
+from django.test import TestCase
+
+class SimpleTest(TestCase):
+    def test_basic_addition(self):
+        """
+        Tests that 1 + 1 always equals 2.
+        """
+        self.failUnlessEqual(1 + 1, 2)
+
+__test__ = {"doctest": """
+Another way to test that 1 + 1 is equal to 2.
+
+>>> 1 + 1 == 2
+True
+"""}
+
Index: treasury/finance_core/urls.py
===================================================================
--- treasury/finance_core/urls.py	(revision f468e6dc2e7a343e90f44bdf7dd5281b8e93698c)
+++ treasury/finance_core/urls.py	(revision f468e6dc2e7a343e90f44bdf7dd5281b8e93698c)
@@ -0,0 +1,5 @@
+from django.conf.urls.defaults import *
+import treasury.finance_core.views
+
+urlpatterns = patterns('',
+)
Index: treasury/finance_core/views.py
===================================================================
--- treasury/finance_core/views.py	(revision f468e6dc2e7a343e90f44bdf7dd5281b8e93698c)
+++ treasury/finance_core/views.py	(revision f468e6dc2e7a343e90f44bdf7dd5281b8e93698c)
@@ -0,0 +1,6 @@
+from django.http import HttpResponse
+import treasury.vouchers.models
+
+def display_tree(request):
+    root = treasury.vouchers.models.BudgetArea.get_by_path(['Accounts'])
+    return HttpResponse(root.dump_to_html())
Index: treasury/settings.py
===================================================================
--- treasury/settings.py	(revision 3306b906c96aa9cbf8f6897e4a41842ae188e9cb)
+++ treasury/settings.py	(revision f468e6dc2e7a343e90f44bdf7dd5281b8e93698c)
@@ -88,3 +88,4 @@
     'treebeard',
     'vouchers',
+    'finance_core',
 )
Index: treasury/urls.py
===================================================================
--- treasury/urls.py	(revision 13a23ce1b69fc795bff17ee750050657eb37a393)
+++ treasury/urls.py	(revision f468e6dc2e7a343e90f44bdf7dd5281b8e93698c)
@@ -1,3 +1,6 @@
 from django.conf.urls.defaults import *
+
+# Necessary views
+import treasury.finance_core.views
 
 # Uncomment the next two lines to enable the admin:
@@ -7,5 +10,7 @@
 urlpatterns = patterns('',
     # Example:
+    (r'^accounts/display_tree', treasury.finance_core.views.display_tree),
     (r'^vouchers/', include('treasury.vouchers.urls')),
+    (r'^finance_core/', include('treasury.finance_core.urls')),
 
     # Uncomment the admin/doc line below and add 'django.contrib.admindocs' 
Index: treasury/vouchers/admin.py
===================================================================
--- treasury/vouchers/admin.py	(revision 6027277083dd622ed4434166b3fa678427de0da8)
+++ treasury/vouchers/admin.py	(revision f468e6dc2e7a343e90f44bdf7dd5281b8e93698c)
@@ -1,15 +1,4 @@
 import vouchers
 from django.contrib import admin
-
-indent_str = u"\u00A0\u00A0"
-
-
-class BudgetAreaAdmin(admin.ModelAdmin):
-    pass
-    #fields = [ 'path', 'name', 'comment', 'owner', 'interested', ]
-
-
-class BudgetTermAdmin(admin.ModelAdmin):
-    prepopulated_fields = {"slug": ("name",)}
 
 
@@ -24,5 +13,5 @@
 
     def get_budgetarea_name(self, area, ):
-        return indent_str*area.depth + unicode(area)
+        return area.indented_name()
 
     fieldsets = [
@@ -33,6 +22,3 @@
 
 
-admin.site.register(vouchers.models.BudgetArea, BudgetAreaAdmin)
-admin.site.register(vouchers.models.BudgetTerm, BudgetTermAdmin)
-admin.site.register(vouchers.models.BudgetAreaTerm)
 admin.site.register(vouchers.models.ReimbursementRequest, ReimbursementRequestAdmin)
Index: treasury/vouchers/models.py
===================================================================
--- treasury/vouchers/models.py	(revision 6027277083dd622ed4434166b3fa678427de0da8)
+++ treasury/vouchers/models.py	(revision f468e6dc2e7a343e90f44bdf7dd5281b8e93698c)
@@ -1,81 +1,5 @@
 from django.db import models
-import treebeard.mp_tree
 import settings
-
-
-class BudgetArea(treebeard.mp_tree.MP_Node):
-    name = models.CharField(max_length=50)
-    comment = models.TextField(blank=True)
-    always = models.BooleanField(blank=True, default=False)
-
-    # Contact / ACL information
-    # If not specified, inherit from parent
-    owner = models.EmailField(help_text = 'Email address of the officer responsible for the area', blank=True) # owner of the budget area
-    interested = models.EmailField(help_text='Email address of parties interested in the area', blank=True) # interested parties (ie, whole committee)
-    use_owner = models.BooleanField(default=False, blank=True)
-
-    def contact_address(self,):
-        address = ''
-        if self.use_owner:
-            address = self.owner or self.interested
-        else:
-            address = self.interested or self.owner
-        return address or self.get_parent().contact_address()
-
-    def mark_used(self, term, comment=""):
-        BudgetAreaTerm.objects.get_or_create(
-            budget_area=self,
-            budget_term=term,
-            defaults={'comment':comment}
-        )
-
-    @classmethod
-    def get_by_path(cls, path):
-        root = BudgetArea.objects.get(name=path[0], depth=1)
-        node = root
-        for name in path[1:]:
-            node = node.get_children().filter(name=name)[0]
-        return node
-
-    def dump_to_html(self):
-        struct = self.dump_bulk()
-        return self.struct_to_html(struct, depth=0)
-
-    def struct_to_html(self, struct, depth=0):
-        def format_data(data):
-            return "<em>"+data['name']+"</em> "+unicode(data)
-        prefix = "\t"*depth
-        html = prefix+"<ul>\n"
-        html = html + "\n".join(
-            [("%(prefix)s\t<li>%(data)s\n%(children)s\t%(prefix)s</li>\n" % {
-                'prefix':prefix,
-                'data':format_data(elem['data']),
-                'children':('children' in elem and self.struct_to_html(elem['children'], depth+1) or '')
-            }) for elem in struct])
-        html = html + prefix + "</ul>\n"
-        return html
-
-    def __unicode__(self,):
-        return u"%s [%s] (%s)" % (self.name, self.contact_address(), self.always, )
-
-
-class BudgetTerm(models.Model):
-    name = models.CharField(max_length=20)
-    slug = models.SlugField(max_length=20)
-    start_date = models.DateField()
-    end_date = models.DateField()
-    submit_deadline = models.DateField()
-
-    def __unicode__(self,):
-        return "%s (%s to %s [due %s])" % (self.name, self.start_date, self.end_date, self.submit_deadline, )
-
-
-class BudgetAreaTerm(models.Model):
-    budget_area = models.ForeignKey(BudgetArea)
-    budget_term = models.ForeignKey(BudgetTerm)
-    comment = models.TextField(blank=True, )
-
-    def __unicode__(self,):
-        return "%s during %s" % (self.budget_area, self.budget_term, )
+from finance_core.models import BudgetArea, BudgetTerm
 
 
@@ -88,6 +12,2 @@
     budget_area = models.ForeignKey(BudgetArea)
     budget_term = models.ForeignKey(BudgetTerm)
-
-def coerce_full_email(email):
-    if '@' in email: return email
-    else: return email + '@' + settings.DEFAULT_DOMAIN
Index: treasury/vouchers/urls.py
===================================================================
--- treasury/vouchers/urls.py	(revision 13a23ce1b69fc795bff17ee750050657eb37a393)
+++ treasury/vouchers/urls.py	(revision f468e6dc2e7a343e90f44bdf7dd5281b8e93698c)
@@ -3,4 +3,3 @@
 
 urlpatterns = patterns('',
-    (r'^accounts/display_tree', treasury.vouchers.views.display_tree),
 )
Index: treasury/vouchers/views.py
===================================================================
--- treasury/vouchers/views.py	(revision 13a23ce1b69fc795bff17ee750050657eb37a393)
+++ treasury/vouchers/views.py	(revision f468e6dc2e7a343e90f44bdf7dd5281b8e93698c)
@@ -1,6 +1,1 @@
-from django.http import HttpResponse
-import treasury.vouchers.models
-
-def display_tree(request):
-    root = treasury.vouchers.models.BudgetArea.get_by_path(['Accounts'])
-    return HttpResponse(root.dump_to_html())
+# Create your views here.
