Passed
Push — 2.x ( df5950...91c842 )
by Ramon
07:45
created

bika.lims.content.bikasetup   F

Complexity

Total Complexity 71

Size/Duplication

Total Lines 1400
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 71
eloc 889
dl 0
loc 1400
rs 2.431
c 0
b 0
f 0

35 Methods

Rating   Name   Duplication   Size   Complexity  
A IDFormattingField.getSequenceTypes() 0 5 1
A IDFormattingField.getCounterTypes() 0 5 1
A BikaSetup.getShowLabNameInLogin() 0 8 2
A BikaSetup.setAutoLogOff() 0 11 3
A BikaSetup.getPrefixFor() 0 9 2
A BikaSetup.isRejectionWorkflowEnabled() 0 11 3
A BikaSetup.getRejectionReasonsItems() 0 9 4
A BikaSetup.getCategorizeSampleAnalyses() 0 8 2
A BikaSetup.getAutoLogOff() 0 8 2
A BikaSetup.getIDServerValuesHTML() 0 8 2
A BikaSetup.setEnableGlobalAuditlog() 0 7 2
A BikaSetup.setEmailFromSamplePublication() 0 7 2
A BikaSetup.getSampleAnalysesRequired() 0 8 2
A BikaSetup.getImmediateResultsEntry() 0 8 2
A BikaSetup.setAlwaysCCResponsiblesInReportEmail() 0 7 2
A BikaSetup.getEmailFromSamplePublication() 0 7 2
A BikaSetup.getAnalysisServicesVocabulary() 0 11 2
A BikaSetup.getCountries() 0 4 2
A BikaSetup.setSampleAnalysesRequired() 0 7 2
A BikaSetup.getDateSampledRequired() 0 8 2
A BikaSetup.setCategorizeSampleAnalyses() 0 7 2
A BikaSetup.setMaxNumberOfSamplesAdd() 0 9 2
A BikaSetup.setShowLabNameInLogin() 0 7 2
A BikaSetup.getEmailBodySamplePublication() 0 7 2
A BikaSetup.setAllowManualResultCaptureDate() 0 7 2
A BikaSetup.getInvalidationReasonRequired() 0 8 2
A BikaSetup.getMaxNumberOfSamplesAdd() 0 8 2
A BikaSetup.setImmediateResultsEntry() 0 7 2
A BikaSetup.getAlwaysCCResponsiblesInReportEmail() 0 7 2
A BikaSetup.setEmailBodySamplePublication() 0 7 2
A BikaSetup.getEnableGlobalAuditlog() 0 8 2
A BikaSetup._getNumberOfRequiredVerificationsVocabulary() 0 9 1
A BikaSetup.setDateSampledRequired() 0 7 2
A BikaSetup.getAllowManualResultCaptureDate() 0 8 2
A BikaSetup.setInvalidationReasonRequired() 0 7 2

How to fix   Complexity   

Complexity

Complex classes like bika.lims.content.bikasetup often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

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-2025 by it's authors.
19
# Some rights reserved, see README and LICENSE.
20
21
from AccessControl import ClassSecurityInfo
22
from bika.lims import api
23
from bika.lims import bikaMessageFactory as _
24
from bika.lims.browser.fields import DurationField
25
from bika.lims.browser.fields import UIDReferenceField
26
from bika.lims.browser.widgets import DurationWidget
27
from bika.lims.browser.widgets import RecordsWidget
28
from bika.lims.browser.widgets import RejectionSetupWidget
29
from bika.lims.browser.worksheet.tools import getWorksheetLayouts
30
from bika.lims.config import CURRENCIES
31
from bika.lims.config import DECIMAL_MARKS
32
from bika.lims.config import DEFAULT_WORKSHEET_LAYOUT
33
from bika.lims.config import MULTI_VERIFICATION_TYPE
34
from bika.lims.config import PROJECTNAME
35
from bika.lims.config import SCINOTATION_OPTIONS
36
from bika.lims.config import WEEKDAYS
37
from bika.lims.content.bikaschema import BikaFolderSchema
38
from bika.lims.interfaces import IBikaSetup
39
from plone.app.folder import folder
40
from Products.Archetypes.atapi import BooleanField
41
from Products.Archetypes.atapi import BooleanWidget
42
from Products.Archetypes.atapi import DecimalWidget
43
from Products.Archetypes.atapi import FixedPointField
44
from Products.Archetypes.atapi import InAndOutWidget
45
from Products.Archetypes.atapi import IntegerField
46
from Products.Archetypes.atapi import IntegerWidget
47
from Products.Archetypes.atapi import LinesField
48
from Products.Archetypes.atapi import Schema
49
from Products.Archetypes.atapi import SelectionWidget
50
from Products.Archetypes.atapi import StringField
51
from Products.Archetypes.atapi import TextAreaWidget
52
from Products.Archetypes.atapi import registerType
53
from Products.Archetypes.Field import TextField
54
from Products.Archetypes.utils import DisplayList
55
from Products.Archetypes.utils import IntDisplayList
56
from Products.Archetypes.Widget import RichWidget
57
from Products.Archetypes.Widget import StringWidget
58
from Products.CMFCore.utils import getToolByName
59
from senaite.core.api import geo
60
from senaite.core.browser.fields.records import RecordsField
61
from senaite.core.browser.widgets.referencewidget import ReferenceWidget
62
from senaite.core.interfaces import IHideActionsMenu
63
from senaite.core.interfaces import INumberGenerator
64
from senaite.core.p3compat import cmp
65
from zope.component import getUtility
66
from zope.interface import implements
67
68
69
class IDFormattingField(RecordsField):
70
    """A list of prefixes per portal_type
71
    """
72
    _properties = RecordsField._properties.copy()
73
    _properties.update({
74
        'type': 'prefixes',
75
        'subfields': (
76
            'portal_type',
77
            'form',
78
            'sequence_type',
79
            'context',
80
            'counter_type',
81
            'counter_reference',
82
            'prefix',
83
            'split_length'
84
        ),
85
        'subfield_labels': {
86
            'portal_type': 'Portal Type',
87
            'form': 'Format',
88
            'sequence_type': 'Seq Type',
89
            'context': 'Context',
90
            'counter_type': 'Counter Type',
91
            'counter_reference': 'Counter Ref',
92
            'prefix': 'Prefix',
93
            'split_length': 'Split Length',
94
        },
95
        'subfield_readonly': {
96
            'portal_type': False,
97
            'form': False,
98
            'sequence_type': False,
99
            'context': False,
100
            'counter_type': False,
101
            'counter_reference': False,
102
            'prefix': False,
103
            'split_length': False,
104
        },
105
        'subfield_sizes': {
106
            'portal_type': 20,
107
            'form': 30,
108
            'sequence_type': 1,
109
            'context': 12,
110
            'counter_type': 1,
111
            'counter_reference': 12,
112
            'prefix': 12,
113
            'split_length': 5,
114
        },
115
        'subfield_types': {
116
            'sequence_type': 'selection',
117
            'counter_type': 'selection',
118
            'split_length': 'int',
119
        },
120
        'subfield_vocabularies': {
121
            'sequence_type': 'getSequenceTypes',
122
            'counter_type': 'getCounterTypes',
123
        },
124
        'subfield_maxlength': {
125
            'form': 256,
126
        },
127
    })
