class ProductListView(PicotableListView): model = Product columns = [ Column("sku", _(u"SKU"), display="sku", filter_config=TextFilter(placeholder=_("Filter by SKU..."))), Column("name", _(u"Name"), sort_field="translations__name", display="name", filter_config=TextFilter(filter_field="translations__name", placeholder=_("Filter by name..."))), Column("type", _(u"Type")), Column("category", _(u"Primary Category")), ] def get_queryset(self): return Product.objects.all_except_deleted() def get_object_abstract(self, instance, item): return [{ "text": "%s" % instance, "class": "header" }, { "title": _(u"SKU"), "text": item["sku"] }, { "title": _(u"Type"), "text": item["type"] }, { "title": _(u"Primary Category"), "text": item["category"] }]
class ProductListView(PicotableListView): model = Product columns = [ Column("sku", _(u"SKU"), display="sku", filter_config=TextFilter(placeholder=_("Filter by SKU..."))), Column("name", _(u"Name"), sort_field="translations__name", display="name", filter_config=TextFilter( filter_field="translations__name", placeholder=_("Filter by name...") )), Column("barcode", _(u"Barcode"), display="barcode", filter_config=TextFilter(_("Filter by barcode..."))), Column("type", _(u"Type")), Column("mode", _(u"Mode"), filter_config=ChoicesFilter(ProductMode.choices)), Column("category", _(u"Primary Category")), ] def get_queryset(self): filter = self.get_filter() shop_id = filter.get("shop") qs = Product.objects.all_except_deleted() q = Q() for mode in filter.get("modes", []): q = q | Q(mode=mode) qs = qs.filter(q) if shop_id: qs = qs.filter(shop_products__shop_id=int(shop_id)) return qs def get_object_abstract(self, instance, item): return [ {"text": "%s" % instance, "class": "header"}, {"title": _(u"Barcode"), "text": item["barcode"]}, {"title": _(u"SKU"), "text": item["sku"]}, {"title": _(u"Type"), "text": item["type"]}, {"title": _(u"Primary Category"), "text": item["category"]} ]
class AttributeListView(PicotableListView): model = Attribute columns = [ Column("identifier", _("Identifier"), filter_config=TextFilter( filter_field="identifier", placeholder=_("Filter by identifier..."))), Column("name", _("Name"), sort_field="translations__name", display="name", filter_config=TextFilter(filter_field="translations__name", placeholder=_("Filter by name..."))), Column("type", _("Type"), filter_config=ChoicesFilter(AttributeType.choices)), Column("visibility_mode", _("Visibility Mode"), filter_config=ChoicesFilter(AttributeVisibility.choices)), Column("searchable", _("Searchable")), Column("n_product_types", _("Used in # Product Types")), ] def get_queryset(self): return Attribute.objects.all().annotate( n_product_types=Count("product_types")) def get_object_abstract(self, instance, item): return [ { "text": "%s" % instance, "class": "header" }, ]
class ContactListView(PicotableListView): model = Contact columns = [ Column("name", _(u"Name"), linked=True, filter_config=TextFilter()), Column("type", _(u"Type"), display="get_type_display", sortable=False), # TODO: Add a filter Column("email", _(u"Email"), filter_config=TextFilter()), Column("phone", _(u"Phone"), filter_config=TextFilter()), Column("is_active", _(u"Active"), filter_config=ChoicesFilter([(False, _("no")), (True, _("yes"))], default=True)), Column("n_orders", _(u"# Orders"), class_name="text-right", filter_config=RangeFilter(step=1)), Column("groups", _("Groups"), filter_config=ChoicesFilter(ContactGroup.objects.all(), "groups")) ] def get_queryset(self): groups = self.get_filter().get("groups") query = Q(groups__in=groups) if groups else Q() return super(ContactListView, self).get_queryset().filter(query).annotate( n_orders=Count("customer_orders")) def get_type_display(self, instance): if isinstance(instance, PersonContact): return _(u"Person") elif isinstance(instance, CompanyContact): return _(u"Company") else: return _(u"Contact") def get_object_abstract(self, instance, item): """ :type instance: shoop.core.models.Contact """ bits = filter(None, [ item["type"], _("Active") if instance.is_active else _("Inactive"), _("Email: %s") % (instance.email or "\u2014"), _("Phone: %s") % (instance.phone or "\u2014"), _("%d orders") % instance.n_orders, ]) return [{ "text": instance.name or _("Contact"), "class": "header" }, { "text": ", ".join(bits) }]
class ReservationsAdminList(PicotableListView): model = Reservation columns = [ Column("id", _("ID"), sort_field="id", display="id", filter_config=TextFilter()), Column("name", _("Name"), sort_field="reservable__product__translations__name", display="reservable__product__name", filter_config=TextFilter( filter_field="reservable__product__translations__name")), Column("order", _("From Order"), sort_field="order_line__order", display="order_line__order", filter_config=TextFilter(filter_field="order_line__order__id")), Column("start_time", _("Sign In Time"), sort_field="start_time", display="format_start_time", filter_config=DateRangeFilter(filter_field="start_time")), Column("end_time", _("Sign Out Time"), sort_field="end_time", display="format_end_time", filter_config=DateRangeFilter(filter_field="end_time")), Column("persons", _("Persons"), display="persons"), ] def format_start_time(self, instance, *args, **kwargs): return format_datetime(localtime(instance.start_time), locale=get_current_babel_locale()) def format_end_time(self, instance, *args, **kwargs): return format_datetime(localtime(instance.end_time), locale=get_current_babel_locale()) def get_toolbar(self): toolbar = super(ReservationsAdminList, self).get_toolbar() toolbar.append( URLActionButton( text=_("New Reservation"), icon="fa fa-calendar", url=reverse("shoop_admin:reservations.new"), )) return toolbar def get_object_url(self, instance): return reverse("shoop_admin:reservations.edit", kwargs={"pk": instance.id})
class OrderListView(PicotableListView): model = Order columns = [ Column("identifier", _(u"Order"), linked=True, filter_config=TextFilter(operator="startswith")), Column("order_date", _(u"Order Date"), display="format_order_date", filter_config=DateRangeFilter()), Column( "customer", _(u"Customer"), filter_config=MultiFieldTextFilter(filter_fields=("customer__email", "customer__name")) ), Column("status", _(u"Status"), filter_config=ChoicesFilter(choices=OrderStatus.objects.all())), Column("payment_status", _(u"Payment Status"), filter_config=ChoicesFilter(choices=PaymentStatus.choices)), Column("shipping_status", _(u"Shipping Status"), filter_config=ChoicesFilter(choices=ShippingStatus.choices)), Column( "taxful_total_price", _(u"Total"), display="format_taxful_total_price", class_name="text-right", filter_config=RangeFilter(field_type="number") ), ] def get_queryset(self): return super(OrderListView, self).get_queryset().exclude(deleted=True) def format_order_date(self, instance, *args, **kwargs): return format_datetime(localtime(instance.order_date), locale=get_current_babel_locale()) def format_taxful_total_price(self, instance, *args, **kwargs): return escape(format_home_currency(instance.taxful_total_price)) def get_object_abstract(self, instance, item): return [ {"text": "%s" % instance, "class": "header"}, {"title": _(u"Total"), "text": item["taxful_total_price"]}, {"title": _(u"Status"), "text": item["status"]} ]
class CategoryListView(PicotableListView): model = Category columns = [ Column("name", _(u"Name"), sort_field="translations__name", display="name", linked=True, filter_config=TextFilter(filter_field="translations__name", placeholder=_("Filter by name..."))), Column("status", _(u"Status"), filter_config=ChoicesFilter(choices=CategoryStatus.choices)), Column( "visibility", _(u"Visibility"), filter_config=ChoicesFilter(choices=CategoryVisibility.choices)), Column("parent", _(u"Parent"), sortable=False, display="parent"), ] def get_queryset(self): return Category.objects.all_except_deleted() def get_object_abstract(self, instance, item): return [{ "text": "%s" % instance, "class": "header" }, { "title": _(u"Status"), "text": item["status"] }, { "title": _(u"Parent"), "text": item["parent"] } if instance.parent_id else None]
class ScriptListView(PicotableListView): model = Script columns = [ Column("name", _(u"Name"), linked=True, filter_config=TextFilter(operator="startswith")), Column("event_identifier", _(u"Event"), display="get_event_identifier_text"), Column("enabled", _(u"Enabled")), ] def get_object_url(self, instance): return reverse("shoop_admin:notify.script.edit", kwargs={"pk": instance.pk}) def get_event_identifier_text(self, instance): if not hasattr(self, "_event_identifier_names"): self._event_identifier_names = dict(get_name_map("notify_event")) return self._event_identifier_names.get(instance.event_identifier, instance.event_identifier) def get_toolbar(self): return Toolbar([ URLActionButton( text="New Script", icon="fa fa-plus", extra_css_class="btn-success", url=reverse("shoop_admin:notify.script.new") ) ]) def get_object_abstract(self, instance, item): return [ {"text": "%s" % instance, "class": "header"}, {"title": _(u"Event"), "text": item["event_identifier"]}, {"title": _(u"Enabled"), "text": item["enabled"]} ]
def get_pico(rf, model=None, columns=None): model = model or get_user_model() columns = columns or [ Column("id", "Id", filter_config=Filter(), display=instance_id), Column("username", "Username", sortable=False, filter_config=MultiFieldTextFilter( filter_fields=("username", "email"), operator="iregex")), Column("email", "Email", sortable=False, filter_config=TextFilter()), Column("is_superuser", "Is Superuser", display="superuser_display", filter_config=ChoicesFilter(choices=false_and_true())), Column("is_active", "Is Active", filter_config=ChoicesFilter( choices=false_and_true)), # `choices` callable Column("date_joined", "Date Joined", filter_config=DateRangeFilter()) ] return Picotable(request=rf.get("/"), columns=columns, queryset=model.objects.all(), context=PicoContext())
def test_picotable_correctly_sorts_translated_fields(rf, admin_user, regular_user): """ Make sure that translated fields, such as product names, are correctly sorted """ populate_if_required() columns = [ Column("id", "Id", filter_config=Filter(), display=instance_id), Column("name", "Name", sort_field="translations__name", filter_config=TextFilter(filter_field="translations__name")), ] pico = get_pico(rf, model=Product, columns=columns) # Verify ascending sort sorted_products = pico.get_data({ "perPage": 100, "page": 1, "sort": "+name" }) sorted_names = [p["name"] for p in sorted_products["items"]] assert sorted_names == sorted(sorted_names) # Verify descending sort sorted_products = pico.get_data({ "perPage": 100, "page": 1, "sort": "-name" }) sorted_names = [p["name"] for p in sorted_products["items"]] assert sorted_names == sorted(sorted_names, reverse=True)
class PageListView(PicotableListView): model = Page columns = [ Column("title", _(u"Title"), sort_field="translations__title", display="title", linked=True, filter_config=TextFilter(operator="startswith")), Column("available_from", _(u"Available from")), Column("available_to", _(u"Available to")), Column("created_by", _(u"Created by")), Column("created_on", _(u"Date created")), ] def get_object_abstract(self, instance, item): return [{ "text": "%s" % (instance or _("Page")), "class": "header" }, { "title": _(u"Available from"), "text": item["available_from"] }, { "title": _(u"Available to"), "text": item["available_to"] } if instance.available_to else None]
class SupplierListView(PicotableListView): model = Supplier columns = [ Column("name", _(u"Name"), sort_field="name", display="name", filter_config=TextFilter(filter_field="name", placeholder=_("Filter by name..."))), Column("type", _(u"Type")), Column("module_identifier", _(u"Module"), display="get_module_display", sortable=True) ] def get_module_display(self, instance): return instance.module.name or _( "No %s module") % self.model._meta.verbose_name def get_toolbar(self): if settings.SHOOP_ENABLE_MULTIPLE_SUPPLIERS: return super(SupplierListView, self).get_toolbar() else: return Toolbar([])
class ContactGroupListView(PicotableViewMixin, ListView): model = ContactGroup columns = [ Column("name", _(u"Name"), sort_field="translations__name", display="name", filter_config=TextFilter(filter_field="translations__name", placeholder=_("Filter by name..."))), Column("n_members", _(u"Number of Members")), ] def get_queryset(self): return ContactGroup.objects.all().annotate(n_members=Count("members")) def get_context_data(self, **kwargs): context = super(ContactGroupListView, self).get_context_data(**kwargs) context["toolbar"] = Toolbar( [NewActionButton("shoop_admin:contact-group.new")]) return context def get_object_abstract(self, instance, item): return [ { "text": "%s" % instance, "class": "header" }, ]
class CouponListView(PicotableListView): model = Coupon columns = [ Column("code", _(u"Code"), sort_field="code", display="code", linked=True, filter_config=TextFilter(operator="startswith")), Column("usages", _("Usages"), display="get_usages"), Column("usage_limit_customer", _("Usages Limit per contact")), Column("usage_limit", _("Usage Limit")), Column("active", _("Active")), Column("created_by", _(u"Created by")), Column("created_on", _(u"Date created")), ] def get_usages(self, instance, *args, **kwargs): return instance.usages.count() def get_context_data(self, **kwargs): context = super(CouponListView, self).get_context_data(**kwargs) context["toolbar"] = Toolbar([ NewActionButton("shoop_admin:coupons.new", text=_("Create new Coupon")), ]) return context
class ServiceListView(PicotableListView): model = None # Override in subclass columns = [] base_columns = [ Column( "name", _("Name"), sort_field="translations__name", filter_config=TextFilter(filter_field="translations__name", placeholder=_("Filter by name...")) ), Column( "choice_identifier", _(u"Service choice"), display="format_service_choice", sortable=False, ), Column("enabled", _(u"Enabled"), filter_config=true_or_false_filter), Column("shop", _(u"Shop")) ] def get_object_abstract(self, instance, item): return [ {"text": "%s" % instance, "class": "header"}, ] def format_service_choice(self, instance, *args, **kwargs): if instance.provider: for choice in instance.provider.get_service_choices(): if choice.identifier == instance.choice_identifier: return str(choice.name)
def test_choice_filter_with_default(rf, admin_user, regular_user): columns = [ Column("id", "Id", filter_config=Filter(), display=instance_id), Column("username", "Username", sortable=False, filter_config=MultiFieldTextFilter(filter_fields=("username", "email"), operator="iregex")), Column("email", "Email", sortable=False, filter_config=TextFilter()), Column("is_superuser", "Is Superuser", display="superuser_display", filter_config=ChoicesFilter(choices=false_and_true())), Column("date_joined", "Date Joined", filter_config=DateRangeFilter()) ] is_active = [ Column("is_active", "Is Active", filter_config=ChoicesFilter(choices=false_and_true)) ] is_active_with_default = [ Column("is_active", "Is Active", filter_config=ChoicesFilter(choices=false_and_true, default=True)) ] query = {"perPage": 100, "page": 1, "sort": "+id"} pico_no_defaults = get_pico(rf, columns=(columns + is_active)) data = pico_no_defaults.get_data(query) user_data = data["items"][0] user = get_user_model().objects.get(id=user_data["id"]) assert user.is_active pico_with_defaults = get_pico(rf, columns=(columns + is_active_with_default)) data = pico_with_defaults.get_data(query) user_data = data["items"][0] user_with_defaults = get_user_model().objects.get(id=user_data["id"]) assert user_with_defaults == user user.is_active = False user.save() data = pico_no_defaults.get_data(query) user_data = data["items"][0] new_user = get_user_model().objects.get(id=user_data["id"]) assert new_user == user data = pico_with_defaults.get_data(query) user_data = data["items"][0] new_user_with_defaults = get_user_model().objects.get(id=user_data["id"]) assert new_user_with_defaults != user_with_defaults
class ManufacturerListView(PicotableListView): model = Manufacturer columns = [ Column("name", _(u"Name"), sort_field="name", display="name", filter_config=TextFilter(filter_field="name", placeholder=_("Filter by name..."))), ]
class TaxClassListView(PicotableListView): model = TaxClass columns = [ Column("name", _(u"Name"), sort_field="translations__name", filter_config=TextFilter(filter_field="name", placeholder=_("Filter by name..."))), ]
class UserListView(PicotableListView): model = settings.AUTH_USER_MODEL columns = [ Column("username", _(u"Username"), filter_config=TextFilter()), Column("email", _(u"Email"), filter_config=TextFilter()), Column("first_name", _(u"First Name"), filter_config=TextFilter()), Column("last_name", _(u"Last Name"), filter_config=TextFilter()), Column("is_active", _(u"Active"), filter_config=true_or_false_filter), Column("is_staff", _(u"Staff"), filter_config=true_or_false_filter), Column("is_superuser", _(u"Superuser"), filter_config=true_or_false_filter), ] def get_model(self): return get_user_model() def get_queryset(self): return self.get_model().objects.all() def get_context_data(self, **kwargs): context = super(UserListView, self).get_context_data(**kwargs) context["title"] = force_text( self.get_model()._meta.verbose_name_plural).title() return context def get_object_abstract(self, instance, item): bits = filter(None, [ _("First Name: %s") % (instance.first_name or "\u2014"), _("Last Name: %s") % (instance.last_name or "\u2014"), _("Active") if instance.is_active else _(u"Inactive"), _("Email: %s") % (instance.email or "\u2014"), _("Staff") if instance.is_staff else None, _("Superuser") if instance.is_superuser else None ]) return [{ "text": instance.username or _("User"), "class": "header" }, { "text": ", ".join(bits) }]
class CustomerTaxGroupListView(PicotableListView): model = CustomerTaxGroup columns = [ Column( "name", _("Name"), sort_field="translations__name", filter_config=TextFilter( filter_field="translations__name", placeholder=_("Filter by name..."), ), ), ]
class CampaignListView(PicotableListView): columns = [ Column("name", _(u"Title"), sort_field="name", display="name", linked=True, filter_config=TextFilter(operator="startswith")), Column("start_datetime", _("Starts")), Column("end_datetime", _("Ends")), Column("active", _("Active"), filter_config=ChoicesFilter(choices=[(0, _("No")), (1, _("Yes"))])), ] def start_datetime(self, instance, *args, **kwargs): if not instance.start_datetime: return "" return self._formatted_datetime(instance.start_datetime) def end_datetime(self, instance, *args, **kwargs): if not instance.end_datetime: return "" return self._formatted_datetime(instance.end_datetime) def _formatted_datetime(self, dt): return format_datetime(localtime(dt), locale=get_current_babel_locale()) def add_columns(self, column_id, columns, after=True): # TODO: Make better added = False for idx, column in enumerate(self.columns): if column.id == column_id: found_idx = idx + 1 if after else idx start = self.columns[:found_idx] end = self.columns[found_idx:] self.columns = start + columns + end added = True break if not added: self.columns += columns def get_object_abstract(self, instance, item): return [ { "text": "%s" % (instance or _("CatalogCampaign")), "class": "header" }, ]
class TaxListView(PicotableListView): model = Tax columns = [ Column("code", _(u"Code")), Column("name", _(u"Name"), sort_field="translations__name", filter_config=TextFilter(filter_field="name", placeholder=_("Filter by name..."))), Column("rate", _(u"Rate")), Column("amount", _(u"Amount")), Column("enabled", _(u"Enabled"), filter_config=true_or_false_filter), ]
class ProductTypeListView(PicotableListView): model = ProductType columns = [ Column("name", _(u"Name"), sort_field="translations__name", display="name", filter_config=TextFilter(filter_field="translations__name", placeholder=_("Filter by name..."))), Column("n_attributes", _(u"Number of Attributes")), ] def get_queryset(self): return ProductType.objects.all().annotate( n_attributes=Count("attributes"))
class _BaseMethodListView(PicotableListView): model = None # Overridden below columns = [ Column("name", _(u"Name"), sort_field="translations__name", filter_config=TextFilter( filter_field="name", placeholder=_("Filter by name...") )), Column("status", _(u"Status"), filter_config=ChoicesFilter(choices=MethodStatus.choices)), ] def get_object_abstract(self, instance, item): return [ {"text": "%s" % instance, "class": "header"}, {"title": _(u"Status"), "text": item["status"]} ]
class SalesUnitListView(PicotableListView): model = SalesUnit columns = [ Column("name", _(u"Name"), sort_field="translations__name", display="name", filter_config=TextFilter(filter_field="translations__name", placeholder=_("Filter by name..."))), Column("short_name", _(u"Short Name"), sort_field="translations__short_name", display="short_name"), Column("decimals", _(u"Allowed decimals")), ] def get_queryset(self): return SalesUnit.objects.all()
class SupplierListView(PicotableListView): model = Supplier columns = [ Column( "name", _(u"Name"), sort_field="name", display="name", filter_config=TextFilter( filter_field="name", placeholder=_("Filter by name...") ) ), Column("type", _(u"Type")), Column("module_identifier", _(u"Module"), display="get_module_display", sortable=False) ] def get_module_display(self, instance): return instance.module.name
class PeriodPricingModifierListView(PicotableListView): model = PeriodPriceModifier columns = [ Column("name", _("Name"), sort_field="product__translations__name", display="product__name", filter_config=TextFilter( filter_field="product__translations__name", placeholder=_("Filter by product..."))), Column("start_date", _("Start date"), sort_field="start_date", display="format_start_date"), Column("end_date", _("End date"), sort_field="end_date", display="format_end_date"), Column("modifier", _(u"Modifier"), sort_field="modifier"), ] def format_start_date(self, instance, *args, **kwargs): return format_date(instance.start_date, locale=get_current_babel_locale()) def format_end_date(self, instance, *args, **kwargs): return format_date(instance.end_date, locale=get_current_babel_locale()) def get_toolbar(self): toolbar = super(PeriodPricingModifierListView, self).get_toolbar() toolbar.append( URLActionButton( text=_("New modifier"), icon="fa fa-money", url=reverse("reservable_pricing:modifiers.new"), )) return toolbar def get_object_url(self, instance): return reverse("reservable_pricing:modifiers.edit", kwargs={"pk": instance.id})
class ServiceListView(PicotableListView): model = None # Override in subclass columns = [] base_columns = [ Column("name", _("Name"), sort_field="translations__name", filter_config=TextFilter(filter_field="translations__name", placeholder=_("Filter by name..."))), Column("enabled", _(u"Enabled"), filter_config=true_or_false_filter), Column("shop", _(u"Shop")) ] def get_object_abstract(self, instance, item): return [ { "text": "%s" % instance, "class": "header" }, ]
class ShopListView(PicotableListView): model = Shop columns = [ Column("name", _(u"Name"), sort_field="translations__name", display="name", filter_config=TextFilter(filter_field="translations__name", placeholder=_("Filter by name..."))), Column("domain", _(u"Domain")), Column("identifier", _(u"Identifier")), Column("status", _(u"Status"), filter_config=ChoicesFilter(choices=ShopStatus.choices)), ] def get_toolbar(self): if settings.SHOOP_ENABLE_MULTIPLE_SHOPS: return super(ShopListView, self).get_toolbar() else: return Toolbar([])
class ServiceProviderListView(PicotableListView): model = ServiceProvider columns = [ Column("name", _("Name"), sort_field="base_translations__name", filter_config=TextFilter(filter_field="base_translations__name", placeholder=_("Filter by name..."))), Column("type", _(u"Type"), display="get_type_display", sortable=False), ] def get_type_display(self, instance): return instance._meta.verbose_name.capitalize() def get_object_abstract(self, instance, item): return [ { "text": "%s" % instance, "class": "header" }, { "text": self.get_type_display(instance) }, ]