diff --git a/app/admin/views.py b/app/admin/views.py
index b6e8f3a251a5933bf07c2903909b6b92ecb8fe01..4578bdaf22b24f2023caae8e68a5aed9d62880e4 100644
--- a/app/admin/views.py
+++ b/app/admin/views.py
@@ -9,10 +9,10 @@ This module customizes the admin views.
 :license: BSD 2-Clause, see LICENSE for more details.
 
 """
+from wtforms import validators
 from flask_admin.contrib import sqla
 from flask_login import current_user
-from ..models import Item, User, Group
-from .. import utils
+from ..models import Item, User, Group, ICS_ID_RE
 
 
 class AdminModelView(sqla.ModelView):
@@ -41,12 +41,12 @@ class UserAdmin(AdminModelView):
 
 class ItemAdmin(AdminModelView):
 
+    form_args = {
+        'ics_id': {
+            'label': 'ICS id',
+            'validators': [validators.Regexp(ICS_ID_RE, message='ICS id shall match [A-Z]{3}[0-9]{3}')]
+        }
+    }
+
     def __init__(self, session):
         super().__init__(Item, session)
-
-    def on_model_change(self, form, model, is_created):
-        """Update the hash"""
-        # The hash is supposed to be computed in the __init__ method
-        # of the Item class but flask-admin doesn't pass any parameter
-        # when creating a class - we do it here instead
-        model.hash = utils.compute_hash(model.serial_number)
diff --git a/app/models.py b/app/models.py
index 066f55436bf0d08550fd6276bfcd1dda3c2542d9..e98837032255aff8640df552f5a3ac8ee475634e 100644
--- a/app/models.py
+++ b/app/models.py
@@ -9,10 +9,9 @@ This module implements the models used in the app.
 :license: BSD 2-Clause, see LICENSE for more details.
 
 """
-import uuid
+import re
 import qrcode
-from sqlalchemy.types import TypeDecorator, CHAR
-from sqlalchemy.dialects.postgresql import UUID
+from sqlalchemy.orm import validates
 from sqlalchemy.ext.associationproxy import association_proxy
 from citext import CIText
 from flask import current_app
@@ -21,39 +20,7 @@ from .extensions import db, login_manager, ldap_manager, jwt
 from . import utils
 
 
-class GUID(TypeDecorator):
-    """Platform-independent GUID type.
-
-    Uses Postgresql's UUID type, otherwise uses
-    CHAR(32), storing as stringified hex values.
-
-    From http://docs.sqlalchemy.org/en/rel_0_9/core/custom_types.html?highlight=guid#backend-agnostic-guid-type
-    """
-    impl = CHAR
-
-    def load_dialect_impl(self, dialect):
-        if dialect.name == 'postgresql':
-            return dialect.type_descriptor(UUID())
-        else:
-            return dialect.type_descriptor(CHAR(32))
-
-    def process_bind_param(self, value, dialect):
-        if value is None:
-            return value
-        elif dialect.name == 'postgresql':
-            return str(value)
-        else:
-            if not isinstance(value, uuid.UUID):
-                return "%.32x" % uuid.UUID(value).int
-            else:
-                # hexstring
-                return "%.32x" % value.int
-
-    def process_result_value(self, value, dialect):
-        if value is None:
-            return value
-        else:
-            return uuid.UUID(value)
+ICS_ID_RE = re.compile('[A-Z]{3}[0-9]{3}')
 
 
 @login_manager.user_loader
@@ -219,8 +186,8 @@ class Item(db.Model):
     id = db.Column(db.Integer, primary_key=True)
     _created = db.Column(db.DateTime, default=db.func.now())
     _updated = db.Column(db.DateTime, default=db.func.now(), onupdate=db.func.now())
+    ics_id = db.Column(db.String(6), unique=True, index=True)
     serial_number = db.Column(db.String(100), nullable=False)
-    hash = db.Column(GUID, unique=True)
     manufacturer_id = db.Column(db.Integer, db.ForeignKey('manufacturer.id'))
     model_id = db.Column(db.Integer, db.ForeignKey('model.id'))
     location_id = db.Column(db.Integer, db.ForeignKey('location.id'))
@@ -233,23 +200,30 @@ class Item(db.Model):
     status = db.relationship('Status', back_populates='items')
     children = db.relationship('Item', backref=db.backref('parent', remote_side=[id]))
 
-    def __init__(self, serial_number=None, manufacturer=None, model=None, location=None, status=None):
+    def __init__(self, ics_id=None, serial_number=None, manufacturer=None, model=None, location=None, status=None):
         # All arguments must be optional for this class to work with flask-admin!
+        self.ics_id = ics_id
         self.serial_number = serial_number
         self.manufacturer = utils.convert_to_model(manufacturer, Manufacturer)
         self.model = utils.convert_to_model(model, Model)
         self.location = utils.convert_to_model(location, Location)
         self.status = utils.convert_to_model(status, Status)
-        self.hash = utils.compute_hash(serial_number)
 
     def __str__(self):
-        return self.serial_number
+        return str(self.ics_id)
+
+    @validates('ics_id')
+    def validate_ics_id(self, key, string):
+        """Ensure the ICS id field matches the required format"""
+        if string is not None:
+            assert ICS_ID_RE.fullmatch(string) is not None
+        return string
 
     def to_dict(self):
         return {
             'id': self.id,
+            'ics_id': self.ics_id,
             'serial_number': self.serial_number,
-            'hash': self.hash,
             'manufacturer': utils.format_field(self.manufacturer),
             'model': utils.format_field(self.model),
             'location': utils.format_field(self.location),
diff --git a/app/utils.py b/app/utils.py
index 03f8a885ab721a4f2b48c5720d3ca6ffcd4c595e..1ea13faa0fbad4e49cbeb8290779cd24df97cdc8 100644
--- a/app/utils.py
+++ b/app/utils.py
@@ -12,8 +12,6 @@ This module implements utility functions.
 import base64
 import datetime
 import io
-import hashlib
-import uuid
 
 
 class InventoryError(Exception):
@@ -39,13 +37,6 @@ class InventoryError(Exception):
         return str(self.to_dict())
 
 
-def compute_hash(string):
-    if string is None:
-        return None
-    md5 = hashlib.md5(string.encode()).hexdigest()
-    return uuid.UUID(md5)
-
-
 def image_to_base64(img, format='PNG'):
     """Convert a Pillow image to a base64 string