Exemple #1
0
 def mutate(cls, root, info, id, input):
     restock = input.get('restock')
     fulfillment = get_node(info, id, only_type=Fulfillment)
     order = fulfillment.order
     errors = []
     if not fulfillment.can_edit():
         errors.append(
             Error(
                 field='fulfillment',
                 message=pgettext_lazy(
                     'Cancel fulfillment mutation error',
                     'This fulfillment can\'t be canceled')))
     if errors:
         return cls(errors=errors)
     cancel_fulfillment(fulfillment, restock)
     if restock:
         msg = npgettext_lazy(
             'Dashboard message',
             'Restocked %(quantity)d item',
             'Restocked %(quantity)d items',
             'quantity') % {'quantity': fulfillment.get_total_quantity()}
     else:
         msg = pgettext_lazy(
             'Dashboard message',
             'Fulfillment #%(fulfillment)s canceled') % {
                 'fulfillment': fulfillment.composed_id}
     order.history.create(content=msg, user=info.context.user)
     return FulfillmentCancel(fulfillment=fulfillment)
 def get_visibility_text(self):
     visibility = self.groupcomment_edit_history.visibility
     if visibility == group_models.GroupComment.VISIBILITY_PRIVATE:
         return pgettext_lazy('comment edit history item', 'Only visible to you')
     if visibility == group_models.GroupComment.VISIBILITY_VISIBLE_TO_EXAMINER_AND_ADMINS:
         return pgettext_lazy('comment edit history item', 'Only visible to examiners and admins')
     return pgettext_lazy('comment edit history item', 'Visible to everyone')
Exemple #3
0
def orderline_split(request, order_pk, line_pk):
    order = get_object_or_404(Order, pk=order_pk)
    item = get_object_or_404(OrderedItem.objects.filter(
        delivery_group__order=order), pk=line_pk)
    form = MoveItemsForm(request.POST or None, item=item)
    line_pk = None
    if item:
        line_pk = item.pk
    status = 200
    if form.is_valid():
        old_group = item.delivery_group
        how_many = form.cleaned_data['quantity']
        with transaction.atomic():
            target_group = form.move_items()
        if not old_group.pk:
            old_group = pgettext_lazy(
                'Dashboard message related to a delivery group',
                'removed group')
        msg = pgettext_lazy(
            'Dashboard message related to delivery groups',
            'Moved %(how_many)s items %(item)s from %(old_group)s'
            ' to %(new_group)s') % {
                'how_many': how_many, 'item': item, 'old_group': old_group,
                'new_group': target_group}
        order.create_history_entry(comment=msg, user=request.user)
        messages.success(request, msg)
    elif form.errors:
        status = 400
    ctx = {'order': order, 'object': item, 'form': form, 'line_pk': line_pk}
    template = 'dashboard/order/modal_split_order_line.html'
    return TemplateResponse(request, template, ctx, status=status)
Exemple #4
0
    def clean(self):
        data = super().clean()
        if not self.instance.is_staff:
            return data

        if self.instance == self.user:
            raise forms.ValidationError(
                pgettext_lazy(
                    "Edit customer details in order form error",
                    "You can't delete your own account via dashboard, "
                    "please try from the storefront.",
                )
            )
        if self.instance.is_superuser:
            raise forms.ValidationError(
                pgettext_lazy(
                    "Edit customer details in order form error",
                    "Only superuser can delete his own account.",
                )
            )
        can_manage_staff_users = self.user.has_perm("account.manage_staff")
        if not can_manage_staff_users:
            raise forms.ValidationError(
                pgettext_lazy(
                    "Edit customer details in order form error",
                    "You have insufficient permissions, to edit staff users.",
                )
            )
        return data
 def get_choices(self):
     return [
         ('', ''),
         (pgettext_lazy('exact candidate num', 'Exactly'), self.get_exact_choices()),
         (pgettext_lazy('less than candidate num', 'Less than'), self.get_less_than_choices()),
         (pgettext_lazy('greater than candidate num', 'Greater than'), self.get_greater_than_choices())
     ]
Exemple #6
0
 def get_display(status):
     if status == VariantAvailabilityStatus.AVAILABLE:
         return pgettext_lazy('Variant status', 'available')
     elif status == VariantAvailabilityStatus.OUT_OF_STOCK:
         return pgettext_lazy('Variant status', 'out of stock')
     else:
         raise NotImplementedError('Unknown status: %s' % status)
Exemple #7
0
def order_address(request, order_pk, address_type):
    order = get_object_or_404(Order, pk=order_pk)
    update_prices = False
    if address_type == 'shipping':
        address = order.shipping_address
        success_msg = pgettext_lazy(
            'Dashboard message',
            'Updated shipping address')
        update_prices = True
    else:
        address = order.billing_address
        success_msg = pgettext_lazy(
            'Dashboard message',
            'Updated billing address')
    form = AddressForm(request.POST or None, instance=address)
    if form.is_valid():
        updated_address = form.save()
        if not address:
            save_address_in_order(order, updated_address, address_type)
        if update_prices:
            update_order_prices(order, request.discounts)
        if not order.is_draft():
            order.events.create(
                user=request.user,
                type=OrderEvents.UPDATED.value)
        messages.success(request, success_msg)
        return redirect('dashboard:order-details', order_pk=order_pk)
    ctx = {'order': order, 'address_type': address_type, 'form': form}
    return TemplateResponse(request, 'dashboard/order/address_form.html', ctx)
Exemple #8
0
def add_variant_to_order(request, order_pk):
    """Add variant in given quantity to an order."""
    order = get_object_or_404(Order.objects.drafts(), pk=order_pk)
    taxes = get_taxes_for_address(order.shipping_address)
    form = AddVariantToOrderForm(
        request.POST or None, order=order, discounts=request.discounts,
        taxes=taxes)
    status = 200
    if form.is_valid():
        msg_dict = {
            'quantity': form.cleaned_data.get('quantity'),
            'variant': form.cleaned_data.get('variant')}
        try:
            with transaction.atomic():
                form.save()
            msg = pgettext_lazy(
                'Dashboard message related to an order',
                'Added %(quantity)d x %(variant)s') % msg_dict
            messages.success(request, msg)
        except InsufficientStock:
            msg = pgettext_lazy(
                'Dashboard message related to an order',
                'Insufficient stock: could not add %(quantity)d x %(variant)s'
            ) % msg_dict
            messages.warning(request, msg)
        return redirect('dashboard:order-details', order_pk=order_pk)
    elif form.errors:
        status = 400
    ctx = {'order': order, 'form': form}
    template = 'dashboard/order/modal/add_variant_to_order.html'
    return TemplateResponse(request, template, ctx, status=status)
Exemple #9
0
def order_customer_edit(request, order_pk):
    order = get_object_or_404(Order.objects.drafts(), pk=order_pk)
    form = OrderCustomerForm(request.POST or None, instance=order)
    status = 200
    if form.is_valid():
        form.save()
        update_order_prices(order, request.discounts)
        user_email = form.cleaned_data.get('user_email')
        user = form.cleaned_data.get('user')
        if user_email:
            msg = pgettext_lazy(
                'Dashboard message',
                '%s email assigned to an order') % user_email
        elif user:
            msg = pgettext_lazy(
                'Dashboard message',
                '%s user assigned to an order') % user
        else:
            msg = pgettext_lazy(
                'Dashboard message',
                'Guest user assigned to an order')
        messages.success(request, msg)
        return redirect('dashboard:order-details', order_pk=order_pk)
    elif form.errors:
        status = 400
    ctx = {'order': order, 'form': form}
    return TemplateResponse(
        request, 'dashboard/order/modal/edit_customer.html', ctx,
        status=status)
Exemple #10
0
def read_action(article, action_name, with_text=True, popover_direction=None):

    if action_name.startswith('is_'):
        return {
            'with_text': with_text,
            'popover_direction': popover_direction or 'top',
            'action_name': action_name,
            'action_data': Read.status_data.get(action_name),
            'popover_class': '' if with_text else 'popover-tooltip',
            'js_func': "toggle_status(event, '{0}', '{1}')".format(
                       article.id, action_name)
        }

    return {
        'with_text': with_text,
        'popover_direction': popover_direction or 'top',
        'action_name': action_name,
        'action_data': {
            'do_title':      _(u'Convert to full-text'),
            'undo_title':    _(u'Switch back to original'),
            'do_label':      _(u'Convert to full-text'),
            'undo_label':    _(u'View original'),
            'status_label':  _(u'converted'),
            'do_icon':       pgettext_lazy(u'awesome-font icon name',
                                           u'exchange'),
            'undo_icon':     pgettext_lazy(u'awesome-font icon name',
                                           u'exchange'),
        },
        'popover_class': '' if with_text else 'popover-tooltip',
        'js_func': "switch_view('{0}', '{1}')".format(
                   article.id, action_name)
    }
