예제 #1
0
    def import_row(self, workbasket: WorkBasket) -> Measure:
        creator = MeasureCreationPattern(
            workbasket=workbasket,
            base_date=self.validity_start_date,
        )

        try:
            return creator.create(
                duty_sentence=self.duty_sentence,
                measure_type=self.measure_type,
                goods_nomenclature=self.goods_nomenclature,
                validity_start=self.validity_start_date,
                validity_end=self.validity_end_date,
                geographical_area=self.origin,
                exclusions=self.excluded_origins,
                generating_regulation=self.regulation,
                order_number=self.quota,
                dead_order_number=self.dead_order_number,
                additional_code=self.additional_code,
                dead_additional_code=self.dead_additional_code,
                footnotes=self.footnotes,
                condition_sentence=self.conditions,
            )
        except GoodsNomenclature.DoesNotExist:
            logger.warning(
                f"Commodity {self.item_id}: not imported from EU Taric files yet.",
            )
예제 #2
0
def test_adds_terminating_regulation_with_end_date(
    measure_data,
    date_ranges: Dates,
    measure_creation_pattern: MeasureCreationPattern,
):
    measure_data["validity_end"] = None
    measure = measure_creation_pattern.create(**measure_data)
    assert not measure.valid_between.upper
    assert not measure.terminating_regulation

    measure_data["validity_end"] = date_ranges.normal.upper
    measure = measure_creation_pattern.create(**measure_data)
    assert measure.valid_between.upper
    assert measure.terminating_regulation
예제 #3
0
def test_all_records_in_same_transaction(
    measure_data,
    measure_creation_pattern: MeasureCreationPattern,
):
    tracked_models = measure_creation_pattern.create_measure_tracked_models(
        **measure_data)
    assert len(set(m.transaction for m in tracked_models)) == 1
예제 #4
0
def test_sid_is_next_highest(
    measure_data,
    measure_creation_pattern: MeasureCreationPattern,
):
    measure = factories.MeasureFactory()
    models = list(measure_creation_pattern.create(**measure_data))
    assert models[0].sid == measure.sid + 1
예제 #5
0
def test_condition_sid_is_next_highest(
    authorised_use_measure_data,
    measure_creation_pattern: MeasureCreationPattern,
):
    condition = factories.MeasureConditionFactory()
    measure = measure_creation_pattern.create(**authorised_use_measure_data)
    assert measure.conditions.first().sid == condition.sid + 1
    assert measure.conditions.last().sid == condition.sid + 2
예제 #6
0
def test_starts_on_nomenclature_start(
    measure_data,
    date_ranges: Dates,
    measure_creation_pattern: MeasureCreationPattern,
):
    measure_data["goods_nomenclature"] = factories.GoodsNomenclatureFactory(
        valid_between=date_ranges.adjacent_later, )
    measure = measure_creation_pattern.create(**measure_data)
    assert measure.valid_between.lower == date_ranges.adjacent_later.lower
예제 #7
0
def test_ends_on_nomenclature_end(
    measure_data,
    date_ranges: Dates,
    measure_creation_pattern: MeasureCreationPattern,
):
    measure_data["goods_nomenclature"] = factories.GoodsNomenclatureFactory(
        valid_between=date_ranges.starts_with_normal, )
    measure = measure_creation_pattern.create(**measure_data)
    assert measure.valid_between.upper == date_ranges.starts_with_normal.upper
예제 #8
0
def test_associates_footnotes(
    measure_data,
    measure_creation_pattern: MeasureCreationPattern,
):
    footnote = factories.FootnoteFactory()
    measure_data["footnotes"] = [footnote]

    measure = measure_creation_pattern.create(**measure_data)
    linked_footnote = measure.footnotes.get()
    assert footnote == linked_footnote
예제 #9
0
def test_excludes_area_groups(
    measure_data,
    measure_creation_pattern: MeasureCreationPattern,
):
    membership = factories.GeographicalMembershipFactory()
    measure_data["geographical_area"] = membership.geo_group
    measure_data["exclusions"] = [membership.geo_group]

    measure = measure_creation_pattern.create(**measure_data)
    exclusion = measure.exclusions.get()
    assert exclusion.excluded_geographical_area == membership.member