128
129
    security = ClassSecurityInfo()
130
131
    def getSequenceTypes(self, instance=None):
132
        return DisplayList([
133
            ('', ''),
134
            ('counter', 'Counter'),
135
            ('generated', 'Generated')
136
        ])
137
138
    def getCounterTypes(self, instance=None):
139
        return DisplayList([
140
            ('', ''),
141
            ('backreference', 'Backreference'),
142
            ('contained', 'Contained')
143
        ])
144
145
146
STICKER_AUTO_OPTIONS = DisplayList((
147
    ('None', _('None')),
148
    ('register', _('Register')),
149
    ('receive', _('Receive')),
150
))
151
152
153
schema = BikaFolderSchema.copy() + Schema((
154
    IntegerField(
155
        'AutoLogOff',
156
        schemata="Security",
157
        required=1,
158
        default=0,
159
        widget=IntegerWidget(
160
            label=_("Automatic log-off"),
161
            description=_(
162
                "The number of minutes before a user is automatically logged off. "
163
                "0 disables automatic log-off"),
164
        )
165
    ),
166
    BooleanField(
167
        'RestrictWorksheetUsersAccess',
168
        schemata="Security",
169
        default=True,
170
        widget=BooleanWidget(
171
            label=_("Allow access to worksheets only to assigned analysts"),
172
            description=_("If unchecked, analysts will have access to all worksheets.")
173
        )
174
    ),
175
    BooleanField(
176
        'AllowToSubmitNotAssigned',
177
        schemata="Security",
178
        default=True,
179
        widget=BooleanWidget(
180
            label=_("Allow to submit results for unassigned analyses or for "
181
                    "analyses assigned to others"),
182
            description=_(
183
                "If unchecked, users will only be able to submit results "
184
                "for the analyses they are assigned to, and the submission of "
185
                "results for unassigned analyses won't be permitted. This "
186
                "setting does not apply to users with role Lab Manager")
187
        )
188
    ),
189
    BooleanField(
190
        'RestrictWorksheetManagement',
191
        schemata="Security",
192
        default=True,
193
        widget=BooleanWidget(
194
            label=_("Only lab managers can create and manage worksheets"),
195
            description=_("If unchecked, analysts and lab clerks will "
196
                          "be able to manage Worksheets, too. If the "
197
                          "users have restricted access only to those "
198
                          "worksheets for which they are assigned, "
199
                          "this option will be checked and readonly.")
200
        )
201
    ),
202
    # NOTE: This is a Proxy Field which delegates to the SENAITE Registry!
203
    BooleanField(
204
        "EnableGlobalAuditlog",
205
        schemata="Security",
206
        default=False,
207
        widget=BooleanWidget(
208
            label=_("Enable global Audit Log"),
209
            description=_(
210
                "The global Auditlog shows all modifications of the system. "
211
                "When enabled, all entities will be indexed in a separate "
212
                "catalog. This will increase the time when objects are "
213
                "created or modified."
214
            )
215
        )
216
    ),
217
    BooleanField(
218
        'ShowPrices',
219
        schemata="Accounting",
220
        default=True,
221
        widget=BooleanWidget(
222
            label=_("Include and display pricing information"),
223
        )
224
    ),
225
    StringField(
226
        'Currency',
227
        schemata="Accounting",
228
        required=1,
229
        vocabulary=CURRENCIES,
230
        default='EUR',
231
        widget=SelectionWidget(
232
            label=_("Currency"),
233
            description=_("Select the currency the site will use to display prices."),
234
            format='select',
235
        )
236
    ),
237
    StringField(
238
        'DefaultCountry',
239
        schemata="Accounting",
240
        required=1,
241
        vocabulary='getCountries',
242
        default='',
243
        widget=SelectionWidget(
244
            label=_("Country"),
245
            description=_("Select the country the site will show by default"),
246
            format='select',
247
        )
248
    ),
249
    FixedPointField(
250
        'MemberDiscount',
251
        schemata="Accounting",
252
        default='33.33',
253
        widget=DecimalWidget(
254
            label=_("Member discount %"),
255
            description=_(
256
                "The discount percentage entered here, is applied to the prices for clients "
257
                "flagged as 'members', normally co-operative members or associates deserving "
258
                "of this discount"),
259
        )
260
    ),
261
    FixedPointField(
262
        'VAT',
263
        schemata="Accounting",
264
        default='19.00',
265
        widget=DecimalWidget(
266
            label=_("VAT %"),
267
            description=_(
268
                "Enter percentage value eg. 14.0. This percentage is applied system wide "
269
                "but can be overwrittem on individual items"),
270
        )
271
    ),
272
    StringField(
273
        'DecimalMark',
274
        schemata="Results Reports",
275
        vocabulary=DECIMAL_MARKS,
276
        default=".",
277
        widget=SelectionWidget(
278
            label=_("Default decimal mark"),
279
            description=_("Preferred decimal mark for reports."),
280
            format='select',
281
        )
282
    ),
283
    StringField(
284
        'ScientificNotationReport',
285
        schemata="Results Reports",
286
        default='1',
287
        vocabulary=SCINOTATION_OPTIONS,
288
        widget=SelectionWidget(
289
            label=_("Default scientific notation format for reports"),
290
            description=_("Preferred scientific notation format for reports"),
291
            format='select',
292
        )
293
    ),
294
    IntegerField(
295
        'MinimumResults',
296
        schemata="Results Reports",
297
        required=1,
298
        default=5,
299
        widget=IntegerWidget(
300
            label=_("Minimum number of results for QC stats calculations"),
301
            description=_(
302
                "Using too few data points does not make statistical sense. "
303
                "Set an acceptable minimum number of results before QC statistics "
304
                "will be calculated and plotted"),
305
        )
306
    ),
307
    BooleanField(
308
        'CategoriseAnalysisServices',
309
        schemata="Analyses",
310
        default=False,
311
        widget=BooleanWidget(
312
            label=_("Categorise analysis services"),
313
            description=_("Group analysis services by category in the LIMS tables, helpful when the list is long")
314
        ),
315
    ),
316
    BooleanField(
317
        "CategorizeSampleAnalyses",
318
        schemata="Analyses",
319
        default=False,
320
        widget=BooleanWidget(
321
            label=_("label_bikasetup_categorizesampleanalyses",
322
                    default="Categorize sample analyses"),
323
            description=_("description_bikasetup_categorizesampleanalyses",
324
                          "Group analyses by category for samples")
325
        ),
326
    ),
327
    BooleanField(
328
        "SampleAnalysesRequired",
329
        schemata="Analyses",
330
        default=True,
331
        widget=BooleanWidget(
332
            label=_("label_bikasetup_sampleanalysesrequired",
333
                    default="Require sample analyses"),
334
            description=_("description_bikasetup_sampleanalysesrequired",
335
                          "Analyses are required for sample registration")
336
        ),
337
    ),
338
    BooleanField(
339
        "AllowManualResultCaptureDate",
340
        schemata="Analyses",
341
        default=True,
342
        widget=BooleanWidget(
343
            label=_("label_bikasetup_allow_manual_result_capture_date",
344
                    default="Allow to set the result capture date"),
345
            description=_(
346
                "description_bikasetup_allow_manual_result_capture_date",
347
                default="If this option is activated, the result capture date "
348
                        "can be entered manually for analyses"),
349
        ),
350
    ),
351
    BooleanField(
352
        'EnableARSpecs',
353
        schemata="Analyses",
354
        default=False,
355
        widget=BooleanWidget(
356
            label=_("Enable Sample Specifications"),
357
            description=_(
358
                "Analysis specifications which are edited directly on the "
359
                "Sample."),
360
        ),
361
    ),
362
    IntegerField(
363
        'ExponentialFormatThreshold',
364
        schemata="Analyses",
365
        required=1,
366
        default=7,
367
        widget=IntegerWidget(
368
            label=_("Exponential format threshold"),
369
            description=_(
370
                "Result values with at least this number of significant "
371
                "digits are displayed in scientific notation using the "
372
                "letter 'e' to indicate the exponent.  The precision can be "
373
                "configured in individual Analysis Services."),
374
        )
375
    ),
376
    BooleanField(
377
        "ImmediateResultsEntry",
378
        schemata="Analyses",
379
        default=False,
380
        widget=BooleanWidget(
381
            label=_("label_bikasetup_immediateresultsentry",
382
                    default=u"Immediate results entry"),
383
            description=_(
384
                "description_bikasetup_immediateresultsentry",
385
                default=u"Allow the user to directly enter results after "
386
                "sample creation, e.g. to enter field results immediately, or "
387
                "lab results, when the automatic sample reception is "
388
                "activated."
389
            ),
390
        ),
391
    ),
392
    BooleanField(
393
        'EnableAnalysisRemarks',
394
        schemata="Analyses",
395
        default=False,
396
        widget=BooleanWidget(
397
            label=_("Add a remarks field to all analyses"),
398
            description=_(
399
                "If enabled, a free text field will be displayed close to "
400
                "each analysis in results entry view"
401
            )
402
        ),
403
    ),
404
    BooleanField(
405
        "AutoVerifySamples",
406
        schemata="Analyses",
407
        default=True,
408
        widget=BooleanWidget(
409
            label=_("Automatic verification of samples"),
410
            description=_(
411
                "When enabled, the sample is automatically verified as soon as "
412
                "all results are verified. Otherwise, users with enough "
413
                "privileges have to manually verify the sample afterwards. "
414
                "Default: enabled"
415
            )
416
        )
417
    ),
418
    BooleanField(
419
        'SelfVerificationEnabled',
420
        schemata="Analyses",
421
        default=False,
422
        widget=BooleanWidget(
423
            label=_("Allow self-verification of results"),
424
            description=_(
425
                "If enabled, a user who submitted a result will also be able "
426
                "to verify it. This setting only take effect for those users "
427
                "with a role assigned that allows them to verify results "
428
                "(by default, managers, labmanagers and verifiers)."
429
                "This setting can be overrided for a given Analysis in "
430
                "Analysis Service edit view. By default, disabled."),
431
        ),
432
    ),
433
    IntegerField(
434
        'NumberOfRequiredVerifications',
435
        schemata="Analyses",
436
        default=1,
437
        vocabulary="_getNumberOfRequiredVerificationsVocabulary",
438
        widget=SelectionWidget(
439
            format="select",
440
            label=_("Number of required verifications"),
441
            description=_(
442
                "Number of required verifications before a given result being "
443
                "considered as 'verified'. This setting can be overrided for "
444
                "any Analysis in Analysis Service edit view. By default, 1"),
445
        ),
446
    ),
447
    StringField(
448
        'TypeOfmultiVerification',
449
        schemata="Analyses",
450
        default='self_multi_enabled',
451
        vocabulary=MULTI_VERIFICATION_TYPE,
452
        widget=SelectionWidget(
453
            label=_("Multi Verification type"),
454
            description=_(
455
                "Choose type of multiple verification for the same user."
456
                "This setting can enable/disable verifying/consecutively verifying"
457
                "more than once for the same user."),
458
            format='select',
459
        )
460
    ),
461
    StringField(
462
        'ResultsDecimalMark',
463
        schemata="Analyses",
464
        vocabulary=DECIMAL_MARKS,
465
        default=".",
466
        widget=SelectionWidget(
467
            label=_("Default decimal mark"),
468
            description=_("Preferred decimal mark for results"),
469
            format='select',
470
        )
471
    ),
472
    StringField(
473
        'ScientificNotationResults',
474
        schemata="Analyses",
475
        default='1',
476
        vocabulary=SCINOTATION_OPTIONS,
477
        widget=SelectionWidget(
478
            label=_("Default scientific notation format for results"),
479
            description=_("Preferred scientific notation format for results"),
480
            format='select',
481
        )
482
    ),
483
    StringField(
484
        'WorksheetLayout',
485
        schemata="Appearance",
486
        default=DEFAULT_WORKSHEET_LAYOUT,
487
        vocabulary=getWorksheetLayouts(),
488
        widget=SelectionWidget(
489
            label=_("Default layout in worksheet view"),
490
            description=_("Preferred layout of the results entry table "
491
                          "in the Worksheet view. Classic layout displays "
492
                          "the Samples in rows and the analyses "
493
                          "in columns. Transposed layout displays the "
494
                          "Samples in columns and the analyses "
495
                          "in rows."),
496
            format='select',
497
        )
498
    ),
499
    BooleanField(
500
        'DashboardByDefault',
501
        schemata="Appearance",
502
        default=True,
503
        widget=BooleanWidget(
504
            label=_("Use Dashboard as default front page"),
505
            description=_("Select this to activate the dashboard as a default front page.")
506
        ),
507
    ),
508
    UIDReferenceField(
509
        "LandingPage",
510
        schemata="Appearance",
511
        required=0,
512
        allowed_types=(
513
            "Document",
514
            "Client",
515
            "ClientFolder",
516
            "Samples",
517
            "WorksheetFolder",
518
        ),
519
        mode="rw",
520
        multiValued=0,
521
        relationship="SetupLandingPage",
522
        widget=ReferenceWidget(
523
            label=_(
524
                "label_setup_landingpage",
525
                default="Landing Page"),
526
            description=_(
527
                "description_setup_landingpage",
528
                default="The landing page is shown for non-authenticated users "
529
                "if the Dashboard is not selected as the default front page. "
530
                "If no landing page is selected, the default frontpage is displayed."),
531
            catalog=["uid_catalog"],
532
            query={
533
                "is_active": True,
534
                "sort_on": "id",
535
                "sort_order": "ascending"
536
            },
537
            columns=[
538
                {"name": "Title", "label": _("Title")},
539
                {"name": "portal_type", "label": _("Type")},
540
            ],
541
542
        ),
543
    ),
544
    BooleanField(
545
        'PrintingWorkflowEnabled',
546
        schemata="Sampling",
547
        default=False,
548
        widget=BooleanWidget(
549
            label=_("Enable the Results Report Printing workflow"),
550
            description=_("Select this to allow the user to set an "
551
                          "additional 'Printed' status to those Analysis "
552
                          "Requests that have been Published. "
553
                          "Disabled by default.")
554
        ),
555
    ),
556
    BooleanField(
557
        'SamplingWorkflowEnabled',
558
        schemata="Sampling",
559
        default=False,
560
        widget=BooleanWidget(
561
            label=_("Enable Sampling"),
562
            description=_("Select this to activate the sample collection workflow steps.")
563
        ),
564
    ),
565
    BooleanField(
566
        'ScheduleSamplingEnabled',
567
        schemata="Sampling",
568
        default=False,
569
        widget=BooleanWidget(
570
            label=_("Enable Sampling Scheduling"),
571
            description=_(
572
                "Select this to allow a Sampling Coordinator to" +
573
                " schedule a sampling. This functionality only takes effect" +
574
                " when 'Sampling workflow' is active")
575
        ),
576
    ),
577
    # NOTE: This is a Proxy Field which delegates to the SENAITE Registry!
578
    BooleanField(
579
        "DateSampledRequired",
580
        schemata="Sampling",
581
        default=True,
582
        widget=BooleanWidget(
583
            label=_(
584
                "label_bikasetup_date_sampled_required",
585
                default="Date sampled required"
586
            ),
587
            description=_(
588
                "description_bikasetup_date_sampled_required",
589
                default="Select this to make DateSampled field required on "
590
                        "sample creation. This functionality only takes "
591
                        "effect when 'Sampling workflow' is not active"
592
            ),
593
        ),
594
    ),
595
    BooleanField(
596
        "AutoreceiveSamples",
597
        schemata="Sampling",
598
        default=False,
599
        widget=BooleanWidget(
600
            label=_("Auto-receive samples"),
601
            description=_(
602
                "Select to receive the samples automatically when created by "
603
                "lab personnel and sampling workflow is disabled. Samples "
604
                "created by client contacts won't be received automatically"
605
            ),
606
        ),
607
    ),
608
    BooleanField(
609
        'ShowPartitions',
610
        schemata="Appearance",
611
        default=False,
612
        widget=BooleanWidget(
613
            label=_("Display sample partitions to clients"),
614
            description=_(
615
                "Select to show sample partitions to client contacts. "
616
                "If deactivated, partitions won't be included in listings "
617
                "and no info message with links to the primary sample will "
618
                "be displayed to client contacts.")
619
        ),
620
    ),
621
    BooleanField(
622
        'SamplePreservationEnabled',
623
        schemata="Sampling",
624
        default=False,
625
        widget=BooleanWidget(
626
            label=_("Enable Sample Preservation"),
627
            description=_("")
628
        ),
629
    ),
630
    LinesField(
631
        "Workdays",
632
        schemata="Sampling",
633
        vocabulary=WEEKDAYS,
634
        default=tuple(map(str, range(7))),
635
        required=1,
636
        widget=InAndOutWidget(
637
            visible=True,
638
            label=_("Laboratory Workdays"),
639
            description=_("Only laboratory workdays are considered for the "
640
                          "analysis turnaround time calculation. "),
641
            format="checkbox",
642
        )
643
    ),
644
    DurationField(
645
        'DefaultTurnaroundTime',
646
        schemata="Sampling",
647
        required=1,
648
        default={"days": 5, "hours": 0, "minutes": 0},
649
        widget=DurationWidget(
650
            label=_("Default turnaround time for analyses."),
651
            description=_(
652
                "This is the default maximum time allowed for performing "
653
                "analyses.  It is only used for analyses where the analysis "
654
                "service does not specify a turnaround time. "
655
                "Only laboratory workdays are considered."
656
            ),
657
        )
658
    ),
659
    DurationField(
660
        'DefaultSampleLifetime',
661
        schemata="Sampling",
662
        required=1,
663
        default={"days": 30, "hours": 0, "minutes": 0},
664
        widget=DurationWidget(
665
            label=_("Default sample retention period"),
666
            description=_(
667
                "The number of days before a sample expires and cannot be analysed "
668
                "any more. This setting can be overwritten per individual sample type "
669
                "in the sample types setup"),
670
        )
671
    ),
672
    # NOTE: This is a Proxy Field which delegates to the SENAITE Registry!
673
    StringField(
674
        "EmailFromSamplePublication",
675
        default_method='getEmailFromSamplePublication',
676
        schemata="Notifications",
677
        widget=StringWidget(
678
            label=_(
679
                "label_bikasetup_email_from_sample_publication",
680
                default="Publication 'From' address"
681
            ),
682
            description=_(
683
                "description_bikasetup_email_from_sample_publication",
684
                default="E-mail to use as the 'From' address for outgoing "
685
                        "e-mails when publishing results reports. This "
686
                        "address overrides the value set at portal's 'Mail "
687
                        "settings'."
688
            ),
689
        ),
690
        validators=("isEmail", )
691
    ),
692
    # NOTE: This is a Proxy Field which delegates to the SENAITE Registry!
693
    TextField(
694
        "EmailBodySamplePublication",
695
        default_content_type="text/html",
696
        default_output_type="text/x-html-safe",
697
        schemata="Notifications",
698
        # Needed to fetch the default value from the registry
699
        edit_accessor="getEmailBodySamplePublication",
700
        widget=RichWidget(
701
            label=_(
702
                "label_bikasetup_email_body_sample_publication",
703
                "Email body for Sample publication notifications"),
704
            description=_(
705
                "description_bikasetup_email_body_sample_publication",
706
                default="Set the email body text to be used by default when "
707
                "sending out result reports to the selected recipients. "
708
                "You can use reserved keywords: "
709
                "$client_name, $recipients, $lab_name, $lab_address"),
710
            default_mime_type="text/x-html",
711
            output_mime_type="text/x-html",
712
            allow_file_upload=False,
713
            rows=15,
714
        ),
715
    ),
716
    # NOTE: This is a Proxy Field which delegates to the SENAITE Registry!
717
    BooleanField(
718
        "AlwaysCCResponsiblesInReportEmail",
719
        schemata="Notifications",
720
        default=True,
721
        widget=BooleanWidget(
722
            label=_(
723
                "label_bikasetup_always_cc_responsibles_in_report_emails",
724
                default="Always send publication email to responsibles"),
725
            description=_(
726
                "description_bikasetup_always_cc_responsibles_in_report_emails",
727
                default="When selected, the responsible persons of all "
728
                "involved lab departments will receive publication emails."),
729
        ),
730
    ),
731
    BooleanField(
732
        'NotifyOnSampleRejection',
733
        schemata="Notifications",
734
        default=False,
735
        widget=BooleanWidget(
736
            label=_("Email notification on Sample rejection"),
737
            description=_("Select this to activate automatic notifications "
738
                          "via email to the Client when a Sample is rejected.")
739
        ),
740
    ),
741
    TextField(
742
        "EmailBodySampleRejection",
743
        default_content_type='text/html',
744
        default_output_type='text/x-html-safe',
745
        schemata="Notifications",
746
        label=_("Email body for Sample Rejection notifications"),
747
        default="The sample $sample_link has been rejected because of the "
748
                "following reasons:"
749
                "<br/><br/>$reasons<br/><br/>"
750
                "For further information, please contact us under the "
751
                "following address.<br/><br/>"
752
                "$lab_address",
753
        widget=RichWidget(
754
            label=_("Email body for Sample Rejection notifications"),
755
            description=_(
756
                "Set the text for the body of the email to be sent to the "
757
                "Sample's client contact if the option 'Email notification on "
758
                "Sample rejection' is enabled. You can use reserved keywords: "
759
                "$sample_id, $sample_link, $reasons, $lab_address"),
760
            default_mime_type='text/x-rst',
761
            output_mime_type='text/x-html',
762
            allow_file_upload=False,
763
            rows=15,
764
        ),
765
    ),
766
    # NOTE: This is a Proxy Field which delegates to the SENAITE Registry!
767
    BooleanField(
768
        "InvalidationReasonRequired",
769
        schemata="Notifications",
770
        default=True,
771
        widget=BooleanWidget(
772
            label=_(
773
                "label_bikasetup_invalidation_reason_required",
774
                default="Invalidation reason required"
775
            ),
776
            description=_(
777
                "description_bikasetup_invalidation_reason_required",
778
                default="Specify whether providing a reason is mandatory when "
779
                        "invalidating a sample. If enabled, the '$reason' "
780
                        "placeholder in the sample invalidation notification "
781
                        "email body will be replaced with the entered reason."
782
            ),
783
        ),
784
    ),
785
    TextField(
786
        "EmailBodySampleInvalidation",
787
        default_content_type='text/html',
788
        default_output_type='text/x-html-safe',
789
        schemata="Notifications",
790
        default=
791
            "Some non-conformities have been detected in the results report "
792
            "published for Sample $sample_link. "
793
            "<br/><br/> "
794
            "A new Sample $retest_link has been created automatically, and the "
795
            "previous request has been invalidated. "
796
            "<br/><br/> "
797
            "The root cause is under investigation and corrective "
798
            "action has been initiated. "
799
            "<br/><br/> "
800
            "$lab_address",
801
        widget=RichWidget(
802
            label=_(
803
                "label_bikasetup_invalidation_email_body",
804
                default="Email body for Sample Invalidation notifications"
805
            ),
806
            description=_(
807
                "description_bikasetup_invalidation_email_body",
808
                default=
809
                "Define the template for the email body that will be "
810
                "automatically sent to primary contacts and laboratory "
811
                "managers when a sample is invalidated. The following "
812
                "placeholders are supported: "
813
                "<code title='The ID of the sample'>$sample_id</code>, "
814
                "<code title='The ID of the sample retest'>$retest_id</code>, "
815
                "<code title='The link to the retest'>$retest_link</code>, "
816
                "<code title='The reason(s) for invalidation'>$reason</code>, "
817
                "<code title='The address of the lab'>$lab_address</code>."
818
            ),
819
            default_mime_type='text/x-rst',
820
            output_mime_type='text/x-html',
821
            allow_file_upload=False,
822
            rows=15,
823
        ),
824
    ),
825
826
    StringField(
827
        "AutoPrintStickers",
828
        schemata="Sticker",
829
        vocabulary=STICKER_AUTO_OPTIONS,
830
        widget=SelectionWidget(
831
            format='select',
832
            label=_("Automatic Sticker Printing"),
833
            description=_(
834
                "Choose when stickers should be automatically printed:<br/>"
835
                "<ul>"
836
                "<li><strong>Register:</strong> Stickers are printed "
837
                " automatically when new samples are created.</li>"
838
                "<li><strong>Receive:</strong> Stickers are printed "
839
                " automatically when samples are received.</li>"
840
                "<li><strong>None:</strong> Disables automatic sticker "
841
                "printing.</li>"
842
                "</ul>"
843
            ),
844
        )
845
    ),
846
847
    StringField(
848
        "AutoStickerTemplate",
849
        schemata="Sticker",
850
        vocabulary_factory="senaite.core.vocabularies.stickers",
851
        widget=SelectionWidget(
852
            format='select',
853
            label=_("Default Sticker Template"),
854
            description=_(
855
                "Select the default sticker template used for "
856
                "automatic printing.<br/>"
857
            ),
858
        )
859
    ),
860
861
    StringField(
862
        "SmallStickerTemplate",
863
        schemata="Sticker",
864
        vocabulary_factory="senaite.core.vocabularies.stickers",
865
        default="Code_128_1x48mm.pt",
866
        widget=SelectionWidget(
867
            format='select',
868
            label=_("Small Sticker Template"),
869
            description=_(
870
                "Choose the default template for 'small' stickers.<br/>"
871
                "<strong>Note:</strong> Sample-specific 'small' stickers are "
872
                "configured based on their sample type."
873
            ),
874
        )
875
    ),
876
877
    StringField(
878
        "LargeStickerTemplate",
879
        schemata="Sticker",
880
        vocabulary_factory="senaite.core.vocabularies.stickers",
881
        default="Code_128_1x72mm.pt",
882
        widget=SelectionWidget(
883
            format='select',
884
            label=_("Large Sticker Template"),
885
            description=_(
886
                "Choose the default template for 'large' stickers.<br/>"
887
                "<strong>Note:</strong> Sample-specific 'large' stickers are "
888
                "configured based on their sample type."
889
            ),
890
        )
891
    ),
892
893
    IntegerField(
894
        "DefaultNumberOfCopies",
895
        schemata="Sticker",
896
        required=True,
897
        default=1,
898
        widget=IntegerWidget(
899
            label=_("Default Number of Copies"),
900
            description=_(
901
                "Specify how many copies of each sticker should be printed "
902
                "by default."
903
            ),
904
        )
905
    ),
906
907
    IDFormattingField(
908
        'IDFormatting',
909
        schemata="ID Server",
910
        default=[
911
            {
912
                'form': 'B-{seq:03d}',
913
                'portal_type': 'Batch',
914
                'prefix': 'batch',
915
                'sequence_type': 'generated',
916
                'split_length': 1
917
            }, {
918
                'form': 'D-{seq:03d}',
919
                'portal_type': 'DuplicateAnalysis',
920
                'prefix': 'duplicate',
921
                'sequence_type': 'generated',
922
                'split_length': 1
923
            }, {
924
                'form': 'I-{seq:03d}',
925
                'portal_type': 'Invoice',
926
                'prefix': 'invoice',
927
                'sequence_type': 'generated',
928
                'split_length': 1
929
            }, {
930
                'form': 'QC-{seq:03d}',
931
                'portal_type': 'ReferenceSample',
932
                'prefix': 'refsample',
933
                'sequence_type': 'generated',
934
                'split_length': 1
935
            }, {
936
                'form': 'SA-{seq:03d}',
937
                'portal_type': 'ReferenceAnalysis',
938
                'prefix': 'refanalysis',
939
                'sequence_type': 'generated',
940
                'split_length': 1
941
            }, {
942
                'form': 'WS-{seq:03d}',
943
                'portal_type': 'Worksheet',
944
                'prefix': 'worksheet',
945
                'sequence_type': 'generated',
946
                'split_length': 1
947
            }, {
948
                'form': '{sampleType}-{seq:04d}',
949
                'portal_type': 'AnalysisRequest',
950
                'prefix': 'analysisrequest',
951
                'sequence_type': 'generated',
952
                'split_length': 1
953
            }, {
954
                'form': '{parent_ar_id}-P{partition_count:02d}',
955
                'portal_type': 'AnalysisRequestPartition',
956
                'prefix': 'analysisrequestpartition',
957
                'sequence_type': '',
958
                'split-length': 1
959
            }, {
960
                'form': '{parent_base_id}-R{retest_count:02d}',
961
                'portal_type': 'AnalysisRequestRetest',
962
                'prefix': 'analysisrequestretest',
963
                'sequence_type': '',
964
                'split-length': 1
965
            }, {
966
                'form': '{parent_ar_id}-S{secondary_count:02d}',
967
                'portal_type': 'AnalysisRequestSecondary',
968
                'prefix': 'analysisrequestsecondary',
969
                'sequence_type': '',
970
                'split-length': 1
971
            },
972
        ],
973
        widget=RecordsWidget(
974
            label=_("Formatting Configuration"),
975
            allowDelete=True,
976
            description=_(
977
                " <p>The ID Server provides unique sequential IDs "
978
                "for objects such as Samples and Worksheets etc, based on a "
979
                "format specified for each content type.</p>"
980
                "<p>The format is constructed similarly to the Python format"
981
                " syntax, using predefined variables per content type, and"
982
                " advancing the IDs through a sequence number, 'seq' and its"
983
                " padding as a number of digits, e.g. '03d' for a sequence of"
984
                " IDs from 001 to 999.</p>"
985
                "<p>Alphanumeric prefixes for IDs are included as is in the"
986
                " formats, e.g. WS for Worksheet in WS-{seq:03d} produces"
987
                " sequential Worksheet IDs: WS-001, WS-002, WS-003 etc.</p>"
988
                "<p>For dynamic generation of alphanumeric and sequential IDs,"
989
                " the wildcard {alpha} can be used. E.g WS-{alpha:2a3d}"
990
                " produces WS-AA001, WS-AA002, WS-AB034, etc.</p>"
991
                "<p>Variables that can be used include:"
992
                "<table>"
993
                "<tr>"
994
                "<th style='width:150px'>Content Type</th><th>Variables</th>"
995
                "</tr>"
996
                "<tr><td>Client ID</td><td>{clientId}</td></tr>"
997
                "<tr><td>Year</td><td>{year}</td></tr>"
998
                "<tr><td>Sample ID</td><td>{sampleId}</td></tr>"
999
                "<tr><td>Sample Type</td><td>{sampleType}</td></tr>"
1000
                "<tr><td>Sampling Date</td><td>{samplingDate}</td></tr>"
1001
                "<tr><td>Date Sampled</td><td>{dateSampled}</td></tr>"
1002
                "</table>"
1003
                "</p>"
1004
                "<p>Configuration Settings:"
1005
                "<ul>"
1006
                "<li>format:"
1007
                "<ul><li>a python format string constructed from predefined"
1008
                " variables like sampleId, clientId, sampleType.</li>"
1009
                "<li>special variable 'seq' must be positioned last in the"
1010
                "format string</li></ul></li>"
1011
                "<li>sequence type: [generated|counter]</li>"
1012
                "<li>context: if type counter, provides context the counting"
1013
                " function</li>"
1014
                "<li>counter type: [backreference|contained]</li>"
1015
                "<li>counter reference: a parameter to the counting"
1016
                " function</li>"
1017
                "<li>prefix: default prefix if none provided in format"
1018
                " string</li>"
1019
                "<li>split length: the number of parts to be included in the"
1020
                " prefix</li>"
1021
                "</ul></p>")
1022
        )
1023
    ),
1024
    StringField(
1025
        'IDServerValues',
1026
        schemata="ID Server",
1027
        accessor="getIDServerValuesHTML",
1028
        readonly=True,
1029
        widget=TextAreaWidget(
1030
            label=_("ID Server Values"),
1031
            cols=30,
1032
            rows=30,
1033
        ),
1034
    ),
1035
    RecordsField(
1036
        'RejectionReasons',
1037
        schemata="Analyses",
1038
        widget=RejectionSetupWidget(
1039
            label=_("Enable the rejection workflow"),
1040
            description=_("Select this to activate the rejection workflow "
1041
                          "for Samples. A 'Reject' option will be displayed in "
1042
                          "the actions menu.")
1043
        ),
1044
    ),
1045
    IntegerField(
1046
        'DefaultNumberOfARsToAdd',
1047
        schemata="Analyses",
1048
        required=0,
1049
        default=4,
1050
        widget=IntegerWidget(
1051
            label=_("Default count of Sample to add."),
1052
            description=_("Default value of the 'Sample count' when users click 'ADD' button to create new Samples"),
1053
        )
1054
    ),
1055
    IntegerField(
1056
        "MaxNumberOfSamplesAdd",
1057
        schemata="Analyses",
1058
        required=0,
1059
        default=10,
1060
        widget=IntegerWidget(
1061
            label=_(
1062
                u"label_senaitesetup_maxnumberofsamplesadd",
1063
                default=u"Maximum value for 'Number of samples' field on "
1064
                        u"registration"
1065
            ),
1066
            description=_(
1067
                u"description_senaitesetup_maxnumberofsamplesadd",
1068
                default=u"Maximum number of samples that can be created in "
1069
                        u"accordance with the value set for the field 'Number "
1070
                        u"of samples' on the sample registration form"
1071
            ),
1072
        )
1073
    ),
1074
    # NOTE: This is a Proxy Field which delegates to senaite_setup DX
1075
    BooleanField(
1076
        "ShowLabNameInLogin",
1077
        schemata="Appearance",
1078
        default=False,
1079
        widget=BooleanWidget(
1080
            label=_(
1081
                u"title_senaitesetup_show_lab_name_in_login",
1082
                default=u"Display laboratory name in the login page"),
1083
            description=_(
1084
                u"description_senaitesetup_show_lab_name_in_login",
1085
                default=u"When selected, the laboratory name will be displayed"
1086
                        u"in the login page, above the access credentials."
1087
            ),
1088
        )
1089
    ),
1090
))
1091
1092
schema['title'].validators = ()
1093
schema['title'].widget.visible = False
1094
# Update the validation layer after change the validator in runtime
1095
schema['title']._validationLayer()
1096
1097
1098
class BikaSetup(folder.ATFolder):
1099
    """LIMS Setup
1100
    """