Exemple #11
0
    def __init__(self, *args, **kwargs):
        self.product_attributes = []
        super(ProductForm, self).__init__(*args, **kwargs)
        for field_name, field in self.fields.items():
            field.widget.attrs['class'] = 'form-control'

        field = self.fields['is_featured'] 
        field.widget.attrs['class'] = 'styled' 

        field = self.fields['product_class'] 
        field.widget.attrs['class'] = 'form-control bootstrap-select'

        field = self.fields['available_on'] 
        field.widget.attrs['class'] = 'form-control pickadate-selectors'
         

        field = self.fields['name']
        field.widget.attrs['placeholder'] = pgettext_lazy(
            'Product form placeholder', 'Give your awesome product a name')
        field = self.fields['categories']        
        field.widget.attrs['data-placeholder'] = pgettext_lazy(
            'Product form placeholder', 'Search')
        field.widget.attrs['class'] = 'form-control multiselect'
        field.widget.attrs['multiple'] = 'multiple'         

        field = self.fields['product_tax']
        field.widget.attrs['class'] = 'form-control bootstrap-select'
        product_class = self.instance.product_class
        self.product_attributes = product_class.product_attributes.all()
        self.product_attributes = self.product_attributes.prefetch_related(
            'values')
        self.prepare_fields_for_attributes()
Exemple #12
0
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        product_type = self.instance.product_type
        self.initial['tax_rate'] = (
            self.instance.tax_rate or product_type.tax_rate)
        self.available_attributes = (
            product_type.product_attributes.prefetch_related('values').all())
        self.prepare_fields_for_attributes()
        self.fields['collections'].initial = Collection.objects.filter(
            products__name=self.instance)
        self.fields['seo_description'] = SeoDescriptionField(
            extra_attrs={
                'data-bind': self['description'].auto_id,
                'data-materialize': self['description'].html_name})
        self.fields['seo_title'] = SeoTitleField(
            extra_attrs={'data-bind': self['name'].auto_id})
        self.fields['tax_rate'].choices = get_tax_rate_type_choices()
        if include_taxes_in_prices():
            self.fields['price'].label = pgettext_lazy(
                'Currency gross amount', 'Gross price')
        else:
            self.fields['price'].label = pgettext_lazy(
                'Currency net amount', 'Net price')

        if not product_type.is_shipping_required:
            del self.fields['weight']
        else:
            self.fields['weight'].widget.attrs['placeholder'] = (
                product_type.weight.value)
Exemple #13
0
 def clean(self):
     parent = self.instance.parent
     if parent and parent.level >= 2:
         raise forms.ValidationError(
             pgettext_lazy(
                 'Menu item form error',
                 'Maximum nesting level for menu items equals to 2.'),
             code='invalid')
     url = self.cleaned_data.get('url')
     linked_object = self.cleaned_data.get('linked_object')
     if url and linked_object:
         raise forms.ValidationError(
             pgettext_lazy(
                 'Menu item form error',
                 'A single menu item can\'t point to both an internal link '
                 'and URL.'),
             code='invalid')
     if not url and not linked_object:
         raise forms.ValidationError(
             pgettext_lazy(
                 'Menu item form error',
                 'A single menu item must point to an internal link or '
                 'URL.'),
             code='invalid')
     return self.cleaned_data
Exemple #14
0
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        if self.instance.product.pk:
            self.fields['price_override'].widget.attrs[
                'placeholder'] = self.instance.product.price.amount
            self.available_attributes = (
                self.instance.product.product_type.variant_attributes.all()
                    .prefetch_related('values'))
            self.prepare_fields_for_attributes()

        if include_taxes_in_prices():
            self.fields['price_override'].label = pgettext_lazy(
                'Override price', 'Selling gross price override')
            self.fields['cost_price'].label = pgettext_lazy(
                'Currency amount', 'Cost gross price')
        else:
            self.fields['price_override'].label = pgettext_lazy(
                'Override price', 'Selling net price override')
            self.fields['cost_price'].label = pgettext_lazy(
                'Currency amount', 'Cost net price')

        if not self.instance.product.product_type.is_shipping_required:
            del self.fields['weight']
        else:
            self.fields['weight'].widget.attrs['placeholder'] = (
                getattr(self.instance.product.weight, 'value', None) or
                self.instance.product.product_type.weight.value)
Exemple #15
0
 def get_diet(self):
     diets = {
         self.__class__.DIET_VEGAN: pgettext_lazy(context=self.get_gender(), message='Vegan'),
         self.__class__.DIET_VEGETARIAN: pgettext_lazy(context=self.get_gender(), message='Vegetarian'),
         self.__class__.DIET_CARNIST: pgettext_lazy(context=self.get_gender(), message='Carnist'),
     }
     return diets.get(self.diet)
Exemple #16
0
 def diet_choices(gender):
     return (
         # (__class__.DIET_UNKNOWN, _("Unknown")), # ~~~~ TODO: remove this line!
         (__class__.DIET_VEGAN, pgettext_lazy(context=gender, message="Vegan (eats only plants and fungi)")),
         (__class__.DIET_VEGETARIAN, pgettext_lazy(context=gender, message="Vegetarian (doesn't eat fish and meat)")),
         (__class__.DIET_CARNIST, pgettext_lazy(context=gender, message="Carnist (eats animals)")),
     )
Exemple #17
0
 def clean(self):
     parent = self.instance.parent
     if parent and parent.level >= 2:
         raise forms.ValidationError(
             pgettext_lazy(
                 "Menu item form error",
                 "Maximum nesting level for menu items equals to 2.",
             ),
             code="invalid",
         )
     url = self.cleaned_data.get("url")
     linked_object = self.cleaned_data.get("linked_object")
     if url and linked_object:
         raise forms.ValidationError(
             pgettext_lazy(
                 "Menu item form error",
                 "A single menu item can't point to both an internal link "
                 "and URL.",
             ),
             code="invalid",
         )
     if not url and not linked_object:
         raise forms.ValidationError(
             pgettext_lazy(
                 "Menu item form error",
                 "A single menu item must point to an internal link or " "URL.",
             ),
             code="invalid",
         )
     return self.cleaned_data
Exemple #18
0
def add_variant_to_group(request, order_pk, group_pk):
    """Add variant in given quantity to an existing or new order group."""
    order = get_object_or_404(Order, pk=order_pk)
    group = get_object_or_404(order.groups.all(), pk=group_pk)
    form = AddVariantToDeliveryGroupForm(
        request.POST or None, group=group, discounts=request.discounts)
    status = 200
    if form.is_valid():
        msg_dict = {
            'quantity': form.cleaned_data.get('quantity'),
            'variant': form.cleaned_data.get('variant'),
            'group': group}
        try:
            with transaction.atomic():
                form.save()
            msg = pgettext_lazy(
                'Dashboard message related to a shipment group',
                'Added %(quantity)d x %(variant)s to %(group)s') % msg_dict
            order.create_history_entry(content=msg, user=request.user)
            messages.success(request, msg)
        except InsufficientStock:
            msg = pgettext_lazy(
                'Dashboard message related to a shipment group',
                'Insufficient stock: could not add %(quantity)d x '
                '%(variant)s to %(group)s') % msg_dict
            messages.warning(request, msg)
        return redirect('dashboard:order-details', order_pk=order_pk)
    elif form.errors:
        status = 400
    ctx = {'order': order, 'group': group, 'form': form}
    template = 'dashboard/order/modal/add_variant_to_group.html'
    return TemplateResponse(request, template, ctx, status=status)
Exemple #19
0
def order_customer_edit(request, order_pk):
    order = get_object_or_404(Order.objects.drafts(), pk=order_pk)
    form = OrderCustomerForm(request.POST or None, instance=order)
    status = 200
    if form.is_valid():
        form.save()
        update_order_prices(order, request.discounts)
        user_email = form.cleaned_data.get("user_email")
        user = form.cleaned_data.get("user")
        if user_email:
            msg = (
                pgettext_lazy("Dashboard message", "%s email assigned to an order")
                % user_email
            )
        elif user:
            msg = (
                pgettext_lazy("Dashboard message", "%s user assigned to an order")
                % user
            )
        else:
            msg = pgettext_lazy("Dashboard message", "Guest user assigned to an order")
        messages.success(request, msg)
        return redirect("dashboard:order-details", order_pk=order_pk)
    elif form.errors:
        status = 400
    ctx = {"order": order, "form": form}
    return TemplateResponse(
        request, "dashboard/order/modal/edit_customer.html", ctx, status=status
    )
Exemple #20
0
def order_address(request, order_pk, address_type):
    order = get_object_or_404(Order, pk=order_pk)
    update_prices = False
    if address_type == "shipping":
        address = order.shipping_address
        success_msg = pgettext_lazy("Dashboard message", "Updated shipping address")
        update_prices = True
    else:
        address = order.billing_address
        success_msg = pgettext_lazy("Dashboard message", "Updated billing address")
    form = AddressForm(request.POST or None, instance=address)
    if form.is_valid():
        updated_address = form.save()
        if not address:
            save_address_in_order(order, updated_address, address_type)
        if update_prices:
            update_order_prices(order, request.discounts)
        if not order.is_draft():
            events.order_updated_address_event(
                order=order, user=request.user, address=address
            )
        messages.success(request, success_msg)
        return redirect("dashboard:order-details", order_pk=order_pk)
    ctx = {"order": order, "address_type": address_type, "form": form}
    return TemplateResponse(request, "dashboard/order/address_form.html", ctx)
Exemple #21
0
 def get_display(status):
     if status == VariantAvailabilityStatus.AVAILABLE:
         return pgettext_lazy("Variant status", "available")
     elif status == VariantAvailabilityStatus.OUT_OF_STOCK:
         return pgettext_lazy("Variant status", "out of stock")
     else:
         raise NotImplementedError("Unknown status: %s" % status)
