diff --git a/core/models/caves.py b/core/models/caves.py
index 4e963fb..db86e66 100644
--- a/core/models/caves.py
+++ b/core/models/caves.py
@@ -181,13 +181,13 @@ class Cave(TroggleModel):
         return QM.objects.filter(cave=self)
         
 
-    def new_QM_number(self, year=datetime.date.today().year):
-            """Given a cave and the current year, returns the next QM number."""
-            try:
-                res=QM.objects.filter(found_by__date__year=year, found_by__cave_slug=self.slug).order_by('-number')[0]
-            except IndexError:
-                return 1
-            return res.number+1
+    # def new_QM_number(self, year=datetime.date.today().year):
+            # """Given a cave and the current year, returns the next QM number."""
+            # try:
+                # res=QM.objects.filter(found_by__date__year=year, found_by__cave_slug=self.slug).order_by('-number')[0]
+            # except IndexError:
+                # return 1
+            # return res.number+1
 
     def kat_area(self):
         for a in self.area.all():
@@ -460,17 +460,17 @@ class LogbookEntry(TroggleModel):
     def get_previous_by_id(self):
         LogbookEntry.objects.get(id=self.id-1)
 
-    def new_QM_number(self):
-        """Returns  """
-        if self.cave:
-            nextQMnumber=self.cave.new_QM_number(self.date.year)
-        else:
-            return None
-        return nextQMnumber
+    # def new_QM_number(self):
+        # """Returns  """
+        # if self.cave:
+            # nextQMnumber=self.cave.new_QM_number(self.date.year)
+        # else:
+            # return None
+        # return nextQMnumber
 
-    def new_QM_found_link(self):
-        """Produces a link to a new QM with the next number filled in and this LogbookEntry set as 'found by' """
-        return settings.URL_ROOT + r'/admin/core/qm/add/?' + r'found_by=' + str(self.pk) +'&number=' + str(self.new_QM_number())
+    # def new_QM_found_link(self):
+        # """Produces a link to a new QM with the next number filled in and this LogbookEntry set as 'found by' """
+        # return settings.URL_ROOT + r'/admin/core/qm/add/?' + r'found_by=' + str(self.pk) +'&number=' + str(self.new_QM_number())
 
     def DayIndex(self):
         return list(self.expeditionday.logbookentry_set.all()).index(self)
