Passed
Push — master ( 5e306e...e48f16 )
by Jordi
04:40
created

Client.manage_delObjects()   B

Complexity

Conditions 6

Size

Total Lines 19
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 13
dl 0
loc 19
rs 8.6666
c 0
b 0
f 0
cc 6
nop 3
1
# -*- coding: utf-8 -*-
2
#
3
# This file is part of SENAITE.CORE
4
#
5
# Copyright 2018 by it's authors.
6
# Some rights reserved. See LICENSE.rst, CONTRIBUTORS.rst.
7
8
import sys
9
10
from AccessControl import ClassSecurityInfo
11
from AccessControl import Unauthorized
12
from Products.ATContentTypes.content import schemata
13
from Products.Archetypes.public import BooleanField
14
from Products.Archetypes.public import BooleanWidget
15
from Products.Archetypes.public import ReferenceField
16
from Products.Archetypes.public import ReferenceWidget
17
from Products.Archetypes.public import Schema
18
from Products.Archetypes.public import SelectionWidget
19
from Products.Archetypes.public import StringField
20
from Products.Archetypes.public import StringWidget
21
from Products.Archetypes.public import registerType
22
from Products.Archetypes.utils import DisplayList
23
from Products.CMFCore import permissions
24
from Products.CMFCore.PortalFolder import PortalFolderBase as PortalFolder
25
from Products.CMFCore.utils import _checkPermission
26
from bika.lims import _
27
from bika.lims import api
28
from bika.lims.config import ARIMPORT_OPTIONS
29
from bika.lims.config import DECIMAL_MARKS
30
from bika.lims.config import PROJECTNAME
31
from bika.lims.content.attachment import Attachment
32
from bika.lims.content.organisation import Organisation
33
from bika.lims.interfaces import IClient, IDeactivable
34
from zope.interface import implements
35
36
schema = Organisation.schema.copy() + Schema((
37
    StringField(
38
        "ClientID",
39
        required=1,
40
        searchable=True,
41
        validators=("uniquefieldvalidator", "standard_id_validator"),
42
        widget=StringWidget(
43
            label=_("Client ID"),
44
        ),
45
    ),
46
47
    BooleanField(
48
        "BulkDiscount",
49
        default=False,
50
        widget=BooleanWidget(
51
            label=_("Bulk discount applies"),
52
        ),
53
    ),
54
55
    BooleanField(
56
        "MemberDiscountApplies",
57
        default=False,
58
        widget=BooleanWidget(
59
            label=_("Member discount applies"),
60
        ),
61
    ),
62
63
    StringField(
64
        "CCEmails",
65
        schemata="Preferences",
66
        mode="rw",
67
        widget=StringWidget(
68
            label=_("CC Emails"),
69
            description=_(
70
                "Default Emails to CC all published Samples for this client"),
71
            visible={
72
                "edit": "visible",
73
                "view": "visible",
74
            },
75
        ),
76
    ),
77
78
    ReferenceField(
79
        "DefaultCategories",
80
        schemata="Preferences",
81
        required=0,
82
        multiValued=1,
83
        vocabulary="getAnalysisCategories",
84
        vocabulary_display_path_bound=sys.maxint,
85
        allowed_types=("AnalysisCategory",),
86
        relationship="ClientDefaultCategories",
87
        widget=ReferenceWidget(
88
            checkbox_bound=0,
89
            label=_("Default categories"),
90
            description=_(
91
                "Always expand the selected categories in client views"),
92
        ),
93
    ),
94
95
    ReferenceField(
96
        "RestrictedCategories",
97
        schemata="Preferences",
98
        required=0,
99
        multiValued=1,
100
        vocabulary="getAnalysisCategories",
101
        validators=("restrictedcategoriesvalidator",),
102
        vocabulary_display_path_bound=sys.maxint,
103
        allowed_types=("AnalysisCategory",),
104
        relationship="ClientRestrictedCategories",
105
        widget=ReferenceWidget(
106
            checkbox_bound=0,
107
            label=_("Restrict categories"),
108
            description=_("Show only selected categories in client views"),
109
        ),
110
    ),
111
112
    BooleanField(
113
        "DefaultDecimalMark",
114
        schemata="Preferences",
115
        default=True,
116
        widget=BooleanWidget(
117
            label=_("Default decimal mark"),
118
            description=_(
119
                "The decimal mark selected in Bika Setup will be used."),
120
        )
121
    ),
122
123
    StringField(
124
        "DecimalMark",
125
        schemata="Preferences",
126
        vocabulary=DECIMAL_MARKS,
127
        default=".",
128
        widget=SelectionWidget(
129
            label=_("Custom decimal mark"),
130
            description=_(
131
                "Decimal mark to use in the reports from this Client."),
132
            format="select",
133
        )
134
    ),
135
))
136
137
schema["title"].widget.visible = False
138
schema["description"].widget.visible = False
139
schema["EmailAddress"].schemata = "default"
140
141
schema.moveField("ClientID", after="Name")
142
143
144
class Client(Organisation):
145
    implements(IClient, IDeactivable)
