From 87e4dce0b8717dfa8ab00fce28a8bc6863ab96a4 Mon Sep 17 00:00:00 2001 From: Ema Ciupe Date: Fri, 19 Jan 2024 19:54:00 +0200 Subject: [PATCH 01/26] New Notification to UNICEF Focal persons --- .../core/management/commands/init-celery.py | 5 ++ .../notifications/expiring_partner.py | 16 ++++++ src/etools/applications/partners/tasks.py | 55 +++++++++++++++++++ 3 files changed, 76 insertions(+) create mode 100644 src/etools/applications/partners/notifications/expiring_partner.py diff --git a/src/etools/applications/core/management/commands/init-celery.py b/src/etools/applications/core/management/commands/init-celery.py index 54f539505d..d33638d6c1 100644 --- a/src/etools/applications/core/management/commands/init-celery.py +++ b/src/etools/applications/core/management/commands/init-celery.py @@ -28,6 +28,11 @@ def handle(self, *args, **options): 'enabled': False, 'interval': every_day}) + PeriodicTask.objects.get_or_create(name='Notification Partner Assessment expires', defaults={ + 'task': 'partners.tasks.notify_partner_expires', + 'enabled': False, + 'interval': every_day}) + PeriodicTask.objects.get_or_create(name='Intervention Notification Ending', defaults={ 'task': 'partners.tasks.intervention_notification_ending', 'enabled': False, diff --git a/src/etools/applications/partners/notifications/expiring_partner.py b/src/etools/applications/partners/notifications/expiring_partner.py new file mode 100644 index 0000000000..dfb6925d19 --- /dev/null +++ b/src/etools/applications/partners/notifications/expiring_partner.py @@ -0,0 +1,16 @@ +name = 'partners/expiring_partner' +defaults = { + 'description': 'Partner Assessment due to expire', + 'subject': 'eTools Partnership {{ partner_name }} Assessment is expiring in {{ days }} days', + 'content': """ + Dear Colleague, + + The assessment for the "HACT" or "Core Value" of Partner {{ partner_name }} + with Vendor number {{ partner_number }} in {{ country }} is due to expire in {{ days }} days. + Kindly complete the new assessment and ensure the vendor record is updated with the + latest risk information promptly to avoid any potential transaction blocks in the system. + + + Please note that this is an automated message and any response to this email cannot be replied to. + """ +} diff --git a/src/etools/applications/partners/tasks.py b/src/etools/applications/partners/tasks.py index 84a61eb931..50cb19418c 100644 --- a/src/etools/applications/partners/tasks.py +++ b/src/etools/applications/partners/tasks.py @@ -4,7 +4,9 @@ from django.conf import settings from django.contrib.auth import get_user_model from django.contrib.contenttypes.models import ContentType +from django.contrib.postgres.aggregates import ArrayAgg from django.db import connection, transaction +from django.db.models import DurationField, ExpressionWrapper, F, OuterRef, Subquery from django.utils import timezone from django.utils.translation import gettext as _ @@ -37,6 +39,10 @@ # about each intervention ending {delta} days from now. _INTERVENTION_ENDING_SOON_DELTAS = (15, 30, 60, 90) +# _PARTNER_ASSESSMENT_EXPIRING_SOON_DELTAS is used by notify_partner_expires(). Notifications will be sent to +# UNICEF Focal Points every {delta} days prior expiration (reaching 5 years from the date of the Core Value Assessment). +_PARTNER_ASSESSMENT_EXPIRING_SOON_DELTAS = (30, 60, 90) + def get_intervention_context(intervention): """Return a dict formatting some details about the intervention. @@ -272,6 +278,55 @@ def notify_partner_hidden(partner_pk, tenant_name): ) +# @app.task +def notify_partner_expires(): + """Send notifications to UNICEF Focal Points for partners that will have their Core Value Assessment expire within + 30/60/90 days. + Task will run every 24 hours. + """ + today = datetime.date.today() + notify_end_dates = [datetime.timedelta(days=-delta) for delta in _PARTNER_ASSESSMENT_EXPIRING_SOON_DELTAS] + + for country in Country.objects.exclude(name='Global').all(): + connection.set_tenant(country) + logger.info('Starting Partner Assessment Expire for country {}'.format(country.name)) + + core_value_assessment_expiring = PartnerOrganization.objects\ + .filter(pk=OuterRef("pk"))\ + .annotate(notifs_to_expire=ExpressionWrapper( + today - (F('core_values_assessment_date') + + datetime.timedelta(days=PartnerOrganization.EXPIRING_ASSESSMENT_LIMIT_YEAR * 365) + ), + output_field=DurationField())).values('notifs_to_expire') + partner_qs = PartnerOrganization.objects\ + .active()\ + .annotate(core_value_assessment_expiring=Subquery(core_value_assessment_expiring))\ + .filter(core_value_assessment_expiring__in=notify_end_dates)\ + .distinct() + + for partner in partner_qs: + pds = Intervention.objects\ + .prefetch_related('unicef_focal_points')\ + .filter(agreement__partner=partner)\ + .annotate(unicef_focal_point_emails=ArrayAgg('unicef_focal_points__email')) + focal_points_emails = set() + for pd in pds: + focal_points_emails.update(pd.unicef_focal_point_emails) + + if focal_points_emails: + email_context = { + 'country': country.name, + 'partner_name': partner.name, + 'partner_number': partner.vendor_number, + 'days': partner.core_value_assessment_expiring.days.__abs__(), + } + send_notification_with_template( + recipients=list(focal_points_emails), + template_name='partners/expiring_partner', + context=email_context + ) + + @app.task def check_pca_required(): run_on_all_tenants(send_pca_required_notifications) From a90408c3620c0ccd9f3de87f0f3eb1d70699a1c0 Mon Sep 17 00:00:00 2001 From: Ema Ciupe Date: Wed, 17 Apr 2024 17:07:35 +0300 Subject: [PATCH 02/26] code review changes --- src/etools/applications/partners/tasks.py | 41 ++++++++++++++--------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/src/etools/applications/partners/tasks.py b/src/etools/applications/partners/tasks.py index 50cb19418c..a05d4d38bf 100644 --- a/src/etools/applications/partners/tasks.py +++ b/src/etools/applications/partners/tasks.py @@ -6,7 +6,7 @@ from django.contrib.contenttypes.models import ContentType from django.contrib.postgres.aggregates import ArrayAgg from django.db import connection, transaction -from django.db.models import DurationField, ExpressionWrapper, F, OuterRef, Subquery +from django.db.models import DurationField, ExpressionWrapper, F, OuterRef, Q, Subquery from django.utils import timezone from django.utils.translation import gettext as _ @@ -28,7 +28,7 @@ from etools.applications.partners.validation.agreements import AgreementValid from etools.applications.partners.validation.interventions import InterventionValid from etools.applications.reports.models import CountryProgramme -from etools.applications.users.models import Country +from etools.applications.users.models import Country, User from etools.config.celery import app from etools.libraries.djangolib.utils import get_environment from etools.libraries.tenant_support.utils import run_on_all_tenants @@ -278,14 +278,15 @@ def notify_partner_hidden(partner_pk, tenant_name): ) -# @app.task +@app.task def notify_partner_expires(): - """Send notifications to UNICEF Focal Points for partners that will have their Core Value Assessment expire within - 30/60/90 days. + """Send notifications to UNICEF Focal Points for partners that will have their + Core Value Assessment or HACT Assessment expire within 30/60/90 days. Task will run every 24 hours. """ - today = datetime.date.today() + today = timezone.now() notify_end_dates = [datetime.timedelta(days=-delta) for delta in _PARTNER_ASSESSMENT_EXPIRING_SOON_DELTAS] + delta = datetime.timedelta(days=PartnerOrganization.EXPIRING_ASSESSMENT_LIMIT_YEAR * 365) for country in Country.objects.exclude(name='Global').all(): connection.set_tenant(country) @@ -293,27 +294,37 @@ def notify_partner_expires(): core_value_assessment_expiring = PartnerOrganization.objects\ .filter(pk=OuterRef("pk"))\ - .annotate(notifs_to_expire=ExpressionWrapper( - today - (F('core_values_assessment_date') + - datetime.timedelta(days=PartnerOrganization.EXPIRING_ASSESSMENT_LIMIT_YEAR * 365) - ), - output_field=DurationField())).values('notifs_to_expire') + .annotate(core_assessments_to_expire=ExpressionWrapper( + today - (F('core_values_assessment_date') + delta), + output_field=DurationField())).values('core_assessments_to_expire') + last_assessment_expiring = PartnerOrganization.objects\ + .filter(pk=OuterRef("pk"))\ + .annotate(assessments_to_expire=ExpressionWrapper( + today - (F('last_assessment_date') + delta), + output_field=DurationField())).values('assessments_to_expire') partner_qs = PartnerOrganization.objects\ .active()\ - .annotate(core_value_assessment_expiring=Subquery(core_value_assessment_expiring))\ - .filter(core_value_assessment_expiring__in=notify_end_dates)\ + .annotate(core_value_assessment_expiring=Subquery(core_value_assessment_expiring)) \ + .annotate(assessments_expiring=Subquery(last_assessment_expiring)) \ + .filter(Q(core_value_assessment_expiring__in=notify_end_dates) | + Q(assessments_expiring__in=notify_end_dates))\ .distinct() for partner in partner_qs: pds = Intervention.objects\ .prefetch_related('unicef_focal_points')\ .filter(agreement__partner=partner)\ + .exclude(Q(status__in=[Intervention.CLOSED, Intervention.ENDED]) | + Q(status=Intervention.DRAFT, modified__gt=timezone.now() - datetime.timedelta(days=365)))\ .annotate(unicef_focal_point_emails=ArrayAgg('unicef_focal_points__email')) + focal_points_emails = set() for pd in pds: focal_points_emails.update(pd.unicef_focal_point_emails) + filtered_emails = User.objects.base_qs().filter( + email__in=focal_points_emails, profile__country_override=country).values_list('email', flat=True) - if focal_points_emails: + if filtered_emails: email_context = { 'country': country.name, 'partner_name': partner.name, @@ -321,7 +332,7 @@ def notify_partner_expires(): 'days': partner.core_value_assessment_expiring.days.__abs__(), } send_notification_with_template( - recipients=list(focal_points_emails), + recipients=list(filtered_emails), template_name='partners/expiring_partner', context=email_context ) From dbf749c01416aa645c9a09b6f8cda6b12eff3593 Mon Sep 17 00:00:00 2001 From: Ema Ciupe Date: Fri, 26 Apr 2024 18:35:15 +0300 Subject: [PATCH 03/26] added tests, minor changes --- .../core/management/commands/init-celery.py | 2 +- src/etools/applications/partners/tasks.py | 8 +- .../applications/partners/tests/test_tasks.py | 75 ++++++++++++++++++- 3 files changed, 80 insertions(+), 5 deletions(-) diff --git a/src/etools/applications/core/management/commands/init-celery.py b/src/etools/applications/core/management/commands/init-celery.py index d33638d6c1..e54129c658 100644 --- a/src/etools/applications/core/management/commands/init-celery.py +++ b/src/etools/applications/core/management/commands/init-celery.py @@ -29,7 +29,7 @@ def handle(self, *args, **options): 'interval': every_day}) PeriodicTask.objects.get_or_create(name='Notification Partner Assessment expires', defaults={ - 'task': 'partners.tasks.notify_partner_expires', + 'task': 'partners.tasks.notify_partner_assessment_expires', 'enabled': False, 'interval': every_day}) diff --git a/src/etools/applications/partners/tasks.py b/src/etools/applications/partners/tasks.py index a05d4d38bf..d2d194de2c 100644 --- a/src/etools/applications/partners/tasks.py +++ b/src/etools/applications/partners/tasks.py @@ -279,12 +279,12 @@ def notify_partner_hidden(partner_pk, tenant_name): @app.task -def notify_partner_expires(): +def notify_partner_assessment_expires(): """Send notifications to UNICEF Focal Points for partners that will have their Core Value Assessment or HACT Assessment expire within 30/60/90 days. Task will run every 24 hours. """ - today = timezone.now() + today = timezone.now().date() notify_end_dates = [datetime.timedelta(days=-delta) for delta in _PARTNER_ASSESSMENT_EXPIRING_SOON_DELTAS] delta = datetime.timedelta(days=PartnerOrganization.EXPIRING_ASSESSMENT_LIMIT_YEAR * 365) @@ -325,11 +325,13 @@ def notify_partner_expires(): email__in=focal_points_emails, profile__country_override=country).values_list('email', flat=True) if filtered_emails: + days = partner.core_value_assessment_expiring.days.__abs__() \ + if partner.core_value_assessment_expiring else partner.assessments_expiring.days.__abs__() email_context = { 'country': country.name, 'partner_name': partner.name, 'partner_number': partner.vendor_number, - 'days': partner.core_value_assessment_expiring.days.__abs__(), + 'days': days, } send_notification_with_template( recipients=list(filtered_emails), diff --git a/src/etools/applications/partners/tests/test_tasks.py b/src/etools/applications/partners/tests/test_tasks.py index 9939b5bb07..348f4f749d 100644 --- a/src/etools/applications/partners/tests/test_tasks.py +++ b/src/etools/applications/partners/tests/test_tasks.py @@ -23,8 +23,9 @@ from etools.applications.attachments.tests.factories import AttachmentFactory, AttachmentFileTypeFactory from etools.applications.core.tests.cases import BaseTenantTestCase from etools.applications.funds.tests.factories import FundsReservationHeaderFactory +from etools.applications.organizations.models import OrganizationType from etools.applications.organizations.tests.factories import OrganizationFactory -from etools.applications.partners.models import Agreement, Intervention +from etools.applications.partners.models import Agreement, Intervention, PartnerOrganization from etools.applications.partners.synchronizers import PDVisionUploader from etools.applications.partners.tasks import transfer_active_pds_to_new_cp from etools.applications.partners.tests.factories import ( @@ -1409,3 +1410,75 @@ def test_realms_sync_unicef(self, sync_mock): RealmFactory(user=user) sync_mock.assert_not_called() self.assertEqual(len(commit_callbacks), 0) + + +class TestPartnerAssessmentExpires(BaseTenantTestCase): + @classmethod + def setUpTestData(cls): + call_command("update_notifications") + cls.today = timezone.now().date() + cls.partner_1 = PartnerFactory( + organization=OrganizationFactory(organization_type=OrganizationType.CIVIL_SOCIETY_ORGANIZATION) + ) + cls.intervention_1 = InterventionFactory( + agreement=AgreementFactory(partner=cls.partner_1), + status=Intervention.ACTIVE, + start=cls.today - datetime.timedelta(days=2), + ) + cls.focal_point = UserFactory() + cls.focal_point.profile.country_override = connection.tenant + cls.focal_point.profile.save() + cls.intervention_1.unicef_focal_points.add(cls.focal_point) + + def test_task_last_assessment_date_expiring(self): + last_assessment_date = self.today - datetime.timedelta(days=PartnerOrganization.EXPIRING_ASSESSMENT_LIMIT_YEAR * 365 - 30) + self.partner_1.last_assessment_date = last_assessment_date + self.partner_1.save() + + send_path = "etools.applications.partners.tasks.send_notification_with_template" + mock_send = mock.Mock() + with mock.patch(send_path, mock_send): + etools.applications.partners.tasks.notify_partner_assessment_expires() + self.assertEqual(mock_send.call_count, 1) + self.assertEqual(mock_send.call_args.kwargs['recipients'], [self.focal_point.email]) + + def test_task_core_assessment_date_expiring(self): + core_values_assessment_date = self.today - datetime.timedelta(days=PartnerOrganization.EXPIRING_ASSESSMENT_LIMIT_YEAR * 365 - 60) + + self.partner_1.core_values_assessment_date = core_values_assessment_date + self.partner_1.save() + + send_path = "etools.applications.partners.tasks.send_notification_with_template" + mock_send = mock.Mock() + with mock.patch(send_path, mock_send): + etools.applications.partners.tasks.notify_partner_assessment_expires() + self.assertEqual(mock_send.call_count, 1) + self.assertEqual(mock_send.call_args.kwargs['recipients'], [self.focal_point.email]) + + def test_task_focal_point_without_country(self): + core_values_assessment_date = self.today - datetime.timedelta(days=PartnerOrganization.EXPIRING_ASSESSMENT_LIMIT_YEAR * 365 - 60) + + self.partner_1.core_values_assessment_date = core_values_assessment_date + self.partner_1.save() + self.focal_point.profile.country_override = None + self.focal_point.profile.save() + + send_path = "etools.applications.partners.tasks.send_notification_with_template" + mock_send = mock.Mock() + with mock.patch(send_path, mock_send): + etools.applications.partners.tasks.notify_partner_assessment_expires() + self.assertEqual(mock_send.call_count, 0) + + def test_task_excluded_pd_status(self): + core_values_assessment_date = self.today - datetime.timedelta(days=PartnerOrganization.EXPIRING_ASSESSMENT_LIMIT_YEAR * 365 - 60) + + self.partner_1.core_values_assessment_date = core_values_assessment_date + self.partner_1.save() + self.intervention_1.status = Intervention.CLOSED + self.intervention_1.save() + + send_path = "etools.applications.partners.tasks.send_notification_with_template" + mock_send = mock.Mock() + with mock.patch(send_path, mock_send): + etools.applications.partners.tasks.notify_partner_assessment_expires() + self.assertEqual(mock_send.call_count, 0) From a8a19ea671d8427b7215cef411e5c9ff6a154c9a Mon Sep 17 00:00:00 2001 From: Roman Karpovich Date: Tue, 7 May 2024 19:07:49 +0800 Subject: [PATCH 04/26] update labels for engagement dates --- .../commands/update_audit_permissions.py | 3 --- .../migrations/0031_auto_20240507_1059.py | 23 +++++++++++++++++++ src/etools/applications/audit/models.py | 4 ++-- .../audit/serializers/engagement.py | 9 ++++---- 4 files changed, 30 insertions(+), 9 deletions(-) create mode 100644 src/etools/applications/audit/migrations/0031_auto_20240507_1059.py diff --git a/src/etools/applications/audit/management/commands/update_audit_permissions.py b/src/etools/applications/audit/management/commands/update_audit_permissions.py index 1cc9d56363..d37bb7f4e2 100644 --- a/src/etools/applications/audit/management/commands/update_audit_permissions.py +++ b/src/etools/applications/audit/management/commands/update_audit_permissions.py @@ -62,9 +62,6 @@ class Command(BaseCommand): 'audit.engagement.status', 'audit.engagement.status_date', - 'audit.spotcheck.face_form_start_date', - 'audit.spotcheck.face_form_end_date', - 'purchase_order.purchaseorder.*', 'purchase_order.auditorfirm.*', ] diff --git a/src/etools/applications/audit/migrations/0031_auto_20240507_1059.py b/src/etools/applications/audit/migrations/0031_auto_20240507_1059.py new file mode 100644 index 0000000000..5131816108 --- /dev/null +++ b/src/etools/applications/audit/migrations/0031_auto_20240507_1059.py @@ -0,0 +1,23 @@ +# Generated by Django 3.2.19 on 2024-05-07 10:59 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('audit', '0030_engagement_send_back_comment'), + ] + + operations = [ + migrations.AlterField( + model_name='engagement', + name='end_date', + field=models.DateField(blank=True, null=True, verbose_name='Start date of first reporting FACE'), + ), + migrations.AlterField( + model_name='engagement', + name='start_date', + field=models.DateField(blank=True, null=True, verbose_name='End date of last reporting FACE'), + ), + ] diff --git a/src/etools/applications/audit/models.py b/src/etools/applications/audit/models.py index ea9ff2e2c8..329d4cd2f5 100644 --- a/src/etools/applications/audit/models.py +++ b/src/etools/applications/audit/models.py @@ -111,8 +111,8 @@ class Engagement(InheritedModelMixin, TimeStampedModel, models.Model): ) partner_contacted_at = models.DateField(verbose_name=_('Date IP was contacted'), blank=True, null=True) engagement_type = models.CharField(verbose_name=_('Engagement Type'), max_length=10, choices=TYPES) - start_date = models.DateField(verbose_name=_('Period Start Date'), blank=True, null=True) - end_date = models.DateField(verbose_name=_('Period End Date'), blank=True, null=True) + start_date = models.DateField(verbose_name=_('Start date of first reporting FACE'), blank=True, null=True) + end_date = models.DateField(verbose_name=_('End date of last reporting FACE'), blank=True, null=True) total_value = models.DecimalField( verbose_name=_('Total value of selected FACE form(s)'), default=0, decimal_places=2, max_digits=20 ) diff --git a/src/etools/applications/audit/serializers/engagement.py b/src/etools/applications/audit/serializers/engagement.py index 3c22249d5a..6ecbb2336a 100644 --- a/src/etools/applications/audit/serializers/engagement.py +++ b/src/etools/applications/audit/serializers/engagement.py @@ -257,6 +257,9 @@ class EngagementSerializer( WritableNestedParentSerializerMixin, EngagementListSerializer ): + face_form_start_date = serializers.DateField(label='FACE Form(s) Start Date', read_only=True, source='start_date') + face_form_end_date = serializers.DateField(label='FACE Form(s) End Date', read_only=True, source='end_date') + staff_members = SeparatedReadWriteField( read_field=serializers.SerializerMethodField(), label=_('Audit Staff Team Members') @@ -287,6 +290,7 @@ class EngagementSerializer( class Meta(EngagementListSerializer.Meta): fields = EngagementListSerializer.Meta.fields + [ + 'face_form_start_date', 'face_form_end_date', 'total_value', 'staff_members', 'active_pd', 'authorized_officers', 'users_notified', 'joint_audit', 'year_of_audit', 'shared_ip_with', 'exchange_rate', 'currency_of_report', 'start_date', 'end_date', 'partner_contacted_at', 'date_of_field_visit', 'date_of_draft_report_to_ip', @@ -399,16 +403,13 @@ class Meta(WritableNestedSerializerMixin.Meta): class SpotCheckSerializer(ActivePDValidationMixin, EngagementSerializer): findings = FindingSerializer(many=True, required=False) - face_form_start_date = serializers.DateField(label='FACE Form(s) Start Date', read_only=True, source='start_date') - face_form_end_date = serializers.DateField(label='FACE Form(s) End Date', read_only=True, source='end_date') - pending_unsupported_amount = serializers.DecimalField(20, 2, label=_('Pending Unsupported Amount'), read_only=True) class Meta(EngagementSerializer.Meta): model = SpotCheck fields = EngagementSerializer.Meta.fields + [ 'total_amount_tested', 'total_amount_of_ineligible_expenditure', - 'internal_controls', 'findings', 'face_form_start_date', 'face_form_end_date', + 'internal_controls', 'findings', 'amount_refunded', 'additional_supporting_documentation_provided', 'justification_provided_and_accepted', 'write_off_required', 'pending_unsupported_amount', 'explanation_for_additional_information' From c6509c60ed5338250a572e1c548ec40f5f85c766 Mon Sep 17 00:00:00 2001 From: Ema Ciupe Date: Mon, 13 May 2024 14:53:17 +0300 Subject: [PATCH 05/26] [ch37072]prevent adding duplicated name disaggregation --- src/etools/applications/reports/models.py | 6 ++++ .../applications/reports/serializers/v2.py | 7 +++- .../applications/reports/tests/test_views.py | 35 +++++++++++++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/src/etools/applications/reports/models.py b/src/etools/applications/reports/models.py index 82e286e684..7bc6fec579 100644 --- a/src/etools/applications/reports/models.py +++ b/src/etools/applications/reports/models.py @@ -2,6 +2,7 @@ from datetime import date from string import ascii_lowercase +from django.core.exceptions import ValidationError from django.core.validators import MinValueValidator from django.db import models, transaction from django.db.models import Q, Sum @@ -583,6 +584,11 @@ class Disaggregation(TimeStampedModel): def __str__(self): return self.name + def save(self, *args, **kwargs): + if Disaggregation.objects.filter(name__iexact=self.name).first(): + raise ValidationError("A disaggregation with this name already exists.") + super().save(*args, **kwargs) + class DisaggregationValue(TimeStampedModel): """ diff --git a/src/etools/applications/reports/serializers/v2.py b/src/etools/applications/reports/serializers/v2.py index afc77fb10e..034f18ee7d 100644 --- a/src/etools/applications/reports/serializers/v2.py +++ b/src/etools/applications/reports/serializers/v2.py @@ -1,3 +1,4 @@ +from django.core.exceptions import ValidationError as DjangoValidationError from django.db import transaction from django.utils.translation import gettext as _ @@ -108,7 +109,11 @@ def create(self, validated_data): values_data = validated_data.pop('disaggregation_values') if not values_data or len(values_data) == 1: raise ValidationError('At least 2 Disaggregation Groups must be set.') - disaggregation = Disaggregation.objects.create(**validated_data) + try: + disaggregation = Disaggregation.objects.create(**validated_data) + except DjangoValidationError as exc: + raise ValidationError(exc.messages) + for value_data in values_data: if 'id' in value_data: raise ValidationError( diff --git a/src/etools/applications/reports/tests/test_views.py b/src/etools/applications/reports/tests/test_views.py index 5f556615a0..0c6ef9b33c 100644 --- a/src/etools/applications/reports/tests/test_views.py +++ b/src/etools/applications/reports/tests/test_views.py @@ -365,6 +365,41 @@ def test_create_one_group(self): ) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + def test_create_duplicate_disaggregation(self): + """ + Test creating disaggregations with duplicated name + """ + data = { + "name": "name", + "disaggregation_values": [ + { + "value": "first group", + "active": False + }, + { + "value": "second group", + "active": False + } + ] + } + response = self.forced_auth_req( + 'post', + self.url, + user=self.pme_user, + data=data + ) + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + + data['name'] = 'NAME' + response = self.forced_auth_req( + 'post', + self.url, + user=self.pme_user, + data=data + ) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertIn("A disaggregation with this name already exists.", response.data) + class TestDisaggregationRetrieveUpdateViews(BaseTenantTestCase): """ From 88474990a46665b75960f040131a6974c515e3b5 Mon Sep 17 00:00:00 2001 From: Ema Ciupe Date: Tue, 14 May 2024 16:31:33 +0300 Subject: [PATCH 06/26] only on create --- src/etools/applications/reports/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/etools/applications/reports/models.py b/src/etools/applications/reports/models.py index 7bc6fec579..351b8522a7 100644 --- a/src/etools/applications/reports/models.py +++ b/src/etools/applications/reports/models.py @@ -585,7 +585,7 @@ def __str__(self): return self.name def save(self, *args, **kwargs): - if Disaggregation.objects.filter(name__iexact=self.name).first(): + if not self.pk and Disaggregation.objects.filter(name__iexact=self.name).first(): raise ValidationError("A disaggregation with this name already exists.") super().save(*args, **kwargs) From d9e196f773937590a910e3f8dee7728e980b2f71 Mon Sep 17 00:00:00 2001 From: Ema Ciupe Date: Fri, 17 May 2024 11:42:54 +0300 Subject: [PATCH 07/26] POI p_code unique; added P CODE column on POI imports; export first/last names for transfers --- src/etools/applications/last_mile/admin.py | 20 ++++++++++--------- .../0003_alter_pointofinterest_p_code.py | 18 +++++++++++++++++ src/etools/applications/last_mile/models.py | 2 +- .../applications/last_mile/views_ext.py | 4 ++++ 4 files changed, 34 insertions(+), 10 deletions(-) create mode 100644 src/etools/applications/last_mile/migrations/0003_alter_pointofinterest_p_code.py diff --git a/src/etools/applications/last_mile/admin.py b/src/etools/applications/last_mile/admin.py index 871db8ad30..b0c9bdc0d5 100644 --- a/src/etools/applications/last_mile/admin.py +++ b/src/etools/applications/last_mile/admin.py @@ -29,10 +29,10 @@ class WaybillTransferAttachmentInline(AttachmentSingleInline): @admin.register(models.PointOfInterest) class PointOfInterestAdmin(XLSXImportMixin, admin.ModelAdmin): - list_display = ('name', 'parent', 'poi_type') + list_display = ('name', 'parent', 'poi_type', 'p_code') list_select_related = ('parent',) list_filter = ('private', 'is_active') - search_fields = ('name', ) + search_fields = ('name', 'p_code') raw_id_fields = ('partner_organizations',) formfield_overrides = { models.PointField: {'widget': forms.OSMWidget(attrs={'display_raw': True})}, @@ -44,7 +44,8 @@ class PointOfInterestAdmin(XLSXImportMixin, admin.ModelAdmin): 'PRIMARY TYPE *': 'poi_type', 'IS PRIVATE***': 'private', 'LATITUDE': 'latitude', - 'LONGITUDE': 'longitude' + 'LONGITUDE': 'longitude', + 'P CODE': 'p_code' } def has_import_permission(self, request): @@ -60,8 +61,9 @@ def import_data(self, workbook): continue poi_dict[self.import_field_mapping[col[0].value]] = str(col[row].value).strip() - # add a pcode as it doesn't exist: - poi_dict['p_code'] = generate_hash(poi_dict['partner_org_vendor_no'] + poi_dict['name'], 12) + if not poi_dict.get('p_code'): + # add a pcode if it doesn't exist: + poi_dict['p_code'] = generate_hash(poi_dict['partner_org_vendor_no'] + poi_dict['name'] + poi_dict['poi_type'], 12) long = poi_dict.pop('longitude') lat = poi_dict.pop('latitude') try: @@ -88,11 +90,11 @@ def import_data(self, workbook): continue poi_obj, _ = models.PointOfInterest.all_objects.update_or_create( - point=poi_dict['point'], - name=poi_dict['name'], p_code=poi_dict['p_code'], - poi_type=poi_dict.get('poi_type'), - defaults={'private': poi_dict['private']} + defaults={'private': poi_dict['private'], + 'point': poi_dict['point'], + 'name': poi_dict['name'], + 'poi_type': poi_dict.get('poi_type')} ) poi_obj.partner_organizations.add(partner_org_obj) diff --git a/src/etools/applications/last_mile/migrations/0003_alter_pointofinterest_p_code.py b/src/etools/applications/last_mile/migrations/0003_alter_pointofinterest_p_code.py new file mode 100644 index 0000000000..687a515ec1 --- /dev/null +++ b/src/etools/applications/last_mile/migrations/0003_alter_pointofinterest_p_code.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.19 on 2024-05-17 08:33 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('last_mile', '0002_auto_20240426_1157'), + ] + + operations = [ + migrations.AlterField( + model_name='pointofinterest', + name='p_code', + field=models.CharField(max_length=32, unique=True, verbose_name='P Code'), + ), + ] diff --git a/src/etools/applications/last_mile/models.py b/src/etools/applications/last_mile/models.py index 940a9bc622..8dedf0276b 100644 --- a/src/etools/applications/last_mile/models.py +++ b/src/etools/applications/last_mile/models.py @@ -44,7 +44,7 @@ class PointOfInterest(TimeStampedModel, models.Model): null=True, blank=True ) name = models.CharField(verbose_name=_("Name"), max_length=254) - p_code = models.CharField(verbose_name=_("P Code"), max_length=32, blank=True, default='') + p_code = models.CharField(verbose_name=_("P Code"), max_length=32, unique=True) description = models.CharField(verbose_name=_("Description"), max_length=254) poi_type = models.ForeignKey( PointOfInterestType, diff --git a/src/etools/applications/last_mile/views_ext.py b/src/etools/applications/last_mile/views_ext.py index f80288fb85..25c42e55d8 100644 --- a/src/etools/applications/last_mile/views_ext.py +++ b/src/etools/applications/last_mile/views_ext.py @@ -79,7 +79,11 @@ def get_annotated_qs(qs): if qs.model == models.Transfer: return qs.annotate(vendor_number=F('partner_organization__organization__vendor_number'), checked_out_by_email=F('checked_out_by__email'), + checked_out_by_first_name=F('checked_out_by__first_name'), + checked_out_by_last_name=F('checked_out_by__last_name'), checked_in_by_email=F('checked_in_by__email'), + checked_in_by_last_name=F('checked_in_by__last_name'), + checked_in_by_first_name=F('checked_in_by__first_name'), origin_name=F('origin_point__name'), destination_name=F('destination_point__name'), ).values() From 3a0ef9fde73620845711aab4572e1f2268ec1dfc Mon Sep 17 00:00:00 2001 From: Roman Karpovich Date: Tue, 21 May 2024 13:54:09 +0800 Subject: [PATCH 08/26] remove deprecated hact api --- .../applications/partners/exports_v2.py | 118 ------------------ .../serializers/partner_organization_v2.py | 35 ------ .../partners/tests/test_api_partners.py | 24 ---- src/etools/applications/partners/urls_v2.py | 8 -- .../partners/views/partner_organization_v2.py | 34 ----- 5 files changed, 219 deletions(-) diff --git a/src/etools/applications/partners/exports_v2.py b/src/etools/applications/partners/exports_v2.py index ac5b3554e8..e1f760fd4d 100644 --- a/src/etools/applications/partners/exports_v2.py +++ b/src/etools/applications/partners/exports_v2.py @@ -47,81 +47,6 @@ class PartnerOrganizationCSVRenderer(r.CSVRenderer): } -class PartnerOrganizationHactCsvRenderer(FriendlyCSVRenderer): - - header = [ - 'name', - 'vendor_number', - 'partner_type', - 'shared_with', - 'type_of_assessment', - 'net_ct_cy', - 'reported_cy', - 'total_ct_ytd', - 'rating', - 'flags.expiring_assessment_flag', - 'flags.approaching_threshold_flag', - 'hact_values.programmatic_visits.planned.q1', - 'hact_values.programmatic_visits.planned.q2', - 'hact_values.programmatic_visits.planned.q3', - 'hact_values.programmatic_visits.planned.q4', - 'hact_min_requirements.programmatic_visits', - 'hact_values.programmatic_visits.completed.q1', - 'hact_values.programmatic_visits.completed.q2', - 'hact_values.programmatic_visits.completed.q3', - 'hact_values.programmatic_visits.completed.q4', - 'planned_engagement.spot_check_planned_q1', - 'planned_engagement.spot_check_planned_q2', - 'planned_engagement.spot_check_planned_q3', - 'planned_engagement.spot_check_planned_q4', - 'hact_min_requirements.spot_checks', - 'planned_engagement.spot_check_follow_up', - 'hact_values.spot_checks.completed.q1', - 'hact_values.spot_checks.completed.q2', - 'hact_values.spot_checks.completed.q3', - 'hact_values.spot_checks.completed.q4', - 'hact_min_requirements.audits', - 'hact_values.audits.completed', - 'hact_values.outstanding_findings', - ] - - labels = { - 'name': _('Implementing Partner'), - 'vendor_number': _('Vendor Number'), - 'partner_type': _('Partner Type'), - 'shared_with': _('Shared IP'), - 'type_of_assessment': _('Assessment Type'), - 'net_ct_cy': _('Cash Transfer 1 OCT - 30 SEP'), - 'reported_cy': _('Liquidations 1 OCT - 30 SEP'), - 'total_ct_ytd': _('Cash Transfers Jan - Dec'), - 'rating': _('Risk Rating'), - 'flags.expiring_assessment_flag': _('Expiring Threshold'), - 'flags.approaching_threshold_flag': _('Approach Threshold'), - 'hact_values.programmatic_visits.planned.q1': _('Programmatic Visits Planned Q1'), - 'hact_values.programmatic_visits.planned.q2': _('Q2'), - 'hact_values.programmatic_visits.planned.q3': _('Q3'), - 'hact_values.programmatic_visits.planned.q4': _('Q4'), - 'hact_min_requirements.programmatic_visits': _('Programmatic Visits M.R'), - 'hact_values.programmatic_visits.completed.q1': _('Programmatic Visits Completed Q1'), - 'hact_values.programmatic_visits.completed.q2': _('Q2'), - 'hact_values.programmatic_visits.completed.q3': _('Q3'), - 'hact_values.programmatic_visits.completed.q4': _('Q4'), - 'planned_engagement.spot_check_planned_q1': _('Spot Checks Planned Q1'), - 'planned_engagement.spot_check_planned_q2': _('Q2'), - 'planned_engagement.spot_check_planned_q3': _('Q3'), - 'planned_engagement.spot_check_planned_q4': _('Q4'), - 'hact_min_requirements.spot_checks': _('Spot Checks M.R'), - 'planned_engagement.spot_check_follow_up': _('Follow Up'), - 'hact_values.spot_checks.completed.q1': _('Spot Checks Completed Q1'), - 'hact_values.spot_checks.completed.q2': _('Q2'), - 'hact_values.spot_checks.completed.q3': _('Q3'), - 'hact_values.spot_checks.completed.q4': _('Q4'), - 'hact_min_requirements.audits': _('Audits M.R'), - 'hact_values.audits.completed': _('Audit Completed'), - 'hact_values.outstanding_findings': _('Audits Outstanding Findings'), - } - - class PartnerOrganizationDashboardCsvRenderer(FriendlyCSVRenderer): header = [ 'name', @@ -152,49 +77,6 @@ class PartnerOrganizationDashboardCsvRenderer(FriendlyCSVRenderer): } -class PartnerOrganizationSimpleHactCsvRenderer(FriendlyCSVRenderer): - - header = [ - 'name', - 'vendor_number', - 'partner_type', - 'shared_with', - 'type_of_assessment', - 'total_ct_ytd', - 'rating', - 'flags.expiring_assessment_flag', - 'flags.approaching_threshold_flag', - 'hact_values.programmatic_visits.planned.total', - 'hact_min_requirements.programmatic_visits', - 'hact_values.programmatic_visits.completed.total', - 'planned_engagement.spot_check_required', - 'hact_values.spot_checks.completed.total', - 'hact_min_requirements.audits', - 'hact_values.audits.completed', - 'hact_values.outstanding_findings', - ] - - labels = { - 'name': _('Implementing Partner'), - 'vendor_number': _('Vendor Number'), - 'partner_type': _('Partner Type'), - 'shared_with': _('Shared IP'), - 'total_ct_ytd': _('Cash Transfers Jan - Dec'), - 'type_of_assessment': _('Assessment Type'), - 'rating': _('Risk Rating'), - 'flags.expiring_assessment_flag': _('Expiring Threshold'), - 'flags.approaching_threshold_flag': _('Approach Threshold'), - 'hact_values.programmatic_visits.planned.total': _('Programmatic Visits Planned'), - 'hact_min_requirements.programmatic_visits': _('Programmatic Visits M.R'), - 'hact_values.programmatic_visits.completed.total': _('Programmatic Visits Completed'), - 'planned_engagement.spot_check_required': _('Spot Check Required'), - 'hact_values.spot_checks.completed.total': _('Spot Checks Completed'), - 'hact_min_requirements.audits': _('Audits M.R'), - 'hact_values.audits.completed': _('Audit Completed'), - 'hact_values.outstanding_findings': _('Audits Outstanding Findings'), - } - - class AgreementCSVRenderer(r.CSVRenderer): header = [ "agreement_number", diff --git a/src/etools/applications/partners/serializers/partner_organization_v2.py b/src/etools/applications/partners/serializers/partner_organization_v2.py index 827686806d..ac5f8bec21 100644 --- a/src/etools/applications/partners/serializers/partner_organization_v2.py +++ b/src/etools/applications/partners/serializers/partner_organization_v2.py @@ -438,38 +438,3 @@ class Meta: } } } - - -class PartnerOrganizationHactSerializer(serializers.ModelSerializer): - name = serializers.CharField(source='organization.name') - vendor_number = serializers.CharField(source='organization.vendor_number') - short_name = serializers.CharField(source='organization.short_name') - partner_type = serializers.CharField(source='organization.organization_type') - cso_type = serializers.CharField(source='organization.cso_type') - planned_engagement = PlannedEngagementSerializer(read_only=True) - hact_min_requirements = serializers.JSONField() - rating = serializers.CharField(source='get_rating_display') - - class Meta: - model = PartnerOrganization - fields = ( - "id", - "name", - "vendor_number", - "short_name", - "type_of_assessment", - "partner_type", - "partner_type_slug", - "cso_type", - "rating", - "shared_with", - "total_ct_cp", - "total_ct_cy", - "net_ct_cy", - "reported_cy", - "total_ct_ytd", - "hact_values", - "hact_min_requirements", - "flags", - "planned_engagement" - ) diff --git a/src/etools/applications/partners/tests/test_api_partners.py b/src/etools/applications/partners/tests/test_api_partners.py index bbf29b8b3f..e7fd709925 100644 --- a/src/etools/applications/partners/tests/test_api_partners.py +++ b/src/etools/applications/partners/tests/test_api_partners.py @@ -775,30 +775,6 @@ def test_update_partner_monitoring_activity_groups(self): self.assertEqual(response.data['hact_values']['programmatic_visits']['completed'][get_quarter()], 3) -class TestPartnerOrganizationHactAPIView(BaseTenantTestCase): - @classmethod - def setUpTestData(cls): - cls.url = reverse("partners_api:partner-hact") - cls.unicef_staff = UserFactory(is_staff=True) - cls.partner = PartnerFactory( - total_ct_cp=10.00, - total_ct_cy=8.00, - ) - - def test_get(self): - response = self.forced_auth_req( - 'get', - self.url, - user=self.unicef_staff - ) - self.assertEqual(response.status_code, status.HTTP_200_OK) - response_json = json.loads(response.rendered_content) - self.assertIsInstance(response_json, list) - self.assertEqual(len(response_json), 1) - self.assertIn('id', response_json[0].keys()) - self.assertEqual(response_json[0]['id'], self.partner.pk) - - class TestPartnerOrganizationAddView(BaseTenantTestCase): @classmethod def setUpTestData(cls): diff --git a/src/etools/applications/partners/urls_v2.py b/src/etools/applications/partners/urls_v2.py index 6011056809..f3af99e49b 100644 --- a/src/etools/applications/partners/urls_v2.py +++ b/src/etools/applications/partners/urls_v2.py @@ -46,9 +46,7 @@ PartnerOrganizationDashboardAPIView, PartnerOrganizationDeleteView, PartnerOrganizationDetailAPIView, - PartnerOrganizationHactAPIView, PartnerOrganizationListAPIView, - PartnerOrganizationSimpleHactAPIView, PartnerPlannedVisitsDeleteView, PartnerWithScheduledAuditCompleted, PartnerWithSpecialAuditCompleted, @@ -101,12 +99,6 @@ name='partner-planned-visits-del' ), - re_path(r'^partners/hact/$', - view=PartnerOrganizationHactAPIView.as_view(http_method_names=['get', ]), - name='partner-hact'), - re_path(r'^partners/hact/simple/$', - view=PartnerOrganizationSimpleHactAPIView.as_view(http_method_names=['get', ]), - name='partner-hact-simple'), re_path(r'^partners/dashboard/$', view=PartnerOrganizationDashboardAPIView.as_view(http_method_names=['get', ]), name='partner-dashboard'), diff --git a/src/etools/applications/partners/views/partner_organization_v2.py b/src/etools/applications/partners/views/partner_organization_v2.py index ac0a754bbe..e1efa69f82 100644 --- a/src/etools/applications/partners/views/partner_organization_v2.py +++ b/src/etools/applications/partners/views/partner_organization_v2.py @@ -28,8 +28,6 @@ from etools.applications.partners.exports_v2 import ( PartnerOrganizationCSVRenderer, PartnerOrganizationDashboardCsvRenderer, - PartnerOrganizationHactCsvRenderer, - PartnerOrganizationSimpleHactCsvRenderer, ) from etools.applications.partners.filters import PartnerScopeFilter from etools.applications.partners.models import ( @@ -59,7 +57,6 @@ PartnerOrganizationCreateUpdateSerializer, PartnerOrganizationDashboardSerializer, PartnerOrganizationDetailSerializer, - PartnerOrganizationHactSerializer, PartnerOrganizationListSerializer, PartnerPlannedVisitsSerializer, PlannedEngagementNestedSerializer, @@ -354,37 +351,6 @@ def _add_active_pd_for_non_signed_pca(self, serializer): item['alert_active_pd_for_ended_pca'] = True if item['id'] in qs else False -class PartnerOrganizationHactAPIView(ListAPIView): - - """ - Create new Partners. - Returns a list of Partners. - """ - permission_classes = (IsAdminUser,) - queryset = PartnerOrganization.objects.select_related('planned_engagement')\ - .prefetch_related('assessments').hact_active() - serializer_class = PartnerOrganizationHactSerializer - renderer_classes = (r.JSONRenderer, PartnerOrganizationHactCsvRenderer) - filename = 'detailed_hact_dashboard' - - def list(self, request, format=None): - """ - Checks for format query parameter - :returns: JSON or CSV file - """ - query_params = self.request.query_params - response = super().list(request) - if "format" in query_params.keys(): - if query_params.get("format") == 'csv': - response['Content-Disposition'] = f"attachment;filename={self.filename}.csv" - return response - - -class PartnerOrganizationSimpleHactAPIView(PartnerOrganizationHactAPIView): - renderer_classes = (r.JSONRenderer, PartnerOrganizationSimpleHactCsvRenderer) - filename = 'hact_dashboard' - - class PlannedEngagementAPIView(ListAPIView): """ From a1f612d16fec33f8d5e57ec9447b6d2897b38e24 Mon Sep 17 00:00:00 2001 From: Roman Karpovich Date: Tue, 21 May 2024 14:13:22 +0800 Subject: [PATCH 09/26] update locales --- .../partners/locale/ar/LC_MESSAGES/django.po | 146 ++++++++--------- .../partners/locale/es/LC_MESSAGES/django.po | 146 ++++++++--------- .../partners/locale/fr/LC_MESSAGES/django.po | 146 ++++++++--------- .../partners/locale/pt/LC_MESSAGES/django.po | 149 +++++++++--------- .../partners/locale/ru/LC_MESSAGES/django.po | 146 ++++++++--------- 5 files changed, 367 insertions(+), 366 deletions(-) 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 45d24a4d55..56d5ff8150 100644 --- a/src/etools/applications/partners/locale/ar/LC_MESSAGES/django.po +++ b/src/etools/applications/partners/locale/ar/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-03-26 12:56+0000\n" +"POT-Creation-Date: 2024-05-21 06:13+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -140,66 +140,6 @@ msgstr "الزيارات البرامجية المخططة" msgid "Implementing Partner" msgstr "الشريك المنفذ" -msgid "Shared IP" -msgstr "شريك منفذ مشترك" - -msgid "Cash Transfer 1 OCT - 30 SEP" -msgstr "تحويل نقدي 1 أكتوبر - 30 سبتمبر" - -msgid "Liquidations 1 OCT - 30 SEP" -msgstr "التخليص 1 أكتوبر - 30 سبتمبر" - -msgid "Cash Transfers Jan - Dec" -msgstr "التحويلات النقدية يناير - ديسمبر" - -msgid "Risk Rating" -msgstr "تقييم المخاطرة" - -msgid "Expiring Threshold" -msgstr "عتبة انتهاء الصلاحية" - -msgid "Approach Threshold" -msgstr "عتبة الاقتراب" - -msgid "Programmatic Visits Planned Q1" -msgstr "الزيارات البرجمية المخطط لها ، الربع الأول" - -msgid "Q2" -msgstr "الربع الثاني" - -msgid "Q3" -msgstr "الربع الثالث" - -msgid "Q4" -msgstr "الربع الرابع" - -msgid "Programmatic Visits M.R" -msgstr "الحد الأدنى للزيارات البرامجية" - -msgid "Programmatic Visits Completed Q1" -msgstr "الزيارات البرمجية المنجزة في الربع الأول" - -msgid "Spot Checks Planned Q1" -msgstr "عدد الزيارات الرقابية المفاجئة المخطط لها في الربع الأول" - -msgid "Spot Checks M.R" -msgstr "الحد الأدنى من عدد الزيارات الرقابية المفاجئة المطلوبة" - -msgid "Follow Up" -msgstr "متابعة" - -msgid "Spot Checks Completed Q1" -msgstr "عدد الزيارات الرقابية المفاجئة المكتملة في الربع الأول" - -msgid "Audits M.R" -msgstr "الحد الادنى لعمليات التدقيق المالي" - -msgid "Audit Completed" -msgstr "التدقيق المالي المكتمل" - -msgid "Audits Outstanding Findings" -msgstr "نتائج التدقيق المالي المعلقة" - msgid "Sections" msgstr "الأقسام" @@ -230,18 +170,6 @@ msgstr "مبلغ التحويل النقدي المباشر المستحق بي msgid "Outstanding DCT Amount more than 9 months" msgstr "مبلغ التحويل النقدي المباشر المستحق لأكثر من 9 أشهر" -msgid "Programmatic Visits Planned" -msgstr "الزيارات البرامجية المخطط لها" - -msgid "Programmatic Visits Completed" -msgstr "الزيارات البرمجية المنجزة" - -msgid "Spot Check Required" -msgstr "عدد الزيارات الرقابية المفاجئة المطلوبة" - -msgid "Spot Checks Completed" -msgstr "عدد الزيارات الرقابية المفاجئة المكتملة" - msgid "Reference Number" msgstr "رقم المرجع" @@ -730,6 +658,9 @@ msgstr "دولة" msgid "Alternate ID" msgstr "معرف بديل" +msgid "Risk Rating" +msgstr "تقييم المخاطرة" + msgid "Last Assessment Date" msgstr "تاريخ آخر تقييم" @@ -1114,6 +1045,15 @@ msgstr "تعديلات التدخل" msgid "Q1" msgstr "" +msgid "Q2" +msgstr "الربع الثاني" + +msgid "Q3" +msgstr "الربع الثالث" + +msgid "Q4" +msgstr "الربع الرابع" + msgid "Intervention" msgstr "تدخل" @@ -2246,6 +2186,66 @@ msgstr "لا تحتوي الاستجابة التي أرجعها الخادم ع msgid "Partnership Manager role required for pca export." msgstr "دور مدير الشراكة المطلوب لتصدير pca." +#~ msgid "Shared IP" +#~ msgstr "شريك منفذ مشترك" + +#~ msgid "Cash Transfer 1 OCT - 30 SEP" +#~ msgstr "تحويل نقدي 1 أكتوبر - 30 سبتمبر" + +#~ msgid "Liquidations 1 OCT - 30 SEP" +#~ msgstr "التخليص 1 أكتوبر - 30 سبتمبر" + +#~ msgid "Cash Transfers Jan - Dec" +#~ msgstr "التحويلات النقدية يناير - ديسمبر" + +#~ msgid "Expiring Threshold" +#~ msgstr "عتبة انتهاء الصلاحية" + +#~ msgid "Approach Threshold" +#~ msgstr "عتبة الاقتراب" + +#~ msgid "Programmatic Visits Planned Q1" +#~ msgstr "الزيارات البرجمية المخطط لها ، الربع الأول" + +#~ msgid "Programmatic Visits M.R" +#~ msgstr "الحد الأدنى للزيارات البرامجية" + +#~ msgid "Programmatic Visits Completed Q1" +#~ msgstr "الزيارات البرمجية المنجزة في الربع الأول" + +#~ msgid "Spot Checks Planned Q1" +#~ msgstr "عدد الزيارات الرقابية المفاجئة المخطط لها في الربع الأول" + +#~ msgid "Spot Checks M.R" +#~ msgstr "الحد الأدنى من عدد الزيارات الرقابية المفاجئة المطلوبة" + +#~ msgid "Follow Up" +#~ msgstr "متابعة" + +#~ msgid "Spot Checks Completed Q1" +#~ msgstr "عدد الزيارات الرقابية المفاجئة المكتملة في الربع الأول" + +#~ msgid "Audits M.R" +#~ msgstr "الحد الادنى لعمليات التدقيق المالي" + +#~ msgid "Audit Completed" +#~ msgstr "التدقيق المالي المكتمل" + +#~ msgid "Audits Outstanding Findings" +#~ msgstr "نتائج التدقيق المالي المعلقة" + +#~ msgid "Programmatic Visits Planned" +#~ msgstr "الزيارات البرامجية المخطط لها" + +#~ msgid "Programmatic Visits Completed" +#~ msgstr "الزيارات البرمجية المنجزة" + +#~ msgid "Spot Check Required" +#~ msgstr "عدد الزيارات الرقابية المفاجئة المطلوبة" + +#~ msgid "Spot Checks Completed" +#~ msgstr "عدد الزيارات الرقابية المفاجئة المكتملة" + #~ msgid "" #~ "Final Review must be approved for documents having amount transferred " #~ "greater than 100,000" 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 4fdc23fd88..5595103a38 100644 --- a/src/etools/applications/partners/locale/es/LC_MESSAGES/django.po +++ b/src/etools/applications/partners/locale/es/LC_MESSAGES/django.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: et-partners2-bk\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-03-26 12:56+0000\n" +"POT-Creation-Date: 2024-05-21 06:13+0000\n" "Language: es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -135,66 +135,6 @@ msgstr "Visitas programáticas previstas" msgid "Implementing Partner" msgstr "Socio ejecutor" -msgid "Shared IP" -msgstr "IP compartida" - -msgid "Cash Transfer 1 OCT - 30 SEP" -msgstr "Transferencia de efectivo 1 OCT - 30 SEP" - -msgid "Liquidations 1 OCT - 30 SEP" -msgstr "Liquidaciones 1 OCT - 30 SEP" - -msgid "Cash Transfers Jan - Dec" -msgstr "Transferencias enero - diciembre" - -msgid "Risk Rating" -msgstr "Calificación del riesgo" - -msgid "Expiring Threshold" -msgstr "Umbral de expiración" - -msgid "Approach Threshold" -msgstr "Umbral de aproximación" - -msgid "Programmatic Visits Planned Q1" -msgstr "Visitas programáticas previstas Q1" - -msgid "Q2" -msgstr "Q2" - -msgid "Q3" -msgstr "Q3" - -msgid "Q4" -msgstr "Q4" - -msgid "Programmatic Visits M.R" -msgstr "Visitas programáticas M.R" - -msgid "Programmatic Visits Completed Q1" -msgstr "Visitas programáticas realizadas 1er trimestre" - -msgid "Spot Checks Planned Q1" -msgstr "Controles aleatorios previstos Q1" - -msgid "Spot Checks M.R" -msgstr "Controles aleatorios M.R" - -msgid "Follow Up" -msgstr "Seguimiento" - -msgid "Spot Checks Completed Q1" -msgstr "Controles aleatorios completados Q1" - -msgid "Audits M.R" -msgstr "Auditorías M.R" - -msgid "Audit Completed" -msgstr "Auditoría finalizada" - -msgid "Audits Outstanding Findings" -msgstr "Auditorías Conclusiones pendientes" - msgid "Sections" msgstr "Secciones" @@ -225,18 +165,6 @@ msgstr "Importe pendiente del SES entre 6 y 9 meses" msgid "Outstanding DCT Amount more than 9 months" msgstr "Importe del SES pendiente más de 9 meses" -msgid "Programmatic Visits Planned" -msgstr "Visitas programáticas previstas" - -msgid "Programmatic Visits Completed" -msgstr "Visitas programáticas realizadas" - -msgid "Spot Check Required" -msgstr "Comprobación aleatoria obligatoria" - -msgid "Spot Checks Completed" -msgstr "Controles aleatorios realizados" - msgid "Reference Number" msgstr "Número de referencia" @@ -727,6 +655,9 @@ msgstr "País" msgid "Alternate ID" msgstr "ID alternativo" +msgid "Risk Rating" +msgstr "Calificación del riesgo" + msgid "Last Assessment Date" msgstr "Fecha de la última evaluación" @@ -1111,6 +1042,15 @@ msgstr "Enmiendas de intervención" msgid "Q1" msgstr "" +msgid "Q2" +msgstr "Q2" + +msgid "Q3" +msgstr "Q3" + +msgid "Q4" +msgstr "Q4" + msgid "Intervention" msgstr "Intervención" @@ -2313,6 +2253,66 @@ msgid "Partnership Manager role required for pca export." msgstr "" "Se requiere el rol de Gerente de asociación para la exportación de PCA." +#~ msgid "Shared IP" +#~ msgstr "IP compartida" + +#~ msgid "Cash Transfer 1 OCT - 30 SEP" +#~ msgstr "Transferencia de efectivo 1 OCT - 30 SEP" + +#~ msgid "Liquidations 1 OCT - 30 SEP" +#~ msgstr "Liquidaciones 1 OCT - 30 SEP" + +#~ msgid "Cash Transfers Jan - Dec" +#~ msgstr "Transferencias enero - diciembre" + +#~ msgid "Expiring Threshold" +#~ msgstr "Umbral de expiración" + +#~ msgid "Approach Threshold" +#~ msgstr "Umbral de aproximación" + +#~ msgid "Programmatic Visits Planned Q1" +#~ msgstr "Visitas programáticas previstas Q1" + +#~ msgid "Programmatic Visits M.R" +#~ msgstr "Visitas programáticas M.R" + +#~ msgid "Programmatic Visits Completed Q1" +#~ msgstr "Visitas programáticas realizadas 1er trimestre" + +#~ msgid "Spot Checks Planned Q1" +#~ msgstr "Controles aleatorios previstos Q1" + +#~ msgid "Spot Checks M.R" +#~ msgstr "Controles aleatorios M.R" + +#~ msgid "Follow Up" +#~ msgstr "Seguimiento" + +#~ msgid "Spot Checks Completed Q1" +#~ msgstr "Controles aleatorios completados Q1" + +#~ msgid "Audits M.R" +#~ msgstr "Auditorías M.R" + +#~ msgid "Audit Completed" +#~ msgstr "Auditoría finalizada" + +#~ msgid "Audits Outstanding Findings" +#~ msgstr "Auditorías Conclusiones pendientes" + +#~ msgid "Programmatic Visits Planned" +#~ msgstr "Visitas programáticas previstas" + +#~ msgid "Programmatic Visits Completed" +#~ msgstr "Visitas programáticas realizadas" + +#~ msgid "Spot Check Required" +#~ msgstr "Comprobación aleatoria obligatoria" + +#~ msgid "Spot Checks Completed" +#~ msgstr "Controles aleatorios realizados" + #~ msgid "" #~ "Final Review must be approved for documents having amount transferred " #~ "greater than 100,000" 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 98caab162d..840e6fc745 100644 --- a/src/etools/applications/partners/locale/fr/LC_MESSAGES/django.po +++ b/src/etools/applications/partners/locale/fr/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-03-26 12:56+0000\n" +"POT-Creation-Date: 2024-05-21 06:13+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -143,66 +143,6 @@ msgstr "Visites programmées" msgid "Implementing Partner" msgstr "Partenaire de mise en œuvre" -msgid "Shared IP" -msgstr "IP partagée" - -msgid "Cash Transfer 1 OCT - 30 SEP" -msgstr "Transfert d'argent liquide 1 OCT - 30 SEP" - -msgid "Liquidations 1 OCT - 30 SEP" -msgstr "Liquidations 1 OCT - 30 SEP" - -msgid "Cash Transfers Jan - Dec" -msgstr "Transferts de fonds Jan - Déc" - -msgid "Risk Rating" -msgstr "Évaluation du risque" - -msgid "Expiring Threshold" -msgstr "Seuil d'expiration" - -msgid "Approach Threshold" -msgstr "Seuil d'approche" - -msgid "Programmatic Visits Planned Q1" -msgstr "Visites programmées Q1" - -msgid "Q2" -msgstr "Q2" - -msgid "Q3" -msgstr "Q3" - -msgid "Q4" -msgstr "Q4" - -msgid "Programmatic Visits M.R" -msgstr "Visites programmées M.R" - -msgid "Programmatic Visits Completed Q1" -msgstr "Visites programmatiques achevées Q1" - -msgid "Spot Checks Planned Q1" -msgstr "Contrôles ponctuels prévus Q1" - -msgid "Spot Checks M.R" -msgstr "Contrôles ponctuels M.R" - -msgid "Follow Up" -msgstr "Suivi" - -msgid "Spot Checks Completed Q1" -msgstr "Contrôles ponctuels achevés Q1" - -msgid "Audits M.R" -msgstr "Audits M.R" - -msgid "Audit Completed" -msgstr "Audit terminé" - -msgid "Audits Outstanding Findings" -msgstr "Audits Constatations en suspens" - msgid "Sections" msgstr "Sections" @@ -233,18 +173,6 @@ msgstr "Montant du DCT impayé entre 6 et 9 mois" msgid "Outstanding DCT Amount more than 9 months" msgstr "Montant DCT impayé plus de 9 mois" -msgid "Programmatic Visits Planned" -msgstr "Visites programmées" - -msgid "Programmatic Visits Completed" -msgstr "Visites programmatiques achevées" - -msgid "Spot Check Required" -msgstr "Vérification ponctuelle requise" - -msgid "Spot Checks Completed" -msgstr "Contrôles ponctuels effectués" - msgid "Reference Number" msgstr "Numéro de référence" @@ -736,6 +664,9 @@ msgstr "Pays" msgid "Alternate ID" msgstr "ID alternatif" +msgid "Risk Rating" +msgstr "Évaluation du risque" + msgid "Last Assessment Date" msgstr "Date de la dernière évaluation" @@ -1120,6 +1051,15 @@ msgstr "Modifications de l'intervention" msgid "Q1" msgstr "" +msgid "Q2" +msgstr "Q2" + +msgid "Q3" +msgstr "Q3" + +msgid "Q4" +msgstr "Q4" + msgid "Intervention" msgstr "Intervention" @@ -2327,6 +2267,66 @@ msgstr "" msgid "Partnership Manager role required for pca export." msgstr "Rôle de gestionnaire de partenariat requis pour l'exportation pca." +#~ msgid "Shared IP" +#~ msgstr "IP partagée" + +#~ msgid "Cash Transfer 1 OCT - 30 SEP" +#~ msgstr "Transfert d'argent liquide 1 OCT - 30 SEP" + +#~ msgid "Liquidations 1 OCT - 30 SEP" +#~ msgstr "Liquidations 1 OCT - 30 SEP" + +#~ msgid "Cash Transfers Jan - Dec" +#~ msgstr "Transferts de fonds Jan - Déc" + +#~ msgid "Expiring Threshold" +#~ msgstr "Seuil d'expiration" + +#~ msgid "Approach Threshold" +#~ msgstr "Seuil d'approche" + +#~ msgid "Programmatic Visits Planned Q1" +#~ msgstr "Visites programmées Q1" + +#~ msgid "Programmatic Visits M.R" +#~ msgstr "Visites programmées M.R" + +#~ msgid "Programmatic Visits Completed Q1" +#~ msgstr "Visites programmatiques achevées Q1" + +#~ msgid "Spot Checks Planned Q1" +#~ msgstr "Contrôles ponctuels prévus Q1" + +#~ msgid "Spot Checks M.R" +#~ msgstr "Contrôles ponctuels M.R" + +#~ msgid "Follow Up" +#~ msgstr "Suivi" + +#~ msgid "Spot Checks Completed Q1" +#~ msgstr "Contrôles ponctuels achevés Q1" + +#~ msgid "Audits M.R" +#~ msgstr "Audits M.R" + +#~ msgid "Audit Completed" +#~ msgstr "Audit terminé" + +#~ msgid "Audits Outstanding Findings" +#~ msgstr "Audits Constatations en suspens" + +#~ msgid "Programmatic Visits Planned" +#~ msgstr "Visites programmées" + +#~ msgid "Programmatic Visits Completed" +#~ msgstr "Visites programmatiques achevées" + +#~ msgid "Spot Check Required" +#~ msgstr "Vérification ponctuelle requise" + +#~ msgid "Spot Checks Completed" +#~ msgstr "Contrôles ponctuels effectués" + #~ msgid "" #~ "Final Review must be approved for documents having amount transferred " #~ "greater than 100,000" 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 3aa2bb39f0..e8d3813c18 100644 --- a/src/etools/applications/partners/locale/pt/LC_MESSAGES/django.po +++ b/src/etools/applications/partners/locale/pt/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-03-26 12:56+0000\n" +"POT-Creation-Date: 2024-05-21 06:13+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -148,67 +148,6 @@ msgstr "Visitas programáticas planejadas" msgid "Implementing Partner" msgstr "Parceiro implementador" -msgid "Shared IP" -msgstr "IP compartilhado" - -msgid "Cash Transfer 1 OCT - 30 SEP" -msgstr "Transferência financeira de 1 de outubro a 30 de setembro" - -msgid "Liquidations 1 OCT - 30 SEP" -msgstr "Liquidações de 1 de outubro a 30 de setembro" - -msgid "Cash Transfers Jan - Dec" -msgstr "Transferências financeiras de janeiro a dezembro" - -msgid "Risk Rating" -msgstr "Classificação de risco" - -msgid "Expiring Threshold" -msgstr "Limite de expiração" - -msgid "Approach Threshold" -msgstr "Limiar de Abordagem" - -msgid "Programmatic Visits Planned Q1" -msgstr "Visitas programáticas planejadas para o primeiro trimestre" - -msgid "Q2" -msgstr "Q2" - -msgid "Q3" -msgstr "Q3" - -msgid "Q4" -msgstr "Q4" - -msgid "Programmatic Visits M.R" -msgstr "Visitas programáticas M.R" - -msgid "Programmatic Visits Completed Q1" -msgstr "Visitas programáticas concluídas no primeiro trimestre" - -msgid "Spot Checks Planned Q1" -msgstr "" -"Spot Checks / Verificações pontuais planejadas para o primeiro trimestre" - -msgid "Spot Checks M.R" -msgstr "Spot Checks M.R" - -msgid "Follow Up" -msgstr "Acompanhamento" - -msgid "Spot Checks Completed Q1" -msgstr "Spot Checks / Verificações pontuais concluídas no primeiro trimestre" - -msgid "Audits M.R" -msgstr "Mínimo Requeridas Auditorias" - -msgid "Audit Completed" -msgstr "Auditoria concluída" - -msgid "Audits Outstanding Findings" -msgstr "Auditoria: Apontamentos Importantes " - msgid "Sections" msgstr "Seções" @@ -243,18 +182,6 @@ msgstr "Valor de DCT pendente entre 6 e 9 meses" msgid "Outstanding DCT Amount more than 9 months" msgstr "Valor de DCT pendente há mais de 9 meses" -msgid "Programmatic Visits Planned" -msgstr "Visitas programáticas planejadas" - -msgid "Programmatic Visits Completed" -msgstr "Visitas programáticas concluídas" - -msgid "Spot Check Required" -msgstr "É necessária realizar um Spot Check (verificação pontual)" - -msgid "Spot Checks Completed" -msgstr "Spot Checks/ Verificações pontuais concluídas" - msgid "Reference Number" msgstr "Número de Referência" @@ -746,6 +673,9 @@ msgstr "País" msgid "Alternate ID" msgstr "Identificação alternativa" +msgid "Risk Rating" +msgstr "Classificação de risco" + msgid "Last Assessment Date" msgstr "Data da última avaliação" @@ -1130,6 +1060,15 @@ msgstr "Aditivos da implementação do programa" msgid "Q1" msgstr "" +msgid "Q2" +msgstr "Q2" + +msgid "Q3" +msgstr "Q3" + +msgid "Q4" +msgstr "Q4" + msgid "Intervention" msgstr "Intervenção" @@ -2334,6 +2273,68 @@ msgstr "" msgid "Partnership Manager role required for pca export." msgstr "A função de gerente de parceria é necessária para a exportação de pca." +#~ msgid "Shared IP" +#~ msgstr "IP compartilhado" + +#~ msgid "Cash Transfer 1 OCT - 30 SEP" +#~ msgstr "Transferência financeira de 1 de outubro a 30 de setembro" + +#~ msgid "Liquidations 1 OCT - 30 SEP" +#~ msgstr "Liquidações de 1 de outubro a 30 de setembro" + +#~ msgid "Cash Transfers Jan - Dec" +#~ msgstr "Transferências financeiras de janeiro a dezembro" + +#~ msgid "Expiring Threshold" +#~ msgstr "Limite de expiração" + +#~ msgid "Approach Threshold" +#~ msgstr "Limiar de Abordagem" + +#~ msgid "Programmatic Visits Planned Q1" +#~ msgstr "Visitas programáticas planejadas para o primeiro trimestre" + +#~ msgid "Programmatic Visits M.R" +#~ msgstr "Visitas programáticas M.R" + +#~ msgid "Programmatic Visits Completed Q1" +#~ msgstr "Visitas programáticas concluídas no primeiro trimestre" + +#~ msgid "Spot Checks Planned Q1" +#~ msgstr "" +#~ "Spot Checks / Verificações pontuais planejadas para o primeiro trimestre" + +#~ msgid "Spot Checks M.R" +#~ msgstr "Spot Checks M.R" + +#~ msgid "Follow Up" +#~ msgstr "Acompanhamento" + +#~ msgid "Spot Checks Completed Q1" +#~ msgstr "" +#~ "Spot Checks / Verificações pontuais concluídas no primeiro trimestre" + +#~ msgid "Audits M.R" +#~ msgstr "Mínimo Requeridas Auditorias" + +#~ msgid "Audit Completed" +#~ msgstr "Auditoria concluída" + +#~ msgid "Audits Outstanding Findings" +#~ msgstr "Auditoria: Apontamentos Importantes " + +#~ msgid "Programmatic Visits Planned" +#~ msgstr "Visitas programáticas planejadas" + +#~ msgid "Programmatic Visits Completed" +#~ msgstr "Visitas programáticas concluídas" + +#~ msgid "Spot Check Required" +#~ msgstr "É necessária realizar um Spot Check (verificação pontual)" + +#~ msgid "Spot Checks Completed" +#~ msgstr "Spot Checks/ Verificações pontuais concluídas" + #~ msgid "" #~ "Final Review must be approved for documents having amount transferred " #~ "greater than 100,000" 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 16c7e0d97c..ca4ec08a38 100644 --- a/src/etools/applications/partners/locale/ru/LC_MESSAGES/django.po +++ b/src/etools/applications/partners/locale/ru/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-03-26 12:56+0000\n" +"POT-Creation-Date: 2024-05-21 06:13+0000\n" "Language: ru\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -140,66 +140,6 @@ msgstr "Запланированные программные визиты" msgid "Implementing Partner" msgstr "Исполнительный Партнер" -msgid "Shared IP" -msgstr "Совместный ИП" - -msgid "Cash Transfer 1 OCT - 30 SEP" -msgstr "Денежные переводы за 1 окт.-30 сен." - -msgid "Liquidations 1 OCT - 30 SEP" -msgstr "Ликвидации 1 окт. - 30 сен." - -msgid "Cash Transfers Jan - Dec" -msgstr "Денежные переводы янв - дек" - -msgid "Risk Rating" -msgstr "Рейтинг риска" - -msgid "Expiring Threshold" -msgstr "Порог просрочки" - -msgid "Approach Threshold" -msgstr "Приближается к порогу" - -msgid "Programmatic Visits Planned Q1" -msgstr "Запланированные программные визиты в I кв-ле" - -msgid "Q2" -msgstr "кв-л II" - -msgid "Q3" -msgstr "кв-л III" - -msgid "Q4" -msgstr "кв-л IV" - -msgid "Programmatic Visits M.R" -msgstr "М.Т. программные визиты" - -msgid "Programmatic Visits Completed Q1" -msgstr "Завершенные программные визиты в I кв-ле" - -msgid "Spot Checks Planned Q1" -msgstr "Запланированные выборочные проверки в I кв-ле" - -msgid "Spot Checks M.R" -msgstr "М.Т. выборочные проверки" - -msgid "Follow Up" -msgstr "Последующие действия" - -msgid "Spot Checks Completed Q1" -msgstr "Завершенные выборочные проверки в I кв-ле" - -msgid "Audits M.R" -msgstr "Аудиты М.Т." - -msgid "Audit Completed" -msgstr "Аудит завершен" - -msgid "Audits Outstanding Findings" -msgstr "Неразрешенные выводы аудитов" - msgid "Sections" msgstr "Секции" @@ -230,18 +170,6 @@ msgstr "Непогашенная сумма ПДП от 6 до 9 месяцев" msgid "Outstanding DCT Amount more than 9 months" msgstr "Непогашенная сумма ПДП более 9 месяцев" -msgid "Programmatic Visits Planned" -msgstr "Планируемые программные визиты" - -msgid "Programmatic Visits Completed" -msgstr "Завершенные программные визиты" - -msgid "Spot Check Required" -msgstr "Требуется выборочная проверка" - -msgid "Spot Checks Completed" -msgstr "Завершенные выборочные проверки" - msgid "Reference Number" msgstr "Номер" @@ -729,6 +657,9 @@ msgstr "Страна" msgid "Alternate ID" msgstr "Альтернативный идентификатор" +msgid "Risk Rating" +msgstr "Рейтинг риска" + msgid "Last Assessment Date" msgstr "Дата последней оценки" @@ -1113,6 +1044,15 @@ msgstr "Поправки к программе" msgid "Q1" msgstr "" +msgid "Q2" +msgstr "кв-л II" + +msgid "Q3" +msgstr "кв-л III" + +msgid "Q4" +msgstr "кв-л IV" + msgid "Intervention" msgstr "Программа" @@ -2298,6 +2238,66 @@ msgstr "" msgid "Partnership Manager role required for pca export." msgstr "Для экспорта PCA требуется роль менеджера по партнерству." +#~ msgid "Shared IP" +#~ msgstr "Совместный ИП" + +#~ msgid "Cash Transfer 1 OCT - 30 SEP" +#~ msgstr "Денежные переводы за 1 окт.-30 сен." + +#~ msgid "Liquidations 1 OCT - 30 SEP" +#~ msgstr "Ликвидации 1 окт. - 30 сен." + +#~ msgid "Cash Transfers Jan - Dec" +#~ msgstr "Денежные переводы янв - дек" + +#~ msgid "Expiring Threshold" +#~ msgstr "Порог просрочки" + +#~ msgid "Approach Threshold" +#~ msgstr "Приближается к порогу" + +#~ msgid "Programmatic Visits Planned Q1" +#~ msgstr "Запланированные программные визиты в I кв-ле" + +#~ msgid "Programmatic Visits M.R" +#~ msgstr "М.Т. программные визиты" + +#~ msgid "Programmatic Visits Completed Q1" +#~ msgstr "Завершенные программные визиты в I кв-ле" + +#~ msgid "Spot Checks Planned Q1" +#~ msgstr "Запланированные выборочные проверки в I кв-ле" + +#~ msgid "Spot Checks M.R" +#~ msgstr "М.Т. выборочные проверки" + +#~ msgid "Follow Up" +#~ msgstr "Последующие действия" + +#~ msgid "Spot Checks Completed Q1" +#~ msgstr "Завершенные выборочные проверки в I кв-ле" + +#~ msgid "Audits M.R" +#~ msgstr "Аудиты М.Т." + +#~ msgid "Audit Completed" +#~ msgstr "Аудит завершен" + +#~ msgid "Audits Outstanding Findings" +#~ msgstr "Неразрешенные выводы аудитов" + +#~ msgid "Programmatic Visits Planned" +#~ msgstr "Планируемые программные визиты" + +#~ msgid "Programmatic Visits Completed" +#~ msgstr "Завершенные программные визиты" + +#~ msgid "Spot Check Required" +#~ msgstr "Требуется выборочная проверка" + +#~ msgid "Spot Checks Completed" +#~ msgstr "Завершенные выборочные проверки" + #~ msgid "" #~ "Final Review must be approved for documents having amount transferred " #~ "greater than 100,000" From e4623fcdf1895b797e5e4d3d08c71e51c7425998 Mon Sep 17 00:00:00 2001 From: Robert Avram Date: Tue, 4 Jun 2024 06:39:01 -0700 Subject: [PATCH 10/26] Update tasks.py --- src/etools/applications/partners/tasks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/etools/applications/partners/tasks.py b/src/etools/applications/partners/tasks.py index d2d194de2c..a6223abb5b 100644 --- a/src/etools/applications/partners/tasks.py +++ b/src/etools/applications/partners/tasks.py @@ -322,7 +322,7 @@ def notify_partner_assessment_expires(): for pd in pds: focal_points_emails.update(pd.unicef_focal_point_emails) filtered_emails = User.objects.base_qs().filter( - email__in=focal_points_emails, profile__country_override=country).values_list('email', flat=True) + email__in=focal_points_emails, profile__country=country).values_list('email', flat=True) if filtered_emails: days = partner.core_value_assessment_expiring.days.__abs__() \ From c60ed996619c272540d5a56ad559db92bac1f6ef Mon Sep 17 00:00:00 2001 From: Roman Karpovich Date: Tue, 4 Jun 2024 19:42:53 +0400 Subject: [PATCH 11/26] update locales --- .../partners/locale/ar/LC_MESSAGES/django.po | 146 ++++++++--------- .../partners/locale/es/LC_MESSAGES/django.po | 146 ++++++++--------- .../partners/locale/fr/LC_MESSAGES/django.po | 146 ++++++++--------- .../partners/locale/pt/LC_MESSAGES/django.po | 149 +++++++++--------- .../partners/locale/ru/LC_MESSAGES/django.po | 146 ++++++++--------- 5 files changed, 367 insertions(+), 366 deletions(-) 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 09cea1a9b0..fd7b42fd90 100644 --- a/src/etools/applications/partners/locale/ar/LC_MESSAGES/django.po +++ b/src/etools/applications/partners/locale/ar/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-05-21 04:41+0000\n" +"POT-Creation-Date: 2024-06-04 15:42+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -140,66 +140,6 @@ msgstr "الزيارات البرامجية المخططة" msgid "Implementing Partner" msgstr "الشريك المنفذ" -msgid "Shared IP" -msgstr "شريك منفذ مشترك" - -msgid "Cash Transfer 1 OCT - 30 SEP" -msgstr "تحويل نقدي 1 أكتوبر - 30 سبتمبر" - -msgid "Liquidations 1 OCT - 30 SEP" -msgstr "التخليص 1 أكتوبر - 30 سبتمبر" - -msgid "Cash Transfers Jan - Dec" -msgstr "التحويلات النقدية يناير - ديسمبر" - -msgid "Risk Rating" -msgstr "تقييم المخاطرة" - -msgid "Expiring Threshold" -msgstr "عتبة انتهاء الصلاحية" - -msgid "Approach Threshold" -msgstr "عتبة الاقتراب" - -msgid "Programmatic Visits Planned Q1" -msgstr "الزيارات البرجمية المخطط لها ، الربع الأول" - -msgid "Q2" -msgstr "الربع الثاني" - -msgid "Q3" -msgstr "الربع الثالث" - -msgid "Q4" -msgstr "الربع الرابع" - -msgid "Programmatic Visits M.R" -msgstr "الحد الأدنى للزيارات البرامجية" - -msgid "Programmatic Visits Completed Q1" -msgstr "الزيارات البرمجية المنجزة في الربع الأول" - -msgid "Spot Checks Planned Q1" -msgstr "عدد الزيارات الرقابية المفاجئة المخطط لها في الربع الأول" - -msgid "Spot Checks M.R" -msgstr "الحد الأدنى من عدد الزيارات الرقابية المفاجئة المطلوبة" - -msgid "Follow Up" -msgstr "متابعة" - -msgid "Spot Checks Completed Q1" -msgstr "عدد الزيارات الرقابية المفاجئة المكتملة في الربع الأول" - -msgid "Audits M.R" -msgstr "الحد الادنى لعمليات التدقيق المالي" - -msgid "Audit Completed" -msgstr "التدقيق المالي المكتمل" - -msgid "Audits Outstanding Findings" -msgstr "نتائج التدقيق المالي المعلقة" - msgid "Sections" msgstr "الأقسام" @@ -230,18 +170,6 @@ msgstr "مبلغ التحويل النقدي المباشر المستحق بي msgid "Outstanding DCT Amount more than 9 months" msgstr "مبلغ التحويل النقدي المباشر المستحق لأكثر من 9 أشهر" -msgid "Programmatic Visits Planned" -msgstr "الزيارات البرامجية المخطط لها" - -msgid "Programmatic Visits Completed" -msgstr "الزيارات البرمجية المنجزة" - -msgid "Spot Check Required" -msgstr "عدد الزيارات الرقابية المفاجئة المطلوبة" - -msgid "Spot Checks Completed" -msgstr "عدد الزيارات الرقابية المفاجئة المكتملة" - msgid "Reference Number" msgstr "رقم المرجع" @@ -730,6 +658,9 @@ msgstr "دولة" msgid "Alternate ID" msgstr "معرف بديل" +msgid "Risk Rating" +msgstr "تقييم المخاطرة" + msgid "Last Assessment Date" msgstr "تاريخ آخر تقييم" @@ -1114,6 +1045,15 @@ msgstr "تعديلات التدخل" msgid "Q1" msgstr "" +msgid "Q2" +msgstr "الربع الثاني" + +msgid "Q3" +msgstr "الربع الثالث" + +msgid "Q4" +msgstr "الربع الرابع" + msgid "Intervention" msgstr "تدخل" @@ -2249,6 +2189,66 @@ msgstr "لا تحتوي الاستجابة التي أرجعها الخادم ع msgid "Partnership Manager role required for pca export." msgstr "دور مدير الشراكة المطلوب لتصدير pca." +#~ msgid "Shared IP" +#~ msgstr "شريك منفذ مشترك" + +#~ msgid "Cash Transfer 1 OCT - 30 SEP" +#~ msgstr "تحويل نقدي 1 أكتوبر - 30 سبتمبر" + +#~ msgid "Liquidations 1 OCT - 30 SEP" +#~ msgstr "التخليص 1 أكتوبر - 30 سبتمبر" + +#~ msgid "Cash Transfers Jan - Dec" +#~ msgstr "التحويلات النقدية يناير - ديسمبر" + +#~ msgid "Expiring Threshold" +#~ msgstr "عتبة انتهاء الصلاحية" + +#~ msgid "Approach Threshold" +#~ msgstr "عتبة الاقتراب" + +#~ msgid "Programmatic Visits Planned Q1" +#~ msgstr "الزيارات البرجمية المخطط لها ، الربع الأول" + +#~ msgid "Programmatic Visits M.R" +#~ msgstr "الحد الأدنى للزيارات البرامجية" + +#~ msgid "Programmatic Visits Completed Q1" +#~ msgstr "الزيارات البرمجية المنجزة في الربع الأول" + +#~ msgid "Spot Checks Planned Q1" +#~ msgstr "عدد الزيارات الرقابية المفاجئة المخطط لها في الربع الأول" + +#~ msgid "Spot Checks M.R" +#~ msgstr "الحد الأدنى من عدد الزيارات الرقابية المفاجئة المطلوبة" + +#~ msgid "Follow Up" +#~ msgstr "متابعة" + +#~ msgid "Spot Checks Completed Q1" +#~ msgstr "عدد الزيارات الرقابية المفاجئة المكتملة في الربع الأول" + +#~ msgid "Audits M.R" +#~ msgstr "الحد الادنى لعمليات التدقيق المالي" + +#~ msgid "Audit Completed" +#~ msgstr "التدقيق المالي المكتمل" + +#~ msgid "Audits Outstanding Findings" +#~ msgstr "نتائج التدقيق المالي المعلقة" + +#~ msgid "Programmatic Visits Planned" +#~ msgstr "الزيارات البرامجية المخطط لها" + +#~ msgid "Programmatic Visits Completed" +#~ msgstr "الزيارات البرمجية المنجزة" + +#~ msgid "Spot Check Required" +#~ msgstr "عدد الزيارات الرقابية المفاجئة المطلوبة" + +#~ msgid "Spot Checks Completed" +#~ msgstr "عدد الزيارات الرقابية المفاجئة المكتملة" + #~ msgid "" #~ "Final Review must be approved for documents having amount transferred " #~ "greater than 100,000" 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 5001826676..2f6f7bfa90 100644 --- a/src/etools/applications/partners/locale/es/LC_MESSAGES/django.po +++ b/src/etools/applications/partners/locale/es/LC_MESSAGES/django.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: et-partners2-bk\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-05-21 04:41+0000\n" +"POT-Creation-Date: 2024-06-04 15:42+0000\n" "Language: es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -135,66 +135,6 @@ msgstr "Visitas programáticas previstas" msgid "Implementing Partner" msgstr "Socio ejecutor" -msgid "Shared IP" -msgstr "IP compartida" - -msgid "Cash Transfer 1 OCT - 30 SEP" -msgstr "Transferencia de efectivo 1 OCT - 30 SEP" - -msgid "Liquidations 1 OCT - 30 SEP" -msgstr "Liquidaciones 1 OCT - 30 SEP" - -msgid "Cash Transfers Jan - Dec" -msgstr "Transferencias enero - diciembre" - -msgid "Risk Rating" -msgstr "Calificación del riesgo" - -msgid "Expiring Threshold" -msgstr "Umbral de expiración" - -msgid "Approach Threshold" -msgstr "Umbral de aproximación" - -msgid "Programmatic Visits Planned Q1" -msgstr "Visitas programáticas previstas Q1" - -msgid "Q2" -msgstr "Q2" - -msgid "Q3" -msgstr "Q3" - -msgid "Q4" -msgstr "Q4" - -msgid "Programmatic Visits M.R" -msgstr "Visitas programáticas M.R" - -msgid "Programmatic Visits Completed Q1" -msgstr "Visitas programáticas realizadas 1er trimestre" - -msgid "Spot Checks Planned Q1" -msgstr "Controles aleatorios previstos Q1" - -msgid "Spot Checks M.R" -msgstr "Controles aleatorios M.R" - -msgid "Follow Up" -msgstr "Seguimiento" - -msgid "Spot Checks Completed Q1" -msgstr "Controles aleatorios completados Q1" - -msgid "Audits M.R" -msgstr "Auditorías M.R" - -msgid "Audit Completed" -msgstr "Auditoría finalizada" - -msgid "Audits Outstanding Findings" -msgstr "Auditorías Conclusiones pendientes" - msgid "Sections" msgstr "Secciones" @@ -225,18 +165,6 @@ msgstr "Importe pendiente del SES entre 6 y 9 meses" msgid "Outstanding DCT Amount more than 9 months" msgstr "Importe del SES pendiente más de 9 meses" -msgid "Programmatic Visits Planned" -msgstr "Visitas programáticas previstas" - -msgid "Programmatic Visits Completed" -msgstr "Visitas programáticas realizadas" - -msgid "Spot Check Required" -msgstr "Comprobación aleatoria obligatoria" - -msgid "Spot Checks Completed" -msgstr "Controles aleatorios realizados" - msgid "Reference Number" msgstr "Número de referencia" @@ -727,6 +655,9 @@ msgstr "País" msgid "Alternate ID" msgstr "ID alternativo" +msgid "Risk Rating" +msgstr "Calificación del riesgo" + msgid "Last Assessment Date" msgstr "Fecha de la última evaluación" @@ -1111,6 +1042,15 @@ msgstr "Enmiendas de intervención" msgid "Q1" msgstr "" +msgid "Q2" +msgstr "Q2" + +msgid "Q3" +msgstr "Q3" + +msgid "Q4" +msgstr "Q4" + msgid "Intervention" msgstr "Intervención" @@ -2316,6 +2256,66 @@ msgid "Partnership Manager role required for pca export." msgstr "" "Se requiere el rol de Gerente de asociación para la exportación de PCA." +#~ msgid "Shared IP" +#~ msgstr "IP compartida" + +#~ msgid "Cash Transfer 1 OCT - 30 SEP" +#~ msgstr "Transferencia de efectivo 1 OCT - 30 SEP" + +#~ msgid "Liquidations 1 OCT - 30 SEP" +#~ msgstr "Liquidaciones 1 OCT - 30 SEP" + +#~ msgid "Cash Transfers Jan - Dec" +#~ msgstr "Transferencias enero - diciembre" + +#~ msgid "Expiring Threshold" +#~ msgstr "Umbral de expiración" + +#~ msgid "Approach Threshold" +#~ msgstr "Umbral de aproximación" + +#~ msgid "Programmatic Visits Planned Q1" +#~ msgstr "Visitas programáticas previstas Q1" + +#~ msgid "Programmatic Visits M.R" +#~ msgstr "Visitas programáticas M.R" + +#~ msgid "Programmatic Visits Completed Q1" +#~ msgstr "Visitas programáticas realizadas 1er trimestre" + +#~ msgid "Spot Checks Planned Q1" +#~ msgstr "Controles aleatorios previstos Q1" + +#~ msgid "Spot Checks M.R" +#~ msgstr "Controles aleatorios M.R" + +#~ msgid "Follow Up" +#~ msgstr "Seguimiento" + +#~ msgid "Spot Checks Completed Q1" +#~ msgstr "Controles aleatorios completados Q1" + +#~ msgid "Audits M.R" +#~ msgstr "Auditorías M.R" + +#~ msgid "Audit Completed" +#~ msgstr "Auditoría finalizada" + +#~ msgid "Audits Outstanding Findings" +#~ msgstr "Auditorías Conclusiones pendientes" + +#~ msgid "Programmatic Visits Planned" +#~ msgstr "Visitas programáticas previstas" + +#~ msgid "Programmatic Visits Completed" +#~ msgstr "Visitas programáticas realizadas" + +#~ msgid "Spot Check Required" +#~ msgstr "Comprobación aleatoria obligatoria" + +#~ msgid "Spot Checks Completed" +#~ msgstr "Controles aleatorios realizados" + #~ msgid "" #~ "Final Review must be approved for documents having amount transferred " #~ "greater than 100,000" 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 e993259a2f..386137d2fa 100644 --- a/src/etools/applications/partners/locale/fr/LC_MESSAGES/django.po +++ b/src/etools/applications/partners/locale/fr/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-05-21 04:41+0000\n" +"POT-Creation-Date: 2024-06-04 15:42+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -143,66 +143,6 @@ msgstr "Visites programmées" msgid "Implementing Partner" msgstr "Partenaire de mise en œuvre" -msgid "Shared IP" -msgstr "IP partagée" - -msgid "Cash Transfer 1 OCT - 30 SEP" -msgstr "Transfert d'argent liquide 1 OCT - 30 SEP" - -msgid "Liquidations 1 OCT - 30 SEP" -msgstr "Liquidations 1 OCT - 30 SEP" - -msgid "Cash Transfers Jan - Dec" -msgstr "Transferts de fonds Jan - Déc" - -msgid "Risk Rating" -msgstr "Évaluation du risque" - -msgid "Expiring Threshold" -msgstr "Seuil d'expiration" - -msgid "Approach Threshold" -msgstr "Seuil d'approche" - -msgid "Programmatic Visits Planned Q1" -msgstr "Visites programmées Q1" - -msgid "Q2" -msgstr "Q2" - -msgid "Q3" -msgstr "Q3" - -msgid "Q4" -msgstr "Q4" - -msgid "Programmatic Visits M.R" -msgstr "Visites programmées M.R" - -msgid "Programmatic Visits Completed Q1" -msgstr "Visites programmatiques achevées Q1" - -msgid "Spot Checks Planned Q1" -msgstr "Contrôles ponctuels prévus Q1" - -msgid "Spot Checks M.R" -msgstr "Contrôles ponctuels M.R" - -msgid "Follow Up" -msgstr "Suivi" - -msgid "Spot Checks Completed Q1" -msgstr "Contrôles ponctuels achevés Q1" - -msgid "Audits M.R" -msgstr "Audits M.R" - -msgid "Audit Completed" -msgstr "Audit terminé" - -msgid "Audits Outstanding Findings" -msgstr "Audits Constatations en suspens" - msgid "Sections" msgstr "Sections" @@ -233,18 +173,6 @@ msgstr "Montant du DCT impayé entre 6 et 9 mois" msgid "Outstanding DCT Amount more than 9 months" msgstr "Montant DCT impayé plus de 9 mois" -msgid "Programmatic Visits Planned" -msgstr "Visites programmées" - -msgid "Programmatic Visits Completed" -msgstr "Visites programmatiques achevées" - -msgid "Spot Check Required" -msgstr "Vérification ponctuelle requise" - -msgid "Spot Checks Completed" -msgstr "Contrôles ponctuels effectués" - msgid "Reference Number" msgstr "Numéro de référence" @@ -736,6 +664,9 @@ msgstr "Pays" msgid "Alternate ID" msgstr "ID alternatif" +msgid "Risk Rating" +msgstr "Évaluation du risque" + msgid "Last Assessment Date" msgstr "Date de la dernière évaluation" @@ -1120,6 +1051,15 @@ msgstr "Modifications de l'intervention" msgid "Q1" msgstr "" +msgid "Q2" +msgstr "Q2" + +msgid "Q3" +msgstr "Q3" + +msgid "Q4" +msgstr "Q4" + msgid "Intervention" msgstr "Intervention" @@ -2330,6 +2270,66 @@ msgstr "" msgid "Partnership Manager role required for pca export." msgstr "Rôle de gestionnaire de partenariat requis pour l'exportation pca." +#~ msgid "Shared IP" +#~ msgstr "IP partagée" + +#~ msgid "Cash Transfer 1 OCT - 30 SEP" +#~ msgstr "Transfert d'argent liquide 1 OCT - 30 SEP" + +#~ msgid "Liquidations 1 OCT - 30 SEP" +#~ msgstr "Liquidations 1 OCT - 30 SEP" + +#~ msgid "Cash Transfers Jan - Dec" +#~ msgstr "Transferts de fonds Jan - Déc" + +#~ msgid "Expiring Threshold" +#~ msgstr "Seuil d'expiration" + +#~ msgid "Approach Threshold" +#~ msgstr "Seuil d'approche" + +#~ msgid "Programmatic Visits Planned Q1" +#~ msgstr "Visites programmées Q1" + +#~ msgid "Programmatic Visits M.R" +#~ msgstr "Visites programmées M.R" + +#~ msgid "Programmatic Visits Completed Q1" +#~ msgstr "Visites programmatiques achevées Q1" + +#~ msgid "Spot Checks Planned Q1" +#~ msgstr "Contrôles ponctuels prévus Q1" + +#~ msgid "Spot Checks M.R" +#~ msgstr "Contrôles ponctuels M.R" + +#~ msgid "Follow Up" +#~ msgstr "Suivi" + +#~ msgid "Spot Checks Completed Q1" +#~ msgstr "Contrôles ponctuels achevés Q1" + +#~ msgid "Audits M.R" +#~ msgstr "Audits M.R" + +#~ msgid "Audit Completed" +#~ msgstr "Audit terminé" + +#~ msgid "Audits Outstanding Findings" +#~ msgstr "Audits Constatations en suspens" + +#~ msgid "Programmatic Visits Planned" +#~ msgstr "Visites programmées" + +#~ msgid "Programmatic Visits Completed" +#~ msgstr "Visites programmatiques achevées" + +#~ msgid "Spot Check Required" +#~ msgstr "Vérification ponctuelle requise" + +#~ msgid "Spot Checks Completed" +#~ msgstr "Contrôles ponctuels effectués" + #~ msgid "" #~ "Final Review must be approved for documents having amount transferred " #~ "greater than 100,000" 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 57e8b4c536..68fec92747 100644 --- a/src/etools/applications/partners/locale/pt/LC_MESSAGES/django.po +++ b/src/etools/applications/partners/locale/pt/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-05-21 04:41+0000\n" +"POT-Creation-Date: 2024-06-04 15:42+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -148,67 +148,6 @@ msgstr "Visitas programáticas planejadas" msgid "Implementing Partner" msgstr "Parceiro implementador" -msgid "Shared IP" -msgstr "IP compartilhado" - -msgid "Cash Transfer 1 OCT - 30 SEP" -msgstr "Transferência financeira de 1 de outubro a 30 de setembro" - -msgid "Liquidations 1 OCT - 30 SEP" -msgstr "Liquidações de 1 de outubro a 30 de setembro" - -msgid "Cash Transfers Jan - Dec" -msgstr "Transferências financeiras de janeiro a dezembro" - -msgid "Risk Rating" -msgstr "Classificação de risco" - -msgid "Expiring Threshold" -msgstr "Limite de expiração" - -msgid "Approach Threshold" -msgstr "Limiar de Abordagem" - -msgid "Programmatic Visits Planned Q1" -msgstr "Visitas programáticas planejadas para o primeiro trimestre" - -msgid "Q2" -msgstr "Q2" - -msgid "Q3" -msgstr "Q3" - -msgid "Q4" -msgstr "Q4" - -msgid "Programmatic Visits M.R" -msgstr "Visitas programáticas M.R" - -msgid "Programmatic Visits Completed Q1" -msgstr "Visitas programáticas concluídas no primeiro trimestre" - -msgid "Spot Checks Planned Q1" -msgstr "" -"Spot Checks / Verificações pontuais planejadas para o primeiro trimestre" - -msgid "Spot Checks M.R" -msgstr "Spot Checks M.R" - -msgid "Follow Up" -msgstr "Acompanhamento" - -msgid "Spot Checks Completed Q1" -msgstr "Spot Checks / Verificações pontuais concluídas no primeiro trimestre" - -msgid "Audits M.R" -msgstr "Mínimo Requeridas Auditorias" - -msgid "Audit Completed" -msgstr "Auditoria concluída" - -msgid "Audits Outstanding Findings" -msgstr "Auditoria: Apontamentos Importantes " - msgid "Sections" msgstr "Seções" @@ -243,18 +182,6 @@ msgstr "Valor de DCT pendente entre 6 e 9 meses" msgid "Outstanding DCT Amount more than 9 months" msgstr "Valor de DCT pendente há mais de 9 meses" -msgid "Programmatic Visits Planned" -msgstr "Visitas programáticas planejadas" - -msgid "Programmatic Visits Completed" -msgstr "Visitas programáticas concluídas" - -msgid "Spot Check Required" -msgstr "É necessária realizar um Spot Check (verificação pontual)" - -msgid "Spot Checks Completed" -msgstr "Spot Checks/ Verificações pontuais concluídas" - msgid "Reference Number" msgstr "Número de Referência" @@ -746,6 +673,9 @@ msgstr "País" msgid "Alternate ID" msgstr "Identificação alternativa" +msgid "Risk Rating" +msgstr "Classificação de risco" + msgid "Last Assessment Date" msgstr "Data da última avaliação" @@ -1130,6 +1060,15 @@ msgstr "Aditivos da implementação do programa" msgid "Q1" msgstr "" +msgid "Q2" +msgstr "Q2" + +msgid "Q3" +msgstr "Q3" + +msgid "Q4" +msgstr "Q4" + msgid "Intervention" msgstr "Intervenção" @@ -2337,6 +2276,68 @@ msgstr "" msgid "Partnership Manager role required for pca export." msgstr "A função de gerente de parceria é necessária para a exportação de pca." +#~ msgid "Shared IP" +#~ msgstr "IP compartilhado" + +#~ msgid "Cash Transfer 1 OCT - 30 SEP" +#~ msgstr "Transferência financeira de 1 de outubro a 30 de setembro" + +#~ msgid "Liquidations 1 OCT - 30 SEP" +#~ msgstr "Liquidações de 1 de outubro a 30 de setembro" + +#~ msgid "Cash Transfers Jan - Dec" +#~ msgstr "Transferências financeiras de janeiro a dezembro" + +#~ msgid "Expiring Threshold" +#~ msgstr "Limite de expiração" + +#~ msgid "Approach Threshold" +#~ msgstr "Limiar de Abordagem" + +#~ msgid "Programmatic Visits Planned Q1" +#~ msgstr "Visitas programáticas planejadas para o primeiro trimestre" + +#~ msgid "Programmatic Visits M.R" +#~ msgstr "Visitas programáticas M.R" + +#~ msgid "Programmatic Visits Completed Q1" +#~ msgstr "Visitas programáticas concluídas no primeiro trimestre" + +#~ msgid "Spot Checks Planned Q1" +#~ msgstr "" +#~ "Spot Checks / Verificações pontuais planejadas para o primeiro trimestre" + +#~ msgid "Spot Checks M.R" +#~ msgstr "Spot Checks M.R" + +#~ msgid "Follow Up" +#~ msgstr "Acompanhamento" + +#~ msgid "Spot Checks Completed Q1" +#~ msgstr "" +#~ "Spot Checks / Verificações pontuais concluídas no primeiro trimestre" + +#~ msgid "Audits M.R" +#~ msgstr "Mínimo Requeridas Auditorias" + +#~ msgid "Audit Completed" +#~ msgstr "Auditoria concluída" + +#~ msgid "Audits Outstanding Findings" +#~ msgstr "Auditoria: Apontamentos Importantes " + +#~ msgid "Programmatic Visits Planned" +#~ msgstr "Visitas programáticas planejadas" + +#~ msgid "Programmatic Visits Completed" +#~ msgstr "Visitas programáticas concluídas" + +#~ msgid "Spot Check Required" +#~ msgstr "É necessária realizar um Spot Check (verificação pontual)" + +#~ msgid "Spot Checks Completed" +#~ msgstr "Spot Checks/ Verificações pontuais concluídas" + #~ msgid "" #~ "Final Review must be approved for documents having amount transferred " #~ "greater than 100,000" 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 9728509bdc..74ba69d6c7 100644 --- a/src/etools/applications/partners/locale/ru/LC_MESSAGES/django.po +++ b/src/etools/applications/partners/locale/ru/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-05-21 04:41+0000\n" +"POT-Creation-Date: 2024-06-04 15:42+0000\n" "Language: ru\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -140,66 +140,6 @@ msgstr "Запланированные программные визиты" msgid "Implementing Partner" msgstr "Исполнительный Партнер" -msgid "Shared IP" -msgstr "Совместный ИП" - -msgid "Cash Transfer 1 OCT - 30 SEP" -msgstr "Денежные переводы за 1 окт.-30 сен." - -msgid "Liquidations 1 OCT - 30 SEP" -msgstr "Ликвидации 1 окт. - 30 сен." - -msgid "Cash Transfers Jan - Dec" -msgstr "Денежные переводы янв - дек" - -msgid "Risk Rating" -msgstr "Рейтинг риска" - -msgid "Expiring Threshold" -msgstr "Порог просрочки" - -msgid "Approach Threshold" -msgstr "Приближается к порогу" - -msgid "Programmatic Visits Planned Q1" -msgstr "Запланированные программные визиты в I кв-ле" - -msgid "Q2" -msgstr "кв-л II" - -msgid "Q3" -msgstr "кв-л III" - -msgid "Q4" -msgstr "кв-л IV" - -msgid "Programmatic Visits M.R" -msgstr "М.Т. программные визиты" - -msgid "Programmatic Visits Completed Q1" -msgstr "Завершенные программные визиты в I кв-ле" - -msgid "Spot Checks Planned Q1" -msgstr "Запланированные выборочные проверки в I кв-ле" - -msgid "Spot Checks M.R" -msgstr "М.Т. выборочные проверки" - -msgid "Follow Up" -msgstr "Последующие действия" - -msgid "Spot Checks Completed Q1" -msgstr "Завершенные выборочные проверки в I кв-ле" - -msgid "Audits M.R" -msgstr "Аудиты М.Т." - -msgid "Audit Completed" -msgstr "Аудит завершен" - -msgid "Audits Outstanding Findings" -msgstr "Неразрешенные выводы аудитов" - msgid "Sections" msgstr "Секции" @@ -230,18 +170,6 @@ msgstr "Непогашенная сумма ПДП от 6 до 9 месяцев" msgid "Outstanding DCT Amount more than 9 months" msgstr "Непогашенная сумма ПДП более 9 месяцев" -msgid "Programmatic Visits Planned" -msgstr "Планируемые программные визиты" - -msgid "Programmatic Visits Completed" -msgstr "Завершенные программные визиты" - -msgid "Spot Check Required" -msgstr "Требуется выборочная проверка" - -msgid "Spot Checks Completed" -msgstr "Завершенные выборочные проверки" - msgid "Reference Number" msgstr "Номер" @@ -729,6 +657,9 @@ msgstr "Страна" msgid "Alternate ID" msgstr "Альтернативный идентификатор" +msgid "Risk Rating" +msgstr "Рейтинг риска" + msgid "Last Assessment Date" msgstr "Дата последней оценки" @@ -1113,6 +1044,15 @@ msgstr "Поправки к программе" msgid "Q1" msgstr "" +msgid "Q2" +msgstr "кв-л II" + +msgid "Q3" +msgstr "кв-л III" + +msgid "Q4" +msgstr "кв-л IV" + msgid "Intervention" msgstr "Программа" @@ -2301,6 +2241,66 @@ msgstr "" msgid "Partnership Manager role required for pca export." msgstr "Для экспорта PCA требуется роль менеджера по партнерству." +#~ msgid "Shared IP" +#~ msgstr "Совместный ИП" + +#~ msgid "Cash Transfer 1 OCT - 30 SEP" +#~ msgstr "Денежные переводы за 1 окт.-30 сен." + +#~ msgid "Liquidations 1 OCT - 30 SEP" +#~ msgstr "Ликвидации 1 окт. - 30 сен." + +#~ msgid "Cash Transfers Jan - Dec" +#~ msgstr "Денежные переводы янв - дек" + +#~ msgid "Expiring Threshold" +#~ msgstr "Порог просрочки" + +#~ msgid "Approach Threshold" +#~ msgstr "Приближается к порогу" + +#~ msgid "Programmatic Visits Planned Q1" +#~ msgstr "Запланированные программные визиты в I кв-ле" + +#~ msgid "Programmatic Visits M.R" +#~ msgstr "М.Т. программные визиты" + +#~ msgid "Programmatic Visits Completed Q1" +#~ msgstr "Завершенные программные визиты в I кв-ле" + +#~ msgid "Spot Checks Planned Q1" +#~ msgstr "Запланированные выборочные проверки в I кв-ле" + +#~ msgid "Spot Checks M.R" +#~ msgstr "М.Т. выборочные проверки" + +#~ msgid "Follow Up" +#~ msgstr "Последующие действия" + +#~ msgid "Spot Checks Completed Q1" +#~ msgstr "Завершенные выборочные проверки в I кв-ле" + +#~ msgid "Audits M.R" +#~ msgstr "Аудиты М.Т." + +#~ msgid "Audit Completed" +#~ msgstr "Аудит завершен" + +#~ msgid "Audits Outstanding Findings" +#~ msgstr "Неразрешенные выводы аудитов" + +#~ msgid "Programmatic Visits Planned" +#~ msgstr "Планируемые программные визиты" + +#~ msgid "Programmatic Visits Completed" +#~ msgstr "Завершенные программные визиты" + +#~ msgid "Spot Check Required" +#~ msgstr "Требуется выборочная проверка" + +#~ msgid "Spot Checks Completed" +#~ msgstr "Завершенные выборочные проверки" + #~ msgid "" #~ "Final Review must be approved for documents having amount transferred " #~ "greater than 100,000" From d5e80aaa701f8d4d8a0a238caacc20d721d8c969 Mon Sep 17 00:00:00 2001 From: Ema Ciupe Date: Wed, 5 Jun 2024 11:28:56 +0300 Subject: [PATCH 12/26] fixed test --- src/etools/applications/partners/tests/test_tasks.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/etools/applications/partners/tests/test_tasks.py b/src/etools/applications/partners/tests/test_tasks.py index 348f4f749d..bc6e9e65f5 100644 --- a/src/etools/applications/partners/tests/test_tasks.py +++ b/src/etools/applications/partners/tests/test_tasks.py @@ -1460,8 +1460,11 @@ def test_task_focal_point_without_country(self): self.partner_1.core_values_assessment_date = core_values_assessment_date self.partner_1.save() - self.focal_point.profile.country_override = None - self.focal_point.profile.save() + + focal_point = UserFactory(profile__country=None) + self.intervention_1.unicef_focal_points.remove(self.focal_point) + self.intervention_1.unicef_focal_points.add(focal_point) + self.assertIsNone(focal_point.profile.country) send_path = "etools.applications.partners.tasks.send_notification_with_template" mock_send = mock.Mock() From 52910b68b9d5f2959be522779326fdaa0ed18ff2 Mon Sep 17 00:00:00 2001 From: Roman Karpovich Date: Wed, 5 Jun 2024 13:35:00 +0400 Subject: [PATCH 13/26] remove partner-hact view mentions from tests --- .../partners/tests/test_export_partner.py | 34 ------------------- .../applications/partners/tests/test_views.py | 1 - 2 files changed, 35 deletions(-) diff --git a/src/etools/applications/partners/tests/test_export_partner.py b/src/etools/applications/partners/tests/test_export_partner.py index 1d50c61689..864c4bd7e0 100644 --- a/src/etools/applications/partners/tests/test_export_partner.py +++ b/src/etools/applications/partners/tests/test_export_partner.py @@ -317,37 +317,3 @@ def test_csv_flat_export_api(self): self.assertEqual(len(headers), 15) self.assertIn("Country", headers) self.assertEqual(len(dataset[0]), 15) - - -class TestPartnerOrganizationHactExport(BaseTenantTestCase): - @classmethod - def setUpTestData(cls): - cls.url = reverse("partners_api:partner-hact") - cls.unicef_staff = UserFactory(is_staff=True) - cls.partner = PartnerFactory( - total_ct_cp=10.00, - total_ct_cy=8.00, - ) - - def test_csv_export(self): - response = self.forced_auth_req( - 'get', - self.url, - user=self.unicef_staff, - data={"format": "csv"} - ) - self.assertEqual(response.status_code, status.HTTP_200_OK) - dataset = Dataset().load(response.content.decode('utf-8'), 'csv') - self.assertEqual(dataset.height, 1) - self.assertEqual(len(dataset._get_headers()), 33) - self.assertEqual(len(dataset[0]), 33) - - def test_invalid_format_export_api(self): - response = self.forced_auth_req( - 'get', - self.url, - user=self.unicef_staff, - data={"format": "unknown"}, - ) - - self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) diff --git a/src/etools/applications/partners/tests/test_views.py b/src/etools/applications/partners/tests/test_views.py index adab39b222..299094d092 100644 --- a/src/etools/applications/partners/tests/test_views.py +++ b/src/etools/applications/partners/tests/test_views.py @@ -79,7 +79,6 @@ def test_urls(self): """Verify URL pattern names generate the URLs we expect them to.""" names_and_paths = ( ('partner-list', '', {}), - ('partner-hact', 'hact/', {}), ('partner-engagements', 'engagements/', {}), ('partner-detail', '1/', {'pk': 1}), ('partner-delete', 'delete/1/', {'pk': 1}), From 17a46dd85d2ab050d0e02bb0b1e3712052c09fbc Mon Sep 17 00:00:00 2001 From: Ema Ciupe Date: Wed, 5 Jun 2024 12:49:38 +0300 Subject: [PATCH 14/26] [ch36049] FAM: validation on financial Findings local currency --- .../applications/audit/serializers/engagement.py | 12 +++++++++++- src/etools/applications/audit/tests/test_views.py | 9 +++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/etools/applications/audit/serializers/engagement.py b/src/etools/applications/audit/serializers/engagement.py index 3c22249d5a..eb7e629183 100644 --- a/src/etools/applications/audit/serializers/engagement.py +++ b/src/etools/applications/audit/serializers/engagement.py @@ -561,7 +561,9 @@ def get_number_of_financial_findings(self, obj): def _validate_financial_findings(self, validated_data): financial_findings = validated_data.get('financial_findings') audited_expenditure = validated_data.get('audited_expenditure') - if not (financial_findings or audited_expenditure): + financial_findings_local = validated_data.get('financial_findings_local') + audited_expenditure_local = validated_data.get('audited_expenditure_local') + if not (financial_findings or audited_expenditure) and not (financial_findings_local or audited_expenditure_local): return if not financial_findings: @@ -572,6 +574,14 @@ def _validate_financial_findings(self, validated_data): if audited_expenditure and financial_findings and financial_findings > audited_expenditure: raise serializers.ValidationError({'financial_findings': _('Cannot exceed Audited Expenditure')}) + if not financial_findings_local: + financial_findings_local = self.instance.financial_findings_local if self.instance else None + if not audited_expenditure_local: + audited_expenditure_local = self.instance.audited_expenditure_local if self.instance else None + + if audited_expenditure_local and financial_findings_local and financial_findings_local > audited_expenditure_local: + raise serializers.ValidationError({'financial_findings_local': _('Cannot exceed Audited Expenditure Local')}) + def validate(self, validated_data): self._validate_financial_findings(validated_data) return validated_data diff --git a/src/etools/applications/audit/tests/test_views.py b/src/etools/applications/audit/tests/test_views.py index 8ddd72e5ae..7e6785663a 100644 --- a/src/etools/applications/audit/tests/test_views.py +++ b/src/etools/applications/audit/tests/test_views.py @@ -737,6 +737,15 @@ def test_percent_of_audited_expenditure_invalid(self): self.assertEqual(len(response.data), 1) self.assertIn('financial_findings', response.data) + def test_percent_of_audited_expenditure_local_invalid(self): + response = self._do_update(self.auditor, { + 'audited_expenditure_local': 1, + 'financial_findings_local': 2 + }) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(len(response.data), 1) + self.assertIn('financial_findings_local', response.data) + def test_percent_of_audited_expenditure_valid(self): response = self._do_update(self.auditor, { 'audited_expenditure': 2, From f4d6625d381aa032a7267347883b962fcd153368 Mon Sep 17 00:00:00 2001 From: Roman Karpovich Date: Wed, 5 Jun 2024 15:35:18 +0400 Subject: [PATCH 15/26] validate engagement dates ordering --- .../audit/serializers/engagement.py | 1 + .../applications/audit/serializers/mixins.py | 27 +++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/src/etools/applications/audit/serializers/engagement.py b/src/etools/applications/audit/serializers/engagement.py index 3c22249d5a..ed575778a0 100644 --- a/src/etools/applications/audit/serializers/engagement.py +++ b/src/etools/applications/audit/serializers/engagement.py @@ -573,6 +573,7 @@ def _validate_financial_findings(self, validated_data): raise serializers.ValidationError({'financial_findings': _('Cannot exceed Audited Expenditure')}) def validate(self, validated_data): + validated_data = super().validate(validated_data) self._validate_financial_findings(validated_data) return validated_data diff --git a/src/etools/applications/audit/serializers/mixins.py b/src/etools/applications/audit/serializers/mixins.py index bb876c84a5..4cd109ca89 100644 --- a/src/etools/applications/audit/serializers/mixins.py +++ b/src/etools/applications/audit/serializers/mixins.py @@ -50,6 +50,33 @@ def _validate_dates(self, validated_data): if value and key in date_fields and timezone.now().date() < value: errors[key] = _('Date should be in past.') + start_date = validated_data.get('start_date', self.instance.start_date if self.instance else None) + end_date = validated_data.get('end_date', self.instance.end_date if self.instance else None) + partner_contacted_at = validated_data.get('partner_contacted_at', self.instance.partner_contacted_at if self.instance else None) + date_of_field_visit = validated_data.get('date_of_field_visit', self.instance.date_of_field_visit if self.instance else None) + date_of_draft_report_to_ip = validated_data.get('date_of_draft_report_to_ip', self.instance.date_of_draft_report_to_ip if self.instance else None) + date_of_comments_by_ip = validated_data.get('date_of_comments_by_ip', self.instance.date_of_comments_by_ip if self.instance else None) + date_of_draft_report_to_unicef = validated_data.get('date_of_draft_report_to_unicef', self.instance.date_of_draft_report_to_unicef if self.instance else None) + date_of_comments_by_unicef = validated_data.get('date_of_comments_by_unicef', self.instance.date_of_comments_by_unicef if self.instance else None) + + if start_date and end_date and end_date < start_date: + errors['end_date'] = _('This date should be after Period Start Date.') + if end_date and partner_contacted_at and partner_contacted_at < end_date: + errors['partner_contacted_at'] = _('This date should be after Period End Date.') + + if partner_contacted_at and date_of_field_visit and date_of_field_visit < partner_contacted_at: + errors['date_of_field_visit'] = _('This date should be after Date IP was contacted.') + + if date_of_field_visit and date_of_draft_report_to_ip and date_of_draft_report_to_ip < date_of_field_visit: + errors['date_of_draft_report_to_ip'] = _('This date should be after Date of Field Visit.') + if date_of_draft_report_to_ip and date_of_comments_by_ip and date_of_comments_by_ip < date_of_draft_report_to_ip: + errors['date_of_comments_by_ip'] = _('This date should be after Date Draft Report Issued to UNICEF.') + if date_of_comments_by_ip and date_of_draft_report_to_unicef and date_of_draft_report_to_unicef < date_of_comments_by_ip: + errors['date_of_draft_report_to_unicef'] = _('This date should be after Date Comments Received from IP.') + if date_of_draft_report_to_unicef and date_of_comments_by_unicef and date_of_comments_by_unicef < date_of_draft_report_to_unicef: + errors['date_of_comments_by_unicef'] = _('This date should be after Date Draft Report Issued to UNICEF.') + + if errors: raise serializers.ValidationError(errors) return validated_data From 1072bd1a3e6678f288b9ed8f3f89ac03ed444223 Mon Sep 17 00:00:00 2001 From: Roman Karpovich Date: Wed, 5 Jun 2024 16:14:23 +0400 Subject: [PATCH 16/26] fix date error highlight --- src/etools/applications/audit/serializers/mixins.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/etools/applications/audit/serializers/mixins.py b/src/etools/applications/audit/serializers/mixins.py index 4cd109ca89..8fa18c30f8 100644 --- a/src/etools/applications/audit/serializers/mixins.py +++ b/src/etools/applications/audit/serializers/mixins.py @@ -68,7 +68,8 @@ def _validate_dates(self, validated_data): errors['date_of_field_visit'] = _('This date should be after Date IP was contacted.') if date_of_field_visit and date_of_draft_report_to_ip and date_of_draft_report_to_ip < date_of_field_visit: - errors['date_of_draft_report_to_ip'] = _('This date should be after Date of Field Visit.') + # date of field visit is editable even if date of draft report is readonly, map error to field visit date + errors['date_of_field_visit'] = _('This date should be before Date Draft Report Issued to IP.') if date_of_draft_report_to_ip and date_of_comments_by_ip and date_of_comments_by_ip < date_of_draft_report_to_ip: errors['date_of_comments_by_ip'] = _('This date should be after Date Draft Report Issued to UNICEF.') if date_of_comments_by_ip and date_of_draft_report_to_unicef and date_of_draft_report_to_unicef < date_of_comments_by_ip: From c161fbbd91fa5f74bacb2f83bac84e86037391cd Mon Sep 17 00:00:00 2001 From: Ema Ciupe Date: Wed, 5 Jun 2024 15:15:00 +0300 Subject: [PATCH 17/26] [ch29971] Admin agreements: add-the-ability-to-revert-a-terminated-pca --- src/etools/applications/partners/admin.py | 26 +++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/etools/applications/partners/admin.py b/src/etools/applications/partners/admin.py index b46b8b5e72..051dc0ac62 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 @@ -715,6 +717,7 @@ class AgreementAdmin( HiddenPartnerMixin, CountryUsersAdminMixin, RestrictedEditAdminMixin, + ExtraUrlMixin, SnapshotModelAdmin, ): staff_only = False @@ -811,6 +814,29 @@ 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 + agreement.save(update_fields=['status']) + terminated_interventions = agreement.interventions.filter(status=Intervention.TERMINATED) + 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'] + i.status = previous_status + + Intervention.objects.bulk_update(terminated_interventions, fields=['status']) + + return HttpResponseRedirect(reverse('admin:partners_agreement_change', args=[pk])) + class FileTypeAdmin(RestrictedEditAdmin): From d787aab9a576420f90b7ab0067d32ba71ab6e858 Mon Sep 17 00:00:00 2001 From: Roman Karpovich Date: Wed, 5 Jun 2024 16:16:44 +0400 Subject: [PATCH 18/26] pep8 fix --- src/etools/applications/audit/serializers/mixins.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/etools/applications/audit/serializers/mixins.py b/src/etools/applications/audit/serializers/mixins.py index 8fa18c30f8..12c3524f11 100644 --- a/src/etools/applications/audit/serializers/mixins.py +++ b/src/etools/applications/audit/serializers/mixins.py @@ -77,7 +77,6 @@ def _validate_dates(self, validated_data): if date_of_draft_report_to_unicef and date_of_comments_by_unicef and date_of_comments_by_unicef < date_of_draft_report_to_unicef: errors['date_of_comments_by_unicef'] = _('This date should be after Date Draft Report Issued to UNICEF.') - if errors: raise serializers.ValidationError(errors) return validated_data From fc52449e79c1316b17c549055aac868bc1c4bc2e Mon Sep 17 00:00:00 2001 From: Roman Karpovich Date: Wed, 5 Jun 2024 18:23:18 +0400 Subject: [PATCH 19/26] add tests --- .../applications/audit/tests/factories.py | 10 ++ .../applications/audit/tests/test_views.py | 144 ++++++++++++++++++ 2 files changed, 154 insertions(+) diff --git a/src/etools/applications/audit/tests/factories.py b/src/etools/applications/audit/tests/factories.py index e8b25dd84b..a5475033c8 100644 --- a/src/etools/applications/audit/tests/factories.py +++ b/src/etools/applications/audit/tests/factories.py @@ -1,4 +1,5 @@ import random +from datetime import datetime, timedelta from django.db.models import signals @@ -133,16 +134,25 @@ class Meta: class AuditFactory(EngagementFactory): + start_date = datetime.today() - timedelta(days=30) + end_date = datetime.today() - timedelta(days=10) + class Meta: model = Audit class SpecialAuditFactory(EngagementFactory): + start_date = datetime.today() - timedelta(days=30) + end_date = datetime.today() - timedelta(days=10) + class Meta: model = SpecialAudit class SpotCheckFactory(EngagementFactory): + start_date = datetime.today() - timedelta(days=30) + end_date = datetime.today() - timedelta(days=10) + class Meta: model = SpotCheck diff --git a/src/etools/applications/audit/tests/test_views.py b/src/etools/applications/audit/tests/test_views.py index 8ddd72e5ae..77a2d253bc 100644 --- a/src/etools/applications/audit/tests/test_views.py +++ b/src/etools/applications/audit/tests/test_views.py @@ -1,6 +1,7 @@ import datetime import json import random +from copy import copy from unittest.mock import Mock, patch from django.contrib.contenttypes.models import ContentType @@ -539,6 +540,14 @@ def setUp(self): def _do_create(self, user, data): data = data or {} + for date_field in [ + 'start_date', 'end_date', 'partner_contacted_at', + 'date_of_field_visit', 'date_of_draft_report_to_ip', + 'date_of_comments_by_ip', 'date_of_draft_report_to_unicef', + 'date_of_comments_by_unicef' + ]: + if data.get(date_field): + data[date_field] = data[date_field].date().isoformat() response = self.forced_auth_req( 'post', self.engagements_url(), @@ -602,6 +611,14 @@ class TestMicroAssessmentCreateViewSet(TestEngagementCreateActivePDViewSet, Base BaseTenantTestCase): engagement_factory = MicroAssessmentFactory + def test_partner_contacted_at_validation(self): + # date should be in past + data = copy(self.create_data) + data['partner_contacted_at'] = datetime.datetime.now() + datetime.timedelta(days=1) + response = self._do_create(self.unicef_focal_point, data) + self.assertEquals(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertIn('partner_contacted_at', response.data) + class TestAuditCreateViewSet(TestEngagementCreateActivePDViewSet, BaseTestEngagementsCreateViewSet, BaseTenantTestCase): engagement_factory = AuditFactory @@ -610,6 +627,20 @@ def setUp(self): super().setUp() self.create_data['year_of_audit'] = timezone.now().year + def test_end_date_validation(self): + data = copy(self.create_data) + data['end_date'] = data['start_date'] - datetime.timedelta(days=1) + response = self._do_create(self.unicef_focal_point, data) + self.assertEquals(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertIn('end_date', response.data) + + def test_partner_contacted_at_validation(self): + data = copy(self.create_data) + data['partner_contacted_at'] = data['end_date'] - datetime.timedelta(days=1) + response = self._do_create(self.unicef_focal_point, data) + self.assertEquals(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertIn('partner_contacted_at', response.data) + class TestSpotCheckCreateViewSet(TestEngagementCreateActivePDViewSet, BaseTestEngagementsCreateViewSet, BaseTenantTestCase): @@ -690,6 +721,20 @@ def test_offices(self): sorted([office_1.pk, office_2.pk]), ) + def test_end_date_validation(self): + data = copy(self.create_data) + data['end_date'] = data['start_date'] - datetime.timedelta(days=1) + response = self._do_create(self.unicef_focal_point, data) + self.assertEquals(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertIn('end_date', response.data) + + def test_partner_contacted_at_validation(self): + data = copy(self.create_data) + data['partner_contacted_at'] = data['end_date'] - datetime.timedelta(days=1) + response = self._do_create(self.unicef_focal_point, data) + self.assertEquals(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertIn('partner_contacted_at', response.data) + class SpecialAuditCreateViewSet(BaseTestEngagementsCreateViewSet, BaseTenantTestCase): engagement_factory = SpecialAuditFactory @@ -715,12 +760,34 @@ def test_engagement_without_active_pd(self): self.assertEquals(response.status_code, status.HTTP_201_CREATED) + def test_end_date_validation(self): + data = copy(self.create_data) + data['end_date'] = data['start_date'] - datetime.timedelta(days=1) + response = self._do_create(self.unicef_focal_point, data) + self.assertEquals(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertIn('end_date', response.data) + + def test_partner_contacted_at_validation(self): + data = copy(self.create_data) + data['partner_contacted_at'] = data['end_date'] - datetime.timedelta(days=1) + response = self._do_create(self.unicef_focal_point, data) + self.assertEquals(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertIn('partner_contacted_at', response.data) + class TestEngagementsUpdateViewSet(EngagementTransitionsTestCaseMixin, BaseTenantTestCase): engagement_factory = AuditFactory def _do_update(self, user, data): data = data or {} + for date_field in [ + 'start_date', 'end_date', 'partner_contacted_at', + 'date_of_field_visit', 'date_of_draft_report_to_ip', + 'date_of_comments_by_ip', 'date_of_draft_report_to_unicef', + 'date_of_comments_by_unicef' + ]: + if data.get(date_field): + data[date_field] = data[date_field].date().isoformat() response = self.forced_auth_req( 'patch', '/api/audit/audits/{}/'.format(self.engagement.id), @@ -744,6 +811,83 @@ def test_percent_of_audited_expenditure_valid(self): }) self.assertEqual(response.status_code, status.HTTP_200_OK) + def test_date_of_field_visit_after_partner_contacted_at_validation(self): + self.engagement.partner_contacted_at = self.engagement.end_date + datetime.timedelta(days=1) + self.engagement.save() + response = self._do_update(self.auditor, { + 'date_of_field_visit': self.engagement.end_date, + 'date_of_draft_report_to_ip': None, + 'date_of_comments_by_ip': None, + 'date_of_draft_report_to_unicef': None, + 'date_of_comments_by_unicef': None, + }) + self.assertEquals(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertIn('date_of_field_visit', response.data) + + def test_date_of_draft_report_to_ip_after_date_of_field_visit_validation(self): + self.engagement.partner_contacted_at = self.engagement.end_date + datetime.timedelta(days=1) + self.engagement.save() + response = self._do_update(self.auditor, { + 'date_of_field_visit': self.engagement.end_date + datetime.timedelta(days=2), + 'date_of_draft_report_to_ip': self.engagement.end_date, + 'date_of_comments_by_ip': None, + 'date_of_draft_report_to_unicef': None, + 'date_of_comments_by_unicef': None, + }) + self.assertEquals(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertIn('date_of_field_visit', response.data) + + def test_date_of_comments_by_ip_after_date_of_draft_report_to_ip_validation(self): + self.engagement.partner_contacted_at = self.engagement.end_date + datetime.timedelta(days=1) + self.engagement.save() + response = self._do_update(self.auditor, { + 'date_of_field_visit': self.engagement.end_date + datetime.timedelta(days=2), + 'date_of_draft_report_to_ip': self.engagement.end_date + datetime.timedelta(days=3), + 'date_of_comments_by_ip': self.engagement.end_date, + 'date_of_draft_report_to_unicef': None, + 'date_of_comments_by_unicef': None, + }) + self.assertEquals(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertIn('date_of_comments_by_ip', response.data) + + def test_date_of_draft_report_to_unicef_after_date_of_comments_by_ip_validation(self): + self.engagement.partner_contacted_at = self.engagement.end_date + datetime.timedelta(days=1) + self.engagement.save() + response = self._do_update(self.auditor, { + 'date_of_field_visit': self.engagement.end_date + datetime.timedelta(days=2), + 'date_of_draft_report_to_ip': self.engagement.end_date + datetime.timedelta(days=3), + 'date_of_comments_by_ip': self.engagement.end_date + datetime.timedelta(days=4), + 'date_of_draft_report_to_unicef': self.engagement.end_date, + 'date_of_comments_by_unicef': None, + }) + self.assertEquals(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertIn('date_of_draft_report_to_unicef', response.data) + + def test_date_of_comments_by_unicef_after_date_of_draft_report_to_unicef_validation(self): + self.engagement.partner_contacted_at = self.engagement.end_date + datetime.timedelta(days=1) + self.engagement.save() + response = self._do_update(self.auditor, { + 'date_of_field_visit': self.engagement.end_date + datetime.timedelta(days=2), + 'date_of_draft_report_to_ip': self.engagement.end_date + datetime.timedelta(days=3), + 'date_of_comments_by_ip': self.engagement.end_date + datetime.timedelta(days=4), + 'date_of_draft_report_to_unicef': self.engagement.end_date + datetime.timedelta(days=5), + 'date_of_comments_by_unicef': self.engagement.end_date, + }) + self.assertEquals(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertIn('date_of_comments_by_unicef', response.data) + + def test_dates_update_ok(self): + self.engagement.partner_contacted_at = self.engagement.end_date + datetime.timedelta(days=1) + self.engagement.save() + response = self._do_update(self.auditor, { + 'date_of_field_visit': self.engagement.end_date + datetime.timedelta(days=2), + 'date_of_draft_report_to_ip': self.engagement.end_date + datetime.timedelta(days=3), + 'date_of_comments_by_ip': self.engagement.end_date + datetime.timedelta(days=4), + 'date_of_draft_report_to_unicef': self.engagement.end_date + datetime.timedelta(days=5), + 'date_of_comments_by_unicef': self.engagement.end_date + datetime.timedelta(days=6), + }) + self.assertEquals(response.status_code, status.HTTP_200_OK) + class TestEngagementActionPointViewSet(EngagementTransitionsTestCaseMixin, BaseTenantTestCase): engagement_factory = MicroAssessmentFactory From 8c59eaf68a3c2b27ef08d247190bfe8be34973ee Mon Sep 17 00:00:00 2001 From: Roman Karpovich Date: Thu, 6 Jun 2024 12:29:02 +0400 Subject: [PATCH 20/26] fix tests --- src/etools/applications/audit/tests/factories.py | 15 ++++++++------- src/etools/applications/audit/tests/test_views.py | 8 ++++++-- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/etools/applications/audit/tests/factories.py b/src/etools/applications/audit/tests/factories.py index a5475033c8..a71acf8266 100644 --- a/src/etools/applications/audit/tests/factories.py +++ b/src/etools/applications/audit/tests/factories.py @@ -1,7 +1,8 @@ import random -from datetime import datetime, timedelta +from datetime import timedelta from django.db.models import signals +from django.utils import timezone import factory from factory import fuzzy @@ -134,24 +135,24 @@ class Meta: class AuditFactory(EngagementFactory): - start_date = datetime.today() - timedelta(days=30) - end_date = datetime.today() - timedelta(days=10) + start_date = timezone.now().date() - timedelta(days=30) + end_date = timezone.now().date() - timedelta(days=10) class Meta: model = Audit class SpecialAuditFactory(EngagementFactory): - start_date = datetime.today() - timedelta(days=30) - end_date = datetime.today() - timedelta(days=10) + start_date = timezone.now().date() - timedelta(days=30) + end_date = timezone.now().date() - timedelta(days=10) class Meta: model = SpecialAudit class SpotCheckFactory(EngagementFactory): - start_date = datetime.today() - timedelta(days=30) - end_date = datetime.today() - timedelta(days=10) + start_date = timezone.now().date() - timedelta(days=30) + end_date = timezone.now().date() - timedelta(days=10) class Meta: model = SpotCheck diff --git a/src/etools/applications/audit/tests/test_views.py b/src/etools/applications/audit/tests/test_views.py index 77a2d253bc..7adcc8c09d 100644 --- a/src/etools/applications/audit/tests/test_views.py +++ b/src/etools/applications/audit/tests/test_views.py @@ -547,7 +547,9 @@ def _do_create(self, user, data): 'date_of_comments_by_unicef' ]: if data.get(date_field): - data[date_field] = data[date_field].date().isoformat() + if isinstance(data[date_field], datetime.datetime): + data[date_field] = data[date_field].date() + data[date_field] = data[date_field].isoformat() response = self.forced_auth_req( 'post', self.engagements_url(), @@ -787,7 +789,9 @@ def _do_update(self, user, data): 'date_of_comments_by_unicef' ]: if data.get(date_field): - data[date_field] = data[date_field].date().isoformat() + if isinstance(data[date_field], datetime.datetime): + data[date_field] = data[date_field].date() + data[date_field] = data[date_field].isoformat() response = self.forced_auth_req( 'patch', '/api/audit/audits/{}/'.format(self.engagement.id), From d65616a1a9aa49ccb9326581e994981576cd4151 Mon Sep 17 00:00:00 2001 From: Ema Ciupe Date: Fri, 7 Jun 2024 15:44:31 +0300 Subject: [PATCH 21/26] [ch29971] Admin agreements: add-the-ability-to-revert-a-terminated-pca --- src/etools/applications/partners/admin.py | 26 +++++++++- .../applications/partners/tests/test_admin.py | 52 ++++++++++++++++++- 2 files changed, 75 insertions(+), 3 deletions(-) diff --git a/src/etools/applications/partners/admin.py b/src/etools/applications/partners/admin.py index 051dc0ac62..7c9228007b 100644 --- a/src/etools/applications/partners/admin.py +++ b/src/etools/applications/partners/admin.py @@ -1,3 +1,5 @@ +import os + from django.conf import settings from django.contrib import admin, messages from django.contrib.admin.utils import quote @@ -711,6 +713,11 @@ class AgreementAttachmentInline(AttachmentSingleInline): code = 'partners_agreement' +class AgreementTerminationDocAttachmentInline(AttachmentSingleInline): + verbose_name_plural = _('Termination Doc Attachment') + code = 'partners_agreement_termination_doc' + + class AgreementAdmin( AttachmentInlineAdminMixin, ExportMixin, @@ -769,6 +776,7 @@ class AgreementAdmin( inlines = [ ActivityInline, AgreementAttachmentInline, + AgreementTerminationDocAttachmentInline ] def has_module_permission(self, request): @@ -819,8 +827,19 @@ def get_interventions_admin_urls(self, objs): def revert_termination(self, request, pk): agreement = Agreement.objects.get(pk=pk) agreement.status = Agreement.SIGNED + termination_doc = Attachment.objects.get( + code='partners_agreement_termination_doc', + content_type=ContentType.objects.get_for_model(Agreement), + object_id=agreement.pk + ) + if os.path.isfile(termination_doc.file.path): + os.remove(termination_doc.file.path) + agreement.termination_doc.remove(termination_doc) + 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), @@ -830,10 +849,13 @@ def revert_termination(self, request, pk): ) if not intervention_activities.exists(): continue + previous_status = intervention_activities.last().change['status']['before'] - i.status = previous_status + if previous_status in [Intervention.SIGNED, Intervention.ACTIVE, Intervention.SUSPENDED]: + i.status = previous_status + interventions_to_update.append(i) - Intervention.objects.bulk_update(terminated_interventions, fields=['status']) + Intervention.objects.bulk_update(interventions_to_update, fields=['status']) return HttpResponseRedirect(reverse('admin:partners_agreement_change', args=[pk])) diff --git a/src/etools/applications/partners/tests/test_admin.py b/src/etools/applications/partners/tests/test_admin.py index 4df826a664..ef1da6c995 100644 --- a/src/etools/applications/partners/tests/test_admin.py +++ b/src/etools/applications/partners/tests/test_admin.py @@ -1,15 +1,18 @@ import datetime +import os.path 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 +106,50 @@ 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) + self.assertFalse(os.path.exists(a.file.path)) + + suspended_pd.refresh_from_db() + self.assertEqual(suspended_pd.status, Intervention.SUSPENDED) + + closed_pd.refresh_from_db() + self.assertEqual(closed_pd.status, Intervention.TERMINATED) From 1f60284e0f72cb5af536e6de1e24c754a99af5de Mon Sep 17 00:00:00 2001 From: Ema Ciupe Date: Fri, 7 Jun 2024 16:01:16 +0300 Subject: [PATCH 22/26] locales --- .../applications/partners/locale/ar/LC_MESSAGES/django.po | 5 ++++- .../applications/partners/locale/es/LC_MESSAGES/django.po | 5 ++++- .../applications/partners/locale/fr/LC_MESSAGES/django.po | 5 ++++- .../applications/partners/locale/pt/LC_MESSAGES/django.po | 5 ++++- .../applications/partners/locale/ru/LC_MESSAGES/django.po | 5 ++++- 5 files changed, 20 insertions(+), 5 deletions(-) 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 09cea1a9b0..ff714b3d0c 100644 --- a/src/etools/applications/partners/locale/ar/LC_MESSAGES/django.po +++ b/src/etools/applications/partners/locale/ar/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-05-21 04:41+0000\n" +"POT-Creation-Date: 2024-06-07 12:58+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -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 5001826676..b8cf1826db 100644 --- a/src/etools/applications/partners/locale/es/LC_MESSAGES/django.po +++ b/src/etools/applications/partners/locale/es/LC_MESSAGES/django.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: et-partners2-bk\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-05-21 04:41+0000\n" +"POT-Creation-Date: 2024-06-07 12:58+0000\n" "Language: es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -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 e993259a2f..2d3c8cc946 100644 --- a/src/etools/applications/partners/locale/fr/LC_MESSAGES/django.po +++ b/src/etools/applications/partners/locale/fr/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-05-21 04:41+0000\n" +"POT-Creation-Date: 2024-06-07 12:58+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -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 57e8b4c536..9f55cadb7d 100644 --- a/src/etools/applications/partners/locale/pt/LC_MESSAGES/django.po +++ b/src/etools/applications/partners/locale/pt/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-05-21 04:41+0000\n" +"POT-Creation-Date: 2024-06-07 12:58+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -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 9728509bdc..c7c9adc4f5 100644 --- a/src/etools/applications/partners/locale/ru/LC_MESSAGES/django.po +++ b/src/etools/applications/partners/locale/ru/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-05-21 04:41+0000\n" +"POT-Creation-Date: 2024-06-07 12:58+0000\n" "Language: ru\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -61,6 +61,9 @@ msgstr "ГПДП" msgid "Signed Amendment" msgstr "Подписанная поправка" +msgid "Termination Doc Attachment" +msgstr "" + msgid "Agreement Details" msgstr "Детали соглашения" From d963c930a6d32529cbb75d26c8f4c7ab504aedd1 Mon Sep 17 00:00:00 2001 From: Ema Ciupe Date: Fri, 7 Jun 2024 17:29:39 +0300 Subject: [PATCH 23/26] CR: undo delete file from disk --- src/etools/applications/partners/admin.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/etools/applications/partners/admin.py b/src/etools/applications/partners/admin.py index 7c9228007b..bc8073c38b 100644 --- a/src/etools/applications/partners/admin.py +++ b/src/etools/applications/partners/admin.py @@ -827,14 +827,15 @@ def get_interventions_admin_urls(self, objs): def revert_termination(self, request, pk): agreement = Agreement.objects.get(pk=pk) agreement.status = Agreement.SIGNED - termination_doc = Attachment.objects.get( - code='partners_agreement_termination_doc', - content_type=ContentType.objects.get_for_model(Agreement), - object_id=agreement.pk - ) - if os.path.isfile(termination_doc.file.path): - os.remove(termination_doc.file.path) - agreement.termination_doc.remove(termination_doc) + 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']) From 99f97b814e7d3e008b4404602f49147afa0ed173 Mon Sep 17 00:00:00 2001 From: Ema Ciupe Date: Fri, 7 Jun 2024 17:32:47 +0300 Subject: [PATCH 24/26] >= instead of > for financial_findings_local >= audited_expenditure_local --- src/etools/applications/audit/serializers/engagement.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/etools/applications/audit/serializers/engagement.py b/src/etools/applications/audit/serializers/engagement.py index f1ab8d360b..1e282cb3c9 100644 --- a/src/etools/applications/audit/serializers/engagement.py +++ b/src/etools/applications/audit/serializers/engagement.py @@ -571,7 +571,7 @@ def _validate_financial_findings(self, validated_data): if not audited_expenditure: audited_expenditure = self.instance.audited_expenditure if self.instance else None - if audited_expenditure and financial_findings and financial_findings > audited_expenditure: + if audited_expenditure and financial_findings and financial_findings >= audited_expenditure: raise serializers.ValidationError({'financial_findings': _('Cannot exceed Audited Expenditure')}) if not financial_findings_local: @@ -579,7 +579,7 @@ def _validate_financial_findings(self, validated_data): if not audited_expenditure_local: audited_expenditure_local = self.instance.audited_expenditure_local if self.instance else None - if audited_expenditure_local and financial_findings_local and financial_findings_local > audited_expenditure_local: + if audited_expenditure_local and financial_findings_local and financial_findings_local >= audited_expenditure_local: raise serializers.ValidationError({'financial_findings_local': _('Cannot exceed Audited Expenditure Local')}) def validate(self, validated_data): From 7f434b37ebc83973992504f201acf1091d0cc7ce Mon Sep 17 00:00:00 2001 From: Ema Ciupe Date: Fri, 7 Jun 2024 17:52:02 +0300 Subject: [PATCH 25/26] flake --- src/etools/applications/partners/admin.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/etools/applications/partners/admin.py b/src/etools/applications/partners/admin.py index bc8073c38b..49b95a8f5c 100644 --- a/src/etools/applications/partners/admin.py +++ b/src/etools/applications/partners/admin.py @@ -1,5 +1,3 @@ -import os - from django.conf import settings from django.contrib import admin, messages from django.contrib.admin.utils import quote From c094ecd0ee2bf0816ea4da1b3cc9c5d398678e7c Mon Sep 17 00:00:00 2001 From: Ema Ciupe Date: Fri, 7 Jun 2024 20:07:29 +0300 Subject: [PATCH 26/26] fixed test --- src/etools/applications/partners/tests/test_admin.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/etools/applications/partners/tests/test_admin.py b/src/etools/applications/partners/tests/test_admin.py index ef1da6c995..ca4201a4d4 100644 --- a/src/etools/applications/partners/tests/test_admin.py +++ b/src/etools/applications/partners/tests/test_admin.py @@ -1,5 +1,4 @@ import datetime -import os.path from django.contrib.admin.sites import AdminSite from django.core.files.uploadedfile import SimpleUploadedFile @@ -146,7 +145,6 @@ def test_revert_termination(self): agreement.refresh_from_db() self.assertEqual(agreement.status, Agreement.SIGNED) self.assertEqual(agreement.termination_doc.count(), 0) - self.assertFalse(os.path.exists(a.file.path)) suspended_pd.refresh_from_db() self.assertEqual(suspended_pd.status, Intervention.SUSPENDED)