1101
    implements(IBikaSetup, IHideActionsMenu)
1102
1103
    schema = schema
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable schema does not seem to be defined.
Loading history...
1104
    security = ClassSecurityInfo()
1105
1106
    def setAutoLogOff(self, value):
1107
        """set session lifetime
1108
        """
1109
        value = int(value)
1110
        if value < 0:
1111
            value = 0
1112
        value = value * 60
1113
        acl = api.get_tool("acl_users")
1114
        session = acl.get("session")
1115
        if session:
1116
            session.timeout = value
1117
1118
    def getAutoLogOff(self):
1119
        """get session lifetime
1120
        """
1121
        acl = api.get_tool("acl_users")
1122
        session = acl.get("session")
1123
        if not session:
1124
            return 0
1125
        return session.timeout // 60
1126
1127
    def getAnalysisServicesVocabulary(self):
1128
        """
1129
        Get all active Analysis Services from Bika Setup and return them as Display List.
1130
        """
1131
        bsc = getToolByName(self, 'senaite_catalog_setup')
1132
        brains = bsc(portal_type='AnalysisService',
1133
                     is_active=True)
1134
        items = [(b.UID, b.Title) for b in brains]
1135
        items.insert(0, ("", ""))
1136
        items.sort(lambda x, y: cmp(x[1], y[1]))
