def update(self, instance, validated_data):
        configurable_options = validated_data.pop('configurable_options', None)
        with transaction.atomic():
            # removing name and description from validated data since they should not be updated
            del validated_data['name'], validated_data['description']
            product = validated_data['product']
            component = PluginUtils.get_enduser_component(
                plugin_label=product.module.plugin_label,
                component_name='OrderProduct',
            )

            if component:
                plugin_data = validated_data['plugin_data']
                serializer = component.create_serializer(
                    plugin_data=plugin_data, context=self.context)
                if serializer and serializer.is_valid(raise_exception=True):
                    # update data just in case it was changed during validation process
                    validated_data['plugin_data'] = dict(
                        serializer.validated_data)
                    # TODO: this is a temporary solution, we should abstract this so it would not depend on domains
                    if product.module.plugin_label == 'domains':
                        validated_data['name'] = plugin_data['name']

            order_item = super().update(
                instance=instance,
                validated_data=validated_data)  # type: OrderItem
            order_item.configurable_options.all().delete()
            for config_option in configurable_options:
                # Filter out all configurable options no longer valid
                if not config_option['option'].has_cycle(
                        cycle=order_item.cycle.cycle,
                        cycle_multiplier=order_item.cycle.cycle_multiplier,
                        choice_value=config_option.get('option_value'),
                        currency=order_item.currency.code,
                ):
                    continue
                if config_option['option'].widget == 'yesno':
                    if config_option['option_value'] != 'yes':
                        # Ignore unchecked checkboxes
                        continue
                choice_value = None
                has_price = True
                if config_option['option'].widget == 'text_in':
                    has_price = False
                quantity = config_option.get('quantity')
                if config_option['option'].has_choices:
                    choice_value = config_option['option_value']
                unit_price, price, setupfee = config_option[
                    'option'].get_price_by_cycle_quantity_and_choice(
                        cycle_name=order_item.cycle.cycle,
                        cycle_multiplier=order_item.cycle.cycle_multiplier,
                        currency=order_item.currency,
                        quantity=config_option['quantity'],
                        choice_value=choice_value,
                        option_value=config_option['option_value'],
                    )
                order_item.configurable_options.create(
                    option=config_option['option'],
                    option_value=config_option['option_value'],
                    quantity=quantity,
                    has_price=has_price,
                    taxable=validated_data['taxable'],
                    unit_price=unit_price,
                    price=price,
                    setup_fee=setupfee)
            # Update order item taxes
            order_item.taxes.all().delete()
            self.create_taxes(order_item=order_item)

            return order_item
    def validate(self, attrs):
        cart = attrs['cart']
        product = attrs.get('product')
        cycle = attrs.get('cycle')
        attrs['name'] = product.name
        attrs[
            'description'] = product.description or product.name or 'Product'  # NOTE(tomo): description is required
        if not cycle and product.price_model != PricingModel.free:
            raise serializers.ValidationError(
                {'cycle': _('A billing cycle is required')})
        if cycle:
            try:
                product.cycles.available_for_order(currency=cart.currency).get(
                    pk=cycle.pk)
            except (ProductCycle.DoesNotExist,
                    ProductCycle.MultipleObjectsReturned):
                LOG.debug(
                    'Tried to add a product to cart with an invalid currency.'
                    ' Cart currency {}; Cycle currency: {}'.format(
                        cart.currency, cycle.currency))
                raise serializers.ValidationError(
                    detail=_('Product not available'))
            attrs['cycle_display'] = cycle.display_name

        # update pricing information here
        self.update_pricing_from_product(attrs=attrs)

        # validate configurable options are valid and public and all required are present
        configurable_options = attrs.get('configurable_options')
        if cycle:
            req_filter = dict(cycle=cycle.cycle,
                              cycle_multiplier=cycle.cycle_multiplier,
                              currency=cycle.currency,
                              required=True)
            required_options = product.configurable_options.public(
            ).with_cycles(**req_filter)
            for req_opt in required_options:
                found = False
                for sent_opt in configurable_options:
                    if req_opt.pk == sent_opt['option'].pk:
                        found = True
                if not found:
                    raise serializers.ValidationError(
                        detail=_('Value is required for {}').format(
                            req_opt.description))

        for conf_opt in configurable_options:
            db_opt = conf_opt.get('option')
            if not db_opt:
                raise serializers.ValidationError(
                    detail=_('Invalid option selected'))
            if db_opt.status != ConfigurableOptionStatus.public:
                raise serializers.ValidationError(
                    detail=_('Invalid option selected'))
            if not product.configurable_options.filter(id=db_opt.id).exists():
                raise serializers.ValidationError(
                    detail=_('Invalid option selected'))

        # validate plugin data
        plugin_label = product.module.plugin_label
        component = PluginUtils.get_enduser_component(
            plugin_label=plugin_label,
            component_name='OrderProduct',
        )

        if component:
            plugin_data = attrs['plugin_data']
            serializer = component.create_serializer(plugin_data=plugin_data,
                                                     context=self.context)
            if serializer:
                try:
                    serializer.is_valid(raise_exception=True)
                except ValidationError as e:
                    raise ValidationError(detail={'plugin_data': e.detail})

        return super().validate(attrs)