Exemple #22
0
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        if self.instance.product.pk:
            self.fields["price_override"].widget.attrs[
                "placeholder"
            ] = self.instance.product.price.amount
            qs = self.instance.product.product_type.variant_attributes.all()
            self.available_attributes = qs.prefetch_related("values")
            self.prepare_fields_for_attributes()

        if include_taxes_in_prices():
            self.fields["price_override"].label = pgettext_lazy(
                "Override price", "Selling gross price override"
            )
            self.fields["cost_price"].label = pgettext_lazy(
                "Currency amount", "Cost gross price"
            )
        else:
            self.fields["price_override"].label = pgettext_lazy(
                "Override price", "Selling net price override"
            )
            self.fields["cost_price"].label = pgettext_lazy(
                "Currency amount", "Cost net price"
            )

        if not self.instance.product.product_type.is_shipping_required:
            del self.fields["weight"]
        else:
            self.fields["weight"].widget.attrs["placeholder"] = (
                getattr(self.instance.product.weight, "value", None)
                or self.instance.product.product_type.weight.value
            )
Exemple #23
0
 def smoking_status_choices(gender):
     return (
         # (__class__.SMOKING_STATUS_UNKNOWN, _("Unknown")), # ~~~~ TODO: remove this line!
         (__class__.SMOKING_STATUS_NO, pgettext_lazy(context=gender, message="No")),
         (__class__.SMOKING_STATUS_SOMETIMES, pgettext_lazy(context=gender, message="Sometimes")),
         (__class__.SMOKING_STATUS_YES, pgettext_lazy(context=gender, message="Yes")),
     )
Exemple #24
0
def product_image_edit(request, product_pk, img_pk=None):
    product = get_object_or_404(Product, pk=product_pk)
    if img_pk:
        product_image = get_object_or_404(product.images, pk=img_pk)
    else:
        product_image = ProductImage(product=product)
    show_variants = product.product_class.has_variants
    form = forms.ProductImageForm(request.POST or None, request.FILES or None,
                                  instance=product_image)
    if form.is_valid():
        product_image = form.save()
        if img_pk:
            msg = pgettext_lazy(
                'Dashboard message',
                'Updated image %s') % product_image.image.name
        else:
            msg = pgettext_lazy(
                'Dashboard message',
                'Added image %s') % product_image.image.name
        messages.success(request, msg)
        success_url = request.POST['success_url']
        if is_safe_url(success_url, request.get_host()):
            return redirect(success_url)
    ctx = {'form': form, 'product': product, 'product_image': product_image,
           'show_variants': show_variants}
    return TemplateResponse(
        request, 'dashboard/product/product_image_form.html', ctx)
Exemple #25
0
def variant_edit(request, product_pk, variant_pk=None):
    product = get_object_or_404(Product.objects.all(),
                                pk=product_pk)
    form_initial = {}
    if variant_pk:
        variant = get_object_or_404(product.variants.all(),
                                    pk=variant_pk)
    else:
        variant = ProductVariant(product=product)
    form = forms.ProductVariantForm(request.POST or None, instance=variant,
                                    initial=form_initial)
    attribute_form = forms.VariantAttributeForm(request.POST or None,
                                                instance=variant)
    if all([form.is_valid(), attribute_form.is_valid()]):
        form.save()
        attribute_form.save()
        if variant_pk:
            msg = pgettext_lazy(
                'Dashboard message',
                'Updated variant %s') % variant.name
        else:
            msg = pgettext_lazy(
                'Dashboard message',
                'Added variant %s') % variant.name
        messages.success(request, msg)
        success_url = request.POST['success_url']
        if is_safe_url(success_url, request.get_host()):
            return redirect(success_url)
    ctx = {'attribute_form': attribute_form, 'form': form, 'product': product,
           'variant': variant}
    return TemplateResponse(
        request, 'dashboard/product/variant_form.html', ctx)
Exemple #26
0
 def clean(self):
     super().clean()
     errors = []
     if self.instance.get_total_quantity() == 0:
         errors.append(
             forms.ValidationError(
                 pgettext_lazy(
                     "Create draft order form error",
                     "Could not create order without any products",
                 )
             )
         )
     if self.instance.is_shipping_required():
         method = self.instance.shipping_method
         shipping_address = self.instance.shipping_address
         shipping_not_valid = (
             method
             and shipping_address
             and shipping_address.country.code not in method.shipping_zone.countries
         )  # noqa
         if shipping_not_valid:
             errors.append(
                 forms.ValidationError(
                     pgettext_lazy(
                         "Create draft order form error",
                         "Shipping method is not valid for chosen shipping "
                         "address",
                     )
                 )
             )
     if errors:
         raise forms.ValidationError(errors)
     return self.cleaned_data
Exemple #27
0
def address_view(request, order_pk, address_type):
    order = Order.objects.get(pk=order_pk)
    if address_type == 'shipping':
        address = order.shipping_address
        success_msg = pgettext_lazy(
            'Dashboard message',
            'Updated shipping address')
    else:
        address = order.billing_address
        success_msg = pgettext_lazy(
            'Dashboard message',
            'Updated billing address')
    form = AddressForm(request.POST or None, instance=address)
    if form.is_valid():
        updated_address = form.save()
        if address is None:
            if address_type == 'shipping':
                order.shipping_address = updated_address
            else:
                order.billing_address = updated_address
            order.save()
        order.create_history_entry(content=success_msg, user=request.user)
        messages.success(request, success_msg)
        return redirect('dashboard:order-details', order_pk=order_pk)
    ctx = {'order': order, 'address_type': address_type, 'form': form}
    return TemplateResponse(request, 'dashboard/order/address_form.html', ctx)
Exemple #28
0
    def clean(self):
        data = super(ProductClassForm, self).clean()
        has_variants = self.cleaned_data['has_variants']
        product_attr = set(self.cleaned_data['product_attributes'])
        variant_attr = set(self.cleaned_data['variant_attributes'])
        if not has_variants and len(variant_attr) > 0:
            msg = pgettext_lazy(
                'Product class form error',
                'Product variants are disabled.')
            self.add_error('variant_attributes', msg)
        if len(product_attr & variant_attr) > 0:
            msg = pgettext_lazy(
                'Product class form error',
                'A single attribute can\'t belong to both a product '
                'and its variant.')
            self.add_error('variant_attributes', msg)

        if self.instance.pk:
            variants_changed = not (self.fields['has_variants'].initial ==
                                    has_variants)
            if variants_changed:
                query = self.instance.products.all()
                query = query.annotate(variants_counter=Count('variants'))
                query = query.filter(variants_counter__gt=1)
                if query.exists():
                    msg = pgettext_lazy(
                        'Product class form error',
                        'Some products of this type have more than '
                        'one variant.')
                    self.add_error('has_variants', msg)
        return data
Exemple #29
0
def fulfill_order_lines(request, order_pk):
    orders = Order.objects.confirmed().prefetch_related('lines')
    order = get_object_or_404(orders, pk=order_pk)
    unfulfilled_lines = order.lines.filter(
        quantity_fulfilled__lt=F('quantity'))
    status = 200
    form = FulfillmentForm(
        request.POST or None, order=order, instance=Fulfillment())
    FulfillmentLineFormSet = modelformset_factory(
        FulfillmentLine, form=FulfillmentLineForm,
        extra=len(unfulfilled_lines), formset=BaseFulfillmentLineFormSet)
    initial = [
        {'order_line': line, 'quantity': line.quantity_unfulfilled}
        for line in unfulfilled_lines]
    formset = FulfillmentLineFormSet(
        request.POST or None, queryset=FulfillmentLine.objects.none(),
        initial=initial)
    all_forms_valid = all([line_form.is_valid() for line_form in formset])
    if all_forms_valid and form.is_valid():
        forms_to_save = [
            line_form for line_form in formset
            if line_form.cleaned_data.get('quantity') > 0]
        if forms_to_save:
            fulfillment = form.save()
            quantity_fulfilled = 0
            for line_form in forms_to_save:
                line = line_form.save(commit=False)
                line.fulfillment = fulfillment
                line.save()
                quantity_fulfilled += line_form.cleaned_data.get('quantity')
            # update to refresh prefetched lines quantity_fulfilled
            order = orders.get(pk=order_pk)
            update_order_status(order)
            msg = npgettext_lazy(
                'Dashboard message related to an order',
                'Fulfilled %(quantity_fulfilled)d item',
                'Fulfilled %(quantity_fulfilled)d items',
                'quantity_fulfilled') % {
                    'quantity_fulfilled': quantity_fulfilled}
            order.history.create(content=msg, user=request.user)
            if form.cleaned_data.get('send_mail'):
                send_fulfillment_confirmation.delay(order.pk, fulfillment.pk)
                send_mail_msg = pgettext_lazy(
                    'Dashboard message related to an order',
                    'Shipping confirmation email was sent to user'
                    '(%(email)s)') % {'email': order.get_user_current_email()}
                order.history.create(content=send_mail_msg, user=request.user)
        else:
            msg = pgettext_lazy(
                'Dashboard message related to an order', 'No items fulfilled')
        messages.success(request, msg)
        return redirect('dashboard:order-details', order_pk=order.pk)
    elif form.errors:
        status = 400
    ctx = {
        'form': form, 'formset': formset, 'order': order,
        'unfulfilled_lines': unfulfilled_lines}
    template = 'dashboard/order/fulfillment.html'
    return TemplateResponse(request, template, ctx, status=status)
    def render(self, data, accepted_media_type=None, renderer_context=None):
        if renderer_context and isinstance(data, dict):
            data['page_size'] = renderer_context['view'].paginate_by

            # localization pager
            data['previous'] = pgettext_lazy('pager_prev', 'Prev')
            data['next'] = pgettext_lazy('pager_next', 'Next')
        return super(TicketJSONRenderer, self).render(data, accepted_media_type, renderer_context)