1137
        return DisplayList(list(items))
1138
1139
    def getPrefixFor(self, portal_type):
1140
        """Return the prefix for a portal_type.
1141
           If not found, simply uses the portal_type itself
1142
        """
1143
        prefix = [p for p in self.getIDFormatting() if p['portal_type'] == portal_type]
1144
        if prefix:
1145
            return prefix[0]['prefix']
1146
        else:
1147
            return portal_type
1148
1149
    def getCountries(self):
1150
        items = geo.get_countries()
1151
        items = map(lambda country: (country.alpha_2, country.name), items)
1152
        return items
1153
1154
    def isRejectionWorkflowEnabled(self):
1155
        """Return true if the rejection workflow is enabled (its checkbox is set)
1156
        """
1157
        widget = self.getRejectionReasons()
1158
        # widget will be something like:
1159
        # [{'checkbox': u'on', 'textfield-2': u'b', 'textfield-1': u'c', 'textfield-0': u'a'}]
1160
        if len(widget) > 0:
1161
            checkbox = widget[0].get('checkbox', False)
1162
            return True if checkbox == 'on' and len(widget[0]) > 1 else False
1163
        else:
1164
            return False
1165
1166
    def getRejectionReasonsItems(self):
1167
        """Return the list of predefined rejection reasons
1168
        """
