From 26cd75696c121aa3e61207abc26a77447be90cf4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jordi=20Puiggen=C3=A9?= <jp@naralabs.com>
Date: Thu, 5 Dec 2024 14:14:23 +0100
Subject: [PATCH] Fix method is not updated on instrument update in worksheet

---
 src/bika/lims/content/instrument.py | 30 ++++++++++-----
 src/bika/lims/content/worksheet.py  | 57 +++++++++++++++++------------
 2 files changed, 55 insertions(+), 32 deletions(-)

diff --git a/src/bika/lims/content/instrument.py b/src/bika/lims/content/instrument.py
index f1ad9af24d..6495ff2b58 100644
--- a/src/bika/lims/content/instrument.py
+++ b/src/bika/lims/content/instrument.py
@@ -66,6 +66,7 @@
 from senaite.core.catalog import SETUP_CATALOG
 from senaite.core.exportimport import instruments
 from senaite.core.p3compat import cmp
+from zope.deprecation import deprecate
 from zope.interface import implements
 
 schema = BikaFolderSchema.copy() + BikaSchema.copy() + Schema((
@@ -160,15 +161,21 @@
     ),
 
     UIDReferenceField(
-        'Methods',
-        vocabulary='_getAvailableMethods',
-        allowed_types=('Method',),
-        relationship='InstrumentMethods',
+        "Methods",
+        vocabulary="_getAvailableMethods",
+        allowed_types=("Method",),
+        relationship="InstrumentMethods",
         required=0,
         multiValued=1,
         widget=PicklistWidget(
             size=10,
-            label=_("Methods"),
+            label=_(u"label_instrument_methods",
+                    default=u"Supported methods"),
+            description=_(
+                u"description_instrument_methods",
+                default=u"Methods that are supported by this analytical "
+                        u"instrument"
+            )
         ),
     ),
 
@@ -414,11 +421,16 @@ def getMaintenanceTypesList(self):
     def getCalibrationAgentsList(self):
         return getCalibrationAgents(self)
 
+    def getRawMethods(self):
+        """Returns the UIDs of the methods supported by this instrument
+
+        :returns: Method UIDs
+        """
+        return self.getField("Methods").getRaw(self)
+
+    @deprecate("Use getRawMethods instead")
     def getMethodUIDs(self):
-        uids = []
-        if self.getMethods():
-            uids = [m.UID() for m in self.getMethods()]
-        return uids
+        return self.getRawMethods()
 
     def _getAvailableMethods(self):
         """ Returns the available (active) methods.
diff --git a/src/bika/lims/content/worksheet.py b/src/bika/lims/content/worksheet.py
index f0b51372d2..c05b1930a3 100644
--- a/src/bika/lims/content/worksheet.py
+++ b/src/bika/lims/content/worksheet.py
@@ -1194,36 +1194,47 @@ def getNumberOfRegularSamples(self):
         return len(set(samples))
 
     def setInstrument(self, instrument, override_analyses=False):
-        """ Sets the specified instrument to the Analysis from the
-            Worksheet. Only sets the instrument if the Analysis
-            allows it, according to its Analysis Service and Method.
-            If an analysis has already assigned an instrument, it won't
-            be overriden.
-            The Analyses that don't allow the instrument specified will
-            not be modified.
-            Returns the number of analyses affected
+        """Assigns the specified analytical instrument to the analyses in this
+        worksheet that are compatible with the instrument. The system will
+        attempt to assign the first method supported by the instrument that is
+        also compatible with each analysis.
+
+        By default, the instrument and method assigned to the analysis won't be
+        replaced unless the analysis does not have an instrument assigned yet
+        or the parameter override_analyses is set to True. Analyses that are
+        incompatible with the specified instrument will remain unchanged.
         """
-        analyses = [an for an in self.getAnalyses()
-                    if (not an.getInstrument() or override_analyses) and
-                    an.isInstrumentAllowed(instrument)]
+        analyses = self.getAnalyses()
+        instrument = api.get_object(instrument, default=None)
+
+        # find out the methods supported by the instrument, if any
+        supported_methods = instrument.getRawMethods() if instrument else []
+
         total = 0
         for an in analyses:
-            # An analysis can be done using differents Methods.
-            # Un method can be supported by more than one Instrument,
-            # but not all instruments support one method.
-            # We must force to set the instrument's method too. Otherwise,
-            # the WS manage results view will display the an's default
-            # method and its instruments displaying, only the instruments
-            # for the default method in the picklist.
-            instr_methods = instrument.getMethods()
-            meth = instr_methods[0] if instr_methods else None
-            if meth and an.isMethodAllowed(meth):
-                if an.getMethod() not in instr_methods:
-                    an.setMethod(meth)
 
+            if not override_analyses and an.getRawInstrument():
+                # skip, no overwrite analysis if an instrument is set
+                continue
+
+            if not an.isInstrumentAllowed(instrument):
+                # skip, instrument cannot run this analysis
+                continue
+
+            # assign the instrument
             an.setInstrument(instrument)
             total += 1
 
+            if an.getRawMethod() in supported_methods:
+                # the analysis method is supported by this instrument
+                continue
+
+            # reset and try to assign the first supported method
+            allowed = an.getRawAllowedMethods()
+            methods = list(filter(lambda m: m in allowed, supported_methods))
+            method = methods[0] if methods else None
+            an.setMethod(method)
+
         self.getField('Instrument').set(self, instrument)
         return total