146
147
    security = ClassSecurityInfo()
148
    displayContentsTab = False
149
    schema = schema
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable schema does not seem to be defined.
Loading history...
150
    _at_rename_after_creation = True
151
152
    def _renameAfterCreation(self, check_auto_id=False):
153
        from bika.lims.idserver import renameAfterCreation
154
        renameAfterCreation(self)
155
156
    def Title(self):
157
        """Return the Organisation's Name as its title
158
        """
159
        return self.getName()
160
161
    security.declarePublic("getContactFromUsername")
162
163
    def getContactFromUsername(self, username):
164
        for contact in self.objectValues("Contact"):
165
            if contact.getUsername() == username:
166
                return contact.UID()
167
168
    security.declarePublic("getContactUIDForUser")
169
170
    def getContactUIDForUser(self):
171
        """Get the UID of the user associated with the authenticated user
172
        """
173
        membership_tool = api.get_tool("portal_membership")
174
        member = membership_tool.getAuthenticatedMember()
175
        username = member.getUserName()
176
        r = self.portal_catalog(
177
            portal_type="Contact",
178
            getUsername=username
179
        )
180
        if len(r) == 1:
181
            return r[0].UID
182
183
    security.declarePublic("getARImportOptions")
184
185
    def getARImportOptions(self):
186
        return ARIMPORT_OPTIONS
187
188
    security.declarePublic("getAnalysisCategories")
189
190
    def getAnalysisCategories(self):
191
        """Return all available analysis categories
192
        """
193
        bsc = api.get_tool("bika_setup_catalog")
194
        cats = []
195
        for st in bsc(portal_type="AnalysisCategory",
196
                      is_active=True,
197
                      sort_on="sortable_title"):
198
            cats.append((st.UID, st.Title))
199
        return DisplayList(cats)
200
201
    def getContacts(self, only_active=True):
202
        """Return an array containing the contacts from this Client
203
        """
204
        contacts = self.objectValues("Contact")
205
        if only_active:
206
            contacts = filter(api.is_active, contacts)
207
        return contacts
208
209
    def getDecimalMark(self):
210
        """Return the decimal mark to be used on reports for this client
211
212
        If the client has DefaultDecimalMark selected, the Default value from
213
        the LIMS Setup will be returned.
214
215
        Otherwise, will return the value of DecimalMark.
216
        """
217
        if self.getDefaultDecimalMark() is False:
218
            return self.Schema()["DecimalMark"].get(self)
219
        return self.bika_setup.getDecimalMark()
220
221
    def getCountry(self, default=None):
222
        """Return the Country from the Physical or Postal Address
223
        """
224
        physical_address = self.getPhysicalAddress().get("country", default)
225
        postal_address = self.getPostalAddress().get("country", default)
226
        return physical_address or postal_address
227
228
    def getProvince(self, default=None):
229
        """Return the Province from the Physical or Postal Address
230
        """
231
        physical_address = self.getPhysicalAddress().get("state", default)
232
        postal_address = self.getPostalAddress().get("state", default)
233
        return physical_address or postal_address
234
235
    def getDistrict(self, default=None):
236
        """Return the Province from the Physical or Postal Address
237
        """
238
        physical_address = self.getPhysicalAddress().get("district", default)
239
        postal_address = self.getPostalAddress().get("district", default)
240
        return physical_address or postal_address
241
242
    # TODO Security Make Attachments live inside ARs (instead of Client)
243
    # Since the Attachments live inside Client, we are forced here to overcome
244
    # the DeleteObjects permission when objects to delete are from Attachment
245
    # type. And we want to keep the DeleteObjects permission at Client level
246
    # because is the main container for Samples!
247
    # For some statuses of the AnalysisRequest type (e.g. received), the
248
    # permission "DeleteObjects" is granted, allowing the user to remove e.g.
249
    # analyses. Attachments are closely bound to Analysis and Samples, so they
250
    # should live inside Analysis Request.
251
    # Then, we will be able to remove this function from here
252
    def manage_delObjects(self, ids=None, REQUEST=None):
253
        """Overrides parent function. If the ids passed in are from Attachment
254
        types, the function ignores the DeleteObjects permission. For the rest
255
        of types, it works as usual (checks the permission)
256
        """
257
        if ids is None:
258
            ids = []
259
        if isinstance(ids, basestring):
260
            ids = [ids]
261
262
        for id in ids:
263
            item = self._getOb(id)
264
            if isinstance(item, Attachment):
265
                # Ignore DeleteObjects permission check
266
                continue
267
            if not _checkPermission(permissions.DeleteObjects, item):
268
                raise Unauthorized, (
269
                    "Do not have permissions to remove this object")
270
        return PortalFolder.manage_delObjects(self, ids, REQUEST=REQUEST)
271
272
273
schemata.finalizeATCTSchema(schema, folderish=True, moveDiscussion=False)
274
275
registerType(Client, PROJECTNAME)
276