Exemple #1
0
 def _notify_consent_service(self, validated_data):
     """
     Trigger the update_contact_consent task with the current version
     of `validated_data`. The actual enqueuing of the task happens in the
     on_commit hook so it won't actually notify the consent service unless
     the database transaction was successful.
     """
     if 'accepts_dit_email_marketing' not in validated_data:
         # If no consent value in request body
         return
     # Remove the accepts_dit_email_marketing from validated_data
     accepts_dit_email_marketing = validated_data.pop(
         'accepts_dit_email_marketing')
     # If consent value in POST, notify
     combiner = DataCombiner(self.instance, validated_data)
     request = self.context.get('request', None)
     transaction.on_commit(
         lambda: update_contact_consent.apply_async(
             args=(
                 combiner.get_value('email'),
                 accepts_dit_email_marketing,
             ),
             kwargs={
                 'modified_at': now().isoformat(),
                 'zipkin_headers': get_zipkin_headers(request),
             },
         ), )
    def __call__(self, data=None, order=None):
        """
        Check that:
        - vat_status is specified
        - if vat_status == eu:
            - vat_verified is specified
            - if vat_verified == True:
                - vat_number is specified
        """
        data_combiner = DataCombiner(order, data)

        vat_status = data_combiner.get_value('vat_status')
        if not vat_status:
            raise ValidationError({
                'vat_status': [self.message],
            })

        if vat_status == VATStatus.eu:
            vat_verified = data_combiner.get_value('vat_verified')
            if vat_verified is None:
                raise ValidationError({
                    'vat_verified': [self.message],
                })

            vat_number = data_combiner.get_value('vat_number')
            if vat_verified and not vat_number:
                raise ValidationError({
                    'vat_number': [self.message],
                })
    def __call__(self, data=None, order=None):
        """Validate that all the fields required are set."""
        data_combiner = DataCombiner(order, data)

        meta = order._meta
        errors = defaultdict(list)

        # direct required fields
        for field_name in self.REQUIRED_FIELDS:
            field = meta.get_field(field_name)

            if isinstance(field, models.ManyToManyField):
                value = data_combiner.get_value_to_many(field_name)
            else:
                value = data_combiner.get_value(field_name)

            if not value:
                errors[field_name] = [self.message]

        # extra validators
        extra_errors = self._run_extra_validators(data, order)
        for field, field_errors in extra_errors.items():
            errors[field] += field_errors

        if errors:
            raise ValidationError(errors)
Exemple #4
0
def validate(instance=None, update_data=None, fields=None, next_stage=False):
    """Validates an investment project for the current stage.

    :param instance:    Model instance (for update operations only)
    :param update_data: New data to update or create the instance with
    :param fields:      Fields to restrict validation to
    :param next_stage:  Perform validation for the next stage (rather than the current stage)
    :return:            dict containing errors for incomplete fields
    """
    combiner = DataCombiner(instance, update_data, model=InvestmentProject)
    desired_stage = combiner.get_value('stage') or Stage.prospect.value
    desired_stage_order = _get_desired_stage_order(desired_stage, next_stage)

    errors = {}

    for field, req_stage in (InvestmentProjectStageValidationConfig.
                             get_required_fields_after_stage().items()):
        if _should_skip_rule(field, fields, desired_stage_order,
                             req_stage.order):
            continue

        if field_incomplete(combiner, field):
            errors[field] = REQUIRED_MESSAGE

    for field, rule in (InvestmentProjectStageValidationConfig.
                        get_conditional_rules_after_stage().items()):
        if _should_skip_rule(field, fields, desired_stage_order,
                             rule.stage.order):
            continue

        if _check_rule(combiner, rule) and field_incomplete(combiner, field):
            errors[field] = REQUIRED_MESSAGE

    return errors
