예제 #1
0
    def db_get(self, request, model_name, pk):
        """
        As part of the REST API, return a specific instance of the given model
        with the given pk.
        """
        # get model
        try:
            model = get_class_from_string(model_name)
        except:
            return self._db_error('Unknown model.')

        # get instance
        try:
            qs = model.objects.filter(pk=pk)
            instance = Acl.of(model).filter(request, qs)[0]
        except:
            return self._db_error('Unknown pk.')

        # result
        return {
            'success': True,
            'message': 'OK',
            'result': model_to_dict(instance,
                                    exclude_many_to_many=True,
                                    json=True)
        }
예제 #2
0
    def test_model_to_dict_should_only_return_fields_in_the_schema(self):
        self.instance.foo = 'bar'
        self.instance.save()
        def _model_to_dict_does_not_contain_field():
            model_to_dict(self.instance)['foo']

        self.assertEqual(len(model_to_dict(self.instance)), 4)
        self.assertRaises(KeyError, _model_to_dict_does_not_contain_field)
예제 #3
0
 def test_model_to_dict_should_return_reverse_fields_if_specified(self):
     m = TestModelWithManyToMany(id=1, title='Foo')
     m.save()
     try:
         d = model_to_dict(m, fetch_related=True)
         self.assertIsInstance(d.get('pages'), QuerySet)
         self.assertEqual(1, d.get('id'))
         self.assertEqual('Foo', d.get('title'))
     finally:
         m.delete()
예제 #4
0
    def db_get_all(self, request, model_name):
        """
        As part of the REST API, return a list of all model instances.
        """
        # get model
        try:
            model = get_class_from_string(model_name)
        except:
            return self._db_error('Unknown model.')

        # get list
        qs = model.objects.all()
        instances = Acl.of(model).filter(request, qs)
        result = [
            model_to_dict(instance, exclude_many_to_many=True, json=True)
            for instance in instances
        ]

        # result
        return {'success': True, 'message': 'OK', 'result': result}
