|
50 | 50 | from django.db.models import Q |
51 | 51 | from django.utils import timezone |
52 | 52 | from registration.models.operation_designated_operator_timeline import OperationDesignatedOperatorTimeline |
| 53 | +from reporting.models.reporting_year import ReportingYear |
| 54 | +from service.reporting_year_service import ReportingYearService |
53 | 55 | from django.conf import settings |
| 56 | +from service.operation_designated_operator_timeline_service import OperationDesignatedOperatorTimelineService |
| 57 | +from reporting.models.report import Report |
54 | 58 |
|
55 | 59 |
|
56 | 60 | class OperationService: |
@@ -749,3 +753,157 @@ def handle_change_of_registration_purpose( |
749 | 753 | payload.regulated_products = [] |
750 | 754 |
|
751 | 755 | return payload |
| 756 | + |
| 757 | + @classmethod |
| 758 | + def _get_previous_reporting_years(cls) -> QuerySet[ReportingYear]: |
| 759 | + """ |
| 760 | + Returns reporting years that are eligible for the Start Past Report workflow |
| 761 | +
|
| 762 | + Only reporting years prior to the current reporting year are returned |
| 763 | + """ |
| 764 | + |
| 765 | + current_reporting_year = ReportingYearService.get_current_reporting_year() |
| 766 | + |
| 767 | + return ReportingYear.objects.filter(reporting_year__lt=current_reporting_year.reporting_year).order_by( |
| 768 | + "-reporting_year" |
| 769 | + ) |
| 770 | + |
| 771 | + @staticmethod |
| 772 | + def _build_reportable_operation_row( |
| 773 | + timeline: OperationDesignatedOperatorTimeline, |
| 774 | + reporting_year: ReportingYear, |
| 775 | + ) -> dict[str, UUID | str | int]: |
| 776 | + """ |
| 777 | + Builds a reportable operation response row |
| 778 | + """ |
| 779 | + return { |
| 780 | + "operation_id": timeline.operation.id, |
| 781 | + "operation_name": timeline.operation.name, |
| 782 | + "reporting_year": reporting_year.reporting_year, |
| 783 | + } |
| 784 | + |
| 785 | + @classmethod |
| 786 | + def list_previous_reportable_operations( |
| 787 | + cls, |
| 788 | + user_guid: UUID, |
| 789 | + ) -> list[dict]: |
| 790 | + """ |
| 791 | + Returns the reporting year/operation combinations for which the current user is eligible to create a report |
| 792 | +
|
| 793 | + An reporting year/operation combination is eligible if: |
| 794 | + 1. The operation was designated to the user's operator for that reporting year, or |
| 795 | + 2. The operation is currently registered to the user's operator as a fallback |
| 796 | +
|
| 797 | + Existing reports are excluded |
| 798 | + """ |
| 799 | + |
| 800 | + user_operator = UserDataAccessService.get_user_operator_by_user(user_guid) |
| 801 | + |
| 802 | + reporting_years = cls._get_previous_reporting_years() |
| 803 | + year_values = {reporting_year.reporting_year for reporting_year in reporting_years} |
| 804 | + |
| 805 | + # Find all registered operation timelines associated with the user's operator |
| 806 | + timelines = list( |
| 807 | + OperationDesignatedOperatorTimeline.objects.select_related("operation") |
| 808 | + .filter( |
| 809 | + operator_id=user_operator.operator_id, |
| 810 | + operation__status=Operation.Statuses.REGISTERED, |
| 811 | + ) |
| 812 | + .order_by("operation__name", "start_date") |
| 813 | + ) |
| 814 | + |
| 815 | + # Find all currently registered operations for fallback handling |
| 816 | + current_registered_operations = list( |
| 817 | + Operation.objects.filter( |
| 818 | + operator_id=user_operator.operator_id, |
| 819 | + status=Operation.Statuses.REGISTERED, |
| 820 | + ).order_by("name") |
| 821 | + ) |
| 822 | + |
| 823 | + all_operation_ids = {timeline.operation.id for timeline in timelines} | { |
| 824 | + operation.id for operation in current_registered_operations |
| 825 | + } |
| 826 | + |
| 827 | + # Bulk fetch existing reports |
| 828 | + existing_reports = set( |
| 829 | + Report.objects.filter( |
| 830 | + operation_id__in=all_operation_ids, |
| 831 | + reporting_year_id__in=year_values, |
| 832 | + ).values_list( |
| 833 | + "operation_id", |
| 834 | + "reporting_year_id", |
| 835 | + ) |
| 836 | + ) |
| 837 | + |
| 838 | + # Bulk fetch designated operator timelines |
| 839 | + designations_lookup = ( |
| 840 | + OperationDesignatedOperatorTimelineService.get_operation_designated_operators_for_reporting_years( |
| 841 | + operation_ids=all_operation_ids, |
| 842 | + reporting_years=year_values, |
| 843 | + ) |
| 844 | + ) |
| 845 | + |
| 846 | + reportable_operations: list[dict] = [] |
| 847 | + added_operation_years: set[tuple[UUID, int]] = set() |
| 848 | + |
| 849 | + # Add combinations that are valid based on historical designated operator timelines |
| 850 | + for timeline in timelines: |
| 851 | + for reporting_year in reporting_years: |
| 852 | + operation_year = ( |
| 853 | + timeline.operation.id, |
| 854 | + reporting_year.reporting_year, |
| 855 | + ) |
| 856 | + |
| 857 | + if operation_year in added_operation_years: |
| 858 | + continue |
| 859 | + |
| 860 | + if operation_year in existing_reports: |
| 861 | + continue |
| 862 | + |
| 863 | + designated_operator_timeline = designations_lookup.get(operation_year) |
| 864 | + |
| 865 | + if not designated_operator_timeline: |
| 866 | + continue |
| 867 | + |
| 868 | + if designated_operator_timeline.operator.id != user_operator.operator_id: |
| 869 | + continue |
| 870 | + |
| 871 | + reportable_operations.append( |
| 872 | + { |
| 873 | + **cls._build_reportable_operation_row( |
| 874 | + timeline, |
| 875 | + reporting_year, |
| 876 | + ), |
| 877 | + "is_current_registered_fallback": False, |
| 878 | + } |
| 879 | + ) |
| 880 | + |
| 881 | + added_operation_years.add(operation_year) |
| 882 | + |
| 883 | + # Fallback: include current registered operations for operation/year combinations not already returned by the historical designation logic |
| 884 | + for operation in current_registered_operations: |
| 885 | + for reporting_year in reporting_years: |
| 886 | + operation_year = ( |
| 887 | + operation.id, |
| 888 | + reporting_year.reporting_year, |
| 889 | + ) |
| 890 | + |
| 891 | + if operation_year in added_operation_years: |
| 892 | + continue |
| 893 | + |
| 894 | + if operation_year in existing_reports: |
| 895 | + continue |
| 896 | + |
| 897 | + reportable_operations.append( |
| 898 | + { |
| 899 | + "operation_id": operation.id, |
| 900 | + "operation_name": operation.name, |
| 901 | + "reporting_year": reporting_year.reporting_year, |
| 902 | + "registration_purpose": operation.registration_purpose, |
| 903 | + "is_current_registered_fallback": True, |
| 904 | + } |
| 905 | + ) |
| 906 | + |
| 907 | + added_operation_years.add(operation_year) |
| 908 | + |
| 909 | + return reportable_operations |
0 commit comments