1169
        reasons = self.getRejectionReasons()
1170
        if not reasons:
1171
            return []
1172
        reasons = reasons[0]
1173
        keys = filter(lambda key: key != "checkbox", reasons.keys())
1174
        return map(lambda key: reasons[key], sorted(keys)) or []
1175
1176
    def _getNumberOfRequiredVerificationsVocabulary(self):
1177
        """
1178
        Returns a DisplayList with the available options for the
1179
        multi-verification list: '1', '2', '3', '4'
1180
        :returns: DisplayList with the available options for the
1181
            multi-verification list
1182
        """
1183
        items = [(1, '1'), (2, '2'), (3, '3'), (4, '4')]
1184
        return IntDisplayList(list(items))
1185
1186
    def getIDServerValuesHTML(self):
1187
        number_generator = getUtility(INumberGenerator)
1188
        keys = number_generator.keys()
1189
        values = number_generator.values()
1190
        results = []
1191
        for i in range(len(keys)):
1192
            results.append('%s: %s' % (keys[i], values[i]))
1193
        return "\n".join(results)
1194
1195
    def getEmailFromSamplePublication(self):
1196
        """Get the value from the senaite setup
1197
        """
1198
        setup = api.get_senaite_setup()
1199
        # setup is `None` during initial site content structure installation
