class CertificateDescription(DescriptionMixin, ValidityMixin, TrackedModel): record_code = "205" subrecord_code = "10" period_record_code = "205" period_subrecord_code = "05" sid = SignedIntSID(db_index=True) description = ShortDescription() described_certificate = models.ForeignKey( Certificate, related_name="descriptions", on_delete=models.PROTECT, ) indirect_business_rules = (business_rules.CE6,) business_rules = ( business_rules.NoOverlappingDescriptions, business_rules.ContiguousDescriptions, ) def __str__(self): return self.identifying_fields_to_string( identifying_fields=("described_certificate", "valid_between"), ) class Meta: ordering = ("valid_between",)
class FootnoteDescription(DescriptionMixin, ValidityMixin, TrackedModel): """ The footnote description contains the text associated with a footnote, for a given language and for a particular period. Description period(s) associated with footnote text. The description of a footnote may change independently of the footnote id. The footnote description period contains the validity start date of the footnote description. """ record_code = "200" subrecord_code = "10" period_record_code = "200" period_subrecord_code = "05" described_footnote = models.ForeignKey( Footnote, on_delete=models.CASCADE, related_name="descriptions", ) description = models.TextField() sid = SignedIntSID(db_index=True) indirect_business_rules = (business_rules.FO4,) def __str__(self): return f"for Footnote {self.described_footnote}" class Meta: ordering = ("valid_between",)
class AdditionalCodeDescription(DescriptionMixin, ValidityMixin, TrackedModel): """ The additional code description contains the description of the additional code for a particular period. This model combines the additional code description and the additional code description period domain objects, because we only care about 1 language. """ record_code = "245" subrecord_code = "10" period_record_code = "245" period_subrecord_code = "05" # Store the additional code description period sid so that we can send it in TARIC3 # updates to systems that expect it. sid = SignedIntSID(db_index=True) described_additionalcode = models.ForeignKey( AdditionalCode, on_delete=models.PROTECT, related_name="descriptions", ) description = models.TextField() indirect_business_rules = (business_rules.ACN5, ) def __str__(self): return self.identifying_fields_to_string( identifying_fields=("described_additionalcode", "valid_between"), ) class Meta: ordering = ("valid_between", )
class CertificateDescription(DescriptionMixin, TrackedModel): record_code = "205" subrecord_code = "10" period_record_code = "205" period_subrecord_code = "05" identifying_fields = ("sid", ) sid = SignedIntSID(db_index=True) description = ShortDescription() described_certificate = models.ForeignKey( Certificate, related_name="descriptions", on_delete=models.PROTECT, ) indirect_business_rules = (business_rules.CE6, ) def save(self, *args, **kwargs): if getattr(self, "sid") is None: highest_sid = CertificateDescription.objects.aggregate( Max("sid"))["sid__max"] self.sid = highest_sid + 1 return super().save(*args, **kwargs) class Meta: ordering = ("validity_start", )
class GeographicalAreaDescription(DescriptionMixin, TrackedModel): record_code = "250" subrecord_code = "10" period_record_code = "250" period_subrecord_code = "05" identifying_fields = ("sid", ) described_geographicalarea = models.ForeignKey( GeographicalArea, on_delete=models.CASCADE, related_name="descriptions", ) description = ShortDescription() sid = SignedIntSID(db_index=True) def save(self, *args, **kwargs): if getattr(self, "sid") is None: highest_sid = GeographicalAreaDescription.objects.aggregate( Max("sid"))["sid__max"] self.sid = highest_sid + 1 return super().save(*args, **kwargs) url_pattern_name_prefix = "geo_area_description" class Meta: ordering = ("validity_start", )
class QuotaOrderNumber(TrackedModel, ValidityMixin): """ The order number is the identification of a quota. It is defined for tariff quotas and surveillances. If an operator wants to benefit from a tariff quota, they must refer to it via the order number in the customs declaration. An order number may have multiple associated quota definitions, for example to divide a quota over several time periods. """ record_code = "360" subrecord_code = "00" sid = SignedIntSID(db_index=True) order_number = models.CharField( max_length=6, validators=[validators.quota_order_number_validator], db_index=True, ) mechanism = models.PositiveSmallIntegerField( choices=validators.AdministrationMechanism.choices, ) category = models.PositiveSmallIntegerField( choices=validators.QuotaCategory.choices, ) origins = models.ManyToManyField( "geo_areas.GeographicalArea", through="QuotaOrderNumberOrigin", related_name="quotas", ) indirect_business_rules = ( business_rules.ON7, business_rules.ON8, business_rules.QBP2, business_rules.QD1, business_rules.QD7, ) business_rules = ( business_rules.ON1, business_rules.ON2, business_rules.ON9, business_rules.ON11, ) def __str__(self): return self.order_number def in_use(self): return (self.measure_set.model.objects.filter( order_number__sid=self.sid, ).approved_up_to_transaction( self.transaction).exists()) class Meta: verbose_name = "quota"
class QuotaSuspension(TrackedModel, ValidityMixin): """Defines a suspension period for a quota.""" record_code = "370" subrecord_code = "15" sid = SignedIntSID(db_index=True) quota_definition = models.ForeignKey(QuotaDefinition, on_delete=models.PROTECT) description = ShortDescription() business_rules = (business_rules.QSP2, )
class QuotaBlocking(TrackedModel, ValidityMixin): """Defines a blocking period for a (sub-)quota.""" record_code = "370" subrecord_code = "10" sid = SignedIntSID(db_index=True) quota_definition = models.ForeignKey(QuotaDefinition, on_delete=models.PROTECT) blocking_period_type = models.PositiveSmallIntegerField( choices=validators.BlockingPeriodType.choices, ) description = ShortDescription() business_rules = (business_rules.QBP2, )
class GeographicalAreaDescription(TrackedModel, ValidityMixin): record_code = "250" subrecord_code = "10" period_record_code = "250" period_subrecord_code = "05" area = models.ForeignKey( GeographicalArea, on_delete=models.CASCADE, related_name="descriptions", ) description = ShortDescription() sid = SignedIntSID(db_index=True) def __str__(self): return f'description ({self.sid}) - "{self.description}" for {self.area}' class Meta: ordering = ("valid_between", )
class AdditionalCode(TrackedModel, ValidityMixin): """ The additional code identifies a piece of text associated with a goods nomenclature code within a measure. An additional code can be re-used over time. """ record_code = "245" subrecord_code = "00" sid = SignedIntSID(db_index=True) type = models.ForeignKey(AdditionalCodeType, on_delete=models.PROTECT) code = models.CharField( max_length=3, validators=[validators.additional_code_validator], ) indirect_business_rules = ( footnotes_business_rules.FO15, footnotes_business_rules.FO9, measures_business_rules.ME1, ) business_rules = ( business_rules.ACN1, business_rules.ACN2, business_rules.ACN4, business_rules.ACN5, business_rules.ACN13, business_rules.ACN14, business_rules.ACN17, ) def __str__(self): return f"{self.type.sid}{self.code}" def in_use(self): return (self.measure_set.model.objects.filter( additional_code__sid=self.sid, ).approved_up_to_transaction( self.transaction).exists())
class FootnoteDescription(DescriptionMixin, TrackedModel): """ The footnote description contains the text associated with a footnote, for a given language and for a particular period. Description period(s) associated with footnote text. The description of a footnote may change independently of the footnote id. The footnote description period contains the validity start date of the footnote description. """ record_code = "200" subrecord_code = "10" period_record_code = "200" period_subrecord_code = "05" identifying_fields = ("sid", ) described_footnote = models.ForeignKey( Footnote, on_delete=models.CASCADE, related_name="descriptions", ) description = LongDescription() sid = SignedIntSID(db_index=True) indirect_business_rules = (business_rules.FO4, ) def save(self, *args, **kwargs): if getattr(self, "sid") is None: highest_sid = FootnoteDescription.objects.aggregate( Max("sid"))["sid__max"] self.sid = highest_sid + 1 return super().save(*args, **kwargs) class Meta: ordering = ("validity_start", )
class QuotaOrderNumberOrigin(TrackedModel, ValidityMixin): """The order number origin defines a quota as being available only to imports from a specific origin, usually a country or group of countries.""" record_code = "360" subrecord_code = "10" identifying_fields = ("sid", ) sid = SignedIntSID(db_index=True) order_number = models.ForeignKey(QuotaOrderNumber, on_delete=models.PROTECT) geographical_area = models.ForeignKey( "geo_areas.GeographicalArea", on_delete=models.PROTECT, ) excluded_areas = models.ManyToManyField( "geo_areas.GeographicalArea", through="QuotaOrderNumberOriginExclusion", related_name="+", ) indirect_business_rules = ( business_rules.ON13, business_rules.ON14, ) business_rules = ( business_rules.ON5, business_rules.ON6, business_rules.ON7, business_rules.ON10, business_rules.ON12, UniqueIdentifyingFields, UpdateValidity, ) def order_number_in_use(self, transaction): return self.order_number.in_use(transaction)
class AdditionalCode(TrackedModel, ValidityMixin, DescribedMixin): """ The additional code identifies a piece of text associated with a goods nomenclature code within a measure. An additional code can be re-used over time. """ record_code = "245" subrecord_code = "00" identifying_fields = ("sid", ) sid = SignedIntSID(db_index=True) type = models.ForeignKey(AdditionalCodeType, on_delete=models.PROTECT) code = models.CharField( max_length=3, validators=[validators.additional_code_validator], ) indirect_business_rules = ( footnotes_business_rules.FO15, footnotes_business_rules.FO9, measures_business_rules.ME1, ) business_rules = ( business_rules.ACN1, business_rules.ACN2, business_rules.ACN4, business_rules.ACN5, business_rules.ACN13, business_rules.ACN14, business_rules.ACN17, UpdateValidity, UniqueIdentifyingFields, ) def __str__(self): return f"{self.type.sid}{self.code}"
class AdditionalCodeDescription(DescriptionMixin, TrackedModel): """ The additional code description contains the description of the additional code for a particular period. This model combines the additional code description and the additional code description period domain objects, because we only care about 1 language. """ record_code = "245" subrecord_code = "10" period_record_code = "245" period_subrecord_code = "05" identifying_fields = ("sid", ) # Store the additional code description period sid so that we can send it in TARIC3 # updates to systems that expect it. sid = SignedIntSID(db_index=True) described_additionalcode = models.ForeignKey( AdditionalCode, on_delete=models.PROTECT, related_name="descriptions", ) description = LongDescription() indirect_business_rules = (business_rules.ACN5, ) def save(self, *args, **kwargs): if getattr(self, "sid") is None: highest_sid = AdditionalCodeDescription.objects.aggregate( Max("sid"))["sid__max"] self.sid = highest_sid + 1 return super().save(*args, **kwargs) class Meta: ordering = ("validity_start", )
class QuotaOrderNumberOrigin(TrackedModel, ValidityMixin): """The order number origin defines a quota as being available only to imports from a specific origin, usually a country or group of countries.""" record_code = "360" subrecord_code = "10" sid = SignedIntSID(db_index=True) order_number = models.ForeignKey(QuotaOrderNumber, on_delete=models.PROTECT) geographical_area = models.ForeignKey( "geo_areas.GeographicalArea", on_delete=models.PROTECT, ) excluded_areas = models.ManyToManyField( "geo_areas.GeographicalArea", through="QuotaOrderNumberOriginExclusion", related_name="+", ) indirect_business_rules = ( business_rules.ON13, business_rules.ON14, ) business_rules = ( business_rules.ON5, business_rules.ON6, business_rules.ON7, business_rules.ON10, business_rules.ON12, ) def in_use(self): return (self.order_number.measure_set.model.objects.filter( order_number__sid=self.order_number.sid, ).approved_up_to_transaction(self.transaction).exists())
class Measure(TrackedModel, ValidityMixin): """ Defines the validity period in which a particular measure type is applicable to particular nomenclature for a particular geographical area. Measures in the TARIC database are stored against the nomenclature code which is at the highest level appropriate in the hierarchy. Thus, measures which apply to all the declarable codes in a complete chapter are stored against the nomenclature code for the chapter (i.e. at the 2-digit level only); those which apply to all sub-divisions of an HS code are stored against that HS code (i.e. at the 6-digit level only). The advantage of this system is that it reduces the number of measures stored in the database; the data capture workload (thus diminishing the possibility of introducing errors) and the transmission volumes. """ record_code = "430" subrecord_code = "00" sid = SignedIntSID(db_index=True) measure_type = models.ForeignKey(MeasureType, on_delete=models.PROTECT) geographical_area = models.ForeignKey( "geo_areas.GeographicalArea", on_delete=models.PROTECT, related_name="measures", ) goods_nomenclature = models.ForeignKey( "commodities.GoodsNomenclature", on_delete=models.PROTECT, related_name="measures", null=True, blank=True, ) additional_code = models.ForeignKey( "additional_codes.AdditionalCode", on_delete=models.PROTECT, null=True, blank=True, ) dead_additional_code = models.CharField( max_length=16, null=True, blank=True, db_index=True, ) order_number = models.ForeignKey( "quotas.QuotaOrderNumber", on_delete=models.PROTECT, null=True, blank=True, ) dead_order_number = models.CharField( max_length=6, validators=[quota_order_number_validator], null=True, blank=True, db_index=True, ) reduction = models.PositiveSmallIntegerField( validators=[validators.validate_reduction_indicator], null=True, blank=True, db_index=True, ) generating_regulation = models.ForeignKey( "regulations.Regulation", on_delete=models.PROTECT, ) terminating_regulation = models.ForeignKey( "regulations.Regulation", on_delete=models.PROTECT, related_name="terminated_measures", null=True, blank=True, ) stopped = models.BooleanField(default=False) export_refund_nomenclature_sid = SignedIntSID(null=True, blank=True, default=None) footnotes = models.ManyToManyField( "footnotes.Footnote", through="FootnoteAssociationMeasure", ) identifying_fields = ("sid",) indirect_business_rules = ( business_rules.MA4, business_rules.MC3, business_rules.ME42, business_rules.ME49, business_rules.ME61, business_rules.ME65, business_rules.ME66, business_rules.ME67, business_rules.ME71, business_rules.ME73, ) business_rules = ( business_rules.ME1, business_rules.ME2, business_rules.ME3, business_rules.ME4, business_rules.ME5, business_rules.ME6, business_rules.ME7, business_rules.ME8, business_rules.ME88, business_rules.ME16, business_rules.ME115, business_rules.ME25, business_rules.ME32, business_rules.ME10, business_rules.ME116, business_rules.ME119, business_rules.ME9, business_rules.ME12, business_rules.ME17, business_rules.ME24, business_rules.ME87, business_rules.ME33, business_rules.ME34, business_rules.ME40, business_rules.ME45, business_rules.ME46, business_rules.ME47, business_rules.ME109, business_rules.ME110, business_rules.ME111, business_rules.ME104, ) objects = PolymorphicManager.from_queryset(MeasuresQuerySet)() validity_field_name = "db_effective_valid_between" @property def effective_end_date(self): """Measure end dates may be overridden by regulations.""" # UK measures will have explicit end dates only # if self.national: # return self.valid_between.upper reg = self.generating_regulation effective_end_date = ( date( reg.effective_end_date.year, reg.effective_end_date.month, reg.effective_end_date.day, ) if reg.effective_end_date else None ) if self.valid_between.upper and reg and effective_end_date: if self.valid_between.upper > effective_end_date: return effective_end_date return self.valid_between.upper if self.valid_between.upper and self.terminating_regulation: return self.valid_between.upper if reg: return effective_end_date return self.valid_between.upper @property def effective_valid_between(self): return TaricDateRange(self.valid_between.lower, self.effective_end_date) @classmethod def objects_with_validity_field(cls): return super().objects_with_validity_field().with_effective_valid_between() def has_components(self): return ( MeasureComponent.objects.approved_up_to_transaction( transaction=self.transaction, ) .filter(component_measure__sid=self.sid) .exists() ) def has_condition_components(self): return ( MeasureConditionComponent.objects.approved_up_to_transaction( transaction=self.transaction, ) .filter(condition__dependent_measure__sid=self.sid) .exists() ) def get_conditions(self): return MeasureCondition.objects.filter( dependent_measure__sid=self.sid, ).latest_approved() def terminate(self, workbasket, when: date): """ Returns a new version of the measure updated to end on the specified date. If the measure would not have started on that date, the measure is deleted instead. If the measure will already have ended by this date, then does nothing. """ starts_after_date = self.valid_between.lower >= when ends_before_date = ( not self.valid_between.upper_inf and self.valid_between.upper < when ) if ends_before_date: return self update_params = {} if starts_after_date: update_params["update_type"] = UpdateType.DELETE else: update_params["update_type"] = UpdateType.UPDATE update_params["valid_between"] = TaricDateRange( lower=self.valid_between.lower, upper=when, ) if not self.terminating_regulation: update_params["terminating_regulation"] = self.generating_regulation return self.new_draft(workbasket, **update_params)
class QuotaDefinition(TrackedModel, ValidityMixin): """ Defines the validity period and quantity for which a quota is applicable. This model also represents sub-quotas, via a parent-child recursive relation through QuotaAssociation. The monetary unit code and the measurement unit code (with its optional unit qualifier code) are mutually exclusive – each quota definition must have one and only one of monetary or measurement unit. The pair of measurement and measurement unit qualifier must appear as a valid measurement in the measurements table. """ record_code = "370" subrecord_code = "00" identifying_fields = ("sid", ) sid = SignedIntSID(db_index=True) order_number = models.ForeignKey(QuotaOrderNumber, on_delete=models.PROTECT) volume = models.DecimalField(max_digits=14, decimal_places=3) initial_volume = models.DecimalField(max_digits=14, decimal_places=3) monetary_unit = models.ForeignKey( "measures.MonetaryUnit", on_delete=models.PROTECT, null=True, blank=True, ) measurement_unit = models.ForeignKey( "measures.MeasurementUnit", on_delete=models.PROTECT, null=True, blank=True, ) measurement_unit_qualifier = models.ForeignKey( "measures.MeasurementUnitQualifier", on_delete=models.PROTECT, null=True, blank=True, ) maximum_precision = models.PositiveSmallIntegerField( validators=[validators.validate_max_precision], ) quota_critical = models.BooleanField(default=False) # the percentage at which the quota becomes critical quota_critical_threshold = models.PositiveSmallIntegerField( validators=[validators.validate_percentage], ) description = ShortDescription() sub_quotas = models.ManyToManyField( "self", through="QuotaAssociation", through_fields=("main_quota", "sub_quota"), ) indirect_business_rules = ( business_rules.QA2, business_rules.QA3, business_rules.QA5, business_rules.QSP2, ) business_rules = ( business_rules.ON8, business_rules.QD1, business_rules.QD7, business_rules.QD8, business_rules.QD10, business_rules.QD11, business_rules.PreventQuotaDefinitionDeletion, business_rules.QuotaAssociationMustReferToANonDeletedSubQuota, business_rules.QuotaSuspensionMustReferToANonDeletedQuotaDefinition, business_rules. QuotaBlockingPeriodMustReferToANonDeletedQuotaDefinition, business_rules.OverlappingQuotaDefinition, business_rules.VolumeAndInitialVolumeMustMatch, UniqueIdentifyingFields, UpdateValidity, ) class Meta: constraints = [ models.CheckConstraint( check=(models.Q( monetary_unit__isnull=False, measurement_unit__isnull=True, ) | models.Q( monetary_unit__isnull=True, measurement_unit__isnull=False, )), name="quota_definition_must_have_one_unit", ), ] def __str__(self): return str(self.sid)
class QuotaOrderNumber(TrackedModel, ValidityMixin): """ The order number is the identification of a quota. It is defined for tariff quotas and surveillances. If an operator wants to benefit from a tariff quota, they must refer to it via the order number in the customs declaration. An order number may have multiple associated quota definitions, for example to divide a quota over several time periods. """ record_code = "360" subrecord_code = "00" identifying_fields = ("sid", ) sid = SignedIntSID(db_index=True) order_number = models.CharField( max_length=6, validators=[validators.quota_order_number_validator], db_index=True, ) mechanism = models.PositiveSmallIntegerField( choices=validators.AdministrationMechanism.choices, ) category = models.PositiveSmallIntegerField( choices=validators.QuotaCategory.choices, ) origins = models.ManyToManyField( "geo_areas.GeographicalArea", through="QuotaOrderNumberOrigin", related_name="quotas", ) required_certificates = models.ManyToManyField( "certificates.Certificate", related_name="quotas", ) indirect_business_rules = ( business_rules.ON7, business_rules.ON8, business_rules.QBP2, business_rules.QD1, business_rules.QD7, business_rules.CertificateValidityPeriodMustSpanQuotaOrderNumber, business_rules.CertificatesMustExist, ) business_rules = ( business_rules.ON1, business_rules.ON2, business_rules.ON9, business_rules.ON11, UniqueIdentifyingFields, UpdateValidity, ) objects = TrackedModelManager.from_queryset( querysets.QuotaOrderNumberQuerySet)() def __str__(self): return self.order_number @property def autocomplete_label(self): return str(self) @property def is_origin_quota(self): return any(self.required_certificates.all()) class Meta: verbose_name = "quota"
class Measure(TrackedModel, ValidityMixin): """ Defines the validity period in which a particular measure type is applicable to particular nomenclature for a particular geographical area. Measures in the TARIC database are stored against the nomenclature code which is at the highest level appropriate in the hierarchy. Thus, measures which apply to all the declarable codes in a complete chapter are stored against the nomenclature code for the chapter (i.e. at the 2-digit level only); those which apply to all sub-divisions of an HS code are stored against that HS code (i.e. at the 6-digit level only). The advantage of this system is that it reduces the number of measures stored in the database; the data capture workload (thus diminishing the possibility of introducing errors) and the transmission volumes. """ record_code = "430" subrecord_code = "00" sid = SignedIntSID(db_index=True) measure_type = models.ForeignKey(MeasureType, on_delete=models.PROTECT) geographical_area = models.ForeignKey( "geo_areas.GeographicalArea", on_delete=models.PROTECT, related_name="measures", ) goods_nomenclature = models.ForeignKey( "commodities.GoodsNomenclature", on_delete=models.PROTECT, related_name="measures", null=True, blank=True, ) additional_code = models.ForeignKey( "additional_codes.AdditionalCode", on_delete=models.PROTECT, null=True, blank=True, ) dead_additional_code = models.CharField( max_length=16, null=True, blank=True, db_index=True, ) order_number = models.ForeignKey( "quotas.QuotaOrderNumber", on_delete=models.PROTECT, null=True, blank=True, ) dead_order_number = models.CharField( max_length=6, validators=[quota_order_number_validator], null=True, blank=True, db_index=True, ) reduction = models.PositiveSmallIntegerField( validators=[validators.validate_reduction_indicator], null=True, blank=True, db_index=True, ) generating_regulation = models.ForeignKey( "regulations.Regulation", on_delete=models.PROTECT, ) terminating_regulation = models.ForeignKey( "regulations.Regulation", on_delete=models.PROTECT, related_name="terminated_measures", null=True, blank=True, ) stopped = models.BooleanField(default=False) export_refund_nomenclature_sid = SignedIntSID(null=True, blank=True, default=None) footnotes = models.ManyToManyField( "footnotes.Footnote", through="FootnoteAssociationMeasure", ) identifying_fields = ("sid", ) indirect_business_rules = ( business_rules.MA4, business_rules.MC3, business_rules.ME42, business_rules.ME49, business_rules.ME61, business_rules.ME65, business_rules.ME66, business_rules.ME67, business_rules.ME71, business_rules.ME73, ) business_rules = ( business_rules.ME1, business_rules.ME2, business_rules.ME3, business_rules.ME4, business_rules.ME5, business_rules.ME6, business_rules.ME7, business_rules.ME8, business_rules.ME88, business_rules.ME16, business_rules.ME115, business_rules.ME25, business_rules.ME32, business_rules.ME10, business_rules.ME116, business_rules.ME119, business_rules.ME9, business_rules.ME12, business_rules.ME17, business_rules.ME24, business_rules.ME27, business_rules.ME87, business_rules.ME33, business_rules.ME34, business_rules.ME40, business_rules.ME45, business_rules.ME46, business_rules.ME47, business_rules.ME109, business_rules.ME110, business_rules.ME111, business_rules.ME104, UniqueIdentifyingFields, UpdateValidity, ) objects = TrackedModelManager.from_queryset(MeasuresQuerySet)() @property def footnote_application_codes( self) -> Set[footnote_validators.ApplicationCode]: codes = {footnote_validators.ApplicationCode.DYNAMIC_FOOTNOTE} if self.goods_nomenclature: codes.add(footnote_validators.ApplicationCode.OTHER_MEASURES) if not self.goods_nomenclature.is_taric_code: codes.add(footnote_validators.ApplicationCode.CN_MEASURES) return codes validity_field_name = "db_effective_valid_between" @property def effective_end_date(self) -> date: """Measure end dates may be overridden by regulations.""" if not hasattr(self, self.validity_field_name): effective_valid_between = ( type(self).objects.with_validity_field().filter( pk=self.pk).get().db_effective_valid_between) setattr(self, self.validity_field_name, effective_valid_between) return getattr(self, self.validity_field_name).upper def __str__(self): return str(self.sid) @property def effective_valid_between(self) -> TaricDateRange: if hasattr(self, self.validity_field_name): return getattr(self, self.validity_field_name) return TaricDateRange(self.valid_between.lower, self.effective_end_date) @property def duty_sentence(self) -> str: return MeasureComponent.objects.duty_sentence(self) @classproperty def auto_value_fields(cls): """Remove export refund SID because we don't want to auto-increment it – it should really be a foreign key to an ExportRefundNomenclature model but as we don't use them in the UK Tariff we don't store them.""" counters = super().auto_value_fields counters.remove(cls._meta.get_field("export_refund_nomenclature_sid")) return counters def has_components(self, transaction): return (MeasureComponent.objects.approved_up_to_transaction( transaction).filter(component_measure__sid=self.sid).exists()) def has_condition_components(self, transaction): return (MeasureConditionComponent.objects.approved_up_to_transaction( transaction).filter( condition__dependent_measure__sid=self.sid).exists())
class GeographicalArea(TrackedModel, ValidityMixin, DescribedMixin): """ A Geographical Area covers three distinct types of object: 1) A Country 2) A Region (a trading area which is not recognised as a country) 3) A Grouping of the above These objects are generally used when linked to data structures such as measures. As a measure does not care to distinguish between a country, region or group, the 3 types are stored as one for relational purposes. As a country or region can belong to a group there is a self-referential many-to-many field which is restricted. Yet groups can also have parent groups - in which case measures must of the parent must also apply to a child. To accomodate this there is a separate foreign key for group to group relations. """ record_code = "250" subrecord_code = "00" identifying_fields = ("sid", ) url_pattern_name_prefix = "geo_area" sid = SignedIntSID(db_index=True) area_id = models.CharField(max_length=4, validators=[area_id_validator]) area_code = models.PositiveSmallIntegerField(choices=AreaCode.choices) # This deals with countries and regions belonging to area groups memberships = models.ManyToManyField("self", through="GeographicalMembership") # This deals with subgroups of other groups parent = models.ForeignKey("self", on_delete=models.PROTECT, null=True, blank=True) objects = PolymorphicManager.from_queryset(GeographicalAreaQuerySet)() indirect_business_rules = ( business_rules.GA14, business_rules.GA16, business_rules.GA17, measures_business_rules.ME1, measures_business_rules.ME65, measures_business_rules.ME66, measures_business_rules.ME67, quotas_business_rules.ON13, quotas_business_rules.ON14, quotas_business_rules.ON6, ) business_rules = ( business_rules.GA1, business_rules.GA3, business_rules.GA4, business_rules.GA5, business_rules.GA6, business_rules.GA7, business_rules.GA10, business_rules.GA11, business_rules.GA21, business_rules.GA22, UniqueIdentifyingFields, UpdateValidity, ) def get_current_memberships(self): return (GeographicalMembership.objects.filter( Q(geo_group__sid=self.sid) | Q(member__sid=self.sid), ).current().select_related( "member", "geo_group")) def is_single_region_or_country(self): return self.area_code == AreaCode.COUNTRY or self.area_code == AreaCode.REGION def is_all_countries(self): return self.area_code == AreaCode.GROUP and self.area_id == "1011" def is_group(self): return self.area_code == AreaCode.GROUP def __str__(self): return f"{self.get_area_code_display()} {self.area_id}" class Meta: constraints = (CheckConstraint( name="only_groups_have_parents", check=Q(area_code=1) | Q(parent__isnull=True), ), )
class MeasureCondition(TrackedModel): """ A measure may be dependent on conditions. These are expressed in a series of conditions, each having zero or more components. Conditions for the same condition type will have sequence numbers. Conditions of different types may be combined. """ record_code = "430" subrecord_code = "10" url_pattern_name_prefix = "measure" url_suffix = "#conditions" identifying_fields = ("sid", ) sid = SignedIntSID(db_index=True) dependent_measure = models.ForeignKey( Measure, on_delete=models.PROTECT, related_name="conditions", ) condition_code = models.ForeignKey( MeasureConditionCode, on_delete=models.PROTECT, related_name="conditions", ) component_sequence_number = models.PositiveSmallIntegerField( validators=[validators.validate_component_sequence_number], ) duty_amount = models.DecimalField( max_digits=10, decimal_places=3, null=True, blank=True, ) monetary_unit = models.ForeignKey( MonetaryUnit, on_delete=models.PROTECT, null=True, blank=True, ) condition_measurement = models.ForeignKey( Measurement, on_delete=models.PROTECT, null=True, blank=True, ) action = models.ForeignKey( MeasureAction, on_delete=models.PROTECT, null=True, blank=True, ) required_certificate = models.ForeignKey( "certificates.Certificate", on_delete=models.PROTECT, null=True, blank=True, ) objects = TrackedModelManager.from_queryset(MeasureConditionQuerySet)() indirect_business_rules = ( business_rules.MA2, business_rules.MC4, business_rules.ME53, ) business_rules = ( business_rules.MC3, business_rules.MA4, business_rules.ME56, business_rules.ME57, business_rules.ME58, business_rules.ME59, business_rules.ME60, business_rules.ME61, business_rules.ME62, business_rules.ME63, business_rules.ME64, business_rules.ActionRequiresDuty, business_rules.ConditionCodeAcceptance, UniqueIdentifyingFields, UpdateValidity, ) class Meta: ordering = [ "dependent_measure__sid", "condition_code__code", "component_sequence_number", ] def is_certificate_required(self): return self.condition_code.code in ("A", "B", "C", "H", "Q", "Y", "Z") @property def description(self) -> str: out: list[str] = [] out.append( f"Condition of type {self.condition_code.code} - {self.condition_code.description}", ) if self.required_certificate: out.append( f"On presentation of certificate {self.required_certificate.code},", ) elif self.is_certificate_required(): out.append("On presentation of no certificate,") if hasattr(self, "reference_price_string") and self.reference_price_string: out.append(f"If reference price > {self.reference_price_string},") out.append( f"perform action {self.action.code} - {self.action.description}") if self.condition_string: out.append(f"\n\nApplicable duty is {self.condition_string}") return " ".join(out) @property def condition_string(self) -> str: out: list[str] = [] components = self.components.latest_approved() measures: set[str] = set() measure_types: set[str] = set() additional_codes: set[str] = set() for mcc in components: measures.add(mcc.condition.dependent_measure.sid) measure_types.add(mcc.condition.dependent_measure.measure_type.sid) if mcc.condition.dependent_measure.additional_code: additional_codes.add( mcc.condition.dependent_measure.additional_code.sid, ) if (len(measures) == len(measure_types) == len(additional_codes) == 1 or len(measure_types) > 1 or len(additional_codes) > 1): out.append(self.duty_sentence) return "".join(out) @property def duty_sentence(self) -> str: return MeasureConditionComponent.objects.duty_sentence(self)