Exemple #31
0
 class Meta:
     verbose_name = pgettext_lazy('Category model', 'category')
     verbose_name_plural = pgettext_lazy('Category model', 'categories')
     app_label = 'product'
Exemple #32
0
 class Meta:
     permissions = (
         ('view_voucher',
          pgettext_lazy('Permission description', 'Can view vouchers')),
         ('edit_voucher',
          pgettext_lazy('Permission description', 'Can edit vouchers')))
Exemple #33
0
 class Meta:
     verbose_name = pgettext_lazy(
         'Product class model', 'product class')
     verbose_name_plural = pgettext_lazy(
         'Product class model', 'product classes')
     app_label = 'product'
Exemple #34
0
from django.conf import settings
from django.template import Library
from django.utils.translation import npgettext_lazy, pgettext_lazy
from django_prices.templatetags import prices
from prices import Money

from ...order import events
from ...order.models import OrderEvent

register = Library()

EMAIL_CHOICES = {
    events.OrderEventsEmails.PAYMENT:
    pgettext_lazy("Email type", "Payment confirmation"),
    events.OrderEventsEmails.SHIPPING:
    pgettext_lazy("Email type", "Shipping confirmation"),
    events.OrderEventsEmails.FULFILLMENT:
    pgettext_lazy("Email type", "Fulfillment confirmation"),
    events.OrderEventsEmails.ORDER:
    pgettext_lazy("Email type", "Order confirmation"),
}


def get_money_from_params(amount):
    """Retrieve the correct money amount from the given object.

    Money serialization changed at one point, as for now it's serialized
    as a dict. But we keep those settings for the legacy data.

    Can be safely removed after migrating to Dashboard 2.0
    """
Exemple #35
0
class Sale(models.Model):
    FIXED = 'fixed'
    PERCENTAGE = 'percentage'

    DISCOUNT_TYPE_CHOICES = (
        (FIXED, pgettext_lazy('Discount type', settings.DEFAULT_CURRENCY)),
        (PERCENTAGE, pgettext_lazy('Discount type', '%')))

    name = models.CharField(
        pgettext_lazy('Sale (discount) field', 'name'), max_length=255)
    type = models.CharField(
        pgettext_lazy('Sale (discount) field', 'type'),
        max_length=10, choices=DISCOUNT_TYPE_CHOICES, default=FIXED)
    value = models.DecimalField(
        pgettext_lazy('Sale (discount) field', 'value'),
        max_digits=12, decimal_places=2, default=0)
    products = models.ManyToManyField(
        'product.Product', blank=True,
        verbose_name=pgettext_lazy('Sale (discount) field', 'products'))
    categories = models.ManyToManyField(
        'product.Category', blank=True,
        verbose_name=pgettext_lazy('Sale (discount) field', 'categories'))

    class Meta:
        app_label = 'discount'
        permissions = (
            ('view_sale',
             pgettext_lazy('Permission description', 'Can view sales')),
            ('edit_sale',
             pgettext_lazy('Permission description', 'Can edit sales')))

    def __repr__(self):
        return 'Sale(name=%r, value=%r, type=%s)' % (
            str(self.name), self.value, self.get_type_display())

    def __str__(self):
        return self.name

    def get_discount(self):
        if self.type == self.FIXED:
            discount_price = Price(net=self.value,
                                   currency=settings.DEFAULT_CURRENCY)
            return FixedDiscount(amount=discount_price, name=self.name)
        elif self.type == self.PERCENTAGE:
            return percentage_discount(value=self.value, name=self.name)
        raise NotImplementedError('Unknown discount type')

    def _product_has_category_discount(self, product, discounted_categories):
        for category in product.categories.all():
            for discounted_category in discounted_categories:
                if category.is_descendant_of(discounted_category,
                                             include_self=True):
                    return True
        return False

    def modifier_for_product(self, product):
        discounted_products = {p.pk for p in self.products.all()}
        discounted_categories = set(self.categories.all())
        if product.pk in discounted_products:
            return self.get_discount()
        if self._product_has_category_discount(
                product, discounted_categories):
            return self.get_discount()
        raise NotApplicable(
            pgettext(
                'Voucher not applicable',
                'Discount not applicable for this product'))
Exemple #36
0
 class Meta:
     app_label = 'product'
     verbose_name = pgettext_lazy('Product variant model', 'product variant')
     verbose_name_plural = pgettext_lazy('Product variant model', 'product variants')
Exemple #37
0
class Product(models.Model, ItemRange, index.Indexed):
    product_class = models.ForeignKey(
        ProductClass, related_name='products',
        verbose_name=pgettext_lazy('Product field', 'product class'))
    name = models.CharField(
        pgettext_lazy('Product field', 'name'), max_length=128)
    description = models.TextField(
        verbose_name=pgettext_lazy('Product field', 'description'))
    categories = models.ManyToManyField(
        Category, verbose_name=pgettext_lazy('Product field', 'categories'),
        related_name='products')
    price = PriceField(
        pgettext_lazy('Product field', 'price'),
        currency=settings.DEFAULT_CURRENCY, max_digits=12, decimal_places=2)
    available_on = models.DateField(
        pgettext_lazy('Product field', 'available on'), blank=True, null=True)
    attributes = HStoreField(pgettext_lazy('Product field', 'attributes'),
                             default={})
    updated_at = models.DateTimeField(
        pgettext_lazy('Product field', 'updated at'), auto_now=True, null=True)
    is_featured = models.BooleanField(
        pgettext_lazy('Product field', 'is featured'), default=False)

    objects = ProductManager()

    search_fields = [
        index.SearchField('name', partial_match=True),
        index.SearchField('description'),
        index.FilterField('available_on')]

    class Meta:
        app_label = 'product'
        verbose_name = pgettext_lazy('Product model', 'product')
        verbose_name_plural = pgettext_lazy('Product model', 'products')

    def __iter__(self):
        if not hasattr(self, '__variants'):
            setattr(self, '__variants', self.variants.all())
        return iter(getattr(self, '__variants'))

    def __repr__(self):
        class_ = type(self)
        return '<%s.%s(pk=%r, name=%r)>' % (
            class_.__module__, class_.__name__, self.pk, self.name)

    def __str__(self):
        return self.name

    def get_absolute_url(self):
        return reverse('product:details', kwargs={'slug': self.get_slug(),
                                                  'product_id': self.id})

    def get_slug(self):
        return slugify(smart_text(unidecode(self.name)))

    def is_in_stock(self):
        return any(variant.is_in_stock() for variant in self)

    def get_first_category(self):
        for category in self.categories.all():
            if not category.hidden:
                return category
        return None

    def is_available(self):
        today = datetime.date.today()
        return self.available_on is None or self.available_on <= today

    def get_first_image(self):
        first_image = self.images.first()

        if first_image:
            return first_image.image
        return None

    def get_attribute(self, pk):
        return self.attributes.get(smart_text(pk))

    def set_attribute(self, pk, value_pk):
        self.attributes[smart_text(pk)] = smart_text(value_pk)

    def get_price_range(self, discounts=None,  **kwargs):
        if not self.variants.exists():
            price = calculate_discounted_price(self, self.price, discounts,
                                               **kwargs)
            return PriceRange(price, price)
        else:
            return super(Product, self).get_price_range(
                discounts=discounts, **kwargs)
Exemple #38
0
 class Meta:
     verbose_name = pgettext_lazy("Model name", "Decision")
     verbose_name_plural = pgettext_lazy("Model name", "Decisions")
Exemple #39
0
 class Meta(NameModel.Meta):
     verbose_name = pgettext_lazy("Model name", "Decision maker")
     verbose_name_plural = pgettext_lazy("Model name", "Decision makers")
Exemple #40
0
class AddToCartForm(forms.Form):
    """Add-to-cart form.

    Allows selection of a product variant and quantity.

    The save method adds it to the cart.
    """

    quantity = QuantityField(
        label=pgettext_lazy('Add to cart form field label', 'Quantity'))
    error_messages = {
        'not-available':
        pgettext_lazy('Add to cart form error',
                      'Sorry. This product is currently not available.'),
        'empty-stock':
        pgettext_lazy('Add to cart form error',
                      'Sorry. This product is currently out of stock.'),
        'variant-does-not-exists':
        pgettext_lazy('Add to cart form error',
                      'Oops. We could not find that product.'),
        'insufficient-stock':
        npgettext_lazy('Add to cart form error', 'Only %d remaining in stock.',
                       'Only %d remaining in stock.')
    }

    def __init__(self, *args, **kwargs):
        self.cart = kwargs.pop('cart')
        self.product = kwargs.pop('product')
        self.discounts = kwargs.pop('discounts', ())
        super().__init__(*args, **kwargs)

    def clean(self):
        """Clean the form.

        Makes sure the total quantity in cart (taking into account what was
        already there) does not exceed available quantity.
        """
        cleaned_data = super().clean()
        quantity = cleaned_data.get('quantity')
        if quantity is None:
            return cleaned_data
        try:
            product_variant = self.get_variant(cleaned_data)
        except ObjectDoesNotExist:
            msg = self.error_messages['variant-does-not-exists']
            self.add_error(NON_FIELD_ERRORS, msg)
        else:
            cart_line = self.cart.get_line(product_variant)
            used_quantity = cart_line.quantity if cart_line else 0
            new_quantity = quantity + used_quantity
            try:
                product_variant.check_quantity(new_quantity)
            except InsufficientStock as e:
                remaining = e.item.quantity_available - used_quantity
                if remaining:
                    msg = self.error_messages['insufficient-stock']
                    self.add_error('quantity', msg % remaining)
                else:
                    msg = self.error_messages['empty-stock']
                    self.add_error('quantity', msg)
        return cleaned_data

    def save(self):
        """Add the selected product variant and quantity to the cart."""
        product_variant = self.get_variant(self.cleaned_data)
        return self.cart.add(variant=product_variant,
                             quantity=self.cleaned_data['quantity'])

    def get_variant(self, cleaned_data):
        """Return a product variant that matches submitted values.

        This allows specialized implementations to select the variant based on
        multiple fields (like size and color) instead of having a single
        variant selection widget.
        """
        raise NotImplementedError()
