Passed
Push — master ( f42093...226b52 )
by Ramon
04:24
created

bika.lims.content.analysisprofile   A

Complexity

Total Complexity 12

Size/Duplication

Total Lines 239
Duplicated Lines 8.79 %

Importance

Changes 0
Metric Value
wmc 12
eloc 133
dl 21
loc 239
rs 10
c 0
b 0
f 0

7 Methods

Rating   Name   Duplication   Size   Complexity  
A AnalysisProfile.getVATAmount() 0 5 1
A AnalysisProfile.getAnalysisServiceSettings() 0 9 2
A AnalysisProfile.isAnalysisServiceHidden() 21 21 5
A AnalysisProfile._renameAfterCreation() 0 3 1
A AnalysisProfile.remove_service() 0 24 1
A AnalysisProfile.getPrice() 0 4 1
A AnalysisProfile.getTotalPrice() 0 6 1

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
# -*- coding: utf-8 -*-
2
#
3
# This file is part of SENAITE.CORE.
4
#
5
# SENAITE.CORE is free software: you can redistribute it and/or modify it under
6
# the terms of the GNU General Public License as published by the Free Software
7
# Foundation, version 2.
8
#
9
# This program is distributed in the hope that it will be useful, but WITHOUT
10
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12
# details.
13
#
14
# You should have received a copy of the GNU General Public License along with
15
# this program; if not, write to the Free Software Foundation, Inc., 51
16
# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17
#
18
# Copyright 2018-2019 by it's authors.
19
# Some rights reserved, see README and LICENSE.
20
21
"""
22
    AnalysisRequests often use the same configurations.
23
    AnalysisProfile is used to save these common configurations (templates).
24
"""
25
26
from AccessControl import ClassSecurityInfo
27
from bika.lims import api
28
from bika.lims import PMF, bikaMessageFactory as _
29
from bika.lims.browser.fields.remarksfield import RemarksField
30
from bika.lims.browser.widgets import AnalysisProfileAnalysesWidget
31
from bika.lims.browser.widgets import RemarksWidget
32
from bika.lims.browser.widgets import ServicesWidget
33
from bika.lims.config import PROJECTNAME
34
from bika.lims.content.bikaschema import BikaSchema
35
from Products.Archetypes.public import *
36
37
from bika.lims.content.clientawaremixin import ClientAwareMixin
38
from bika.lims.interfaces import IAnalysisProfile, IDeactivable
39
from Products.Archetypes.references import HoldingReference
40
from Products.ATExtensions.field import RecordsField
41
from Products.CMFCore.permissions import View, ModifyPortalContent
42
from Products.CMFCore.utils import getToolByName
43
from zope.interface import Interface, implements
44
import sys
45
from bika.lims.interfaces import IAnalysisProfile
46
from bika.lims.interfaces import IClient
47
48
schema = BikaSchema.copy() + Schema((
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable Schema does not seem to be defined.
Loading history...
49
    StringField('ProfileKey',
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable StringField does not seem to be defined.
Loading history...
50
        widget = StringWidget(
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable StringWidget does not seem to be defined.
Loading history...
51
            label = _("Profile Keyword"),
52
            description = _("The profile's keyword is used to uniquely identify " + \
53
                          "it in import files. It has to be unique, and it may " + \
54
                          "not be the same as any Calculation Interim field ID."),
55
        ),
56
    ),
57
    ReferenceField('Service',
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable ReferenceField does not seem to be defined.
Loading history...
58
        schemata = 'Analyses',
59
        required = 1,
60
        multiValued = 1,
61
        allowed_types = ('AnalysisService',),
62
        relationship = 'AnalysisProfileAnalysisService',
63
        widget = AnalysisProfileAnalysesWidget(
64
            label = _("Profile Analyses"),
65
            description = _("The analyses included in this profile, grouped per category"),
66
        )
67
    ),
68
    RemarksField('Remarks',
69
        searchable=True,
70
        widget=RemarksWidget(
71
            label=_("Remarks")
72
        )
73
    ),
74
    # Custom settings for the assigned analysis services
75
    # https://jira.bikalabs.com/browse/LIMS-1324
76
    # Fields:
77
    #   - uid: Analysis Service UID
78
    #   - hidden: True/False. Hide/Display in results reports
79
    RecordsField('AnalysisServicesSettings',
80
         required=0,
81
         subfields=('uid', 'hidden',),
82
         widget=ComputedWidget(visible=False),
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable ComputedWidget does not seem to be defined.
Loading history...
83
    ),
84
    StringField('CommercialID',
85
        searchable=1,
86
        required=0,
87
        schemata='Accounting',
88
        widget=StringWidget(
89
            visible={'view': 'visible', 'edit': 'visible'},
90
            label=_('Commercial ID'),
91
            description=_("The profile's commercial ID for accounting purposes."),
92
        ),
93
    ),
94
    # When it's set, the system uses the analysis profile's price to quote and the system's VAT is overridden by the
95
    # the analysis profile's specific VAT
96
    BooleanField('UseAnalysisProfilePrice',
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable BooleanField does not seem to be defined.
Loading history...
97
        default=False,
98
        schemata='Accounting',
99
        widget=BooleanWidget(
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable BooleanWidget does not seem to be defined.
Loading history...
100
            label=_("Use Analysis Profile Price"),
101
            description=_("When it's set, the system uses the analysis profile's price to quote and the system's VAT is"
102
                          " overridden by the analysis profile's specific VAT"),
103
        )
104
    ),
105
    # The price will only be used if the checkbox "use analysis profiles' price" is set.
106
    # This price will be used to quote the analyses instead of analysis service's price.
107
    FixedPointField('AnalysisProfilePrice',
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable FixedPointField does not seem to be defined.
Loading history...
108
        schemata="Accounting",
109
        default='0.00',
110
        widget=DecimalWidget(
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable DecimalWidget does not seem to be defined.
Loading history...
111
            label = _("Price (excluding VAT)"),
112
            visible={'view': 'visible', 'edit': 'visible'},
113
        ),
114
    ),
115
    # When the checkbox "use analysis profiles' price" is set, the AnalysisProfilesVAT should override
116
    # the system's VAT
117
    FixedPointField('AnalysisProfileVAT',
118
        schemata = "Accounting",
119
        default = '14.00',
120
        widget = DecimalWidget(
121
            label=_("VAT %"),
122
            description=_(
123
                "Enter percentage value eg. 14.0. This percentage is applied on the Analysis Profile only, overriding "
124
                "the systems VAT"),
125
                visible={'view': 'visible', 'edit': 'visible'},
126
        )
127
    ),
128
    # This VAT amount is computed using the AnalysisProfileVAT instead of systems VAT
129
    ComputedField('VATAmount',
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable ComputedField does not seem to be defined.
Loading history...
130
        schemata="Accounting",
131
        expression='context.getVATAmount()',
132
        widget=ComputedWidget(
133
            label = _("VAT"),
134
            visible={'view': 'visible', 'edit': 'invisible'},
135
            ),
136
    ),
137
    ComputedField('TotalPrice',
138
          schemata="Accounting",
139
          expression='context.getTotalPrice()',
140
          widget=ComputedWidget(
141
              label = _("Total price"),
142
              visible={'edit': 'hidden', }
143
          ),
144
    ),
145
)
146
)
147
schema['title'].widget.visible = True
148
schema['description'].widget.visible = True
149
IdField = schema['id']
150
151
152
class AnalysisProfile(BaseContent, ClientAwareMixin):
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable BaseContent does not seem to be defined.
Loading history...
153
    security = ClassSecurityInfo()