예제 #10
0
def test_excludes_countries_and_regions(
    measure_data,
    measure_creation_pattern: MeasureCreationPattern,
):
    membership = factories.GeographicalMembershipFactory()
    measure_data["geographical_area"] = membership.geo_group
    measure_data["exclusions"] = [membership.member]

    models = list(measure_creation_pattern.create(**measure_data))
    exclusion = models[0].exclusions.get()
    assert exclusion.excluded_geographical_area == membership.member
예제 #11
0
def test_starts_on_minimum_date(
    measure_data,
    date_ranges: Dates,
    measure_creation_pattern: MeasureCreationPattern,
):
    measure_data["validity_start"] = date_ranges.no_end_before(
        date_ranges.now).lower
    measure_data["goods_nomenclature"] = factories.GoodsNomenclatureFactory(
        valid_between=TaricDateRange(date_ranges.now, None), )

    measure = measure_creation_pattern.create(**measure_data)
    assert measure.valid_between.lower == date_ranges.now
예제 #12
0
def test_components_are_sequenced_correctly(
    measure_data,
    measure_creation_pattern: MeasureCreationPattern,
):
    measure_data["duty_sentence"] = "0.0% + 1.23 %"

    measure = measure_creation_pattern.create(**measure_data)
    components = measure.components.all()
    assert components[0].duty_amount == Decimal("0.000")
    assert components[0].duty_expression.sid == 1
    assert components[1].duty_amount == Decimal("1.230")
    assert components[1].duty_expression.sid == 4
예제 #13
0
def measure_creation_pattern(
    workbasket: WorkBasket,
    date_ranges: Dates,
    duty_sentence_parser,
) -> MeasureCreationPattern:
    return MeasureCreationPattern(
        workbasket,
        date_ranges.now,
        defaults={
            "generating_regulation": factories.RegulationFactory(),
        },
        duty_sentence_parser=duty_sentence_parser,
    )
예제 #14
0
def test_sid_is_next_highest(
    measure_data,
    measure_creation_pattern: MeasureCreationPattern,
):
    measure = factories.MeasureFactory()

    expected_sids = [measure.sid, measure.sid + 1, measure.sid + 2]
    actual_sids = [
        measure.sid,
        measure_creation_pattern.create(**measure_data).sid,
        measure_creation_pattern.create(**measure_data).sid,
    ]

    assert expected_sids == actual_sids
예제 #15
0
def test_attaches_conditions_from_sentence(
    condition_measure_data,
    measure_creation_pattern: MeasureCreationPattern,
):
    measure = measure_creation_pattern.create(**condition_measure_data)
    conditions = measure.conditions.all()
    assert len(conditions) == 2
    assert conditions[0].condition_code.code == "B"
    assert conditions[0].required_certificate.certificate_type.sid == "C"
    assert conditions[0].required_certificate.sid == "001"
    assert conditions[0].action.code == "29"
    assert conditions[0].component_sequence_number == 1
    assert conditions[1].condition_code.code == "B"
    assert conditions[1].required_certificate is None
    assert conditions[1].action.code == "09"
    assert conditions[1].component_sequence_number == 2
예제 #16
0
def test_attaches_origin_quota_conditions(
    required_certificates_data,
    measure_creation_pattern: MeasureCreationPattern,
):
    measure = measure_creation_pattern.create(**required_certificates_data)
    conditions = measure.conditions.all()
    assert len(conditions) == 2
    assert conditions[0].condition_code.code == "Q"
    assert conditions[0].required_certificate.certificate_type.sid == "U"
    assert conditions[0].required_certificate.sid == "123"
    assert conditions[0].action.code == "27"
    assert conditions[0].component_sequence_number == 1
    assert conditions[1].condition_code.code == "Q"
    assert conditions[1].required_certificate is None
    assert conditions[1].action.code == "07"
    assert conditions[1].component_sequence_number == 2
