diff --git a/aleksis/apps/tezor/migrations/0003_manual_invoicing.py b/aleksis/apps/tezor/migrations/0003_manual_invoicing.py
new file mode 100644
index 0000000000000000000000000000000000000000..3751573d6ba5f37a7934371fb750d92808c97437
--- /dev/null
+++ b/aleksis/apps/tezor/migrations/0003_manual_invoicing.py
@@ -0,0 +1,54 @@
+# Generated by Django 3.2.12 on 2022-03-12 21:41
+
+import django.contrib.sites.managers
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('core', '0038_notification_send_at.py'),
+        ('sites', '0002_alter_domain_unique'),
+        ('tezor', '0002_invoice_due_date'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='InvoiceItem',
+            fields=[
+                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('extended_data', models.JSONField(default=dict, editable=False)),
+                ('sku', models.CharField(blank=True, max_length=255, verbose_name='Article no.')),
+                ('description', models.CharField(max_length=255, verbose_name='Purchased item')),
+                ('price', models.DecimalField(decimal_places=2, default='0.0', max_digits=9, verbose_name='Item gross price')),
+                ('currency', models.CharField(max_length=10, verbose_name='Currency')),
+                ('tax_rate', models.DecimalField(decimal_places=1, default='0.0', max_digits=4, verbose_name='Tax rate')),
+            ],
+            options={
+                'abstract': False,
+            },
+            managers=[
+                ('objects', django.contrib.sites.managers.CurrentSiteManager()),
+            ],
+        ),
+        migrations.AddField(
+            model_name='invoice',
+            name='person',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='core.person', verbose_name='Invoice recipient (person)'),
+        ),
+        migrations.AddConstraint(
+            model_name='invoice',
+            constraint=models.CheckConstraint(check=models.Q(('for_object_id__isnull', True), ('person__isnull', True), _connector='OR'), name='object_or_person'),
+        ),
+        migrations.AddField(
+            model_name='invoiceitem',
+            name='site',
+            field=models.ForeignKey(default=1, editable=False, on_delete=django.db.models.deletion.CASCADE, to='sites.site'),
+        ),
+        migrations.AddField(
+            model_name='invoice',
+            name='items',
+            field=models.ManyToManyField(to='tezor.InvoiceItem', verbose_name='Invoice items'),
+        ),
+    ]
diff --git a/aleksis/apps/tezor/models/invoice.py b/aleksis/apps/tezor/models/invoice.py
index 81d743a6a3e821c0bceae2b16aace9560d45a8e6..7c87380376ead88f18aa9e677cf269cc1dfb6dfd 100644
--- a/aleksis/apps/tezor/models/invoice.py
+++ b/aleksis/apps/tezor/models/invoice.py
@@ -101,7 +101,7 @@ class Invoice(BasePayment, PureDjangoModel):
     class Meta:
         constraints = [
             models.UniqueConstraint(fields=["number", "group"], name="number_uniq_per_group"),
-            models.CheckConstraint(Q(for_object_id__isnull=True) | Q(person__isnull=True)),
+            models.CheckConstraint(check=(Q(for_object_id__isnull=True) | Q(person__isnull=True)), name="object_or_person"),
         ]
 
     @property