154
    schema = schema
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable schema does not seem to be defined.
Loading history...
155
    displayContentsTab = False
156
    implements(IAnalysisProfile, IDeactivable)
157
158
    _at_rename_after_creation = True
159
    def _renameAfterCreation(self, check_auto_id=False):
160
        from bika.lims.idserver import renameAfterCreation
161
        renameAfterCreation(self)
162
163
    def getAnalysisServiceSettings(self, uid):
164
        """ Returns a dictionary with the settings for the analysis
165
            service that match with the uid provided.
166
            If there are no settings for the analysis service and
167
            profile, returns a dictionary with the key 'uid'
168
        """
169
        sets = [s for s in self.getAnalysisServicesSettings() \
170
                if s.get('uid','') == uid]
171
        return sets[0] if sets else {'uid': uid}
172
173 View Code Duplication
    def isAnalysisServiceHidden(self, uid):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
174
        """ Checks if the analysis service that match with the uid
175
            provided must be hidden in results.
176
            If no hidden assignment has been set for the analysis in
177
            this profile, returns the visibility set to the analysis
178
            itself.
179
            Raise a TypeError if the uid is empty or None
180
            Raise a ValueError if there is no hidden assignment in this
181
                profile or no analysis service found for this uid.
182
        """
183
        if not uid:
184
            raise TypeError('None type or empty uid')
185
        sets = self.getAnalysisServiceSettings(uid)
186
        if 'hidden' not in sets:
187
            uc = getToolByName(self, 'uid_catalog')
188
            serv = uc(UID=uid)
189
            if serv and len(serv) == 1:
190
                return serv[0].getObject().getRawHidden()
191
            else:
192
                raise ValueError('%s is not valid' % uid)
193
        return sets.get('hidden', False)
194
195
    def getVATAmount(self):
196
        """ Compute AnalysisProfileVATAmount
197
        """
198
        price, vat = self.getAnalysisProfilePrice(), self.getAnalysisProfileVAT()
199
        return float(price) * float(vat) / 100
200
201
    def getPrice(self):
202
        """Returns the price of the profile, without VAT
203
        """
204
        return self.getAnalysisProfilePrice()
205
206
    def getTotalPrice(self):
207
        """
208
        Computes the final price using the VATAmount and the subtotal price
209
        """
210
        price, vat = self.getAnalysisProfilePrice(), self.getVATAmount()
211
        return float(price) + float(vat)
212
213
    def remove_service(self, service):
214
        """Removes the service passed in from the services offered by the
215
        current Profile. If the Analysis Service passed in is not assigned to
216
        this Analysis Profile, returns False.
217
        :param service: the service to be removed from this Analysis Profile
218
        :type service: AnalysisService
219
        :return: True if the AnalysisService has been removed successfully
220
        """
221
        obj = api.get_object(service)
222
        uid = api.get_uid(obj)
223
224
        # Remove the service from the referenced services
225
        services = self.getService()
226
        num_services = len(services)
227
        services.remove(obj)
228
        self.setService(services)
229
        removed = len(services) < num_services
230
231
        # Remove the service from the settings map
232
        settings = self.getAnalysisServicesSettings()
233
        settings = [item for item in settings if item.get('uid', '') != uid]
234
        self.setAnalysisServicesSettings(settings)
235
236
        return removed
237
238
registerType(AnalysisProfile, PROJECTNAME)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable registerType does not seem to be defined.
Loading history...
239