예제 #17
0
def test_attaches_authorised_use_conditions(
    authorised_use_measure_data,
    measure_creation_pattern: MeasureCreationPattern,
):
    measure = measure_creation_pattern.create(**authorised_use_measure_data)
    conditions = measure.conditions.all()
    assert len(conditions) == 2
    assert conditions[0].condition_code.code == "B"
    assert conditions[0].required_certificate.certificate_type.sid == "N"
    assert conditions[0].required_certificate.sid == "990"
    assert conditions[0].action.code == "27"
    assert conditions[0].component_sequence_number == 1
    assert conditions[1].condition_code.code == "B"
    assert conditions[1].required_certificate is None
    assert conditions[1].action.code == "08"
    assert conditions[1].component_sequence_number == 2
예제 #18
0
    def create_measures(self, data):
        """Returns a list of the created measures."""
        measure_start_date = data["valid_between"].lower
        workbasket = WorkBasket.current(self.request)
        measure_creation_pattern = MeasureCreationPattern(
            workbasket=workbasket,
            base_date=measure_start_date,
            defaults={
                "generating_regulation": data["generating_regulation"],
            },
        )

        measures_data = []

        for commodity_data in data.get("formset-commodities", []):
            if not commodity_data.get("DELETE"):
                for geo_area in data["geo_area_list"]:

                    measure_data = {
                        "measure_type":
                        data["measure_type"],
                        "geographical_area":
                        geo_area,
                        "exclusions":
                        data.get("geo_area_exclusions", None) or [],
                        "goods_nomenclature":
                        commodity_data["commodity"],
                        "additional_code":
                        data["additional_code"],
                        "order_number":
                        data["order_number"],
                        "validity_start":
                        measure_start_date,
                        "validity_end":
                        data["valid_between"].upper,
                        "footnotes": [
                            item["footnote"]
                            for item in data.get("formset-footnotes", [])
                            if not item.get("DELETE")
                        ],
                        # condition_sentence here, or handle separately and duty_sentence after?
                        "duty_sentence":
                        commodity_data["duties"],
                    }

                    measures_data.append(measure_data)

        created_measures = []

        for measure_data in measures_data:
            measure = measure_creation_pattern.create(**measure_data)
            parser = DutySentenceParser.get(
                measure.valid_between.lower,
                component_output=MeasureConditionComponent,
            )
            for component_sequence_number, condition_data in enumerate(
                    data.get("formset-conditions", []),
                    start=1,
            ):
                if not condition_data.get("DELETE"):

                    measure_creation_pattern.create_condition_and_components(
                        condition_data,
                        component_sequence_number,
                        measure,
                        parser,
                        workbasket,
                    )

            created_measures.append(measure)

        return created_measures
예제 #19
0
    def save(self, commit=True):
        """Get the measure instance after form submission, get from session
        storage any footnote pks created via the Footnote formset and any pks
        not removed from the measure after editing and create footnotes via
        FootnoteAssociationMeasure."""
        instance = super().save(commit=False)
        if commit:
            instance.save()

        sid = instance.sid

        measure_creation_pattern = MeasureCreationPattern(
            workbasket=WorkBasket.current(self.request),
            base_date=instance.valid_between.lower,
            defaults={
                "generating_regulation": self.cleaned_data["generating_regulation"],
            },
        )

        if self.cleaned_data.get("exclusions"):
            for exclusion in self.cleaned_data.get("exclusions"):
                pattern = (
                    measure_creation_pattern.create_measure_excluded_geographical_areas(
                        instance,
                        exclusion,
                    )
                )
                [p for p in pattern]

        if (
            self.request.session[f"instance_duty_sentence_{self.instance.sid}"]
            != self.cleaned_data["duty_sentence"]
        ):
            diff_components(
                instance,
                self.cleaned_data["duty_sentence"],
                self.cleaned_data["valid_between"].lower,
                WorkBasket.current(self.request),
                # Creating components in the same transaction as the new version
                # of the measure minimises number of transaction and groups the
                # creation of measure and related objects in the same
                # transaction.
                instance.transaction,
                models.MeasureComponent,
                "component_measure",
            )

        footnote_pks = [
            dct["footnote"]
            for dct in self.request.session.get(f"formset_initial_{sid}", [])
        ]
        footnote_pks.extend(self.request.session.get(f"instance_footnotes_{sid}", []))

        self.request.session.pop(f"formset_initial_{sid}", None)
        self.request.session.pop(f"instance_footnotes_{sid}", None)

        for pk in footnote_pks:
            footnote = (
                Footnote.objects.filter(pk=pk)
                .approved_up_to_transaction(instance.transaction)
                .first()
            )

            existing_association = (
                models.FootnoteAssociationMeasure.objects.approved_up_to_transaction(
                    instance.transaction,
                )
                .filter(
                    footnoted_measure__sid=instance.sid,
                    associated_footnote__footnote_id=footnote.footnote_id,
                    associated_footnote__footnote_type__footnote_type_id=footnote.footnote_type.footnote_type_id,
                )
                .first()
            )
            if existing_association:
                existing_association.new_version(
                    workbasket=WorkBasket.current(self.request),
                    transaction=instance.transaction,
                    footnoted_measure=instance,
                )
            else:
                models.FootnoteAssociationMeasure.objects.create(
                    footnoted_measure=instance,
                    associated_footnote=footnote,
                    update_type=UpdateType.CREATE,
                    transaction=instance.transaction,
                )

        return instance
