Source code for shuup.admin.modules.products.views.edit
import bleach
from django import forms
from django.conf import settings
from django.contrib import messages
from django.core.exceptions import ObjectDoesNotExist
from django.db.transaction import atomic
from django.utils.translation import get_language
from django.utils.translation import gettext_lazy as _
from shuup.admin.form_part import FormPart, FormPartsViewMixin, SaveFormPartsMixin, TemplatedFormDef
from shuup.admin.modules.products.forms import (
ProductAttributesForm,
ProductBaseForm,
ProductImageMediaFormSet,
ProductMediaFormSet,
ShopProductForm,
)
from shuup.admin.shop_provider import get_shop
from shuup.admin.supplier_provider import get_supplier
from shuup.admin.utils.tour import is_tour_complete
from shuup.admin.utils.views import CreateOrUpdateView
from shuup.apps.provides import get_provide_objects
from shuup.core.models import Product, ProductType, SalesUnit, ShopProduct, Supplier, TaxClass
from shuup.core.specs.product_kind import DefaultProductKindSpec, get_product_kind_specs
from .toolbars import EditProductToolbar
[docs]
class ProductBaseFormPart(FormPart):
priority = -1000 # Show this first, no matter what
[docs]
def get_form_defs(self):
yield TemplatedFormDef(
"base",
ProductBaseForm,
template_name="shuup/admin/products/_edit_base_form.jinja",
required=True,
kwargs={
"instance": self.object.product,
"languages": settings.LANGUAGES,
"initial": self.get_initial(),
"request": self.request,
},
)
yield TemplatedFormDef(
"base_extra",
forms.Form,
template_name="shuup/admin/products/_edit_extra_base_form.jinja",
required=False,
)
[docs]
def form_valid(self, form_group):
self.object.product = form_group["base"].save()
self.object.save()
return self.object.product
[docs]
def get_sku(self):
sku = self.request.GET.get("sku", "")
if not sku:
last_id = Product.objects.values_list("id", flat=True).first()
sku = last_id + 1 if last_id else 1
return sku
[docs]
def get_initial(self):
if not self.object.product_id:
# Sane defaults...
name_field = f"name__{get_language()}"
return {
name_field: self.request.GET.get("name", ""),
"sku": self.get_sku(),
"type": ProductType.objects.first(),
"tax_class": TaxClass.objects.first(),
"sales_unit": SalesUnit.objects.first(),
}
[docs]
class ShopProductFormPart(FormPart):
priority = -900
[docs]
def __init__(self, request, **kwargs):
super().__init__(request, **kwargs)
self.shop = request.shop
[docs]
def get_form_defs(self):
yield TemplatedFormDef(
f"shop{self.shop.pk}",
ShopProductForm,
template_name="shuup/admin/products/_edit_shop_form.jinja",
required=True,
kwargs={
"instance": self.object,
"initial": self.get_initial(),
"request": self.request,
"languages": settings.LANGUAGES,
},
)
# the hidden extra form template that uses ShopProductForm
yield TemplatedFormDef(
f"shop{self.shop.pk}_extra",
forms.Form,
template_name="shuup/admin/products/_edit_extra_shop_form.jinja",
required=False,
)
[docs]
def form_valid(self, form):
shop_product_form = form[f"shop{self.shop.pk}"]
if not shop_product_form.changed_data:
return
if not shop_product_form.instance.pk:
shop_product_form.instance.product = self.object
original_quantity = shop_product_form.instance.minimum_purchase_quantity
rounded_quantity = self.object.sales_unit.round(original_quantity) # type: ignore
if original_quantity != rounded_quantity:
messages.info(
self.request,
_("Minimum Purchase Quantity has been rounded to match Sales Unit."),
)
shop_product_form.instance.minimum_purchase_quantity = rounded_quantity
inst = shop_product_form.save()
messages.success(self.request, _("Changes to shop instance for %s saved.") % inst.shop)
[docs]
def get_initial(self):
if not self.object.pk: # type: ignore
return {"suppliers": [Supplier.objects.enabled(shop=get_shop(self.request)).first()]} # type: ignore
[docs]
class ProductAttributeFormPart(FormPart):
priority = -800
[docs]
def get_form_defs(self):
if not self.object.product.get_available_attribute_queryset(): # type: ignore
return
yield TemplatedFormDef(
"attributes",
ProductAttributesForm,
template_name="shuup/admin/products/_edit_attribute_form.jinja",
required=False,
kwargs={"product": self.object.product, "languages": settings.LANGUAGES}, # type: ignore
)
[docs]
class BaseProductMediaFormPart(FormPart):
[docs]
def get_form_defs(self):
if not self.object.pk: # type: ignore
return
yield TemplatedFormDef(
self.name, # type: ignore
self.formset, # type: ignore
template_name="shuup/admin/products/_edit_media_form.jinja",
required=False,
kwargs={
"product": self.object.product, # type: ignore
"languages": settings.LANGUAGES,
"request": self.request,
},
)
[docs]
def form_valid(self, form):
if self.name in form.forms: # type: ignore
frm = form.forms[self.name] # type: ignore
frm.save()
[docs]
class ProductMediaFormPart(BaseProductMediaFormPart):
name = "media"
priority = -700
formset = ProductMediaFormSet
[docs]
class ProductImageMediaFormPart(BaseProductMediaFormPart):
name = "images"
priority = -600
formset = ProductImageMediaFormSet
[docs]
class ProductEditView(SaveFormPartsMixin, FormPartsViewMixin, CreateOrUpdateView):
model = ShopProduct
context_object_name = "product"
template_name = "shuup/admin/products/edit.jinja"
base_form_part_classes = []
form_part_class_provide_key = "admin_product_form_part"
add_form_errors_as_messages = True
product_listing_names = [DefaultProductKindSpec.admin_listing_name]
[docs]
def get_listing_product_kinds_values(self):
return [
product_kind_spec.value
for product_kind_spec in get_product_kind_specs()
if product_kind_spec.admin_listing_name in self.product_listing_names
]
[docs]
def get_object(self, queryset=None):
if not self.kwargs.get(self.pk_url_kwarg):
instance = self.model()
instance.shop = self.request.shop
instance.product = Product()
return instance
return super().get_object(queryset)
[docs]
def get_queryset(self):
qs = (
super()
.get_queryset()
.filter(
shop=get_shop(self.request),
product__kind__in=self.get_listing_product_kinds_values(),
)
)
supplier = get_supplier(self.request)
if supplier:
qs = qs.filter(suppliers=supplier)
return qs
[docs]
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
orderability_errors = []
shop = get_shop(self.request)
if self.object.pk:
context["title"] = self.object.product.name
try:
shop_product = self.object
orderability_errors.extend(
[
f"{shop.name}: {msg.message}"
for msg in shop_product.get_orderability_errors(
supplier=None,
quantity=shop_product.minimum_purchase_quantity,
customer=None,
)
]
)
except ObjectDoesNotExist:
orderability_errors.extend(["Error! {}: {}".format(shop.name, _("Product is not available."))])
product_validator_provides = sorted(
get_provide_objects("admin_product_validator"),
key=lambda provides: provides.ordering,
)
context["bleach"] = bleach
validation_issues = []
for admin_product_validator in product_validator_provides:
for validation_issue in admin_product_validator.get_validation_issues(
shop_product=self.object,
shop=shop,
user=self.request.user,
supplier=get_supplier(self.request),
):
if validation_issue:
validation_issues.append(validation_issue)
context["validation_issues"] = sorted(validation_issues, key=lambda x: x.get_issue_type_priority())
context["orderability_errors"] = orderability_errors
context["product_sections"] = []
context["tour_key"] = "product"
context["tour_complete"] = is_tour_complete(get_shop(self.request), "product", user=self.request.user)
product_sections_provides = sorted(get_provide_objects("admin_product_section"), key=lambda x: x.order)
for admin_product_section in product_sections_provides:
if admin_product_section.visible_for_object(self.object.product, self.request):
context["product_sections"].append(admin_product_section)
context[admin_product_section.identifier] = admin_product_section.get_context_data(
self.object.product, self.request
)
return context