Exemple #41
0
 class Meta(NameModel.Meta):
     verbose_name = pgettext_lazy("Model name", "Condition type")
     verbose_name_plural = pgettext_lazy("Model name", "Condition types")
Exemple #42
0
class Account(models.Model):

    ACCOUNT_STATUS_CHOICE = (("open", "Open"), ("close", "Close"))

    name = models.CharField(pgettext_lazy("Name of Account", "Name"), max_length=64)
    mail = models.EmailField()
    phone = PhoneNumberField(null=True)
    industry = models.CharField(
        _("Industry Type"), max_length=255, choices=INDCHOICES, blank=True, null=True
    )
    # billing_address = models.ForeignKey(
    #     Address, related_name='account_billing_address', on_delete=models.CASCADE, blank=True, null=True)
    # shipping_address = models.ForeignKey(
    #     Address, related_name='account_shipping_address', on_delete=models.CASCADE, blank=True, null=True)
    billing_address_line = models.CharField(
        _("Address"), max_length=255, blank=True, null=True
    )
    billing_street = models.CharField(_("Street"), max_length=55, blank=True, null=True)
    billing_city = models.CharField(_("City"), max_length=255, blank=True, null=True)
    billing_state = models.CharField(_("State"), max_length=255, blank=True, null=True)
    billing_postcode = models.CharField(
        _("Post/Zip-code"), max_length=64, blank=True, null=True
    )
    billing_country = models.CharField(
        max_length=3, choices=COUNTRIES, blank=True, null=True
    )
    website = models.URLField(_("Website"), blank=True, null=True)
    description = models.TextField(blank=True, null=True)
    created_by = models.ForeignKey('commons.User', on_delete=models.SET_NULL, null=True)
    created_on = models.DateTimeField(_("Created on"), auto_now_add=True)
    is_active = models.BooleanField(default=False)
    tags = models.ManyToManyField(Tag, blank=True)
    status = models.CharField(choices=ACCOUNT_STATUS_CHOICE, max_length=64, default="open")
    lead = models.ForeignKey("crm.Lead", on_delete=models.SET_NULL, null=True,)
    contact_name = models.CharField(pgettext_lazy("Name of Contact", "Contact Name"), max_length=120)
    contacts = models.ManyToManyField("crm.Contact",)
    company = models.ForeignKey('commons.Company', on_delete=models.SET_NULL, null=True, blank=True)

    def __str__(self):
        return self.name

    class Meta:
        ordering = ["-created_on"]
        app_label = "crm"


    def get_complete_address(self):
        """Concatenates complete address."""
        address = ""
        add_to_address = [
            self.billing_street,
            self.billing_city,
            self.billing_state,
            self.billing_postcode,
            self.get_billing_country_display(),
        ]
        address = append_str_to(address, *add_to_address)

        return address

    @property
    def created_on_arrow(self):
        return arrow.get(self.created_on).humanize()

    @property
    def contact_values(self):
        contacts = list(self.contacts.values_list("id", flat=True))
        return ",".join(str(contact) for contact in contacts)
Exemple #43
0
from django.db.models import Q
from django.utils.translation import npgettext, pgettext_lazy
from django_filters import CharFilter, OrderingFilter

from ...core.filters import SortedFilterSet
from ...menu.models import Menu, MenuItem

MENU_SORT_BY_FIELDS = {
    'slug': pgettext_lazy('Menu list sorting option', 'Internal name')}

MENU_ITEM_SORT_BY_FIELDS = {
    'name': pgettext_lazy('Menu item list sorting option', 'Name')}


