diff --git a/scipost_django/finances/admin.py b/scipost_django/finances/admin.py
index d374c58441ded84b8cd08a9c3a7891f1fd37bbf5..8da21fc79d49a528cff3c204500284f5368ef5ed 100644
--- a/scipost_django/finances/admin.py
+++ b/scipost_django/finances/admin.py
@@ -10,6 +10,7 @@ from .models import (
     SubsidyAttachment,
     PubFrac,
     PubFracCompensation,
+    PublicationExpenditureCoverage,
     WorkLog,
     PeriodicReportType,
     PeriodicReport,
@@ -111,6 +112,30 @@ class PubFracCompensationAdmin(admin.ModelAdmin):
         return (obj.pubfrac.publication.doi_label)
 
 
+@admin.register(PublicationExpenditureCoverage)
+class PublicationExpenditureCoverageAdmin(admin.ModelAdmin):
+    list_display = [
+        "subsidy",
+        "doi_label_display",
+        "amount",
+    ]
+    autocomplete_fields = [
+        "subsidy",
+        "publication",
+    ]
+    search_fields = [
+        "subsidy",
+        "subsidy__organization__name",
+        "subsidy__organization__name_original",
+        "subsidy__organization__acronym",
+        "publication__doi_label",
+    ]
+
+    @admin.display(description='doi label')
+    def doi_label_display(self, obj):
+        return (obj.publication.doi_label)
+
+
 @admin.register(WorkLog)
 class WorkLogAdmin(admin.ModelAdmin):
     autocomplete_fields = ["user"]
diff --git a/scipost_django/finances/migrations/0040_alter_pubfraccompensation_options_and_more.py b/scipost_django/finances/migrations/0040_alter_pubfraccompensation_options_and_more.py
new file mode 100644
index 0000000000000000000000000000000000000000..27c0dde3daf5264bac26ce4f9cbebbcda12eec35
--- /dev/null
+++ b/scipost_django/finances/migrations/0040_alter_pubfraccompensation_options_and_more.py
@@ -0,0 +1,63 @@
+# Generated by Django 4.2.10 on 2024-03-16 03:48
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ("journals", "0130_remove_publication_pubfractions_confirmed_by_authors"),
+        ("finances", "0039_alter_pubfraccompensation_pubfrac"),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name="pubfraccompensation",
+            options={
+                "verbose_name": "PubFrac compensation",
+                "verbose_name_plural": "PubFrac compensations",
+            },
+        ),
+        migrations.CreateModel(
+            name="PublicationExpenditureCoverage",
+            fields=[
+                (
+                    "id",
+                    models.AutoField(
+                        auto_created=True,
+                        primary_key=True,
+                        serialize=False,
+                        verbose_name="ID",
+                    ),
+                ),
+                ("amount", models.PositiveIntegerField()),
+                (
+                    "publication",
+                    models.ForeignKey(
+                        on_delete=django.db.models.deletion.CASCADE,
+                        related_name="pex_coverages",
+                        to="journals.publication",
+                    ),
+                ),
+                (
+                    "subsidy",
+                    models.ForeignKey(
+                        on_delete=django.db.models.deletion.CASCADE,
+                        related_name="pex_coverages",
+                        to="finances.subsidy",
+                    ),
+                ),
+            ],
+            options={
+                "verbose_name": "PEX coverage",
+                "verbose_name_plural": "PEX coverages",
+            },
+        ),
+        migrations.AddConstraint(
+            model_name="publicationexpenditurecoverage",
+            constraint=models.UniqueConstraint(
+                fields=("subsidy", "publication"), name="unique_subsidy_publication"
+            ),
+        ),
+    ]
diff --git a/scipost_django/finances/models/__init__.py b/scipost_django/finances/models/__init__.py
index a370b148d4ff1a0efca1f3a54905585b3613f4fe..c1669c99e7df453c2f0a73ee77c213dce8a80170 100644
--- a/scipost_django/finances/models/__init__.py
+++ b/scipost_django/finances/models/__init__.py
@@ -8,6 +8,8 @@ from .periodic_report import (
     PeriodicReport,
 )
 
+from .pex_coverage import PublicationExpenditureCoverage
+
 from .pubfrac import PubFrac
 
 from .pubfrac_compensation import PubFracCompensation
