diff --git a/mayan/apps/document_states/__init__.py b/mayan/apps/document_states/__init__.py index b155e4c0d8..cc54b663ef 100644 --- a/mayan/apps/document_states/__init__.py +++ b/mayan/apps/document_states/__init__.py @@ -52,6 +52,10 @@ register_model_list_columns(WorkflowInstance, [ 'name': _('Current state'), 'attribute': 'get_current_state' }, + { + 'name': _('User'), + 'attribute': encapsulate(lambda workflow: getattr(workflow.get_last_log_entry(), 'user', _('None'))) + }, { 'name': _('Last transition'), 'attribute': 'get_last_transition' @@ -78,10 +82,18 @@ register_model_list_columns(WorkflowInstanceLogEntry, [ 'name': _('Date and time'), 'attribute': 'datetime' }, + { + 'name': _('User'), + 'attribute': 'user' + }, { 'name': _('Transition'), 'attribute': 'transition' }, + { + 'name': _('Comment'), + 'attribute': 'comment' + }, ]) register_links([Document], [link_document_workflow_instance_list], menu_name='form_header') diff --git a/mayan/apps/document_states/forms.py b/mayan/apps/document_states/forms.py index fd7c0331b5..b764d3754d 100644 --- a/mayan/apps/document_states/forms.py +++ b/mayan/apps/document_states/forms.py @@ -39,6 +39,7 @@ class WorkflowInstanceTransitionForm(forms.Form): self.fields['transition'].choices = workflow.get_transition_choices().values_list('pk', 'label') transition = forms.ChoiceField(label=_('Transition')) + comment = forms.CharField(label=_('Comment'), widget=forms.widgets.Textarea()) class WorkflowInstanceDetailForm(DetailForm): diff --git a/mayan/apps/document_states/models.py b/mayan/apps/document_states/models.py index 5f25b50f54..b610fa404b 100644 --- a/mayan/apps/document_states/models.py +++ b/mayan/apps/document_states/models.py @@ -1,5 +1,6 @@ from __future__ import unicode_literals +from django.contrib.auth.models import User from django.core.urlresolvers import reverse from django.db import models from django.utils.encoding import python_2_unicode_compatible @@ -41,7 +42,7 @@ class Workflow(models.Model): class WorkflowState(models.Model): workflow = models.ForeignKey(Workflow, related_name='states', verbose_name=_('Workflow')) label = models.CharField(max_length=255, verbose_name=_('Label')) - initial = models.BooleanField(default=False, verbose_name=_('Initial')) + initial = models.BooleanField(default=False, help_text=_('Select if this will be the state with which you want the workflow to start in. Only one state can be the initial state.'), verbose_name=_('Initial')) def __str__(self): return self.label @@ -82,10 +83,10 @@ class WorkflowInstance(models.Model): def get_absolute_url(self): return reverse('document_states:workflow_instance_detail', args=[str(self.pk)]) - def do_transition(self, transition): + def do_transition(self, comment, transition, user): try: if transition in self.get_current_state().origin_transitions.all(): - self.log_entries.create(transition=transition) + self.log_entries.create(comment=comment, transition=transition, user=user) except AttributeError: # No initial state has been set for this workflow pass @@ -123,8 +124,10 @@ class WorkflowInstance(models.Model): @python_2_unicode_compatible class WorkflowInstanceLogEntry(models.Model): workflow_instance = models.ForeignKey(WorkflowInstance, related_name='log_entries', verbose_name=_('Workflow instance')) - datetime = models.DateTimeField(auto_now_add=True, verbose_name=_('Datetime')) + datetime = models.DateTimeField(auto_now_add=True, db_index=True, verbose_name=_('Datetime')) transition = models.ForeignKey(WorkflowTransition, verbose_name=_('Transition')) + user = models.ForeignKey(User, verbose_name=_('User')) + comment = models.TextField(blank=True, verbose_name=_('Comment')) def __str__(self): return unicode(self.transition) diff --git a/mayan/apps/document_states/south_migrations/0008_auto__add_field_workflowinstancelogentry_user__add_field_workflowinsta.py b/mayan/apps/document_states/south_migrations/0008_auto__add_field_workflowinstancelogentry_user__add_field_workflowinsta.py new file mode 100644 index 0000000000..3f998c5b02 --- /dev/null +++ b/mayan/apps/document_states/south_migrations/0008_auto__add_field_workflowinstancelogentry_user__add_field_workflowinsta.py @@ -0,0 +1,121 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding field 'WorkflowInstanceLogEntry.user' + db.add_column(u'document_states_workflowinstancelogentry', 'user', + self.gf('django.db.models.fields.related.ForeignKey')(default=1, to=orm['auth.User']), + keep_default=False) + + # Adding field 'WorkflowInstanceLogEntry.comment' + db.add_column(u'document_states_workflowinstancelogentry', 'comment', + self.gf('django.db.models.fields.TextField')(default='', blank=True), + keep_default=False) + + + def backwards(self, orm): + # Deleting field 'WorkflowInstanceLogEntry.user' + db.delete_column(u'document_states_workflowinstancelogentry', 'user_id') + + # Deleting field 'WorkflowInstanceLogEntry.comment' + db.delete_column(u'document_states_workflowinstancelogentry', 'comment') + + + models = { + u'auth.group': { + 'Meta': {'object_name': 'Group'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + u'auth.permission': { + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + u'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + u'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + u'document_states.workflow': { + 'Meta': {'object_name': 'Workflow'}, + 'document_types': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "u'workflows'", 'symmetrical': 'False', 'to': u"orm['documents.DocumentType']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'label': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}) + }, + u'document_states.workflowinstance': { + 'Meta': {'unique_together': "((u'document', u'workflow'),)", 'object_name': 'WorkflowInstance'}, + 'document': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'workflows'", 'to': u"orm['documents.Document']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'instances'", 'to': u"orm['document_states.Workflow']"}) + }, + u'document_states.workflowinstancelogentry': { + 'Meta': {'object_name': 'WorkflowInstanceLogEntry'}, + 'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'datetime': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'transition': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['document_states.WorkflowTransition']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}), + 'workflow_instance': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'log_entries'", 'to': u"orm['document_states.WorkflowInstance']"}) + }, + u'document_states.workflowstate': { + 'Meta': {'unique_together': "((u'workflow', u'label'),)", 'object_name': 'WorkflowState'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'initial': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'label': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'states'", 'to': u"orm['document_states.Workflow']"}) + }, + u'document_states.workflowtransition': { + 'Meta': {'unique_together': "((u'workflow', u'label', u'origin_state', u'destination_state'),)", 'object_name': 'WorkflowTransition'}, + 'destination_state': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'destination_transitions'", 'to': u"orm['document_states.WorkflowState']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'label': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'origin_state': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'origin_transitions'", 'to': u"orm['document_states.WorkflowState']"}), + 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'transitions'", 'to': u"orm['document_states.Workflow']"}) + }, + u'documents.document': { + 'Meta': {'ordering': "[u'-date_added']", 'object_name': 'Document'}, + 'date_added': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'document_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'documents'", 'to': u"orm['documents.DocumentType']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'label': ('django.db.models.fields.CharField', [], {'default': "u'Uninitialized document'", 'max_length': '255', 'db_index': 'True'}), + 'language': ('django.db.models.fields.CharField', [], {'default': "u'eng'", 'max_length': '8'}), + 'uuid': ('django.db.models.fields.CharField', [], {'default': "u'9c109575-8a14-44ac-b32d-6f1d6ff056ee'", 'max_length': '48'}) + }, + u'documents.documenttype': { + 'Meta': {'ordering': "[u'name']", 'object_name': 'DocumentType'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32'}), + 'ocr': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + } + } + + complete_apps = ['document_states'] \ No newline at end of file diff --git a/mayan/apps/document_states/south_migrations/0009_auto__add_index_workflowinstancelogentry_datetime.py b/mayan/apps/document_states/south_migrations/0009_auto__add_index_workflowinstancelogentry_datetime.py new file mode 100644 index 0000000000..3f884f2719 --- /dev/null +++ b/mayan/apps/document_states/south_migrations/0009_auto__add_index_workflowinstancelogentry_datetime.py @@ -0,0 +1,111 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding index on 'WorkflowInstanceLogEntry', fields ['datetime'] + db.create_index(u'document_states_workflowinstancelogentry', ['datetime']) + + + def backwards(self, orm): + # Removing index on 'WorkflowInstanceLogEntry', fields ['datetime'] + db.delete_index(u'document_states_workflowinstancelogentry', ['datetime']) + + + models = { + u'auth.group': { + 'Meta': {'object_name': 'Group'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + u'auth.permission': { + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + u'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + u'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + u'document_states.workflow': { + 'Meta': {'object_name': 'Workflow'}, + 'document_types': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "u'workflows'", 'symmetrical': 'False', 'to': u"orm['documents.DocumentType']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'label': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}) + }, + u'document_states.workflowinstance': { + 'Meta': {'unique_together': "((u'document', u'workflow'),)", 'object_name': 'WorkflowInstance'}, + 'document': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'workflows'", 'to': u"orm['documents.Document']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'instances'", 'to': u"orm['document_states.Workflow']"}) + }, + u'document_states.workflowinstancelogentry': { + 'Meta': {'object_name': 'WorkflowInstanceLogEntry'}, + 'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'datetime': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'transition': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['document_states.WorkflowTransition']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}), + 'workflow_instance': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'log_entries'", 'to': u"orm['document_states.WorkflowInstance']"}) + }, + u'document_states.workflowstate': { + 'Meta': {'unique_together': "((u'workflow', u'label'),)", 'object_name': 'WorkflowState'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'initial': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'label': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'states'", 'to': u"orm['document_states.Workflow']"}) + }, + u'document_states.workflowtransition': { + 'Meta': {'unique_together': "((u'workflow', u'label', u'origin_state', u'destination_state'),)", 'object_name': 'WorkflowTransition'}, + 'destination_state': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'destination_transitions'", 'to': u"orm['document_states.WorkflowState']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'label': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'origin_state': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'origin_transitions'", 'to': u"orm['document_states.WorkflowState']"}), + 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'transitions'", 'to': u"orm['document_states.Workflow']"}) + }, + u'documents.document': { + 'Meta': {'ordering': "[u'-date_added']", 'object_name': 'Document'}, + 'date_added': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'document_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'documents'", 'to': u"orm['documents.DocumentType']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'label': ('django.db.models.fields.CharField', [], {'default': "u'Uninitialized document'", 'max_length': '255', 'db_index': 'True'}), + 'language': ('django.db.models.fields.CharField', [], {'default': "u'eng'", 'max_length': '8'}), + 'uuid': ('django.db.models.fields.CharField', [], {'default': "u'56623f41-d7c2-40b2-9f01-c53fd42b2abd'", 'max_length': '48'}) + }, + u'documents.documenttype': { + 'Meta': {'ordering': "[u'name']", 'object_name': 'DocumentType'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32'}), + 'ocr': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + } + } + + complete_apps = ['document_states'] \ No newline at end of file diff --git a/mayan/apps/document_states/views.py b/mayan/apps/document_states/views.py index 79d0c66a6f..33e2908fdd 100644 --- a/mayan/apps/document_states/views.py +++ b/mayan/apps/document_states/views.py @@ -79,7 +79,7 @@ class WorkflowInstanceDetailView(SingleObjectListView): return get_object_or_404(WorkflowInstance, pk=self.kwargs['pk']) def get_queryset(self): - return self.get_workflow_instance().log_entries.all() + return self.get_workflow_instance().log_entries.order_by('-datetime') def get_context_data(self, **kwargs): form = WorkflowInstanceDetailForm(instance=self.get_workflow_instance(), extra_fields=[ @@ -133,7 +133,7 @@ class WorkflowInstanceTransitionView(FormView): def form_valid(self, form): transition = self.get_workflow_instance().workflow.transitions.get(pk=form.cleaned_data['transition']) - self.get_workflow_instance().do_transition(transition) + self.get_workflow_instance().do_transition(comment=form.cleaned_data['comment'], transition=transition, user=self.request.user) return HttpResponseRedirect(self.get_success_url()) def get_form_kwargs(self):