1200
        if setup:
1201
            return setup.getEmailFromSamplePublication()
1202
1203
    def setEmailFromSamplePublication(self, value):
1204
        """Set the value in the senaite setup
1205
        """
1206
        setup = api.get_senaite_setup()
1207
        # setup is `None` during initial site content structure installation
1208
        if setup:
1209
            setup.setEmailFromSamplePublication(value)
1210
1211
    def getEmailBodySamplePublication(self):
1212
        """Get the value from the senaite setup
1213
        """
1214
        setup = api.get_senaite_setup()
1215
        # setup is `None` during initial site content structure installation
1216
        if setup:
1217
            return setup.getEmailBodySamplePublication()
1218
1219
    def setEmailBodySamplePublication(self, value):
1220
        """Set the value in the senaite setup
1221
        """
1222
        setup = api.get_senaite_setup()
1223
        # setup is `None` during initial site content structure installation
1224
        if setup:
1225
            setup.setEmailBodySamplePublication(value)
1226
1227
    def getAlwaysCCResponsiblesInReportEmail(self):
1228
        """Get the value from the senaite setup
1229
        """
1230
        setup = api.get_senaite_setup()
1231
        # setup is `None` during initial site content structure installation
1232
        if setup:
1233
            return setup.getAlwaysCCResponsiblesInReportEmail()