예제 #20
0
def test_all_records_in_same_transaction(
    measure_data,
    measure_creation_pattern: MeasureCreationPattern,
):
    models = list(measure_creation_pattern.create(**measure_data))
    assert len(set(m.transaction for m in models)) == 1
예제 #21
0
    def create_conditions(self, obj):
        """
        Gets condition formset from context data, loops over these forms and
        validates the data, checking for the condition_sid field in the data to
        indicate whether an existing condition is being updated or a new one
        created from scratch.

        Then deletes any existing conditions that are not being updated,
        before calling the MeasureCreationPattern.create_condition_and_components with the appropriate parser and condition data.
        """
        formset = self.get_context_data()["conditions_formset"]
        excluded_sids = []
        conditions_data = []
        workbasket = WorkBasket.current(self.request)
        existing_conditions = obj.conditions.approved_up_to_transaction(
            workbasket.get_current_transaction(self.request), )

        for f in formset.forms:
            f.is_valid()
            condition_data = f.cleaned_data
            # If the form has changed and "condition_sid" is in the changed data,
            # this means that the condition is preexisting and needs to updated
            # so that its dependent_measure points to the latest version of measure
            if f.has_changed() and "condition_sid" in f.changed_data:
                excluded_sids.append(f.initial["condition_sid"])
                update_type = UpdateType.UPDATE
                condition_data["version_group"] = existing_conditions.get(
                    sid=f.initial["condition_sid"], ).version_group
                condition_data["sid"] = f.initial["condition_sid"]
            # If changed and condition_sid not in changed_data, then this is a newly created condition
            elif f.has_changed() and "condition_sid" not in f.changed_data:
                update_type = UpdateType.CREATE

            condition_data["update_type"] = update_type
            conditions_data.append(condition_data)

        workbasket = WorkBasket.current(self.request)

        # Delete all existing conditions from the measure instance, except those that need to be updated
        for condition in existing_conditions.exclude(sid__in=excluded_sids):
            condition.new_version(
                workbasket=workbasket,
                update_type=UpdateType.DELETE,
                transaction=obj.transaction,
            )

        if conditions_data:
            measure_creation_pattern = MeasureCreationPattern(
                workbasket=workbasket,
                base_date=obj.valid_between.lower,
            )
            parser = DutySentenceParser.get(
                obj.valid_between.lower,
                component_output=MeasureConditionComponent,
            )

            # Loop over conditions_data, starting at 1 because component_sequence_number has to start at 1
            for component_sequence_number, condition_data in enumerate(
                    conditions_data,
                    start=1,
            ):
                # Create conditions and measure condition components, using instance as `dependent_measure`
                measure_creation_pattern.create_condition_and_components(
                    condition_data,
                    component_sequence_number,
                    obj,
                    parser,
                    workbasket,
                )