diff --git a/doc/api/resources/item_variations.rst b/doc/api/resources/item_variations.rst
index 2a356fe6d9..371e9a2462 100644
--- a/doc/api/resources/item_variations.rst
+++ b/doc/api/resources/item_variations.rst
@@ -18,6 +18,8 @@ default_price money (string) The price set d
price money (string) The price used for this variation. This is either the
same as ``default_price`` if that value is set or equal
to the item's ``default_price`` (read-only).
+free_price_suggestion money (string) A suggested price, used as a default value if
+ ``Item.free_price`` is set (or ``null``).
original_price money (string) An original price, shown for comparison, not used
for price calculations (or ``null``).
active boolean If ``false``, this variation will not be sold or shown.
@@ -53,6 +55,10 @@ meta_data object Values set for
The ``meta_data`` and ``checkin_attention`` attributes have been added.
+.. versionchanged:: 2023.9
+
+ The ``free_price_suggestion`` attribute has been added.
+
Endpoints
---------
@@ -103,6 +109,7 @@ Endpoints
"default_price": "223.00",
"price": 223.0,
"original_price": null,
+ "free_price_suggestion": null,
"meta_data": {}
},
{
@@ -116,10 +123,16 @@ Endpoints
"require_membership": false,
"require_membership_hidden": false,
"require_membership_types": [],
+ "sales_channels": ["web"],
+ "available_from": null,
+ "available_until": null,
+ "hide_without_voucher": false,
"description": {},
"position": 1,
- "default_price": null,
- "price": 15.0,
+ "default_price": "223.00",
+ "price": 223.0,
+ "original_price": null,
+ "free_price_suggestion": null,
"meta_data": {}
}
]
@@ -163,6 +176,7 @@ Endpoints
"default_price": "10.00",
"price": "10.00",
"original_price": null,
+ "free_price_suggestion": null,
"active": true,
"checkin_attention": false,
"require_approval": false,
@@ -231,6 +245,7 @@ Endpoints
"default_price": "10.00",
"price": "10.00",
"original_price": null,
+ "free_price_suggestion": null,
"active": true,
"checkin_attention": false,
"require_approval": false,
@@ -291,6 +306,7 @@ Endpoints
"default_price": "10.00",
"price": "10.00",
"original_price": null,
+ "free_price_suggestion": null,
"active": false,
"checkin_attention": false,
"require_approval": false,
diff --git a/doc/api/resources/items.rst b/doc/api/resources/items.rst
index 366105fb81..78be63ddd5 100644
--- a/doc/api/resources/items.rst
+++ b/doc/api/resources/items.rst
@@ -29,6 +29,8 @@ free_price boolean If ``true``,
they buy the product (however, the price can't be set
lower than the price defined by ``default_price`` or
otherwise).
+free_price_suggestion money (string) A suggested price, used as a default value if
+ ``free_price`` is set (or ``null``).
tax_rate decimal (string) The VAT rate to be applied for this item (read-only,
set through ``tax_rule``).
tax_rule integer The internal ID of the applied tax rule (or ``null``).
@@ -123,6 +125,8 @@ variations list of objects A list with o
├ price money (string) The price used for this variation. This is either the
same as ``default_price`` if that value is set or equal
to the item's ``default_price``.
+├ free_price_suggestion money (string) A suggested price, used as a default value if
+ ``free_price`` is set (or ``null``).
├ original_price money (string) An original price, shown for comparison, not used
for price calculations (or ``null``).
├ active boolean If ``false``, this variation will not be sold or shown.
@@ -196,6 +200,10 @@ meta_data object Values set fo
The ``media_policy`` and ``media_type`` attributes have been added.
+.. versionchanged:: 2023.9
+
+ The ``free_price_suggestion`` and ``variations[x].free_price_suggestion`` attributes have been added.
+
Notes
-----
@@ -246,6 +254,7 @@ Endpoints
"active": true,
"description": null,
"free_price": false,
+ "free_price_suggestion": null,
"tax_rate": "0.00",
"tax_rule": 1,
"admission": false,
@@ -291,6 +300,7 @@ Endpoints
"default_price": "10.00",
"price": "10.00",
"original_price": null,
+ "free_price_suggestion": null,
"active": true,
"checkin_attention": false,
"require_approval": false,
@@ -309,6 +319,7 @@ Endpoints
"default_price": null,
"price": "23.00",
"original_price": null,
+ "free_price_suggestion": null,
"active": true,
"checkin_attention": false,
"require_approval": false,
@@ -377,6 +388,7 @@ Endpoints
"active": true,
"description": null,
"free_price": false,
+ "free_price_suggestion": null,
"tax_rate": "0.00",
"tax_rule": 1,
"admission": false,
@@ -422,6 +434,7 @@ Endpoints
"default_price": "10.00",
"price": "10.00",
"original_price": null,
+ "free_price_suggestion": null,
"active": true,
"checkin_attention": false,
"require_approval": false,
@@ -440,6 +453,7 @@ Endpoints
"default_price": null,
"price": "23.00",
"original_price": null,
+ "free_price_suggestion": null,
"active": true,
"checkin_attention": false,
"require_approval": false,
@@ -489,6 +503,7 @@ Endpoints
"active": true,
"description": null,
"free_price": false,
+ "free_price_suggestion": null,
"tax_rate": "0.00",
"tax_rule": 1,
"admission": false,
@@ -533,6 +548,7 @@ Endpoints
"default_price": "10.00",
"price": "10.00",
"original_price": null,
+ "free_price_suggestion": null,
"active": true,
"checkin_attention": false,
"require_approval": false,
@@ -551,6 +567,7 @@ Endpoints
"default_price": null,
"price": "23.00",
"original_price": null,
+ "free_price_suggestion": null,
"active": true,
"checkin_attention": false,
"require_approval": false,
@@ -588,6 +605,7 @@ Endpoints
"active": true,
"description": null,
"free_price": false,
+ "free_price_suggestion": null,
"tax_rate": "0.00",
"tax_rule": 1,
"admission": false,
@@ -633,6 +651,7 @@ Endpoints
"default_price": "10.00",
"price": "10.00",
"original_price": null,
+ "free_price_suggestion": null,
"active": true,
"checkin_attention": false,
"require_approval": false,
@@ -651,6 +670,7 @@ Endpoints
"default_price": null,
"price": "23.00",
"original_price": null,
+ "free_price_suggestion": null,
"active": true,
"checkin_attention": false,
"require_approval": false,
@@ -719,6 +739,7 @@ Endpoints
"active": true,
"description": null,
"free_price": false,
+ "free_price_suggestion": null,
"tax_rate": "0.00",
"tax_rule": 1,
"admission": false,
@@ -764,6 +785,7 @@ Endpoints
"default_price": "10.00",
"price": "10.00",
"original_price": null,
+ "free_price_suggestion": null,
"active": true,
"checkin_attention": false,
"require_approval": false,
@@ -782,6 +804,7 @@ Endpoints
"default_price": null,
"price": "23.00",
"original_price": null,
+ "free_price_suggestion": null,
"active": true,
"checkin_attention": false,
"require_approval": false,
diff --git a/src/pretix/api/serializers/item.py b/src/pretix/api/serializers/item.py
index f8d72c7656..09e9777e01 100644
--- a/src/pretix/api/serializers/item.py
+++ b/src/pretix/api/serializers/item.py
@@ -59,7 +59,7 @@ class InlineItemVariationSerializer(I18nAwareModelSerializer):
class Meta:
model = ItemVariation
fields = ('id', 'value', 'active', 'description',
- 'position', 'default_price', 'price', 'original_price', 'require_approval',
+ 'position', 'default_price', 'price', 'original_price', 'free_price_suggestion', 'require_approval',
'require_membership', 'require_membership_types', 'require_membership_hidden',
'checkin_attention', 'available_from', 'available_until',
'sales_channels', 'hide_without_voucher', 'meta_data')
@@ -83,7 +83,7 @@ class ItemVariationSerializer(I18nAwareModelSerializer):
class Meta:
model = ItemVariation
fields = ('id', 'value', 'active', 'description',
- 'position', 'default_price', 'price', 'original_price', 'require_approval',
+ 'position', 'default_price', 'price', 'original_price', 'free_price_suggestion', 'require_approval',
'require_membership', 'require_membership_types', 'require_membership_hidden',
'checkin_attention', 'available_from', 'available_until',
'sales_channels', 'hide_without_voucher', 'meta_data')
@@ -234,8 +234,8 @@ class ItemSerializer(I18nAwareModelSerializer):
class Meta:
model = Item
fields = ('id', 'category', 'name', 'internal_name', 'active', 'sales_channels', 'description',
- 'default_price', 'free_price', 'tax_rate', 'tax_rule', 'admission', 'personalized',
- 'position', 'picture', 'available_from', 'available_until',
+ 'default_price', 'free_price', 'free_price_suggestion', 'tax_rate', 'tax_rule', 'admission',
+ 'personalized', 'position', 'picture', 'available_from', 'available_until',
'require_voucher', 'hide_without_voucher', 'allow_cancel', 'require_bundling',
'min_per_order', 'max_per_order', 'checkin_attention', 'has_variations', 'variations',
'addons', 'bundles', 'original_price', 'require_approval', 'generate_tickets',
diff --git a/src/pretix/base/migrations/0248_item_free_price_suggestion.py b/src/pretix/base/migrations/0248_item_free_price_suggestion.py
new file mode 100644
index 0000000000..e1c5c2f590
--- /dev/null
+++ b/src/pretix/base/migrations/0248_item_free_price_suggestion.py
@@ -0,0 +1,22 @@
+# Generated by Django 4.2.4 on 2023-10-25 12:01
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ("pretixbase", "0247_checkinlist"),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name="item",
+ name="free_price_suggestion",
+ field=models.DecimalField(decimal_places=2, max_digits=13, null=True),
+ ),
+ migrations.AddField(
+ model_name="itemvariation",
+ name="free_price_suggestion",
+ field=models.DecimalField(decimal_places=2, max_digits=13, null=True),
+ ),
+ ]
diff --git a/src/pretix/base/models/items.py b/src/pretix/base/models/items.py
index dff10a149d..44c2dbaff2 100644
--- a/src/pretix/base/models/items.py
+++ b/src/pretix/base/models/items.py
@@ -431,6 +431,12 @@ class Item(LoggedModel):
"additional donations for your event. This is currently not supported for products that are "
"bought as an add-on to other products.")
)
+ free_price_suggestion = models.DecimalField(
+ verbose_name=_("Suggested price"),
+ help_text=_("This price will be used as the default value of the input field. The user can choose a lower "
+ "value, but not lower than the price this product would have without the free price option."),
+ max_digits=13, decimal_places=2, null=True, blank=True,
+ )
tax_rule = models.ForeignKey(
'TaxRule',
verbose_name=_('Sales tax'),
@@ -1021,6 +1027,12 @@ class ItemVariation(models.Model):
help_text=_('If set, this will be displayed next to the current price to show that the current price is a '
'discounted one. This is just a cosmetic setting and will not actually impact pricing.')
)
+ free_price_suggestion = models.DecimalField(
+ verbose_name=_("Suggested price"),
+ help_text=_("This price will be used as the default value of the input field. The user can choose a lower "
+ "value, but not lower than the price this product would have without the free price option."),
+ max_digits=13, decimal_places=2, null=True, blank=True,
+ )
require_approval = models.BooleanField(
verbose_name=_('Require approval'),
default=False,
diff --git a/src/pretix/control/forms/item.py b/src/pretix/control/forms/item.py
index 5a5ea3a6fa..aec549810e 100644
--- a/src/pretix/control/forms/item.py
+++ b/src/pretix/control/forms/item.py
@@ -577,6 +577,8 @@ class ItemUpdateForm(I18nModelForm):
)
self.fields['category'].widget.choices = self.fields['category'].choices
+ self.fields['free_price_suggestion'].widget.attrs['data-display-dependency'] = '#id_free_price'
+
qs = self.event.organizer.membership_types.all()
if qs:
self.fields['require_membership_types'].queryset = qs
@@ -664,6 +666,7 @@ class ItemUpdateForm(I18nModelForm):
'picture',
'default_price',
'free_price',
+ 'free_price_suggestion',
'tax_rule',
'available_from',
'available_until',
@@ -797,6 +800,8 @@ class ItemVariationForm(I18nModelForm):
del self.fields['require_membership']
del self.fields['require_membership_types']
+ self.fields['free_price_suggestion'].widget.attrs['data-display-dependency'] = '#id_free_price'
+
self.meta_fields = []
meta_defaults = {}
if self.instance.pk:
@@ -829,6 +834,7 @@ class ItemVariationForm(I18nModelForm):
'value',
'active',
'default_price',
+ 'free_price_suggestion',
'original_price',
'description',
'require_approval',
diff --git a/src/pretix/control/templates/pretixcontrol/item/include_variations.html b/src/pretix/control/templates/pretixcontrol/item/include_variations.html
index 4baa808e74..d9db4fdc90 100644
--- a/src/pretix/control/templates/pretixcontrol/item/include_variations.html
+++ b/src/pretix/control/templates/pretixcontrol/item/include_variations.html
@@ -72,6 +72,7 @@
{% bootstrap_field form.active layout="control" %}
{% bootstrap_field form.value layout="control" %}
{% bootstrap_field form.default_price addon_after=request.event.currency layout="control" %}
+ {% bootstrap_field form.free_price_suggestion addon_after=request.event.currency layout="control" %}
{% bootstrap_field form.original_price addon_after=request.event.currency layout="control" %}
{% bootstrap_field form.description layout="control" %}
{% if form.meta_fields %}
@@ -170,6 +171,7 @@
{% bootstrap_field formset.empty_form.active layout="control" %}
{% bootstrap_field formset.empty_form.value layout="control" %}
{% bootstrap_field formset.empty_form.default_price addon_after=request.event.currency layout="control" %}
+ {% bootstrap_field formset.empty_form.free_price_suggestion addon_after=request.event.currency layout="control" %}
{% bootstrap_field formset.empty_form.original_price addon_after=request.event.currency layout="control" %}
{% bootstrap_field formset.empty_form.description layout="control" %}
{% if formset.empty_form.meta_fields %}
diff --git a/src/pretix/control/templates/pretixcontrol/item/index.html b/src/pretix/control/templates/pretixcontrol/item/index.html
index 789fffb5ca..ae9e8f8766 100644
--- a/src/pretix/control/templates/pretixcontrol/item/index.html
+++ b/src/pretix/control/templates/pretixcontrol/item/index.html
@@ -147,6 +147,7 @@
{% bootstrap_field form.default_price addon_after=request.event.currency layout="control" %}
{% bootstrap_field form.tax_rule layout="control" %}
{% bootstrap_field form.free_price layout="control" %}
+ {% bootstrap_field form.free_price_suggestion addon_after=request.event.currency layout="control" %}
{% bootstrap_field form.original_price addon_after=request.event.currency layout="control" %}