1234
1235
    def setAlwaysCCResponsiblesInReportEmail(self, value):
1236
        """Set the value in the senaite setup
1237
        """
1238
        setup = api.get_senaite_setup()
1239
        # setup is `None` during initial site content structure installation
1240
        if setup:
1241
            setup.setAlwaysCCResponsiblesInReportEmail(value)
1242
1243
    def getEnableGlobalAuditlog(self):
1244
        """Get the value from the senaite setup
1245
        """
1246
        setup = api.get_senaite_setup()
1247
        # setup is `None` during initial site content structure installation
1248
        if setup:
1249
            return setup.getEnableGlobalAuditlog()
1250
        return False
1251
1252
    def setEnableGlobalAuditlog(self, value):
1253
        """Set the value in the senaite setup
1254
        """
1255
        setup = api.get_senaite_setup()
1256
        # setup is `None` during initial site content structure installation
1257
        if setup:
1258
            setup.setEnableGlobalAuditlog(value)
1259
1260
    def getImmediateResultsEntry(self):
1261
        """Get the value from the senaite setup
1262
        """
1263
        setup = api.get_senaite_setup()
1264
        # setup is `None` during initial site content structure installation
1265
        if setup:
1266
            return setup.getImmediateResultsEntry()
1267
        return False
1268
1269
    def setImmediateResultsEntry(self, value):
