diff --git a/aleksis/apps/tezor/models/invoice.py b/aleksis/apps/tezor/models/invoice.py
index d8e9073529e5f58717daae79abe8b63dc12e7ccb..3d3583a352c2f51fe52300e3cbc3da1ac3c330d4 100644
--- a/aleksis/apps/tezor/models/invoice.py
+++ b/aleksis/apps/tezor/models/invoice.py
@@ -10,6 +10,7 @@ from payments import PurchasedItem
 from payments.models import BasePayment
 
 from aleksis.core.mixins import ExtensibleModel, PureDjangoModel
+from aleksis.core.models import Person
 
 from .base import Client
 from ..tables import PurchasedItemsTable, TotalsTable
@@ -113,3 +114,37 @@ class Invoice(BasePayment, PureDjangoModel):
 
     def get_failure_url(self):
         return reverse("invoice_by_token", kwargs={"slug": self.token})
+
+
+class ManualInvoice(ExtensibleModel):
+    invoice_group = models.ForeignKey(InvoiceGroup, on_delete=models.CASCADE, verbose_name=_("Invoice group"))
+    person = models.ForeignKey(Person, on_delete=models.SET_NULL, verbose_name=_("Invoice recipient (person)"))
+
+    item_description = models.CharField(max_length=255, verbose_name=_("Purchased item"))
+    price = models.DecimalField(verbose_name=_("Item gross price"), max_digits=9, decimal_places=2, default="0.0")
+    currency = models.CharField(max_length=10, verbose_name=_("Currency"))
+    tax_rate = models.DecimalField(verbose_name=_("Tax rate"), max_digits=4, decimal_places=1, default="0.0")
+
+    variant = models.CharField(verbose_name=_("Payment variant"), max_length=255, blank=True)
+
+    def get_purchased_items(self):
+        yield PurchasedItem(name=self.item_description, quantity=1, price=self.price, currency=self.currency, sku="", tax_rate=self.tax_rate)
+
+    def get_invoice(self):
+        invoice, __ = Invoice.objects.get_or_create(for_content_type=ContentType.objects.get_for_model(self), for_object_id=self.pk, defaults={
+            "group": self.invoice_group,
+            "variant": self.variant,
+            "number": f"FIXME",
+            "currency": self.currency,
+            "total": self.price,
+            "tax": self.total / (self.tax_rate + 100) * self.tax_rate,
+            "description": self.item_description,
+            "billing_first_name": self.person.first_name,
+            "billing_last_name": self.person.last_name,
+            "billing_address_1": f"{self.person.street} {self.person.housenumber}",
+            "billing_city": self.person.place,
+            "billing_postcode": self.person.postal_code,
+            "billing_email": self.person.email,
+        })
+
+        return invoice