diff --git a/scipost_django/finances/models/pex_coverage.py b/scipost_django/finances/models/pex_coverage.py
new file mode 100644
index 0000000000000000000000000000000000000000..4f68c987eed695787e76b203f002b5ae8a205e54
--- /dev/null
+++ b/scipost_django/finances/models/pex_coverage.py
@@ -0,0 +1,44 @@
+__copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)"
+__license__ = "AGPL v3"
+
+
+from django.db import models
+
+
+class PublicationExpenditureCoverage(models.Model):
+    """
+    An amount from a Subsidy which is ascribed to a Publication as expenditure coverage.
+
+    A Coverage is applied to a Publication as a whole, not to individual PubFracs.
+    This class thus complements PubFracCompensation, which compensates costs
+    at the PubFrac level.
+    """
+
+    subsidy = models.ForeignKey(
+        "finances.Subsidy",
+        related_name="pex_coverages",
+        on_delete=models.CASCADE,
+    )
+
+    publication = models.ForeignKey(
+        "journals.Publication",
+        related_name="pex_coverages",
+        on_delete=models.CASCADE,
+    )
+
+    amount = models.PositiveIntegerField()
+
+    class Meta:
+        constraints = [
+            models.UniqueConstraint(
+                fields=["subsidy", "publication"], name="unique_subsidy_publication"
+            ),
+        ]
+        verbose_name = "PEX coverage"
+        verbose_name_plural = "PEX coverages"
+
+    def __str__(self):
+        return (
+            f"€{self.amount} for {self.publication.doi_label} "
+            "from {self.subsidy.organization}"
+        )
diff --git a/scipost_django/finances/models/pubfrac_compensation.py b/scipost_django/finances/models/pubfrac_compensation.py
index 9539d973b17ea5463a6e54cb4e6c27044675c48e..0aa5c48ab22f3c2de6c79d9d5addedc7336f2645 100644
--- a/scipost_django/finances/models/pubfrac_compensation.py
+++ b/scipost_django/finances/models/pubfrac_compensation.py
@@ -30,4 +30,5 @@ class PubFracCompensation(models.Model):
                 fields=["subsidy", "pubfrac"], name="unique_subsidy_pubfrac"
             ),
         ]
-        verbose_name_plural = "PubFrac Compensations"
+        verbose_name = "PubFrac compensation"
+        verbose_name_plural = "PubFrac compensations"
diff --git a/scipost_django/journals/models/publication.py b/scipost_django/journals/models/publication.py
index 888d0ca1f1143e6c4b6c117a5bd80211f1cb1fbd..2f92b0959b4c8074d7ac6d4a56946a10ae963071 100644
--- a/scipost_django/journals/models/publication.py
+++ b/scipost_django/journals/models/publication.py
@@ -421,8 +421,8 @@ class Publication(models.Model):
         )
 
     @property
-    def expenditure(self):
-        """The expenditure (as defined by the Journal) to produce this Publication."""
+    def expenditures(self):
+        """The expenditures (as defined by the Journal) to produce this Publication."""
         return self.get_journal().cost_per_publication(self.publication_date.year)
 
     @property
@@ -430,16 +430,31 @@ class Publication(models.Model):
         """Checks that the support fractions sum up to one."""
         return self.pubfracs.aggregate(Sum("fraction"))["fraction__sum"] == 1
 
+    @property
+    def covered_expenditures(self):
+        """Covered part of expenditures for this Publication."""
+        return (
+            self.pex_coverages.aggregate(Sum("amount"))["amount__sum"]
+            if self.pex_coverages.exists()
+            else 0
+        )
+
     @property
     def compensated_expenditures(self):
-        """Uncompensated part of expenditures for this Publication."""
+        """Compensated part of expenditures for this Publication."""
         qs = PubFracCompensation.objects.filter(pubfrac__publication=self)
-        return qs.aggregate(Sum("amount"))["amount__sum"]
+        return qs.aggregate(Sum("amount"))["amount__sum"] if qs.exists() else 0
 
     @property
     def uncompensated_expenditures(self):
-        """Compensated part of expenditures for this Publication."""
-        return self.expenditure - self.compensated_expenditures
+        """Unompensated part of expenditures for this Publication."""
+        return self.expenditures - self.compensated_expenditures
+
+    @property
+    def outstanding_expenditures(self):
+        """Expenditures which hasn't been compensated or covered."""
+        return (self.expenditures - self.covered_expenditures -
+                self.compensated_expenditures)
 
     @property
     def citation(self):