1270
        """Set the value in the senaite setup
1271
        """
1272
        setup = api.get_senaite_setup()
1273
        # setup is `None` during initial site content structure installation
1274
        if setup:
1275
            setup.setImmediateResultsEntry(value)
1276
1277
    def getCategorizeSampleAnalyses(self):
1278
        """Get the value from the senaite setup
1279
        """
1280
        setup = api.get_senaite_setup()
1281
        # setup is `None` during initial site content structure installation
1282
        if setup:
1283
            return setup.getCategorizeSampleAnalyses()
1284
        return False
1285
1286
    def setCategorizeSampleAnalyses(self, value):
1287
        """Set the value in the senaite setup
1288
        """
1289
        setup = api.get_senaite_setup()
1290
        # setup is `None` during initial site content structure installation
1291
        if setup:
1292
            setup.setCategorizeSampleAnalyses(value)
1293
1294
    def getSampleAnalysesRequired(self):
1295
        """Get the value from the senaite setup
1296
        """
1297
        setup = api.get_senaite_setup()
1298
        # setup is `None` during initial site content structure installation
1299
        if setup:
1300
            return setup.getSampleAnalysesRequired()
1301
        return False
1302
1303
    def setSampleAnalysesRequired(self, value):
1304
        """Set the value in the senaite setup
1305
        """
1306
        setup = api.get_senaite_setup()
1307
        # setup is `None` during initial site content structure installation
1308
        if setup:
1309
            setup.setSampleAnalysesRequired(value)
1310
1311
    def getAllowManualResultCaptureDate(self):
1312
        """Get the value from the senaite setup
1313
        """
1314
        setup = api.get_senaite_setup()
1315
        # setup is `None` during initial site content structure installation
1316
        if setup:
1317
            return setup.getAllowManualResultCaptureDate()
1318
        return False
1319
1320
    def setAllowManualResultCaptureDate(self, value):
1321
        """Set the value in the senaite setup
1322
        """
1323
        setup = api.get_senaite_setup()
1324
        # setup is `None` during initial site content structure installation
1325
        if setup:
1326
            setup.setAllowManualResultCaptureDate(value)
1327
1328
    def getMaxNumberOfSamplesAdd(self):
1329
        """Get the value from the senaite setup
1330
        """
1331
        setup = api.get_senaite_setup()
1332
        # setup is `None` during initial site content structure installation
1333
        if setup:
1334
            return setup.getMaxNumberOfSamplesAdd()
1335
        return self.getField("MaxNumberOfSamplesAdd").default
1336
1337
    def setMaxNumberOfSamplesAdd(self, value):
1338
        """Set the value in the senaite setup
1339
        """
1340
        setup = api.get_senaite_setup()
1341
        # setup is `None` during initial site content structure installation
1342
        if setup:
1343
            # we get a string value here!
1344
            value = api.to_int(value, default=10)
1345
            setup.setMaxNumberOfSamplesAdd(value)
1346
1347
    def getShowLabNameInLogin(self):
1348
        """Get the value from the senaite setup
1349
        """
1350
        setup = api.get_senaite_setup()
1351
        # setup is `None` during initial site content structure installation
1352
        if setup:
1353
            return setup.getShowLabNameInLogin()
1354
        return False
1355
1356
    def setShowLabNameInLogin(self, value):
1357
        """Set the value in the senaite setup
1358
        """
1359
        setup = api.get_senaite_setup()
1360
        # setup is `None` during initial site content structure installation
1361
        if setup:
1362
            setup.setShowLabNameInLogin(value)
1363
1364
    def getDateSampledRequired(self):
1365
        """Get the value form the senaite setup
1366
        """
1367
        setup = api.get_senaite_setup()
1368
        # setup is `None` during initial site content structure installation
1369
        if setup:
1370
            return setup.getDateSampledRequired()
1371
        return self.getField("DateSampledRequired").default
1372
1373
    def setDateSampledRequired(self, value):
1374
        """Set the value in the senaite setup
1375
        """
1376
        setup = api.get_senaite_setup()
1377
        # setup is `None` during initial site content structure installation
1378
        if setup:
1379
            setup.setDateSampledRequired(value)
1380
1381
    def getInvalidationReasonRequired(self):
1382
        """Get the value form the senaite setup
1383
        """
1384
        setup = api.get_senaite_setup()
1385
        # setup is `None` during initial site content structure installation
1386
        if setup:
1387
            return setup.getInvalidationReasonRequired()
1388
        return self.getField("InvalidationReasonRequired").default
1389
1390
    def setInvalidationReasonRequired(self, value):
1391
        """Set the value in the senaite setup
1392
        """
1393
        setup = api.get_senaite_setup()
1394
        # setup is `None` during initial site content structure installation
1395
        if setup:
1396
            setup.setInvalidationReasonRequired(value)
1397
1398
1399
registerType(BikaSetup, PROJECTNAME)
1400