diff --git a/aleksis/apps/tezor/models/__init__.py b/aleksis/apps/tezor/models/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/aleksis/apps/tezor/models/base.py b/aleksis/apps/tezor/models/base.py
new file mode 100644
index 0000000000000000000000000000000000000000..e8feec9086097aa215fea9fdf5f865d7a3e51e9c
--- /dev/null
+++ b/aleksis/apps/tezor/models/base.py
@@ -0,0 +1,13 @@
+from django.db import models
+from django.utils.translation import gettext_lazy as _
+
+from aleksis.core.mixins import ExtensibleModel
+
+
+class Client(ExtensibleModel):
+    name = models.CharField(verbose_name=_("Name"))
+
+    class Meta:
+        constraints = [
+            models.UniqueConstraint(fields=["name", "site"], name="uniq_client_per_site")
+        ]
diff --git a/aleksis/apps/tezor/models/invoice.py b/aleksis/apps/tezor/models/invoice.py
new file mode 100644
index 0000000000000000000000000000000000000000..9c82d15930b53d0fa8fce9f17f209b30c9056868
--- /dev/null
+++ b/aleksis/apps/tezor/models/invoice.py
@@ -0,0 +1,55 @@
+from django.db import models
+from django.utils.translation import gettext_lazy as _
+
+from djmoney.models.fields import CurrencyField, MoneyField
+
+from aleksis.core.mixins import ExtensibleModel
+
+from .base import Client
+
+
+class InvoiceGroup(ExtensibleModel):
+    name = models.CharField(verbose_name=_("Invoice group name"), max_length=255)
+    client = models.ForeignKey(Client, verbose_name=_("Linked client"), related_name="invoice_groups")
+
+    class Meta:
+        constraints = [
+            models.UniqueConstraint(fields=["client", "name"], name="group_uniq_per_client")
+        ]
+
+
+class Invoice(ExtensibleModel):
+    group = models.ForeignKey(InvoiceGroup, verbose_name=_("Invoice group"), related_name="invoices")
+
+    number = models.CharField(verbose_name=_("Invoice number"))
+    date = models.DateField(verbose_name=_("Invoice date"))
+
+    @property
+    def currency(self):
+        try:
+            return self.positions.first().net_amount_currency
+        except InvoicePosition.DoesNotExist:
+            return None
+
+    class Meta:
+        constraints = [
+            models.UniqueConstraint(fields=["group", "number"], name="number_uniq_per_group")
+        ]
+
+
+class InvoicePosition(ExtensibleModel):
+    invoice = models.ForeignKey(Invoice, verbose_name=_("Invoice"), related_name="positions")
+
+    description = models.TextField(verbose_name=_("Description"))
+
+    vat_rate = models.PositiveSmallIntegerField(verbose_name=_("VAT rate"), default=0)
+    net_amount = models.MoneyField(verbose_name=_("Net amount"))
+
+    @property
+    def gross_amount(self):
+        # FIXME turn into manager/annotation/etc.
+        return self.net_amount * (1 + self.vat_rate / 100)
+
+    class Meta:
+        # FIXME Add check constraint for currency
+        pass