예제 #5
0
    def sku(self, request):
        # get product
        product_id = request.GET.get('pk')
        product = get_object_or_404(get_product_model(), pk=product_id)

        # get varieties
        _varieties = Variety.objects.prefetch_related(
            Prefetch('options',
                     queryset=VarietyOption.objects.order_by('title'))
        ).filter(sku=True).exclude(options=None).exclude(
            style=Variety.STYLE_ATTRIBUTE).order_by('title').distinct()
        skus = ProductSKU.objects.filter(product=product)
        assigned_option_ids = [
            a.variety_option.id
            for a in VarietyAssignment.objects.select_related('variety_option')
            .filter(product=product, variety_option__variety__sku=True)
        ]

        # initial dataset currently present
        initial = {}
        for sku in skus:
            initial[sku.pk] = sku
            initial[sku.pk].errors = []

        # determine barcode system
        cms_settings = get_cms_settings()
        barcode_system = cms_settings.get_barcode_system(product)

        # create template form
        form_template = ProductSKUForm()
        form_template.configure(request, barcode_system)

        def has_var(prefix, name):
            return 'f-%s-%s' % (prefix, name) in request.POST

        def get_var(prefix, name, default=None):
            return request.POST.get('f-%s-%s' % (prefix, name), default)

        def get_int_var(prefix, name, default=None):
            return parse_int(get_var(prefix, name), default)

        # construct list of variety option names
        varieties = []
        variety_index = {}
        for variety in _varieties:
            variety_index[variety.id] = {
                'id': variety.id,
                'title': variety.title,
                'sku': variety.sku,
                'options': {}
            }

            item = {
                'id': variety.id,
                'title': variety.title,
                'sku': variety.sku,
                'options': [],
                'n_assigned_options': 0
            }
            for option in variety.options.all():
                variety_index[variety.id].get('options')[option.id] = {
                    'id': option.id,
                    'title': option.title,
                    'fullTitle':
                    '%s: <em>%s</em>' % (variety.title, option.title)
                }
                item.get('options').append({
                    'id':
                    option.id,
                    'title':
                    option.title,
                    'assigned':
                    option.id in assigned_option_ids
                })
                if option.pk in assigned_option_ids:
                    item['n_assigned_options'] += 1
            varieties.append(item)

        # sort varieties by number of assigned options, so that varieties that
        # have been assigned are at the top of the list. The rest remains sorted
        # alphabetically...
        varieties.sort(key=lambda x: -x.get('n_assigned_options', 0))

        # validation
        is_valid = True
        if request.method == 'POST':
            # process sku records
            prefixes = request.POST.getlist('skus')
            assigned_option_ids = []
            skus_to_save = []
            sku_code_processed = []
            barcodes_processed = []
            for index, prefix in enumerate(prefixes):
                # extract relevant informatioin from post for
                # individual combination
                _id = get_var(prefix, '_id')
                d = {
                    'enabled': get_var(prefix, 'enabled') == 'on',
                    'sku': get_var(prefix, 'sku'),
                    'barcode': get_var(prefix, 'barcode'),
                    'price': get_var(prefix, 'price'),
                    'stocklevel': get_int_var(prefix, 'stocklevel', 0)
                }

                # parse assigned variety options from request data
                n_variety_option = 1
                d['variety_options'] = []
                while len(d['variety_options']) <= 16:
                    _name = 'vo_%d' % n_variety_option
                    if has_var(prefix, _name):
                        d['variety_options'].append(get_int_var(prefix, _name))
                        n_variety_option += 1
                    else:
                        break

                # make sure that sku, barcode and price are None
                # instead of empty
                if _id == '': _id = None
                if d.get('sku') == '': d['sku'] = None
                if d.get('barcode') == '': d['barcode'] = None
                if d.get('price') == '': d['price'] = None

                # construct form based on this data and validate
                form = ProductSKUForm(d)
                form.configure(request, barcode_system)

                # get variety options
                variety_options = VarietyOption.objects.filter(
                    pk__in=d.get('variety_options'))

                # create or edit?
                sku = initial.get(_id, None)
                if sku is None:
                    sku = ProductSKU.objects.get_by_variety_options(
                        product, variety_options)

                    # still not found? -> create new item
                    if sku is None:
                        sku = ProductSKU()
                        sku.product = product

                # remember the sku record to be saved once we processed
                # everything. We will not save anything until everything
                # is considered to be valid.
                skus_to_save.append(sku)

                # mark any assigned variety options as selected, so that they
                # indeed remain selected, even if they have actually not been
                # properly assigned yet because if from errors for example
                for _variety in varieties:
                    _options = _variety.get('options')
                    for _option in _options:
                        for _assigned_option in variety_options:
                            if _option.get('id') == _assigned_option.pk:
                                _option['assigned'] = True
                                break

                # inject error information and keep track of error states
                sku.errors = []
                if form.is_valid():
                    # update data from from
                    d = form.cleaned_data
                else:
                    for field, error in form.errors.items():
                        sku.errors.append({'field': field, 'error': error[0]})
                    is_valid = False

                # copy original data or cleaned data
                sku.enabled = d.get('enabled', False)
                sku.sku = d.get('sku')
                sku.barcode = d.get('barcode')
                sku.price = d.get('price')
                sku.stocklevel = d.get('stocklevel', 0)

                # keep track of variety options that should be assigned due to
                # SKU's that are enabled
                if sku.enabled:
                    assigned_option_ids.extend(
                        [option.pk for option in variety_options])

                # set variety options (saved later)
                sku._variety_options = variety_options

                # verify uniqueness of the SKU code
                if not self._verify_sku(product, sku, sku_code_processed,
                                        initial):
                    is_valid = False

                # verify uniqueness of barcode
                if not self._verify_barcode(product, sku, barcodes_processed,
                                            initial):
                    is_valid = False

                # maintain changed data in initial data set, so that all
                # changes make theire way back into the view, even through
                # we might not have saved changes due to errors
                _id = ('idx_%d' % index) if sku.pk is None else sku.pk
                initial[_id] = sku

        # process if everything is valid
        if request.method == 'POST' and is_valid:
            # create missing option assignments
            assigned_option_ids = list(
                set(filter(lambda x: x is not None, assigned_option_ids)))
            for option_id in assigned_option_ids:
                try:
                    assignment = VarietyAssignment.objects.get(
                        product=product, variety_option__pk=option_id)
                except VarietyAssignment.DoesNotExist:
                    VarietyAssignment.objects.create(
                        product=product, variety_option_id=option_id)

            # remove deprecated option assignments
            deprecated_assignments = VarietyAssignment.objects.select_related(
                'variety_option').filter(
                    product=product,
                    variety_option__variety__sku=True).exclude(
                        variety_option__pk__in=assigned_option_ids)
            for deprecated_assignment in deprecated_assignments:
                deprecated_assignment.delete()

            # save changes to sku records. Null sku, so that we would not
            # collide when making updates
            sku_ids_saved = []
            for sku in skus_to_save:
                # save product sku itself
                sku._sku = sku.sku
                sku.sku = None
                sku.save()
                sku_ids_saved.append(sku.id)

                # assign and save variety options
                sku.variety_options = sku._variety_options

            # remove all previous SKU
            deprecated_skus = ProductSKU.objects.filter(
                product=product).exclude(pk__in=sku_ids_saved)
            for deprecated_sku in deprecated_skus:
                deprecated_sku.delete()

            # apply new sku names, which is now safe to do
            for sku in skus_to_save:
                if request.settings.sku_is_barcode:
                    sku.sku = sku.barcode
                else:
                    sku.sku = sku._sku
                sku.save()

        # redirect and message
        if request.method == 'POST' and is_valid:
            messages.add_message(
                request, messages.SUCCESS,
                'Product Varieties and SKUs saved for <em>%s</em>.' %
                product.title)

            if request.POST.get('cubane_save_and_continue') is None:
                return self._redirect(request, 'index')
            else:
                return self._redirect(request, 'sku', product)

        # materialise current initial data
        _initial = {}
        for _id, _sku in initial.items():
            _initial[_id] = model_to_dict(_sku)
            if _sku.pk is None:
                _initial[_id]['variety_options'] = [
                    option.pk for option in _sku._variety_options
                ]
            else:
                _initial[_id]['variety_options'] = [
                    option.pk for option in _sku.variety_options.all()
                ]
            _initial[_id]['errors'] = _sku.errors if hasattr(_sku,
                                                             'errors') else []

        # template context
        return {
            'product': product,
            'varieties': varieties,
            'initial': to_json(_initial),
            'variety_index_json': to_json(variety_index),
            'form_template': form_template
        }
예제 #6
0
 def _model_to_dict_does_not_contain_field():
     model_to_dict(self.instance)['foo']
예제 #7
0
 def test_model_to_dict_should_return_correct_fields(self):
     self.assertEqual(len(model_to_dict(self.instance)), 4)
예제 #8
0
 def test_model_to_dict_should_return_dict(self):
     self.assertIsInstance(model_to_dict(self.instance), dict)