class MenuFilter(SortedFilterSet):
    slug = CharFilter(
        label=pgettext_lazy('Menu list filter label', 'Internal name'),
        lookup_expr='icontains')
    sort_by = OrderingFilter(
        label=pgettext_lazy('Menu list filter label', 'Sort by'),
        fields=MENU_SORT_BY_FIELDS.keys(),
        field_labels=MENU_SORT_BY_FIELDS)

    class Meta:
        model = Menu
        fields = []

    def get_summary_message(self):
        counter = self.qs.count()
        return npgettext(
            'Number of matching records in the dashboard menus list',
Exemple #44
0
class LiteInstallButton(InstallButton):
    install_class = ['lite']
    button_class = ['caution']
    install_text = pgettext_lazy('install_button', u'Experimental')
Exemple #45
0
 class Meta:
     ordering = ('name', )
     verbose_name = pgettext_lazy('Product attribute model', 'product attribute')
     verbose_name_plural = pgettext_lazy('Product attribute model', 'product attributes')
 def payment_error(self, message):
     self.add_error(
         None,
         pgettext_lazy('Payment form error', 'Payment gateway error: %s') %
         message)
Exemple #47
0
class StockLocation(models.Model):
    name = models.CharField(
        pgettext_lazy('Stock location field', 'location'), max_length=100)

    def __str__(self):
        return self.name
class OrderNoteForm(forms.Form):
    message = forms.CharField(label=pgettext_lazy('Order note', 'Note'),
                              widget=forms.Textarea())
Exemple #49
0
class ProductVariant(models.Model, Item):
    sku = models.CharField(
        pgettext_lazy('Product variant field', 'SKU'), max_length=32, unique=True)
    name = models.CharField(
        pgettext_lazy('Product variant field', 'variant name'), max_length=100,
        blank=True)
    price_override = PriceField(
        pgettext_lazy('Product variant field', 'price override'),
        currency=settings.DEFAULT_CURRENCY, max_digits=12, decimal_places=2,
        blank=True, null=True)
    product = models.ForeignKey(Product, related_name='variants')
    attributes = HStoreField(
        pgettext_lazy('Product variant field', 'attributes'), default={})
    images = models.ManyToManyField(
        'ProductImage', through='VariantImage',
        verbose_name=pgettext_lazy('Product variant field', 'images'))

    class Meta:
        app_label = 'product'
        verbose_name = pgettext_lazy('Product variant model', 'product variant')
        verbose_name_plural = pgettext_lazy('Product variant model', 'product variants')

    def __str__(self):
        return self.name or self.display_variant()

    def check_quantity(self, quantity):
        available_quantity = self.get_stock_quantity()
        if quantity > available_quantity:
            raise InsufficientStock(self)

    def get_stock_quantity(self):
        if not len(self.stock.all()):
            return 0
        return max([stock.quantity_available for stock in self.stock.all()])

    def get_price_per_item(self, discounts=None, **kwargs):
        price = self.price_override or self.product.price
        price = calculate_discounted_price(self.product, price, discounts,
                                           **kwargs)
        return price

    def get_absolute_url(self):
        slug = self.product.get_slug()
        product_id = self.product.id
        return reverse('product:details',
                       kwargs={'slug': slug, 'product_id': product_id})

    def as_data(self):
        return {
            'product_name': str(self),
            'product_id': self.product.pk,
            'variant_id': self.pk,
            'unit_price': str(self.get_price_per_item().gross)}

    def is_shipping_required(self):
        return self.product.product_class.is_shipping_required

    def is_in_stock(self):
        return any(
            [stock.quantity_available > 0 for stock in self.stock.all()])

    def get_attribute(self, pk):
        return self.attributes.get(smart_text(pk))

    def set_attribute(self, pk, value_pk):
        self.attributes[smart_text(pk)] = smart_text(value_pk)

    def display_variant(self, attributes=None):
        if attributes is None:
            attributes = self.product.product_class.variant_attributes.all()
        values = get_attributes_display_map(self, attributes)
        if values:
            return ', '.join(
                ['%s: %s' % (smart_text(attributes.get(id=int(key))),
                             smart_text(value))
                 for (key, value) in six.iteritems(values)])
        else:
            return smart_text(self.sku)

    def display_product(self):
        return '%s (%s)' % (smart_text(self.product),
                            smart_text(self))

    def get_first_image(self):
        return self.product.get_first_image()

    def select_stockrecord(self, quantity=1):
        # By default selects stock with lowest cost price
        stock = filter(
            lambda stock: stock.quantity_available >= quantity,
            self.stock.all())
        stock = sorted(stock, key=lambda stock: stock.cost_price, reverse=True)
        if stock:
            return stock[0]

    def get_cost_price(self):
        stock = self.select_stockrecord()
        if stock:
            return stock.cost_price
 class Meta:
     model = Order
     fields = ['user', 'user_email']
     labels = {'user_email': pgettext_lazy('Order customer email', 'Email')}
Exemple #51
0
def display_order_event(order_event: OrderEvent):
    """Keep the backwards compatibility with the old dashboard and new type of events.

    The new one is storing enums values instead of raw messages.
    """
    event_type = order_event.type
    params = order_event.parameters
    if event_type == events.OrderEvents.PLACED_FROM_DRAFT:
        return pgettext_lazy("Dashboard message related to an order",
                             "Order placed from draft order")
    if event_type == events.OrderEvents.PAYMENT_VOIDED:
        return pgettext_lazy(
            "Dashboard message related to an order",
            "Payment was voided by %(user_name)s" %
            {"user_name": order_event.user},
        )
    if event_type == events.OrderEvents.PAYMENT_REFUNDED:
        amount = get_money_from_params(params["amount"])
        return pgettext_lazy(
            "Dashboard message related to an order",
            "Successfully refunded: %(amount)s" %
            {"amount": prices.amount(amount)},
        )
    if event_type == events.OrderEvents.PAYMENT_CAPTURED:
        amount = get_money_from_params(params["amount"])
        return pgettext_lazy(
            "Dashboard message related to an order",
            "Successfully captured: %(amount)s" %
            {"amount": prices.amount(amount)},
        )
    if event_type == events.OrderEvents.ORDER_MARKED_AS_PAID:
        return pgettext_lazy("Dashboard message related to an order",
                             "Order manually marked as paid")
    if event_type == events.OrderEvents.CANCELED:
        return pgettext_lazy("Dashboard message related to an order",
                             "Order was canceled")
    if event_type == events.OrderEvents.FULFILLMENT_RESTOCKED_ITEMS:
        return npgettext_lazy(
            "Dashboard message related to an order",
            "We restocked %(quantity)d item",
            "We restocked %(quantity)d items",
            number="quantity",
        ) % {
            "quantity": params["quantity"]
        }
    if event_type == events.OrderEvents.NOTE_ADDED:
        return pgettext_lazy(
            "Dashboard message related to an order",
            "%(user_name)s added note: %(note)s" % {
                "note": params["message"],
                "user_name": order_event.user
            },
        )
    if event_type == events.OrderEvents.FULFILLMENT_CANCELED:
        return pgettext_lazy(
            "Dashboard message",
            "Fulfillment #%(fulfillment)s canceled by %(user_name)s",
        ) % {
            "fulfillment": params["composed_id"],
            "user_name": order_event.user
        }
    if event_type == events.OrderEvents.FULFILLMENT_FULFILLED_ITEMS:
        return pgettext_lazy("Dashboard message related to an order",
                             "Fulfilled some items")
    if event_type == events.OrderEvents.PLACED:
        return pgettext_lazy("Dashboard message related to an order",
                             "Order was placed")
    if event_type == events.OrderEvents.ORDER_FULLY_PAID:
        return pgettext_lazy("Dashboard message related to an order",
                             "Order was fully paid")
    if event_type == events.OrderEvents.EMAIL_SENT:
        return pgettext_lazy(
            "Dashboard message related to an order",
            "%(email_type)s email was sent to the customer "
            "(%(email)s)",
        ) % {
            "email_type": EMAIL_CHOICES[params["email_type"]],
            "email": params["email"],
        }
    if event_type == events.OrderEvents.TRACKING_UPDATED:
        return pgettext_lazy(
            "Dashboard message related to an order",
            "Fulfillment #%(fulfillment)s tracking was updated to"
            " %(tracking_number)s by %(user_name)s",
        ) % {
            "fulfillment": params["composed_id"],
            "tracking_number": params["tracking_number"],
            "user_name": order_event.user,
        }
    if event_type == events.OrderEvents.DRAFT_CREATED:
        return pgettext_lazy(
            "Dashboard message related to an order",
            "The draft was created by %(user_name)s",
        ) % {
            "user_name": order_event.user
        }
    if event_type == events.OrderEvents.DRAFT_ADDED_PRODUCTS:
        return pgettext_lazy("Dashboard message related to an order",
                             "%(user_name)s added some products") % {
                                 "user_name": order_event.user
                             }
    if event_type == events.OrderEvents.DRAFT_REMOVED_PRODUCTS:
        return pgettext_lazy(
            "Dashboard message related to an order",
            "%(user_name)s removed some products",
        ) % {
            "user_name": order_event.user
        }
    if event_type == events.OrderEvents.OVERSOLD_ITEMS:
        return pgettext_lazy(
            "Dashboard message related to an order",
            "%(user_name)s placed the order by bypassing oversold items",
        ) % {
            "user_name": order_event.user
        }
    if event_type == events.OrderEvents.UPDATED_ADDRESS:
        return pgettext_lazy(
            "Dashboard message related to an order",
            "The order address was updated by %(user_name)s",
        ) % {
            "user_name": order_event.user
        }
    if event_type == events.OrderEvents.PAYMENT_FAILED:
        return pgettext_lazy(
            "Dashboard message related to an order",
            "The payment was failed by %(user_name)s",
        ) % {
            "user_name": order_event.user
        }

    if event_type == events.OrderEvents.OTHER:
        return order_event.parameters["message"]
    raise ValueError("Not supported event type: %s" % (event_type))
            raise forms.ValidationError(
                pgettext_lazy('Remove voucher form error',
                              'This order has no voucher'))
        return data

    def remove_voucher(self):
        decrease_voucher_usage(self.instance.voucher)
        self.instance.discount_amount = 0
        self.instance.discount_name = ''
        self.instance.translated_discount_name = ''
        self.instance.voucher = None
        recalculate_order(self.instance)


PAYMENT_STATUS_CHOICES = (
    [('', pgettext_lazy('Payment status field value', 'All'))] +
    ChargeStatus.CHOICES)


class PaymentFilterForm(forms.Form):
    status = forms.ChoiceField(choices=PAYMENT_STATUS_CHOICES)


class AddVariantToOrderForm(forms.Form):
    """Allow adding lines with given quantity to an order."""

    variant = AjaxSelect2ChoiceField(
        queryset=ProductVariant.objects.filter(
            product__in=Product.objects.published()),
        fetch_data_url=reverse_lazy('dashboard:ajax-available-variants'),
        label=pgettext_lazy(
Exemple #53
0
class Voucher(models.Model):

    APPLY_TO_ONE_PRODUCT = 'one'
    APPLY_TO_ALL_PRODUCTS = 'all'

    APPLY_TO_PRODUCT_CHOICES = [
        (
            APPLY_TO_ONE_PRODUCT,
            pgettext_lazy('Voucher application', 'Apply to a single item')),
        (
            APPLY_TO_ALL_PRODUCTS,
            pgettext_lazy(
                'Voucher application', 'Apply to all matching products'))]

    DISCOUNT_VALUE_FIXED = 'fixed'
    DISCOUNT_VALUE_PERCENTAGE = 'percentage'

    DISCOUNT_VALUE_TYPE_CHOICES = [
        (
            DISCOUNT_VALUE_FIXED,
            pgettext_lazy('Voucher discount type', settings.DEFAULT_CURRENCY)),
        (
            DISCOUNT_VALUE_PERCENTAGE,
            pgettext_lazy('Voucher discount type', '%'))]

    PRODUCT_TYPE = 'product'
    CATEGORY_TYPE = 'category'
    SHIPPING_TYPE = 'shipping'
    VALUE_TYPE = 'value'

    TYPE_CHOICES = [
        (VALUE_TYPE, pgettext_lazy('Voucher: discount for', 'All purchases')),
        (PRODUCT_TYPE, pgettext_lazy('Voucher: discount for', 'One product')),
        (
            CATEGORY_TYPE,
            pgettext_lazy('Voucher: discount for', 'A category of products')),
        (SHIPPING_TYPE, pgettext_lazy('Voucher: discount for', 'Shipping'))]

    type = models.CharField(
        pgettext_lazy('Voucher field', 'discount for'), max_length=20,
        choices=TYPE_CHOICES, default=VALUE_TYPE)
    name = models.CharField(
        pgettext_lazy('Voucher field', 'name'), max_length=255, null=True,
        blank=True)
    code = models.CharField(
        pgettext_lazy('Voucher field', 'code'), max_length=12, unique=True,
        db_index=True)
    usage_limit = models.PositiveIntegerField(
        pgettext_lazy('Voucher field', 'usage limit'), null=True, blank=True)
    used = models.PositiveIntegerField(default=0, editable=False)
    start_date = models.DateField(
        pgettext_lazy('Voucher field', 'start date'), default=date.today)
    end_date = models.DateField(
        pgettext_lazy('Voucher field', 'end date'), null=True, blank=True)

    discount_value_type = models.CharField(
        pgettext_lazy('Voucher field', 'discount type'), max_length=10,
        choices=DISCOUNT_VALUE_TYPE_CHOICES, default=DISCOUNT_VALUE_FIXED)
    discount_value = models.DecimalField(
        pgettext_lazy('Voucher field', 'discount value'), max_digits=12,
        decimal_places=2)

    # not mandatory fields, usage depends on type
    product = models.ForeignKey(
        'product.Product', blank=True, null=True,
        verbose_name=pgettext_lazy('Voucher field', 'product'),
        on_delete=models.CASCADE)
    category = models.ForeignKey(
        'product.Category', blank=True, null=True,
        verbose_name=pgettext_lazy('Voucher field', 'category'),
        on_delete=models.CASCADE)
    apply_to = models.CharField(
        pgettext_lazy('Voucher field', 'apply to'),
        max_length=20, blank=True, null=True)
    limit = PriceField(
        pgettext_lazy('Voucher field', 'limit'),
        max_digits=12, decimal_places=2, null=True,
        blank=True, currency=settings.DEFAULT_CURRENCY)

    objects = VoucherQueryset.as_manager()

    @property
    def is_free(self):
        return (self.discount_value == Decimal(100) and
                self.discount_value_type == Voucher.DISCOUNT_VALUE_PERCENTAGE)

    class Meta:
        permissions = (
            ('view_voucher',
             pgettext_lazy('Permission description', 'Can view vouchers')),
            ('edit_voucher',
             pgettext_lazy('Permission description', 'Can edit vouchers')))

    def __str__(self):
        if self.name:
            return self.name
        discount = '%s %s' % (
            self.discount_value, self.get_discount_value_type_display())
        if self.type == Voucher.SHIPPING_TYPE:
            if self.is_free:
                return pgettext('Voucher type', 'Free shipping')
            else:
                return pgettext(
                    'Voucher type',
                    '%(discount)s off shipping') % {'discount': discount}
        if self.type == Voucher.PRODUCT_TYPE:
            return pgettext(
                'Voucher type',
                '%(discount)s off %(product)s') % {
                    'discount': discount, 'product': self.product}
        if self.type == Voucher.CATEGORY_TYPE:
            return pgettext(
                'Voucher type',
                '%(discount)s off %(category)s') % {
                    'discount': discount, 'category': self.category}
        return pgettext(
            'Voucher type', '%(discount)s off') % {'discount': discount}

    def get_apply_to_display(self):
        if self.type == Voucher.SHIPPING_TYPE and self.apply_to:
            return countries.name(self.apply_to)
        if self.type == Voucher.SHIPPING_TYPE:
            return pgettext('Voucher', 'Any country')
        if self.apply_to and self.type in {
                Voucher.PRODUCT_TYPE, Voucher.CATEGORY_TYPE}:
            choices = dict(self.APPLY_TO_PRODUCT_CHOICES)
            return choices[self.apply_to]

    def get_fixed_discount_for(self, amount):
        if self.discount_value_type == self.DISCOUNT_VALUE_FIXED:
            discount_price = Price(net=self.discount_value,
                                   currency=settings.DEFAULT_CURRENCY)
            discount = FixedDiscount(
                amount=discount_price, name=smart_text(self))
        elif self.discount_value_type == self.DISCOUNT_VALUE_PERCENTAGE:
            discount = percentage_discount(
                value=self.discount_value, name=smart_text(self))
            fixed_discount_value = amount - discount.apply(amount)
            discount = FixedDiscount(
                amount=fixed_discount_value, name=smart_text(self))
        else:
            raise NotImplementedError('Unknown discount value type')
        if discount.amount > amount:
            return FixedDiscount(amount, name=smart_text(self))
        else:
            return discount

    def validate_limit(self, value):
        limit = self.limit if self.limit is not None else value
        if value < limit:
            msg = pgettext(
                'Voucher not applicable',
                'This offer is only valid for orders over %(amount)s.')
            raise NotApplicable(msg % {'amount': net(limit)}, limit=limit)

    def get_discount_for_checkout(self, checkout):
        if self.type == Voucher.VALUE_TYPE:
            cart_total = checkout.get_subtotal()
            self.validate_limit(cart_total)
            return self.get_fixed_discount_for(cart_total)

        elif self.type == Voucher.SHIPPING_TYPE:
            if not checkout.is_shipping_required:
                msg = pgettext(
                    'Voucher not applicable',
                    'Your order does not require shipping.')
                raise NotApplicable(msg)
            shipping_method = checkout.shipping_method
            if not shipping_method:
                msg = pgettext(
                    'Voucher not applicable',
                    'Please select a shipping method first.')
                raise NotApplicable(msg)
            if (self.apply_to and
                    shipping_method.country_code != self.apply_to):
                msg = pgettext(
                    'Voucher not applicable',
                    'This offer is only valid in %(country)s.')
                raise NotApplicable(
                    msg % {'country': self.get_apply_to_display()})
            cart_total = checkout.get_subtotal()
            self.validate_limit(cart_total)
            return self.get_fixed_discount_for(shipping_method.price)

        elif self.type in (Voucher.PRODUCT_TYPE, Voucher.CATEGORY_TYPE):
            if self.type == Voucher.PRODUCT_TYPE:
                prices = list(
                    (item[1] for item in get_product_variants_and_prices(
                        checkout.cart, self.product)))
            else:
                prices = list(
                    (item[1] for item in get_category_variants_and_prices(
                        checkout.cart, self.category)))
            if len(prices) == 0:
                msg = pgettext(
                    'Voucher not applicable',
                    'This offer is only valid for selected items.')
                raise NotApplicable(msg)
            if self.apply_to == Voucher.APPLY_TO_ALL_PRODUCTS:
                discounts = (
                    self.get_fixed_discount_for(price) for price in prices)
                discount_total = sum(
                    (discount.amount for discount in discounts),
                    Price(0, currency=settings.DEFAULT_CURRENCY))
                return FixedDiscount(discount_total, smart_text(self))
            else:
                product_total = sum(
                    prices, Price(0, currency=settings.DEFAULT_CURRENCY))
                return self.get_fixed_discount_for(product_total)

        else:
            raise NotImplementedError('Unknown discount type')
Exemple #54
0
class Paginator(DjangoPaginator):
    template_name = 'django_listing/paginator.html'
    has_page_info = True
    has_editable_page_info = False
    page_info_tpl = pgettext_lazy('paginator',
                                  'Page {page_number} of {nb_pages}')
    has_row_info = False
    row_info_tpl = pgettext_lazy('paginator',
                                 '{row_first}-{row_last} of {nb_rows}')
    has_prev_next = True
    prev_text = pgettext_lazy('paginator', 'Previous')
    next_text = pgettext_lazy('paginator', 'Next')
    has_first_last = False
    first_text = pgettext_lazy('paginator', 'First')
    last_text = pgettext_lazy('paginator', 'Last')
    fast_page_step = 0
    fast_page_prev_tpl = '-{step}'
    fast_page_next_tpl = '+{step}'
    page_scale_size = 0
    page_scale_ellipsis = 0
    hide_disabled_buttons = False
    hide_single_page = False
    parts_order = ('first,fastprev,prev,pageinfo,rowinfo,scale,'
                   'next,fastnext,last;gotopage')
    has_goto_page = False
    goto_page_tpl = pgettext_lazy('paginator', 'Go to page {goto_form}')
    in_footer = False

    theme_first_last_has_icon = True
    theme_first_last_has_text = True
    theme_first_icon = 'listing-icon-to-start-1'
    theme_last_icon = 'listing-icon-to-end-1'
    theme_fast_page_has_icon = True
    theme_fast_page_has_text = True
    theme_fast_prev_icon = 'listing-icon-fast-backward'
    theme_fast_next_icon = 'listing-icon-fast-forward'
    theme_prev_next_has_icon = True
    theme_prev_next_has_text = True
    theme_prev_icon = 'listing-icon-left-dir'
    theme_next_icon = 'listing-icon-right-dir'
    theme_button_a_class = 'page-link'
    theme_button_li_class = 'page-item'
    theme_button_text_class = 'button-text'

    def __init__(self,
                 listing,
                 object_list,
                 per_page,
                 orphans=0,
                 allow_empty_first_page=True):
        super().__init__(object_list, per_page, orphans,
                         allow_empty_first_page)
        self.listing = listing
        for k in PAGINATOR_PARAMS_KEYS:
            listing_key = 'paginator_' + k
            if hasattr(listing, listing_key):
                setattr(self, k, getattr(listing, listing_key))
        if isinstance(self.parts_order, str):
            self.parts_order = self.parts_order.replace(' ', '')
            self.parts_order = list(
                map(lambda s: s.split(','), self.parts_order.split(';')))

    def get_context(self):
        get_url = self.listing.get_url
        page = self.listing.current_page
        fast_page_prev = max(1, page.number - self.fast_page_step)
        fast_page_next = min(self.num_pages, page.number + self.fast_page_step)
        beginning_ellipsis_pages = []
        beginning_ellipsis_display = True
        ending_ellipsis_pages = []
        ending_ellipsis_display = True
        if self.page_scale_size:
            scale_range_min = max(1,
                                  page.number - int(self.page_scale_size / 2))
            scale_range_max = min(scale_range_min + self.page_scale_size - 1,
                                  self.num_pages)

            if self.page_scale_ellipsis:
                beginning_ellipsis_pages = [
                    (p, get_url(page=p), p == page.number) for p in range(
                        1, min(self.page_scale_ellipsis + 1, scale_range_min))
                ]
                if scale_range_min < self.page_scale_ellipsis + 2:
                    beginning_ellipsis_display = False
                if scale_range_max == self.num_pages:
                    scale_range_min = max(
                        1, self.num_pages - self.page_scale_size)
                else:
                    ending_ellipsis_range_min = max(
                        scale_range_max + 1,
                        self.num_pages - self.page_scale_ellipsis + 1)
                    if ending_ellipsis_range_min == scale_range_max + 1:
                        beginning_ellipsis_display = False
                    ending_ellipsis_range_max = self.num_pages
                    ending_ellipsis_pages = [
                        (p, get_url(page=p), p == page.number)
                        for p in range(ending_ellipsis_range_min,
                                       ending_ellipsis_range_max + 1)
                    ]

            scale_pages = [(p, get_url(page=p), p == page.number)
                           for p in range(scale_range_min, scale_range_max + 1)
                           ]
        else:
            scale_pages = []

        if self.has_editable_page_info or self.has_goto_page:
            goto_form = '<form class="goto-page">'
            goto_form += self.listing.get_hiddens_html(without='page')
            goto_form += ('<input type="text" '
                          'value="{val}" '
                          'name="page{suffix}">'.format(
                              val=page.number, suffix=self.listing.suffix))
            goto_form += '</form>'
        else:
            goto_form = ''

        if self.has_editable_page_info:
            page_number = goto_form
        else:
            page_number = page.number

        nb_page_rows = page.end_index() - page.start_index() + 1

        return RenderContext(
            first_page_url=get_url(page=1),
            last_page_url=get_url(page=self.num_pages),
            prev_page_url=get_url(
                page=page.previous_page_number() if page.number > 1 else 1),
            next_page_url=get_url(page=page.next_page_number(
            ) if page.number < self.num_pages else self.num_pages),
            fast_page_prev_url=get_url(page=fast_page_prev),
            fast_page_next_url=get_url(page=fast_page_next),
            fast_page_prev_text=self.fast_page_prev_tpl.format(
                step=self.fast_page_step),
            fast_page_next_text=self.fast_page_next_tpl.format(
                step=self.fast_page_step),
            scale_pages=scale_pages,
            beginning_ellipsis_pages=beginning_ellipsis_pages,
            beginning_ellipsis_display=beginning_ellipsis_display,
            ending_ellipsis_pages=ending_ellipsis_pages,
            ending_ellipsis_display=ending_ellipsis_display,
            nb_page_rows=nb_page_rows,
            page_info=self.page_info_tpl.format(page_number=page_number,
                                                nb_pages=self.num_pages),
            row_info=self.row_info_tpl.format(row_first=page.start_index(),
                                              row_last=page.end_index(),
                                              nb_rows=self.count,
                                              nb_page_rows=nb_page_rows),
            goto_page=self.goto_page_tpl.format(goto_form=goto_form),
            show_paginator_single_page=(self.num_pages > 1
                                        or not self.hide_single_page),
            current_page=page,
            paginator=self,
        )
Exemple #55
0
from django import forms
from django.utils.translation import npgettext, pgettext_lazy
from django_filters import (
    CharFilter,
    ChoiceFilter,
    ModelMultipleChoiceFilter,
    OrderingFilter,
    RangeFilter,
)

from ...core.filters import SortedFilterSet
from ...product.models import Attribute, Category, Product, ProductType
from ..widgets import MoneyRangeWidget

PRODUCT_SORT_BY_FIELDS = {
    "name": pgettext_lazy("Product list sorting option", "name"),
    "price_amount": pgettext_lazy("Product type list sorting option", "price"),
}

PRODUCT_ATTRIBUTE_SORT_BY_FIELDS = {
    "name": pgettext_lazy("Product attribute list sorting option", "name")
}

PRODUCT_TYPE_SORT_BY_FIELDS = {
    "name": pgettext_lazy("Product type list sorting option", "name")
}

PUBLISHED_CHOICES = (
    ("1", pgettext_lazy("Is publish filter choice", "Published")),
    ("0", pgettext_lazy("Is publish filter choice", "Not published")),
)
Exemple #56
0
class AddToCheckoutForm(forms.Form):
    """Add-to-checkout form.

    Allows selection of a product variant and quantity.

    The save method adds it to the checkout.
    """

    quantity = QuantityField(
        label=pgettext_lazy("Add to checkout form field label", "Quantity")
    )
    error_messages = {
        "not-available": pgettext_lazy(
            "Add to checkout form error",
            "Sorry. This product is currently not available.",
        ),
        "empty-stock": pgettext_lazy(
            "Add to checkout form error",
            "Sorry. This product is currently out of stock.",
        ),
        "variant-too-many-in-checkout": pgettext_lazy(
            "Add to checkout form error",
            "Sorry. You can't add more than %d times this item.",
        ),
        "variant-does-not-exists": pgettext_lazy(
            "Add to checkout form error", "Oops. We could not find that product."
        ),
        "insufficient-stock": npgettext_lazy(
            "Add to checkout form error",
            "Only %d remaining in stock.",
            "Only %d remaining in stock.",
        ),
    }

    def __init__(self, *args, **kwargs):
        self.checkout = kwargs.pop("checkout")
        self.product = kwargs.pop("product")
        self.discounts = kwargs.pop("discounts", ())
        self.country = kwargs.pop("country", {})
        self.extensions = kwargs.pop("extensions", None)
        super().__init__(*args, **kwargs)

    def add_error_i18n(self, field, error_name, fmt: Any = tuple()):
        self.add_error(field, self.error_messages[error_name] % fmt)

    def clean(self):
        """Clean the form.

        Makes sure the total quantity in checkout (taking into account what was
        already there) does not exceed available quantity.
        """
        cleaned_data = super().clean()
        quantity = cleaned_data.get("quantity")
        if quantity is None:
            return cleaned_data
        variant = self.get_variant(cleaned_data)
        if variant is None:
            self.add_error_i18n(NON_FIELD_ERRORS, "variant-does-not-exists")
        else:
            line = self.checkout.get_line(variant)
            used_quantity = line.quantity if line else 0
            new_quantity = quantity + used_quantity

            if new_quantity > settings.MAX_CHECKOUT_LINE_QUANTITY:
                self.add_error_i18n(
                    "quantity",
                    "variant-too-many-in-checkout",
                    settings.MAX_CHECKOUT_LINE_QUANTITY,
                )
                return cleaned_data

            try:
                variant.check_quantity(new_quantity)
            except InsufficientStock as e:
                remaining = e.item.quantity_available - used_quantity
                if remaining:
                    self.add_error_i18n("quantity", "insufficient-stock", remaining)
                else:
                    self.add_error_i18n("quantity", "empty-stock")
        return cleaned_data

    def save(self):
        """Add the selected product variant and quantity to the checkout."""
        from .utils import add_variant_to_checkout

        variant = self.get_variant(self.cleaned_data)
        quantity = self.cleaned_data["quantity"]
        add_variant_to_checkout(self.checkout, variant, quantity)

    def get_variant(self, cleaned_data):
        """Return a product variant that matches submitted values.

        This allows specialized implementations to select the variant based on
        multiple fields (like size and color) instead of having a single
        variant selection widget.
        """
        raise NotImplementedError()
Exemple #57
0
 class Meta:
     ordering = ("-last_change", )
     permissions = ((
         CheckoutPermissions.MANAGE_CHECKOUTS.codename,
         pgettext_lazy("Permission description", "Manage checkouts"),
     ), )
Exemple #58
0
    return _("Not available")


def get_keyname(instance):
    if hasattr(instance, "key_name"):
        keyname = instance.key_name
        return keyname
    return _("Not available")


def get_power_state(instance):
    return POWER_STATES.get(getattr(instance, "OS-EXT-STS:power_state", 0), '')


STATUS_DISPLAY_CHOICES = (
    ("deleted", pgettext_lazy("Current status of an Instance", u"Deleted")),
    ("active", pgettext_lazy("Current status of an Instance", u"Active")),
    ("shutoff", pgettext_lazy("Current status of an Instance", u"Shutoff")),
    ("suspended", pgettext_lazy("Current status of an Instance",
                                u"Suspended")),
    ("paused", pgettext_lazy("Current status of an Instance", u"Paused")),
    ("error", pgettext_lazy("Current status of an Instance", u"Error")),
    ("resize", pgettext_lazy("Current status of an Instance",
                             u"Resize/Migrate")),
    ("verify_resize", pgettext_lazy("Current status of an Instance",
                                    u"Confirm or Revert Resize/Migrate")),
    ("revert_resize", pgettext_lazy(
        "Current status of an Instance", u"Revert Resize/Migrate")),
    ("reboot", pgettext_lazy("Current status of an Instance", u"Reboot")),
    ("hard_reboot", pgettext_lazy("Current status of an Instance",
                                  u"Hard Reboot")),
Exemple #59
0
class EditNetwork(policy.PolicyTargetMixin, tables.LinkAction):
    name = "update"
    verbose_name = _("Edit Network")
    url = "horizon:admin:networks:update"
    classes = ("ajax-modal", )
    icon = "pencil"
    policy_rules = (("network", "update_network"), )


# def _get_subnets(network):
#    cidrs = [subnet.get('cidr') for subnet in network.subnets]
#    return ','.join(cidrs)

DISPLAY_CHOICES = (
    ("UP", pgettext_lazy("Admin state of a Network", u"UP")),
    ("DOWN", pgettext_lazy("Admin state of a Network", u"DOWN")),
)


class NetworksTable(tables.DataTable):
    tenant = tables.Column("tenant_name", verbose_name=_("Project"))
    name = tables.Column("name_or_id",
                         verbose_name=_("Network Name"),
                         link='horizon:admin:networks:detail')
    subnets = tables.Column(
        project_tables.get_subnets,
        verbose_name=_("Subnets Associated"),
    )
    num_agents = tables.Column("num_agents", verbose_name=_("DHCP Agents"))
    shared = tables.Column("shared",
Exemple #60
0
class UnreviewedInstallButton(InstallButton):
    install_class = ['unreviewed']
    install_text = pgettext_lazy('install_button', u'Not Reviewed')
    button_class = 'download caution'.split()