diff --git a/docs/releases/2.5.rst b/docs/releases/2.5.rst index 73c2754894..0a4d590a15 100644 --- a/docs/releases/2.5.rst +++ b/docs/releases/2.5.rst @@ -87,6 +87,7 @@ Other Changes will be loaded and visible by default when a view loads. - Improve usability and appearance on medium and small devices like tablets and smartphones. +- Improve document detection by initial workflow state. Removals -------- diff --git a/mayan/apps/document_states/api_views.py b/mayan/apps/document_states/api_views.py index 62de61d99e..d3f76526ce 100644 --- a/mayan/apps/document_states/api_views.py +++ b/mayan/apps/document_states/api_views.py @@ -31,7 +31,9 @@ class APIDocumentTypeWorkflowListView(generics.ListAPIView): """ Returns a list of all the document type workflows. """ - return super(APIDocumentTypeWorkflowListView, self).get(*args, **kwargs) + return super( + APIDocumentTypeWorkflowListView, self + ).get(*args, **kwargs) def get_document_type(self): document_type = get_object_or_404(DocumentType, pk=self.kwargs['pk']) @@ -600,4 +602,6 @@ class APIWorkflowInstanceLogEntryListView(generics.ListCreateAPIView): Transition a document workflow by creating a new document workflow log entry. """ - return super(APIWorkflowInstanceLogEntryListView, self).post(*args, **kwargs) + return super( + APIWorkflowInstanceLogEntryListView, self + ).post(*args, **kwargs) diff --git a/mayan/apps/document_states/apps.py b/mayan/apps/document_states/apps.py index fd8c7d36a6..cb02e5c647 100644 --- a/mayan/apps/document_states/apps.py +++ b/mayan/apps/document_states/apps.py @@ -1,7 +1,7 @@ from __future__ import unicode_literals from django.apps import apps -from django.db.models.signals import pre_delete, post_delete, post_save +from django.db.models.signals import post_save from django.utils.translation import ugettext_lazy as _ from kombu import Exchange, Queue diff --git a/mayan/apps/document_states/forms.py b/mayan/apps/document_states/forms.py index ca8612f170..30d6fb3c94 100644 --- a/mayan/apps/document_states/forms.py +++ b/mayan/apps/document_states/forms.py @@ -22,8 +22,17 @@ class WorkflowTransitionForm(forms.ModelForm): def __init__(self, *args, **kwargs): workflow = kwargs.pop('workflow') super(WorkflowTransitionForm, self).__init__(*args, **kwargs) - self.fields['origin_state'].queryset = self.fields['origin_state'].queryset.filter(workflow=workflow) - self.fields['destination_state'].queryset = self.fields['destination_state'].queryset.filter(workflow=workflow) + self.fields[ + 'origin_state' + ].queryset = self.fields[ + 'origin_state' + ].queryset.filter(workflow=workflow) + + self.fields[ + 'destination_state' + ].queryset = self.fields[ + 'destination_state' + ].queryset.filter(workflow=workflow) class Meta: fields = ('label', 'origin_state', 'destination_state') diff --git a/mayan/apps/document_states/links.py b/mayan/apps/document_states/links.py index f2d41f7e58..068af016a6 100644 --- a/mayan/apps/document_states/links.py +++ b/mayan/apps/document_states/links.py @@ -96,7 +96,8 @@ link_workflow_list = Link( ) link_workflow_state_document_list = Link( permissions=(permission_workflow_view,), - text=_('State documents'), view='document_states:workflow_state_document_list', + text=_('State documents'), + view='document_states:workflow_state_document_list', args='resolved_object.pk' ) link_workflow_state_list = Link( diff --git a/mayan/apps/document_states/migrations/0003_auto_20170325_0447.py b/mayan/apps/document_states/migrations/0003_auto_20170325_0447.py index 76b055a35b..5dcaa0bee0 100644 --- a/mayan/apps/document_states/migrations/0003_auto_20170325_0447.py +++ b/mayan/apps/document_states/migrations/0003_auto_20170325_0447.py @@ -36,14 +36,24 @@ class Migration(migrations.Migration): ), migrations.AlterModelOptions( name='workflow', - options={'ordering': ('label',), 'verbose_name': 'Workflow', 'verbose_name_plural': 'Workflows'}, + options={ + 'ordering': ('label',), + 'verbose_name': 'Workflow', 'verbose_name_plural': 'Workflows' + }, ), migrations.AlterModelOptions( name='workflowstate', - options={'ordering': ('label',), 'verbose_name': 'Workflow state', 'verbose_name_plural': 'Workflow states'}, + options={ + 'ordering': ('label',), + 'verbose_name': 'Workflow state', + 'verbose_name_plural': 'Workflow states' + }, ), migrations.AlterModelOptions( name='workflowtransition', - options={'ordering': ('label',), 'verbose_name': 'Workflow transition', 'verbose_name_plural': 'Workflow transitions'}, + options={ + 'ordering': ('label',), 'verbose_name': 'Workflow transition', + 'verbose_name_plural': 'Workflow transitions' + }, ), ] diff --git a/mayan/apps/document_states/models.py b/mayan/apps/document_states/models.py index 05ae0b6581..3c70c7395e 100644 --- a/mayan/apps/document_states/models.py +++ b/mayan/apps/document_states/models.py @@ -6,6 +6,7 @@ from django.conf import settings from django.core.exceptions import PermissionDenied, ValidationError from django.core.urlresolvers import reverse from django.db import IntegrityError, models +from django.db.models import F, Max, Q from django.utils.encoding import force_text, python_2_unicode_compatible from django.utils.translation import ugettext_lazy as _ @@ -106,6 +107,12 @@ class WorkflowState(models.Model): ), verbose_name=_('Completion') ) + class Meta: + ordering = ('label',) + unique_together = ('workflow', 'label') + verbose_name = _('Workflow state') + verbose_name_plural = _('Workflow states') + def __str__(self): return self.label @@ -114,11 +121,30 @@ class WorkflowState(models.Model): self.workflow.states.all().update(initial=False) return super(WorkflowState, self).save(*args, **kwargs) - class Meta: - ordering = ('label',) - unique_together = ('workflow', 'label') - verbose_name = _('Workflow state') - verbose_name_plural = _('Workflow states') + def get_documents(self): + latest_entries = WorkflowInstanceLogEntry.objects.annotate( + max_datetime=Max( + 'workflow_instance__log_entries__datetime' + ) + ).filter( + datetime=F('max_datetime') + ) + + state_latest_entries = latest_entries.filter( + transition__destination_state=self + ) + + return Document.objects.filter( + Q( + workflows__pk__in=state_latest_entries.values_list( + 'workflow_instance', flat=True + ) + ) | Q( + workflows__log_entries__isnull=True, + workflows__workflow__states=self, + workflows__workflow__states__initial=True + ) + ).distinct() @python_2_unicode_compatible diff --git a/mayan/apps/document_states/views.py b/mayan/apps/document_states/views.py index c77d77d538..fd0ed0898e 100644 --- a/mayan/apps/document_states/views.py +++ b/mayan/apps/document_states/views.py @@ -2,7 +2,6 @@ from __future__ import absolute_import, unicode_literals from django.contrib import messages from django.core.urlresolvers import reverse, reverse_lazy -from django.db.models import F, Max from django.db.utils import IntegrityError from django.http import HttpResponseRedirect from django.shortcuts import get_object_or_404 @@ -21,8 +20,8 @@ from .forms import ( WorkflowTransitionForm ) from .models import ( - Workflow, WorkflowInstance, WorkflowInstanceLogEntry, WorkflowState, - WorkflowTransition, WorkflowRuntimeProxy, WorkflowStateRuntimeProxy + Workflow, WorkflowInstance, WorkflowState, WorkflowTransition, + WorkflowRuntimeProxy, WorkflowStateRuntimeProxy ) from .permissions import ( permission_workflow_create, permission_workflow_delete, @@ -97,6 +96,11 @@ class WorkflowInstanceTransitionView(FormView): comment=form.cleaned_data['comment'], transition=form.cleaned_data['transition'], user=self.request.user ) + messages.success( + self.request, _( + 'Document "%s" transitioned successfully' + ) % self.get_workflow_instance().document + ) return HttpResponseRedirect(self.get_success_url()) def get_extra_context(self): @@ -405,7 +409,7 @@ class WorkflowListView(SingleObjectListView): def get_extra_context(self): return { - 'hide_link': True, + 'hide_object': True, 'title': _('Workflows') } @@ -437,51 +441,36 @@ class WorkflowDocumentListView(DocumentListView): class WorkflowStateDocumentListView(DocumentListView): - def dispatch(self, request, *args, **kwargs): - self.workflow_state = get_object_or_404( + def get_document_queryset(self): + return self.get_workflow_state().get_documents() + + def get_extra_context(self): + workflow_state = self.get_workflow_state() + return { + 'hide_links': True, + 'object': workflow_state, + 'navigation_object_list': ('object', 'workflow'), + 'workflow': WorkflowRuntimeProxy.objects.get( + pk=workflow_state.workflow.pk + ), + 'title': _( + 'Documents in the workflow "%s", state "%s"' + ) % ( + workflow_state.workflow, workflow_state + ) + } + + def get_workflow_state(self): + workflow_state = get_object_or_404( WorkflowStateRuntimeProxy, pk=self.kwargs['pk'] ) AccessControlList.objects.check_access( - permissions=permission_workflow_view, user=request.user, - obj=self.workflow_state.workflow + permissions=permission_workflow_view, user=self.request.user, + obj=workflow_state.workflow ) - return super( - WorkflowStateDocumentListView, self - ).dispatch(request, *args, **kwargs) - - def get_document_queryset(self): - latest_entries = WorkflowInstanceLogEntry.objects.annotate( - max_datetime=Max( - 'workflow_instance__log_entries__datetime' - ) - ).filter( - datetime=F('max_datetime') - ) - - state_latest_entries = latest_entries.filter( - transition__destination_state=self.workflow_state - ) - - return Document.objects.filter( - workflows__pk__in=state_latest_entries.values_list( - 'workflow_instance', flat=True - ) - ) - - def get_extra_context(self): - return { - 'hide_links': True, - 'object': self.workflow_state, - 'navigation_object_list': ('object', 'workflow'), - 'workflow': WorkflowRuntimeProxy.objects.get( - pk=self.workflow_state.workflow.pk - ), - 'title': _( - 'Documents in the workflow "%s", state "%s"' - ) % (self.workflow_state.workflow, self.workflow_state) - } + return workflow_state class WorkflowStateListView(SingleObjectListView):