@@ -480,16 +480,18 @@ class QM(TroggleModel):
     "Number","Grade","Area","Description","Page reference","Nearest station","Completion description","Comment"
     """
     cave = models.ForeignKey(Cave, related_name='QMs',blank=True, null=True,on_delete=models.SET_NULL )
+    blockname=models.TextField(blank=True,null=True) # NB truncated copy of survexblock name
+    expoyear    = models.CharField(max_length=4,blank=True, null=True) # could change to datetime if logbooks similarly chnaged
     found_by = models.ForeignKey(LogbookEntry, related_name='QMs_found',blank=True, null=True,on_delete=models.SET_NULL )
     ticked_off_by = models.ForeignKey(LogbookEntry, related_name='QMs_ticked_off',blank=True, null=True,on_delete=models.SET_NULL)
-    number = models.IntegerField(help_text="this is the sequential number in the year", )
+    number = models.IntegerField(help_text="this is the sequential number in the year, only unique for CSV imports", )
     GRADE_CHOICES=(
     ('A', 'A: Large obvious lead'),
     ('B', 'B: Average lead'),
     ('C', 'C: Tight unpromising lead'),
     ('D', 'D: Dig'),
     ('X', 'X: Unclimbable aven')
-    ) # also seen "?" 
+    ) # also seen "?" in imported data - see urls.py
     grade = models.CharField(max_length=1, choices=GRADE_CHOICES)
     location_description = models.TextField(blank=True)
     nearest_station_description = models.CharField(max_length=400,blank=True, null=True)
@@ -498,26 +500,21 @@ class QM(TroggleModel):
     area = models.CharField(max_length=100,blank=True, null=True)
     completion_description = models.TextField(blank=True,null=True)
     comment=models.TextField(blank=True,null=True)
-    blockname=models.TextField(blank=True,null=True)
 
     def __str__(self):
         return f'{self.code()}'
 
     def code(self):
-        if self.found_by:
-            return f'{str(self.found_by.cave_slug)[5:]}-{self.found_by.date.year}-{self.blockname}{self.number}{self.grade}' 
-        else:
-            return f'{self.cave.slug()[5:]}-xxxx-{self.blockname}{self.number}{self.grade}'
-
+        return f'{str(self.found_by.cave_slug)[5:]}-{self.expoyear}-{self.blockname}{self.number}{self.grade}' 
+             
+    def newslug(self):
+        qmslug = f'{str(self.cave)}-{self.expoyear}-{self.blockname}{self.number}{self.grade}' 
+        return qmslug
+        
     def get_absolute_url(self):
-        if self.found_by:
-            qmyear = self.found_by.date.year
-        else:
-            qmyear = "1986" # HACK to check if other bits work
         #return settings.URL_ROOT + '/cave/' + self.found_by.cave.kataster_number + '/' + str(self.found_by.date.year) + '-' + '%02d' %self.number
-        return urljoin(settings.URL_ROOT, reverse('qm',kwargs={'cave_id':self.cave.slug(),'year':qmyear, 'blockname':self.blockname,'qm_id':self.number,'grade':self.grade}))
+        return urljoin(settings.URL_ROOT, reverse('qm',kwargs={'cave_id':self.cave.slug(),'year':self.expoyear, 'blockname':self.blockname,'qm_id':self.number,'grade':self.grade}))
             
-
     def get_next_by_id(self):
         return QM.objects.get(id=self.id+1)
 
diff --git a/core/views/caves.py b/core/views/caves.py
index 1b47c39..616e69f 100644
--- a/core/views/caves.py
+++ b/core/views/caves.py
@@ -481,26 +481,48 @@ def caveQMs(request, slug):
     else:
         return render(request,'cave_qms.html', {'cave': cave})
 
-def qm(request,cave_id,qm_id,year,grade=None, blockname=""):
+def qm(request,cave_id,qm_id,year,grade=None, blockname=None):
     '''Reports on one specific QM
-    Fixed and working July 2022, for both CSV imported QMs and for survex-imported QMs,
+    Fixed and working July 2022, for both CSV imported QMs 
     needs refactoring though.
+    For survex-imported QMs we are not getting unique objects returned, so the query needs fixing.
     '''
     year=int(year)
-
-    try:
-        c=getCave(cave_id)
-        manyqms=c.get_QMs()
-        qm=manyqms.get(number=qm_id,found_by__date__year=year, found_by__cave_slug=c.slug()) 
-        return render(request,'qm.html', {'qm': qm})
-    except QM.DoesNotExist:
-        return render(request,'errors/badslug.html', {'badslug': f'{cave_id=} {year=} {qm_id=} {blockname=}'})
-    except MultipleObjectsReturned:
-        qm = manyqms.filter(blockname = blockname)  # we could still get multiple objects..
-        if qm:
+    
+    if blockname == 'None':
+        # CSV import QMs, use old technique
+        try:
+            c=getCave(cave_id)
+            manyqms=c.get_QMs()
+            qm=manyqms.get(number=qm_id,found_by__date__year=year, found_by__cave_slug=c.slug()) 
             return render(request,'qm.html', {'qm': qm})
-        else:
+        except QM.DoesNotExist:
             return render(request,'errors/badslug.html', {'badslug': f'{cave_id=} {year=} {qm_id=} {blockname=}'})
+
+    else:
+        # survex import QMs, need to disambiguate with blockname
+        # slug = f'{str(self.cave)}-{self.found_by.date.year}-{self.blockname}{self.number}{self.grade}' 
+
+        try:
+            qmslug = f'{cave_id}-{year}-{blockname=}{qm_id}{grade}'
+            c=getCave(cave_id)
+            manyqms=c.get_QMs()
+            qm=manyqms.get(found_by__date__year=year, blockname=blockname, number=qm_id, grade=grade) 
+            if qm:
+                print(qm, f'{qmslug=}:{cave_id=} {year=} {qm_id=} {blockname=} {qm.expoyear=}')
+                return render(request,'qm.html', {'qm': qm})
+            else:
+                return render(request,'errors/badslug.html', {'badslug': f'{cave_id=} {year=} {qm_id=} {blockname=}'})
+        except MultipleObjectsReturned:
+            if len(qm) > 1:           
+                for q in qm:
+                    print(qm)
+                return render(request,'qm.html', {'qm': qm[0]})
+            else:
+                return render(request,'qm.html', {'qm': qm})
+        except QM.DoesNotExist:
+            return render(request,'errors/badslug.html', {'badslug': f'{cave_id=} {year=} {qm_id=} {blockname=}'})
+
  
 def get_qms(request, caveslug):
     '''Does not crash, but just returns a text list of the entrances for a cave. 
diff --git a/parsers/QMs.py b/parsers/QMs.py
index ca6583b..ab9ec0f 100644
--- a/parsers/QMs.py
+++ b/parsers/QMs.py
@@ -103,14 +103,17 @@ def parseCaveQMs(cave,inputFile):
 
             newQM.comment=line[6]
             try:
+                # year and number are unique for a cave in CSV imports
                 preexistingQM=QM.objects.get(number=QMnum, found_by__date__year=year)  #if we don't have this one in the DB, save it
-                if preexistingQM.new_since_parsing==False:  #if the pre-existing QM has not been modified, overwrite it
+                if preexistingQM.new_since_parsing==False:  #if the pre-existing QM has not been modified, overwrite it - VERY OLD THING
                     preexistingQM.delete()
+                    newQM.expoyear = year
                     newQM.save()
                 else:  # otherwise, print that it was ignored
                     print((" - preserving " + str(preexistingQM) + ", which was edited in admin \r"))
                     
             except QM.DoesNotExist:         #if there is no pre-existing QM, save the new one
+                newQM.expoyear = year
                 newQM.save() 
             nqms += 1   
         except KeyError: #check on this one
@@ -143,17 +146,22 @@ def parse_KH_QMs(kh, inputFile):
                 # print(message)
                 # DataIssue.objects.create(parser='QMs', message=message)
             lookupArgs={
+                # inadequate now that the sequence numbers are not unique for survex-parsed QMs
                 'found_by':placeholder,
-                'number':res['number']
+                'expoyear':year,
+                'number':res['number'],
+                'cave': kh,
+                'grade':res['grade']
                 }
             nonLookupArgs={
-                'cave': kh,
-                'grade':res['grade'],
                 'nearest_station_name':res['nearest_station'],
                 'location_description':res['description']
                 }
- 
-            save_carefully(QM,lookupArgs,nonLookupArgs)
+            instance, created = save_carefully(QM,lookupArgs,nonLookupArgs) 
+            if created:
+               message = " ! - "+ instance.code() + " QM entry for '161 KH' created. "
+               print(message)
+               DataIssue.objects.create(parser='QMs', message=message)
             nqms += 1
     return nqms
             
diff --git a/parsers/survex.py b/parsers/survex.py
index 1390991..fa92924 100644
--- a/parsers/survex.py
+++ b/parsers/survex.py
@@ -557,8 +557,8 @@ class LoadingSurvex():
                         entry_type="DUMMY", 
                         expedition_id=1, 
                         defaults={"date": survexblock.date,"cave_slug":caveslug, "slug": logslug})
-        print(insp+message)
-        DataIssue.objects.create(parser='survex', message=message)
+        # print(insp+message)
+        # DataIssue.objects.create(parser='survex', message=message)
   
         try:
             qm = QM.objects.create(number=qm_no,
@@ -569,6 +569,7 @@ class LoadingSurvex():
                                               location_description=qm_notes,
                                               blockname = blockname, # only set for survex-imported QMs
                                               found_by = placeholder,
+                                              expoyear = str(survexblock.date.year),
                                               cave = survexblock.survexfile.cave)
             qm.save
             # message = " ! QM{} '{}' CREATED in DB in '{}'".format(qm_no, qm_nearest,survexblock.survexfile.path)
diff --git a/urls.py b/urls.py
index 462f696..e373a42 100644
--- a/urls.py
+++ b/urls.py
@@ -177,7 +177,8 @@ trogglepatterns = [
 
 # QMs pages - must precede other /caves pages?
     re_path(r'^cave/qms/([^/]+)/?$', caveQMs, name="caveQMs"), # Fixed. July 2022
-    re_path(r'^cave/qms/(?P<cave_id>[^/]+)/(?P<year>\d\d\d\d)-(?P<blockname>[^0-9]*)?(?P<qm_id>\d*)(?P<grade>[ABCDXV\?]?)?$', qm, name="qm"), # Fixed. July 2022
+    re_path(r'^cave/qms/(?P<cave_id>[^/]+)/(?P<year>\d\d\d\d)-(?P<blockname>[^0-9]*)?(?P<qm_id>\d*)(?P<grade>[ABCDXV\?]?)?$', qm, name="qm"), # Dogs breakfast
+    # the  resolution of a QM uses several fields together, there is no clean slug field. Artefact of history.
 
 #   Prospecting Guide document
     re_path(r'^prospecting_guide/$', prospecting),    # disabled. Bad links, incompatible image package use and very, very out of date.