diff --git a/src/etools/applications/partners/admin.py b/src/etools/applications/partners/admin.py index b46b8b5e72..49b95a8f5c 100644 --- a/src/etools/applications/partners/admin.py +++ b/src/etools/applications/partners/admin.py @@ -6,6 +6,7 @@ from django.forms import SelectMultiple from django.http.response import HttpResponseRedirect from django.urls import reverse +from django.utils import timezone from django.utils.html import format_html from django.utils.safestring import mark_safe from django.utils.translation import gettext_lazy as _ @@ -17,6 +18,7 @@ from unicef_attachments.admin import AttachmentSingleInline from unicef_attachments.models import Attachment from unicef_snapshot.admin import ActivityInline, SnapshotModelAdmin +from unicef_snapshot.models import Activity from etools.applications.partners.exports import PartnerExport from etools.applications.partners.forms import InterventionAttachmentForm # TODO intervention sector locations cleanup @@ -709,12 +711,18 @@ class AgreementAttachmentInline(AttachmentSingleInline): code = 'partners_agreement' +class AgreementTerminationDocAttachmentInline(AttachmentSingleInline): + verbose_name_plural = _('Termination Doc Attachment') + code = 'partners_agreement_termination_doc' + + class AgreementAdmin( AttachmentInlineAdminMixin, ExportMixin, HiddenPartnerMixin, CountryUsersAdminMixin, RestrictedEditAdminMixin, + ExtraUrlMixin, SnapshotModelAdmin, ): staff_only = False @@ -766,6 +774,7 @@ class AgreementAdmin( inlines = [ ActivityInline, AgreementAttachmentInline, + AgreementTerminationDocAttachmentInline ] def has_module_permission(self, request): @@ -811,6 +820,44 @@ def get_interventions_admin_urls(self, objs): urls.append(formatted_url) return urls + @button(permission=lambda request, obj: request.user.groups.filter(name='RSS').exists() and + obj.status == Agreement.TERMINATED and obj.end > timezone.now().date()) + def revert_termination(self, request, pk): + agreement = Agreement.objects.get(pk=pk) + agreement.status = Agreement.SIGNED + try: + termination_doc = Attachment.objects.get( + code='partners_agreement_termination_doc', + content_type=ContentType.objects.get_for_model(Agreement), + object_id=agreement.pk + ) + agreement.termination_doc.remove(termination_doc) + except Attachment.DoesNotExist: + pass + + agreement.save(update_fields=['status']) + + terminated_interventions = agreement.interventions.filter(status=Intervention.TERMINATED) + interventions_to_update = [] + for i in terminated_interventions: + intervention_activities = Activity.objects.filter( + target_content_type=ContentType.objects.get_for_model(Intervention), + target_object_id=i.id, + action=Activity.UPDATE, + change__status__after=Intervention.TERMINATED, + ) + if not intervention_activities.exists(): + continue + + previous_status = intervention_activities.last().change['status']['before'] + if previous_status in [Intervention.SIGNED, Intervention.ACTIVE, Intervention.SUSPENDED]: + i.status = previous_status + interventions_to_update.append(i) + + Intervention.objects.bulk_update(interventions_to_update, fields=['status']) + + return HttpResponseRedirect(reverse('admin:partners_agreement_change', args=[pk])) + class FileTypeAdmin(RestrictedEditAdmin): diff --git a/src/etools/applications/partners/locale/ar/LC_MESSAGES/django.po b/src/etools/applications/partners/locale/ar/LC_MESSAGES/django.po index fd7b42fd90..5d82a67e51 100644 --- a/src/etools/applications/partners/locale/ar/LC_MESSAGES/django.po +++ b/src/etools/applications/partners/locale/ar/LC_MESSAGES/django.po @@ -64,6 +64,9 @@ msgstr "إطار النهج المنسق للتحويلات النقدية" msgid "Signed Amendment" msgstr "التعديل الموقع" +msgid "Termination Doc Attachment" +msgstr "" + msgid "Agreement Details" msgstr "تفاصيل الاتفاقية" diff --git a/src/etools/applications/partners/locale/es/LC_MESSAGES/django.po b/src/etools/applications/partners/locale/es/LC_MESSAGES/django.po index 2f6f7bfa90..d93a9661db 100644 --- a/src/etools/applications/partners/locale/es/LC_MESSAGES/django.po +++ b/src/etools/applications/partners/locale/es/LC_MESSAGES/django.po @@ -55,6 +55,9 @@ msgstr "Hact" msgid "Signed Amendment" msgstr "Enmienda firmada" +msgid "Termination Doc Attachment" +msgstr "" + msgid "Agreement Details" msgstr "Detalles del acuerdo" diff --git a/src/etools/applications/partners/locale/fr/LC_MESSAGES/django.po b/src/etools/applications/partners/locale/fr/LC_MESSAGES/django.po index 386137d2fa..028da430b9 100644 --- a/src/etools/applications/partners/locale/fr/LC_MESSAGES/django.po +++ b/src/etools/applications/partners/locale/fr/LC_MESSAGES/django.po @@ -63,6 +63,9 @@ msgstr "Hact" msgid "Signed Amendment" msgstr "Amendement signé" +msgid "Termination Doc Attachment" +msgstr "" + msgid "Agreement Details" msgstr "Détails de l'accord" diff --git a/src/etools/applications/partners/locale/pt/LC_MESSAGES/django.po b/src/etools/applications/partners/locale/pt/LC_MESSAGES/django.po index 68fec92747..3adb586cc5 100644 --- a/src/etools/applications/partners/locale/pt/LC_MESSAGES/django.po +++ b/src/etools/applications/partners/locale/pt/LC_MESSAGES/django.po @@ -63,6 +63,9 @@ msgstr "HACT" msgid "Signed Amendment" msgstr "Aditivo assinado" +msgid "Termination Doc Attachment" +msgstr "" + msgid "Agreement Details" msgstr "Detalhes do Acordo" diff --git a/src/etools/applications/partners/locale/ru/LC_MESSAGES/django.po b/src/etools/applications/partners/locale/ru/LC_MESSAGES/django.po index 74ba69d6c7..4ec05c9170 100644 --- a/src/etools/applications/partners/locale/ru/LC_MESSAGES/django.po +++ b/src/etools/applications/partners/locale/ru/LC_MESSAGES/django.po @@ -61,6 +61,9 @@ msgstr "ГПДП" msgid "Signed Amendment" msgstr "Подписанная поправка" +msgid "Termination Doc Attachment" +msgstr "" + msgid "Agreement Details" msgstr "Детали соглашения" diff --git a/src/etools/applications/partners/tests/test_admin.py b/src/etools/applications/partners/tests/test_admin.py index 4df826a664..ca4201a4d4 100644 --- a/src/etools/applications/partners/tests/test_admin.py +++ b/src/etools/applications/partners/tests/test_admin.py @@ -1,15 +1,17 @@ import datetime from django.contrib.admin.sites import AdminSite +from django.core.files.uploadedfile import SimpleUploadedFile from unicef_snapshot.models import Activity +from etools.applications.attachments.tests.factories import AttachmentFactory from etools.applications.core.tests.cases import BaseTenantTestCase from etools.applications.partners.admin import AgreementAdmin, InterventionAdmin from etools.applications.partners.models import Agreement, Intervention from etools.applications.partners.tests.factories import AgreementFactory, InterventionFactory, PartnerFactory from etools.applications.reports.tests.factories import CountryProgrammeFactory -from etools.applications.users.tests.factories import UserFactory +from etools.applications.users.tests.factories import CountryFactory, GroupFactory, RealmFactory, UserFactory class MockRequest: @@ -103,3 +105,49 @@ def test_save_model_update(self): "after": Agreement.TERMINATED } }) + + def test_revert_termination(self): + RealmFactory( + user=self.user, + country=CountryFactory(), + organization=self.user.profile.organization, + group=GroupFactory(name='RSS') + ) + + agreement = AgreementFactory( + partner=self.partner, status=Agreement.TERMINATED, end=datetime.date.today() + datetime.timedelta(days=7)) + a = AttachmentFactory( + code='partners_agreement_termination_doc', + content_object=agreement, + file=SimpleUploadedFile('simple_file.txt', b'simple_file.txt'), + ) + agreement.termination_doc.add(a) + suspended_pd = InterventionFactory( + agreement=agreement, + title='Intervention 1', + status=Intervention.SUSPENDED, + ) + ia = InterventionAdmin(Intervention, self.site) + suspended_pd.status = Intervention.TERMINATED + ia.save_model(self.request, suspended_pd, {}, True) + + closed_pd = InterventionFactory( + agreement=agreement, + title='Intervention 1', + status=Intervention.CLOSED, + ) + closed_pd.status = Intervention.TERMINATED + ia.save_model(self.request, closed_pd, {}, True) + + aa = AgreementAdmin(Agreement, self.site) + aa.revert_termination(self.request, pk=agreement.pk) + + agreement.refresh_from_db() + self.assertEqual(agreement.status, Agreement.SIGNED) + self.assertEqual(agreement.termination_doc.count(), 0) + + suspended_pd.refresh_from_db() + self.assertEqual(suspended_pd.status, Intervention.SUSPENDED) + + closed_pd.refresh_from_db() + self.assertEqual(closed_pd.status, Intervention.TERMINATED)