Exemple #5
0
    def __call__(self, data, serializer):
        """
        Performs validation.

        TODO: this method has do be simplified once `company` field is removed.
        """
        instance = serializer.instance
        company_has_changed = not instance or (
            'company' in data and data['company'] != instance.company)
        companies_have_changed = not instance or ('companies' in data and set(
            data['companies']) != set(instance.companies.all()))

        contacts_have_changed = not instance or ('contacts' in data and set(
            data['contacts']) != set(instance.contacts.all()))

        if not (company_has_changed or companies_have_changed
                or contacts_have_changed):
            return

        combiner = DataCombiner(instance, data)
        company = combiner.get_value('company')

        if company_has_changed and company:
            companies = (company, )
        else:
            companies = combiner.get_value_to_many('companies')

        contacts = combiner.get_value_to_many('contacts')
        if any(contact.company not in companies for contact in contacts):
            raise serializers.ValidationError(
                'The interaction contacts must belong to the specified company.',
                code='inconsistent_contacts_and_company',
            )
 def test_get_value_id_instance(self):
     """Tests getting a foreign key from an instance."""
     subinstance = Mock()
     subinstance.id = 1234
     instance = Mock(field1=subinstance)
     data_combiner = DataCombiner(instance, None)
     assert data_combiner.get_value_id('field1') == str(subinstance.id)
 def test_get_value_id_value(self):
     """Tests getting a foreign key from update data."""
     subinstance = Mock()
     subinstance.id = 1234
     new_subinstance = Mock()
     new_subinstance.id = 456
     instance = Mock(field1=subinstance)
     data_combiner = DataCombiner(instance, {'field1': new_subinstance})
     assert data_combiner.get_value_id('field1') == str(new_subinstance.id)
 def test_is_field_to_many_for_normal_field(self, monkeypatch):
     """Tests that test_is_field_to_many() returns False for a normal field."""
     instance = Mock()
     model = Mock()
     mock_field_info = Mock(relations={})
     monkeypatch.setattr(
         validate_utils, '_get_model_field_info', Mock(return_value=mock_field_info),
     )
     data_combiner = DataCombiner(instance, None, model=model)
     assert not data_combiner.is_field_to_many('field1')
    def _reset_vat_fields_if_necessary(self, data):
        """If vat_status is set and != 'eu', vat_number and vat_verified are reset."""
        data_combiner = DataCombiner(self.instance, data)

        vat_status = data_combiner.get_value('vat_status')
        if vat_status and vat_status != VATStatus.eu:
            data['vat_number'] = ''
            data['vat_verified'] = None

        return data
Exemple #10
0
 def _validate_theme(self, data):
     """Make sure that a theme is not unset once it has been set for an interaction."""
     combiner = DataCombiner(self.instance, data)
     if self.instance and self.instance.theme and not combiner.get_value('theme'):
         error = {
             'theme': [
                 self.error_messages['cannot_unset_theme'],
             ],
         }
         raise serializers.ValidationError(error, code='cannot_unset_theme')
    def __call__(self, data):
        """Validate that contact works at company."""
        data_combiner = DataCombiner(self.instance, data)
        company = data_combiner.get_value(self.company_field)
        contact = data_combiner.get_value(self.contact_field)

        if contact.company != company:
            raise ValidationError({
                self.contact_field: self.message,
            })
 def test_get_value_auto_normal_field(self, monkeypatch):
     """Tests that get_value_auto() returns value for a normal field."""
     instance = Mock(field1=123)
     model = Mock()
     mock_field_info = Mock(relations={})
     monkeypatch.setattr(
         validate_utils, '_get_model_field_info', Mock(return_value=mock_field_info),
     )
     data_combiner = DataCombiner(instance, None, model=model)
     assert data_combiner.get_value_auto('field1') == 123
     assert data_combiner['field1'] == 123
    def __call__(self, data, serializer):
        """Validate that contact works at company."""
        instance = getattr(serializer, 'instance', None)
        data_combiner = DataCombiner(instance, data)
        company = data_combiner.get_value(self.company_field)
        contact = data_combiner.get_value(self.contact_field)

        if contact.company != company:
            raise ValidationError({
                self.contact_field: self.message,
            })
 def test_is_field_to_many_for_to_many_field(self, monkeypatch):
     """Tests that test_is_field_to_many() returns True for a to-many field."""
     instance = Mock()
     model = Mock()
     mock_field_info = Mock(
         relations={
             'field1': Mock(to_many=True),
         },
     )
     monkeypatch.setattr(
         validate_utils, '_get_model_field_info', Mock(return_value=mock_field_info),
     )
     data_combiner = DataCombiner(instance, None, model=model)
     assert data_combiner.is_field_to_many('field1')
