diff --git a/migrations/versions/4b813bbbd8ef_.py b/migrations/versions/4b813bbbd8ef_.py
new file mode 100644
index 0000000000000000000000000000000000000000..53eb3b04c8aa10d8d2ce86386c1f413b89e082fe
--- /dev/null
+++ b/migrations/versions/4b813bbbd8ef_.py
@@ -0,0 +1,38 @@
+"""empty message
+
+Revision ID: 4b813bbbd8ef
+Revises: 8fdd381e6a2a
+Create Date: 2017-03-15 02:18:24.986531
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = '4b813bbbd8ef'
+down_revision = '8fdd381e6a2a'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+    # ### commands auto generated by Alembic - please adjust! ###
+    op.create_table('decisioncategories',
+    sa.Column('id', sa.Integer(), nullable=False),
+    sa.Column('protocoltype_id', sa.Integer(), nullable=True),
+    sa.Column('name', sa.String(), nullable=True),
+    sa.ForeignKeyConstraint(['protocoltype_id'], ['protocoltypes.id'], ),
+    sa.PrimaryKeyConstraint('id')
+    )
+    op.add_column('decisions', sa.Column('category_id', sa.Integer(), nullable=True))
+    op.create_foreign_key(None, 'decisions', 'decisioncategories', ['category_id'], ['id'])
+    # ### end Alembic commands ###
+
+
+def downgrade():
+    # ### commands auto generated by Alembic - please adjust! ###
+    op.drop_constraint(None, 'decisions', type_='foreignkey')
+    op.drop_column('decisions', 'category_id')
+    op.drop_table('decisioncategories')
+    # ### end Alembic commands ###
diff --git a/models/database.py b/models/database.py
index 141b4cc332bbad5081a6da063f7b23ee0b4465f9..c56d9bd7ce4c85b57f7b1f942796889c86c5aeae 100644
--- a/models/database.py
+++ b/models/database.py
@@ -72,6 +72,7 @@ class ProtocolType(DatabaseModel):
     reminders = relationship("MeetingReminder", backref=backref("protocoltype"), cascade="all, delete-orphan", order_by="MeetingReminder.days_before")
     todos = relationship("Todo", backref=backref("protocoltype"), order_by="Todo.id")
     metas = relationship("DefaultMeta", backref=backref("protocoltype"), cascade="all, delete-orphan")
+    decisioncategories = relationship("DecisionCategory", backref=backref("protocoltype"), cascade="all, delete-orphan")
 
     def get_latest_protocol(self):
         candidates = sorted([protocol for protocol in self.protocols if protocol.is_done()], key=lambda p: p.date, reverse=True)
@@ -540,12 +541,25 @@ class Decision(DatabaseModel):
     id = db.Column(db.Integer, primary_key=True)
     protocol_id = db.Column(db.Integer, db.ForeignKey("protocols.id"))
     content = db.Column(db.String)
+    category_id = db.Column(db.Integer, db.ForeignKey("decisioncategories.id"), nullable=True)
 
     document = relationship("DecisionDocument", backref=backref("decision"), cascade="all, delete-orphan", uselist=False)
 
     def get_parent(self):
         return self.protocol
 
+class DecisionCategory(DatabaseModel):
+    __tablename__ = "decisioncategories"
+    __model_name__ = "decisioncategory"
+    id = db.Column(db.Integer, primary_key=True)
+    protocoltype_id = db.Column(db.Integer, db.ForeignKey("protocoltypes.id"))
+    name = db.Column(db.String)
+
+    decisions = relationship("Decision", backref=backref("category"), order_by="Decision.id")
+
+    def get_parent(self):
+        return self.protocoltype
+
 class MeetingReminder(DatabaseModel):
     __tablename__ = "meetingreminders"
     __model_name__ = "meetingreminder"
@@ -621,6 +635,6 @@ class Meta(DatabaseModel):
 
 ALL_MODELS = [
     ProtocolType, Protocol, DefaultTOP, TOP, Document, DecisionDocument,
-    Todo, Decision, MeetingReminder, Error, DefaultMeta, Meta
+    Todo, Decision, MeetingReminder, Error, DefaultMeta, Meta, DecisionCategory
 ]
     
diff --git a/server.py b/server.py
index 8a3fdb2f3e04ee8f1b9c64792531b9b54e326c01..0697ed814070fcee49485c9e459e4f167e9d9a6f 100755
--- a/server.py
+++ b/server.py
@@ -23,9 +23,9 @@ import config
 from shared import db, date_filter, datetime_filter, date_filter_long, date_filter_short, time_filter, time_filter_short, ldap_manager, security_manager, current_user, check_login, login_required, group_required, class_filter, needs_date_test, todostate_name_filter, code_filter, indent_tab_filter
 from utils import is_past, mail_manager, url_manager, get_first_unused_int, set_etherpad_text, get_etherpad_text, split_terms, optional_int_arg
 from decorators import db_lookup, require_public_view_right, require_private_view_right, require_modify_right, require_admin_right
-from models.database import ProtocolType, Protocol, DefaultTOP, TOP, Document, Todo, Decision, MeetingReminder, Error, TodoMail, DecisionDocument, TodoState, Meta, DefaultMeta
-from views.forms import LoginForm, ProtocolTypeForm, DefaultTopForm, MeetingReminderForm, NewProtocolForm, DocumentUploadForm, KnownProtocolSourceUploadForm, NewProtocolSourceUploadForm, ProtocolForm, TopForm, SearchForm, NewProtocolFileUploadForm, NewTodoForm, TodoForm, TodoMailForm, DefaultMetaForm, MetaForm, MergeTodosForm
-from views.tables import ProtocolsTable, ProtocolTypesTable, ProtocolTypeTable, DefaultTOPsTable, MeetingRemindersTable, ErrorsTable, TodosTable, DocumentsTable, DecisionsTable, TodoTable, ErrorTable, TodoMailsTable, DefaultMetasTable
+from models.database import ProtocolType, Protocol, DefaultTOP, TOP, Document, Todo, Decision, MeetingReminder, Error, TodoMail, DecisionDocument, TodoState, Meta, DefaultMeta, DecisionCategory
+from views.forms import LoginForm, ProtocolTypeForm, DefaultTopForm, MeetingReminderForm, NewProtocolForm, DocumentUploadForm, KnownProtocolSourceUploadForm, NewProtocolSourceUploadForm, ProtocolForm, TopForm, SearchForm, DecisionSearchForm, NewProtocolFileUploadForm, NewTodoForm, TodoForm, TodoMailForm, DefaultMetaForm, MetaForm, MergeTodosForm, DecisionCategoryForm
+from views.tables import ProtocolsTable, ProtocolTypesTable, ProtocolTypeTable, DefaultTOPsTable, MeetingRemindersTable, ErrorsTable, TodosTable, DocumentsTable, DecisionsTable, TodoTable, ErrorTable, TodoMailsTable, DefaultMetasTable, DecisionCategoriesTable
 from legacy import import_old_todos, import_old_protocols, import_old_todomails
 
 app = Flask(__name__)
@@ -242,7 +242,8 @@ def show_type(protocoltype):
     default_tops_table = DefaultTOPsTable(protocoltype.default_tops, protocoltype)
     reminders_table = MeetingRemindersTable(protocoltype.reminders, protocoltype)
     metas_table = DefaultMetasTable(protocoltype.metas, protocoltype)
-    return render_template("type-show.html", protocoltype=protocoltype, protocoltype_table=protocoltype_table, default_tops_table=default_tops_table, metas_table=metas_table, reminders_table=reminders_table, mail_active=config.MAIL_ACTIVE)
+    categories_table = DecisionCategoriesTable(protocoltype.decisioncategories, protocoltype)
+    return render_template("type-show.html", protocoltype=protocoltype, protocoltype_table=protocoltype_table, default_tops_table=default_tops_table, metas_table=metas_table, reminders_table=reminders_table, mail_active=config.MAIL_ACTIVE, categories_table=categories_table)
 
 @app.route("/type/delete/<int:protocoltype_id>")
 @login_required
@@ -900,16 +901,30 @@ def list_decisions():
     user = current_user()
     protocoltype = None
     protocoltype_id = None
+    decisioncategory = None
+    decisioncategory_id = None
     try:
         protocoltype_id = int(request.args.get("protocoltype_id"))
     except (ValueError, TypeError):
         pass
+    try:
+        decisioncategory_id = int(request.args.get("decisioncategory_id"))
+    except (ValueError, TypeError):
+        pass
     search_term = request.args.get("search")
     protocoltypes = ProtocolType.get_public_protocoltypes(user)
-    search_form = SearchForm(protocoltypes)
+    decisioncategories = [
+        category
+        for protocoltype in protocoltypes
+        for category in protocoltype.decisioncategories
+    ]
+    search_form = DecisionSearchForm(protocoltypes, decisioncategories)
     if protocoltype_id is not None:
         search_form.protocoltype_id.data = protocoltype_id
         protocoltype = ProtocolType.query.filter_by(id=protocoltype_id).first()
+    if decisioncategory_id is not None:
+        search_form.decisioncategory_id.data = decisioncategory_id
+        decisioncategory = DecisionCategory.query.filter_by(id=decisioncategory_id).first()
     if search_term is not None:
         search_form.search.data = search_term
     decisions = [
@@ -921,6 +936,12 @@ def list_decisions():
             decision for decision in decisions 
             if decision.protocol.protocoltype.id == protocoltype_id
         ]
+    if decisioncategory_id is not None and decisioncategory_id != -1:
+        decisions = [
+            decision for decision in decisions
+            if decision.category is not None
+            and decision.category.id == decisioncategory_id
+        ]
     if search_term is not None and len(search_term.strip()) > 0:
         decisions = [
             decision for decision in decisions
@@ -1126,7 +1147,47 @@ def delete_defaultmeta(defaultmeta):
     type_id = defaultmeta.protocoltype.id
     db.session.delete(meta)
     db.session.commit()
-    flash("Metadatenfeld '{}' gelöscht.".format(name), "alert-error")
+    flash("Metadatenfeld '{}' gelöscht.".format(name), "alert-success")
+    return redirect(request.args.get("next") or url_for("show_type", protocoltype_id=type_id))
+
+@app.route("/decisioncategory/new/<int:protocoltype_id>", methods=["GET", "POST"])
+@login_required
+@db_lookup(ProtocolType)
+@require_modify_right()
+def new_decisioncategory(protocoltype):
+    form = DecisionCategoryForm()
+    if form.validate_on_submit():
+        category = DecisionCategory(protocoltype_id=protocoltype.id)
+        form.populate_obj(category)
+        db.session.add(category)
+        db.session.commit()
+        flash("Beschlusskategorie hinzugefügt.", "alert-success")
+        return redirect(request.args.get("next") or url_for("show_type", protocoltype_id=protocoltype.id))
+    return render_template("decisioncategory-new.html", form=form, protocoltype=protocoltype)
+
+@app.route("/decisioncategory/edit/<int:decisioncategory_id>", methods=["GET", "POST"])
+@login_required
+@db_lookup(DecisionCategory)
+@require_modify_right()
+def edit_decisioncategory(decisioncategory):
+    form = DecisionCategoryForm(obj=decisioncategory)
+    if form.validate_on_submit():
+        form.populate_obj(decisioncategory)
+        db.session.commit()
+        return redirect(request.args.get("next") or url_for("show_type", protocoltype_id=decisioncategory.protocoltype.id))
+    return render_template("decisioncategory-edit.html", form=form, decisioncategory=decisioncategory)
+
+@app.route("/decisioncategory/delete/<int:decisioncategory_id>")
+@login_required
+@group_required(config.ADMIN_GROUP)
+@db_lookup(DecisionCategory)
+@require_modify_right()
+def delete_decisioncategory(decisioncategory):
+    name = decisioncategory.name
+    type_id = decisioncategory.protocoltype.id
+    db.session.delete(decisioncategory)
+    db.session.commit()
+    flash("Beschlusskategorie {} gelöscht.".format(name), "alert-success")
     return redirect(request.args.get("next") or url_for("show_type", protocoltype_id=type_id))
 
 @app.route("/login", methods=["GET", "POST"])
diff --git a/tasks.py b/tasks.py
index 18527844de3a3b909da09c3c89cf911025b815d8..84f469760062bea64397f66d8fb4372d66fe1597 100644
--- a/tasks.py
+++ b/tasks.py
@@ -8,7 +8,7 @@ from datetime import datetime
 import traceback
 from copy import copy
 
-from models.database import Document, Protocol, Error, Todo, Decision, TOP, DefaultTOP, MeetingReminder, TodoMail, DecisionDocument, TodoState, OldTodo
+from models.database import Document, Protocol, Error, Todo, Decision, TOP, DefaultTOP, MeetingReminder, TodoMail, DecisionDocument, TodoState, OldTodo, DecisionCategory
 from models.errors import DateNotMatchingException
 from server import celery, app
 from shared import db, escape_tex, unhyphen, date_filter, datetime_filter, date_filter_long, date_filter_short, time_filter, class_filter, KNOWN_KEYS
@@ -281,7 +281,32 @@ def parse_protocol_async_inner(protocol, encoded_kwargs):
             db.session.add(error)
             db.session.commit()
             return
-        decision = Decision(protocol_id=protocol.id, content=decision_tag.values[0])
+        decision_content = decision_tag.values[0]
+        decision_category_id = None
+        if len(decision_tag.values) > 1:
+            decision_category_name = decision_tag.values[1]
+            decision_category = DecisionCategory.query.filter_by(protocoltype_id=protocol.protocoltype.id, name=decision_category_name).first()
+            if decision_category is None:
+                category_candidates = DecisionCategory.query.filter_by(protocoltype_id=protocol.protocoltype.id).all()
+                category_names = [
+                    "'{}'".format(category.name)
+                    for category in category_candidates
+                ]
+                error = protocol.create_error("Parsing",
+                    "Unknown decision category",
+                    "The decision in line {} has the category {}, "
+                    "but there is no such category. "
+                    "Known categories are {}".format(
+                        decision_tag.linenumber,
+                        decision_category_name,
+                        ", ".join(category_names)))
+                db.session.add(error)
+                db.session.commit()
+                return
+            else:
+                decision_category_id = decision_category.id
+        decision = Decision(protocol_id=protocol.id,
+            content=decision_content, category_id=decision_category_id)
         db.session.add(decision)
         db.session.commit()
         decision_top = decision_tag.fork.get_top()
diff --git a/templates/decisioncategory-edit.html b/templates/decisioncategory-edit.html
new file mode 100644
index 0000000000000000000000000000000000000000..5fe0654a16b56bab754f1ca340bf9b1d221bae8c
--- /dev/null
+++ b/templates/decisioncategory-edit.html
@@ -0,0 +1,9 @@
+{% extends "layout.html" %}
+{% from "macros.html" import render_form %}
+{% block title %}Beschlusskategorie ändern{% endblock %}
+
+{% block content %}
+<div class="container">
+    {{render_form(form, action_url=url_for("edit_decisioncategory", decisioncategory_id=decisioncategory.id, next=url_for("show_type", protocoltype_id=decisioncategory.protocoltype.id)), action_text="Ändern")}}
+</div>
+{% endblock %}
diff --git a/templates/decisioncategory-new.html b/templates/decisioncategory-new.html
new file mode 100644
index 0000000000000000000000000000000000000000..91d0d77dfd0bf23a9b72bacb2582824944315920
--- /dev/null
+++ b/templates/decisioncategory-new.html
@@ -0,0 +1,9 @@
+{% extends "layout.html" %}
+{% from "macros.html" import render_form %}
+{% block title %}Beschlusskategorie hinzufügen{% endblock %}
+
+{% block content %}
+<div class="container">
+    {{render_form(form, action_url=url_for("new_decisioncategory", protocoltype_id=protocoltype.id, next=url_for("show_type", protocoltype_id=protocoltype.id)), action_text="Anlegen")}}
+</div>
+{% endblock %}
diff --git a/templates/type-show.html b/templates/type-show.html
index 6049d106a96c2f7818b27baf4128d37cec1ff4f7..474bebbe6dfe6eaceebc94d3965a5059fb69ec2c 100644
--- a/templates/type-show.html
+++ b/templates/type-show.html
@@ -11,5 +11,6 @@
         {{render_table(reminders_table)}}
     {% endif %}
     {{render_table(metas_table)}}
+    {{render_table(categories_table)}}
 </div>
 {% endblock %}
diff --git a/views/forms.py b/views/forms.py
index cff09373447f0353df827af764007ffb6e24c189..d03016f8886e87d17aebf93984988605020172f9 100644
--- a/views/forms.py
+++ b/views/forms.py
@@ -18,7 +18,17 @@ def get_protocoltype_choices(protocoltypes, add_all=True):
         in sorted(protocoltypes, key=lambda t: t.short_name)
     ]
     if add_all:
-        choices.insert(0, (-1, "Alle"))
+        choices.insert(0, (-1, "Alle Typen"))
+    return choices
+
+def get_category_choices(categories, add_all=True):
+    choices = [
+        (category.id, category.name)
+        for category
+        in sorted(categories, key=lambda c: c.name)
+    ]
+    if add_all:
+        choices.insert(0, (-1, "Alle Kategorien"))
     return choices
 
 def get_todostate_choices():
@@ -188,6 +198,13 @@ class SearchForm(FlaskForm):
         super().__init__(**kwargs)
         self.protocoltype_id.choices = get_protocoltype_choices(protocoltypes)
 
+class DecisionSearchForm(SearchForm):
+    decisioncategory_id = SelectField("Kategorie", choices=[], coerce=int)
+
+    def __init__(self, protocoltypes, categories, **kwargs):
+        super().__init__(protocoltypes=protocoltypes, **kwargs)
+        self.decisioncategory_id.choices = get_category_choices(categories)
+
 class NewTodoForm(FlaskForm):
     protocoltype_id = SelectField("Typ", choices=[], coerce=int)
     who = StringField("Person", validators=[InputRequired("Bitte gib an, wer das Todo erledigen soll.")])
@@ -222,6 +239,9 @@ class DefaultMetaForm(FlaskForm):
     key = StringField("Key", validators=[InputRequired("Bitte gib den Protokoll-Syntax-Schlüssel der Metadaten an.")])
     name = StringField("Name", validators=[InputRequired("Bitte gib den Namen der Metadaten an.")])
 
+class DecisionCategoryForm(FlaskForm):
+    name = StringField("Name", validators=[InputRequired("Bitte gib den Namen der Kategorie an.")])
+
 class MergeTodosForm(FlaskForm):
     todo1 = IntegerField("todo 1", validators=[InputRequired()])
     todo2 = IntegerField("todo 2", validators=[InputRequired()])
diff --git a/views/tables.py b/views/tables.py
index a92f832eebb4fb44849aa9dd1bf1ced58a623fa7..253a73d2e1b51c8eef558d6bef58eec551eb1c50 100644
--- a/views/tables.py
+++ b/views/tables.py
@@ -373,21 +373,36 @@ class TodoTable(SingleValueTable):
 class DecisionsTable(Table):
     def __init__(self, decisions):
         super().__init__("Beschlüsse", decisions)
+        self.category_present = len([
+            decision for decision in decisions
+            if decision.category is not None
+        ]) > 0
 
     def headers(self):
-        return ["Sitzung", "Beschluss", ""]
+        content_part = ["Sitzung", "Beschluss"]
+        category_part = ["Kategorie"]
+        if not self.category_present:
+            category_part = []
+        action_part = [""]
+        return content_part + category_part + action_part
 
     def row(self, decision):
         user = current_user()
-        return [
+        content_part = [
             Table.link(url_for("show_protocol", protocol_id=decision.protocol.id), decision.protocol.get_identifier()),
-            decision.content,
+            decision.content
+        ]
+        category_part = [decision.category.name if decision.category is not None else ""]
+        if not self.category_present:
+            category_part = []
+        action_part = [
             Table.link(url_for("print_decision", decisiondocument_id=decision.document.id), "Drucken")
                 if config.PRINTING_ACTIVE
                 and decision.protocol.protocoltype.has_modify_right(user)
                 and decision.document is not None 
                 else ""
         ]
+        return content_part + category_part + action_part
 
 class DocumentsTable(Table):
     def __init__(self, documents):
@@ -453,3 +468,26 @@ class DefaultMetasTable(Table):
         link_part = [Table.concat(links)]
         return general_part + link_part
 
+class DecisionCategoriesTable(Table):
+    def __init__(self, categories, protocoltype):
+        print(categories)
+        super().__init__(
+            "Beschlusskategorien",
+            categories, 
+            url_for("new_decisioncategory", protocoltype_id=protocoltype.id)
+        )
+
+    def headers(self):
+        return ["Name", ""]
+
+    def row(self, category):
+        user = current_user()
+        general_part = [category.name]
+        action_part = [
+            Table.concat([
+                Table.link(url_for("edit_decisioncategory", decisioncategory_id=category.id), "Ändern"),
+                Table.link(url_for("delete_decisioncategory", decisioncategory_id=category.id), "Löschen", confirm="Bist du dir sicher, dass du die Beschlusskategorie {} löschen willst?".format(category.name))
+            ])
+        ]
+        return general_part + action_part
+