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.", )
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
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
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
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
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
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
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
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
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
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
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
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, )
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
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
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
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
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
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
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
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, )