diff --git a/scipost_django/journals/templates/journals/publication_detail.html b/scipost_django/journals/templates/journals/publication_detail.html
index 4ccc19f61a18fb24e7393010665173c624790df9..200b5c0e4bd29a42f5803036755c8c19990e06d9 100644
--- a/scipost_django/journals/templates/journals/publication_detail.html
+++ b/scipost_django/journals/templates/journals/publication_detail.html
@@ -172,9 +172,10 @@
 
   {% if 'edadmin' in user_roles %}
     <h3 class="mt-4">
-      PubFracs, Compensations and Arrears
+      Expenditures and Balance
     </h3>
-    <table class="table mt-2">
+    <table class="table mt-2 caption-top">
+      <caption>PubFracs, Compensations and Arrears</caption>
       <thead class="table-light">
 	<tr>
 	  <th>Organization</th>
@@ -187,7 +188,7 @@
       <tbody>
 	{% for pubfrac in publication.pubfracs.all %}
 	  <tr>
-	    <td>{{ pubfrac.organization }}</td>
+	    <td><a href="{% url 'organizations:organization_detail' pk=pubfrac.organization.id %}">{{ pubfrac.organization }}</a></td>
 	    <td>{{ pubfrac.fraction }}</td>
 	    <td>&euro;{{ pubfrac.cf_value }}</td>
 	    <td>
@@ -209,7 +210,7 @@
 	      </ul>
 	    </td>
 	    {% with pubfrac.arrears as arrears %}
-	      <td class="{% if arrears == 0 %}bg-success{% elif arrears < pubfrac.cf_value %}bg-warning{% else %}bg-danger{% endif %} bg-opacity-25">
+	      <td class="{% if arrears == 0 %}bg-success{{% else %}bg-danger{% endif %} bg-opacity-25">
 		&euro;{{ pubfrac.arrears }}
 	      </td>
 	    {% endwith %}
@@ -218,13 +219,10 @@
 	<tr>
 	  <th>Totals</th>
 	  <td>1</td>
-	  <td>{{ publication.expenditure }}</td>
+	  <td>{{ publication.expenditures }}</td>
 	  {% if publication.uncompensated_expenditures == 0 %}
 	    <td class="bg-success bg-opacity-25">{{ publication.compensated_expenditures }}</td>
 	    <td class="bg-success bg-opacity-25">{{ publication.uncompensated_expenditures }}</td>
-	  {% elif publication.uncompensated_expenditures < publication.expenditure %}
-	    <td class="bg-warning bg-opacity-25">{{ publication.compensated_expenditures }}</td>
-	    <td class="bg-warning bg-opacity-25">{{ publication.uncompensated_expenditures }}</td>
 	  {% else %}
 	    <td class="bg-danger bg-opacity-25">{{ publication.compensated_expenditures }}</td>
 	    <td class="bg-danger bg-opacity-25">{{ publication.uncompensated_expenditures }}</td>
@@ -232,6 +230,49 @@
 	</tr>
       </tbody>
     </table>
+
+    <table class="table mt-2 caption-top">
+      <caption>Balance</caption>
+      <tbody>
+	<tr>
+	  <th>Expenditures</th>
+	  <td></td>
+	  <td>{{ publication.expenditures }}</td>
+	</tr>
+	<tr>
+	  <th>PubFrac Compensations</th>
+	  <td>
+	  </td>
+	  <td>{{ publication.compensated_expenditures }}</td>
+	</tr>
+	<tr>
+	  <th>Coverages</th>
+	  <td>
+	    <ul>
+	      {% for coverage in publication.pex_coverages.all %}
+		<li>
+		  &euro;{{ coverage.amount }}
+		  from <a href="{% url 'organizations:organization_detail' pk=coverage.subsidy.organization.id %}">{{ coverage.subsidy.organization }}</a>
+		  (<a href="{% url 'finances:subsidy_details' pk=coverage.subsidy.id %}" target="_blank">
+		  see Subsidy details
+		  </a>
+		</li>
+	      {% empty %}
+		<li>No coverage received</li>
+	      {% endfor %}
+	    </ul>
+	  </td>
+	  <td>{{ publication.covered_expenditures }}</td>
+	</tr>
+	{% with publication.outstanding_expenditures as outstanding %}
+	  <tr class="{% if outstanding > 0 %}bg-danger{% else %}bg-success{% endif %} bg-opacity-25">
+	    <th>Outstanding Expenditures</th>
+	    <td></td>
+	    <td>{{ outstanding }}</td>
+	  </tr>
+	{% endwith %}
+      </tbody>
+    </table>
   {% endif %}
 
   {% if publication.status == 'draft' and perms.scipost.can_draft_publication %}