Exemple #15
0
    def validate(self, data):
        """
        Performs cross-field validation after individual fields have been
        validated.

        Ensures that either a person or company name has been provided,
        as well as an email address or phone number.
        """
        errors = {}
        data_combiner = DataCombiner(self.instance, data)
        company_name = data_combiner.get_value('company_name')
        trading_name = data_combiner.get_value('trading_name')
        company = data_combiner.get_value('company')
        first_name = data_combiner.get_value('first_name')
        last_name = data_combiner.get_value('last_name')
        telephone_number = data_combiner.get_value('telephone_number')
        email = data_combiner.get_value('email')

        has_company_name = any((company_name, company, trading_name))
        has_contact_name = first_name and last_name

        if not (has_company_name or has_contact_name):
            errors['company_name'] = NAME_REQUIRED_MESSAGE
            errors['first_name'] = NAME_REQUIRED_MESSAGE
            errors['last_name'] = NAME_REQUIRED_MESSAGE

        if not (email or telephone_number):
            errors['telephone_number'] = CONTACT_REQUIRED_MESSAGE
            errors['email'] = CONTACT_REQUIRED_MESSAGE

        if errors:
            raise serializers.ValidationError(errors)

        return data
 def test_get_value_auto_to_many(self, monkeypatch):
     """Tests that get_value_auto() returns a list-like object for a to-many field."""
     instance = Mock(field1=MagicMock())
     instance.field1.all.return_value = [123]
     model = Mock()
     mock_field_info = Mock(
         relations={'field1': Mock(to_many=True)},
     )
     monkeypatch.setattr(
         validate_utils, '_get_model_field_info', Mock(return_value=mock_field_info),
     )
     data_combiner = DataCombiner(instance, None, model=model)
     assert data_combiner.get_value_auto('field1') == [123]
     assert data_combiner['field1'] == [123]
 def test_get_value_auto_instance(self, monkeypatch):
     """Tests that get_value_auto() returns the ID for a foreign key."""
     subinstance = Mock()
     subinstance.id = 1234
     instance = Mock(field1=subinstance)
     model = Mock()
     mock_field_info = Mock(
         relations={'field1': Mock(to_many=False)},
     )
     monkeypatch.setattr(
         validate_utils, '_get_model_field_info', Mock(return_value=mock_field_info),
     )
     data_combiner = DataCombiner(instance, None, model=model)
     assert data_combiner.get_value_auto('field1') == str(subinstance.id)
     assert data_combiner['field1'] == str(subinstance.id)
Exemple #18
0
 def __call__(self, data):
     """
     Performs validation.
     """
     if not self.instance:
         return
     existing_interaction_complete = self.instance.status == Interaction.STATUSES.complete
     combiner = DataCombiner(self.instance, data)
     new_status = combiner.get_value('status')
     update_changes_status = new_status != self.instance.status
     if existing_interaction_complete and update_changes_status:
         raise ValidationError(
             'The status of a complete interaction cannot change.',
             code='complete_interaction_status_cannot_change',
         )
    def __call__(self, data):
        """Validate the address fields."""
        data_combiner = DataCombiner(self.instance, data)

        data_combined = {
            field_name: data_combiner.get_value(field_name)
            for field_name in self.fields_mapping.keys()
        }

        if not self._should_validate(data_combined):
            return

        errors = self._validate_fields(data_combined)
        if errors:
            raise ValidationError(errors)
Exemple #20
0
 def __call__(self, data, serializer):
     """
     Performs validation.
     """
     instance = serializer.instance
     if not instance:
         return
     existing_interaction_complete = instance.status == Interaction.Status.COMPLETE
     combiner = DataCombiner(instance, data)
     new_status = combiner.get_value('status')
     update_changes_status = new_status != instance.status
     if existing_interaction_complete and update_changes_status:
         raise serializers.ValidationError(
             'The status of a complete interaction cannot change.',
             code='complete_interaction_status_cannot_change',
         )
Exemple #21
0
    def __call__(self, data, serializer):
        """Performs validation."""
        data_combiner = DataCombiner(serializer.instance, data)

        service = data_combiner.get_value('service')
        service_answers = data_combiner.get_value('service_answers')

        expected_questions = {
            str(question.pk): question
            for question in service.interaction_questions.all()
        } if service else {}

        self._validate_type_and_truthiness(expected_questions, service_answers)

        if service_answers is not None:
            self._validate_questions(expected_questions, service_answers)
