diff --git a/biscuit/apps/exlibris/forms.py b/biscuit/apps/exlibris/forms.py
index 3fa3e12c3bb52b1668b489cd0819585c05f0b64b..002aaf411a5d726be092476a32953272633fa780 100644
--- a/biscuit/apps/exlibris/forms.py
+++ b/biscuit/apps/exlibris/forms.py
@@ -49,3 +49,8 @@ class BookCopyEditForm(forms.ModelForm):
 
 class BookCopiesBulkAddForm(forms.Form):
     count = forms.IntegerField(label=_('Number of copies'))
+
+
+class BorrowPersonForm(forms.Form):
+    borrower = forms.ModelChoiceField(label=_('Person'), queryset=Person.objects.all())
+    barcodes = forms.CharField(label=_('Barcodes'), widget=forms.Textarea)
diff --git a/biscuit/apps/exlibris/menus.py b/biscuit/apps/exlibris/menus.py
index 22081eacef513fadc3bb18da1a303b45e7eca79d..363246330b705c91d2a0c481ccb1bd2feeb0ff2f 100644
--- a/biscuit/apps/exlibris/menus.py
+++ b/biscuit/apps/exlibris/menus.py
@@ -22,6 +22,11 @@ MENUS = {
                     'name': _('Open copy'),
                     'url': 'get_copy',
                     'validators': ['menu_generator.validators.is_authenticated']
+                },
+                {
+                    'name': _('Person borrowing'),
+                    'url': 'person_borrow',
+                    'validators': ['menu_generator.validators.is_authenticated']
                 }
             ]
         }
diff --git a/biscuit/apps/exlibris/templates/exlibris/person_borrow.html b/biscuit/apps/exlibris/templates/exlibris/person_borrow.html
new file mode 100644
index 0000000000000000000000000000000000000000..64c32b91695e4fdf47ce930d2e22e6627ddaa03f
--- /dev/null
+++ b/biscuit/apps/exlibris/templates/exlibris/person_borrow.html
@@ -0,0 +1,12 @@
+{% extends "core/base.html" %}
+{% load bootstrap4 i18n %}
+
+{% block content %}
+ <h1>{% blocktrans %}Person batch borrowing{% endblocktrans %}</h1>
+
+ <form method="post">
+  {% csrf_token %}
+  {% bootstrap_form person_borrow_form %}
+  <input type="submit" value="Borrow" />
+ </form>
+{% endblock %}
diff --git a/biscuit/apps/exlibris/urls.py b/biscuit/apps/exlibris/urls.py
index 03693a9d68c63ae0646b85fa60e2c2f1467c4154..ec7ebd00f587dc82096b771b116597a6e1064601 100644
--- a/biscuit/apps/exlibris/urls.py
+++ b/biscuit/apps/exlibris/urls.py
@@ -23,5 +23,6 @@ urlpatterns = [
          name='add_copies_by_book_id'),
     path('book/add', views.add_book, name='add_book'),
     path('book/isbn/<isbn>', views.edit_book, name='edit_book_by_isbn'),
-    path('books', views.books, name='books')
+    path('books', views.books, name='books'),
+    path('books/borrowing/person', views.person_borrow, name='person_borrow')
 ]
diff --git a/biscuit/apps/exlibris/views.py b/biscuit/apps/exlibris/views.py
index 481b147f8c39af9cede657e954ba09b98a80bd04..c1ff9261263e134eb2fd627b5f24015db825fcd7 100644
--- a/biscuit/apps/exlibris/views.py
+++ b/biscuit/apps/exlibris/views.py
@@ -254,3 +254,32 @@ def book_copy(request: HttpRequest, barcode: str, template: str) -> HttpResponse
     context['book_copy'] = book_copy
 
     return render(request, 'exlibris/copy_%s.html' % template, context)
+
+
+@login_required
+def person_borrow(request: HttpRequest) -> HttpResponse:
+    context = {}
+
+    person_borrow_form = PersonBorrowForm(request.POST or None)
+
+    if request.method == 'POST':
+        if person_borrow_form.is_valid():
+            person = person_borrow_form.cleaned_data['person']
+
+            for barcode in person_borrow_form.cleaned_data['barcodes'].splitlines():
+                try:
+                    book_copy = BookCopy.objects.get(barcode=barcode)
+                except BookCopy.DoesNotExist:
+                    messages.error(request, _('Barcode %s is invalid.') % barcode)
+                    continue
+
+                book_copy.borrower = person
+                book_copy.save()
+
+                messages.success(request, _('Book %s borrowed to %s.') % book_copy.book.title)
+
+            person_borrow_form = PersonBorrowForm()
+
+    context['person_borrow_form'] = person_borrow_form
+
+    return render(request, 'exlibris/person_borrow.html', context)