Exemple #22
0
    def _update_status(self, data):
        """Updates the project status when the stage changes to or from Won."""
        old_stage = self.instance.stage if self.instance else None
        new_stage = data.get('stage')

        if not new_stage or new_stage == old_stage:
            return

        combiner = DataCombiner(instance=self.instance, update_data=data)
        new_status = combiner.get_value('status')

        if str(new_stage.id) == InvestmentProjectStage.won.value.id:
            data['status'] = InvestmentProject.STATUSES.won
        elif (old_stage
              and str(old_stage.id) == InvestmentProjectStage.won.value.id
              and new_status == InvestmentProject.STATUSES.won):
            data['status'] = InvestmentProject.STATUSES.ongoing
def get_incomplete_fields(instance, fields):
    """Returns a list of fields that are incomplete."""
    combiner = DataCombiner(instance, {}, model=InvestorProfile)
    incomplete_fields = []
    for field in fields:
        if field_incomplete(combiner, field):
            incomplete_fields.append(field)
    return incomplete_fields
Exemple #24
0
    def validate(self, data):
        """Performs cross-field validation."""
        combiner = DataCombiner(self.instance, data)

        if {'global_headquarters', 'headquarter_type'} & data.keys():
            headquarter_type_id = combiner.get_value_id('headquarter_type')
            global_headquarters_id = combiner.get_value_id(
                'global_headquarters')
            if (headquarter_type_id is not None and UUID(headquarter_type_id)
                    == UUID(HeadquarterType.ghq.value.id)
                    and global_headquarters_id is not None):
                message = self.error_messages[
                    'subsidiary_cannot_be_a_global_headquarters']
                raise serializers.ValidationError({
                    'headquarter_type': message,
                })

        return data
Exemple #25
0
def _get_updated_status(instance, data):
    """Updates the project status when the stage changes to or from Won."""
    old_stage = instance.stage if instance else None
    new_stage = data.get('stage')

    if not new_stage or new_stage == old_stage:
        return None

    combiner = DataCombiner(instance=instance, update_data=data)
    new_status = combiner.get_value('status')

    if str(new_stage.id) == InvestmentProjectStage.won.value.id:
        return InvestmentProject.Status.WON
    elif (
        old_stage and str(old_stage.id) == InvestmentProjectStage.won.value.id
        and new_status == InvestmentProject.Status.WON
    ):
        return InvestmentProject.Status.ONGOING
    return None
    def validate(self, data):
        """
        Performs cross-field validation and adds extra fields to data.
        """
        data = super().validate(data)

        combiner = DataCombiner(self.instance, data)
        self._populate_address_fields(combiner, data)

        return data
    def __call__(self, data):
        """Validate editable fields depending on the order status."""
        if not self.instance or self.instance.status not in self.mapping:
            return

        combiner = DataCombiner(self.instance, data, model=self.instance.__class__)

        editable_fields = self.mapping[self.instance.status]
        for field in combiner.data:
            if field not in editable_fields and self._has_changed(field, combiner):
                raise ValidationError({field: self.message})
    def validate(self, attrs):
        """
        Validates the data if necessary.
        This is needed because some addresses only need to be validated
        if they are passed in.
        """
        validated_data = super().validate(attrs)

        data_combiner = DataCombiner(self.parent.instance, validated_data)
        if self.should_validate(data_combiner):
            errors = {}
            for field_name in self.REQUIRED_FIELDS:
                field = self.fields[field_name]
                value = data_combiner.get_value(field.source)
                if not value:
                    errors[field_name] = self.error_messages['required']

            if errors:
                raise ValidationError(errors)

        return validated_data
Exemple #29
0
    def __call__(self, data, serializer):
        """Performs validation."""
        instance = serializer.instance
        company_has_changed = not instance or (
            'company' in data and data['company'] != instance.company)

        contacts_have_changed = not instance or ('contacts' in data and set(
            data['contacts']) != set(instance.contacts.all()))

        if not (company_has_changed or contacts_have_changed):
            return

        combiner = DataCombiner(instance, data)
        company = combiner.get_value('company')
        contacts = combiner.get_value_to_many('contacts')

        if any(contact.company != company for contact in contacts):
            raise serializers.ValidationError(
                'The interaction contacts must belong to the specified company.',
                code='inconsistent_contacts_and_company',
            )
Exemple #30
0
    def validate(self, attrs):
        """Performs cross-field validation."""
        attrs = super().validate(attrs)

        errors = {}
        combiner = DataCombiner(self.instance, attrs)

        validators = (self._validate_related_trade_agreements, )
        for validator in validators:
            errors.update(validator(combiner))

        if errors:
            raise serializers.ValidationError(errors)

        return attrs