BikaSetup.setExponentialFormatThreshold()   A
last analyzed

Complexity

Conditions 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 4
dl 0
loc 7
rs 10
c 0
b 0
f 0
cc 2
nop 2
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.decimal import DecimalWidget
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 FixedPointField
43
from Products.Archetypes.atapi import InAndOutWidget
44
from Products.Archetypes.atapi import IntegerField
45
from Products.Archetypes.atapi import IntegerWidget
46
from Products.Archetypes.atapi import LinesField
47
from Products.Archetypes.atapi import LinesWidget
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.p3compat import cmp
64
from zope.interface import implements
65
66
67
class IDFormattingField(RecordsField):
68
    """A list of prefixes per portal_type
69
    """
70
    _properties = RecordsField._properties.copy()
71
    _properties.update({
72
        'type': 'prefixes',
73
        'subfields': (
74
            'portal_type',
75
            'form',
76
            'sequence_type',
77
            'context',
78
            'counter_type',
79
            'counter_reference',
80
            'prefix',
81
            'split_length'
82
        ),
83
        'subfield_labels': {
84
            'portal_type': 'Portal Type',
85
            'form': 'Format',
86
            'sequence_type': 'Seq Type',
87
            'context': 'Context',
88
            'counter_type': 'Counter Type',
89
            'counter_reference': 'Counter Ref',
90
            'prefix': 'Prefix',
91
            'split_length': 'Split Length',
92
        },
93
        'subfield_readonly': {
94
            'portal_type': False,
95
            'form': False,
96
            'sequence_type': False,
97
            'context': False,
98
            'counter_type': False,
99
            'counter_reference': False,
100
            'prefix': False,
101
            'split_length': False,
102
        },
103
        'subfield_sizes': {
104
            'portal_type': 20,
105
            'form': 30,
106
            'sequence_type': 1,
107
            'context': 12,
108
            'counter_type': 1,
109
            'counter_reference': 12,
110
            'prefix': 12,
111
            'split_length': 5,
112
        },
113
        'subfield_types': {
114
            'sequence_type': 'selection',
115
            'counter_type': 'selection',
116
            'split_length': 'int',
117
        },
118
        'subfield_vocabularies': {
119
            'sequence_type': 'getSequenceTypes',
120
            'counter_type': 'getCounterTypes',
121
        },
122
        'subfield_maxlength': {
123
            'form': 256,
124
        },
125
    })
126
127
    security = ClassSecurityInfo()
128
129
    def getSequenceTypes(self, instance=None):
130
        return DisplayList([
131
            ('', ''),
132
            ('counter', 'Counter'),
133
            ('generated', 'Generated')
134
        ])
135
136
    def getCounterTypes(self, instance=None):
137
        return DisplayList([
138
            ('', ''),
139
            ('backreference', 'Backreference'),
140
            ('contained', 'Contained')
141
        ])
142
143
144
STICKER_AUTO_OPTIONS = DisplayList((
145
    ('None', _('None')),
146
    ('register', _('Register')),
147
    ('receive', _('Receive')),
148
))
149
150
151
schema = BikaFolderSchema.copy() + Schema((
152
    IntegerField(
153
        'AutoLogOff',
154
        schemata="Security",
155
        required=1,
156
        default=0,
157
        accessor="getAutoLogOff",
158
        edit_accessor="getAutoLogOff",
159
        mutator="setAutoLogOff",
160
        widget=IntegerWidget(
161
            label=_("Automatic log-off"),
162
            description=_(
163
                "The number of minutes before a user is automatically logged off. "
164
                "0 disables automatic log-off"),
165
        )
166
    ),
167
    BooleanField(
168
        'RestrictWorksheetUsersAccess',
169
        schemata="Security",
170
        default=True,
171
        accessor="getRestrictWorksheetUsersAccess",
172
        edit_accessor="getRestrictWorksheetUsersAccess",
173
        mutator="setRestrictWorksheetUsersAccess",
174
        widget=BooleanWidget(
175
            label=_("Restrict worksheet access to assigned analysts"),
176
            description=_("When enabled, analysts can only access worksheets to "
177
                          "which they are assigned. When disabled, analysts have "
178
                          "access to all worksheets.")
179
        )
180
    ),
181
    BooleanField(
182
        'AllowToSubmitNotAssigned',
183
        schemata="Security",
184
        default=True,
185
        accessor="getAllowToSubmitNotAssigned",
186
        edit_accessor="getAllowToSubmitNotAssigned",
187
        mutator="setAllowToSubmitNotAssigned",
188
        widget=BooleanWidget(
189
            label=_("Allow submission of results for unassigned analyses"),
190
            description=_(
191
                "When enabled, users can submit results for analyses not "
192
                "assigned to them or for unassigned analyses. When disabled, "
193
                "users can only submit results for analyses assigned to "
194
                "themselves. This setting does not apply to users with role "
195
                "Lab Manager.")
196
        )
197
    ),
198
    BooleanField(
199
        'RestrictWorksheetManagement',
200
        schemata="Security",
201
        default=True,
202
        accessor="getRestrictWorksheetManagement",
203
        edit_accessor="getRestrictWorksheetManagement",
204
        mutator="setRestrictWorksheetManagement",
205
        widget=BooleanWidget(
206
            label=_("Restrict worksheet management to lab managers"),
207
            description=_("When enabled, only lab managers can create and manage "
208
                          "worksheets. When disabled, analysts and lab clerks can "
209
                          "also manage worksheets. Note: This setting is "
210
                          "automatically enabled and locked when worksheet access "
211
                          "is restricted to assigned analysts.")
212
        )
213
    ),
214
    # NOTE: This is a Proxy Field which delegates to the SENAITE Registry!
215
    BooleanField(
216
        "EnableGlobalAuditlog",
217
        schemata="Security",
218
        default=False,
219
        accessor="getEnableGlobalAuditlog",
220
        edit_accessor="getEnableGlobalAuditlog",
221
        mutator="setEnableGlobalAuditlog",
222
        widget=BooleanWidget(
223
            label=_("Enable global Audit Log"),
224
            description=_(
225
                "The global Auditlog shows all modifications of the system. "
226
                "When enabled, all entities will be indexed in a separate "
227
                "catalog. This will increase the time when objects are "
228
                "created or modified."
229
            )
230
        )
231
    ),
232
    BooleanField(
233
        'ShowPrices',
234
        schemata="Accounting",
235
        default=True,
236
        accessor="getShowPrices",
237
        edit_accessor="getShowPrices",
238
        mutator="setShowPrices",
239
        widget=BooleanWidget(
240
            label=_("Include and display pricing information"),
241
        )
242
    ),
243
    StringField(
244
        'Currency',
245
        schemata="Accounting",
246
        required=1,
247
        vocabulary=CURRENCIES,
248
        default='EUR',
249
        accessor="getCurrency",
250
        edit_accessor="getCurrency",
251
        mutator="setCurrency",
252
        widget=SelectionWidget(
253
            label=_("Currency"),
254
            description=_("Select the currency the site will use to display prices."),
255
            format='select',
256
        )
257
    ),
258
    StringField(
259
        'DefaultCountry',
260
        schemata="Accounting",
261
        required=1,
262
        vocabulary='getCountries',
263
        default='',
264
        accessor="getDefaultCountry",
265
        edit_accessor="getDefaultCountry",
266
        mutator="setDefaultCountry",
267
        widget=SelectionWidget(
268
            label=_("Country"),
269
            description=_("Select the country the site will show by default"),
270
            format='select',
271
        )
272
    ),
273
    FixedPointField(
274
        'MemberDiscount',
275
        schemata="Accounting",
276
        default='33.33',
277
        accessor="getMemberDiscount",
278
        edit_accessor="getMemberDiscount",
279
        mutator="setMemberDiscount",
280
        widget=DecimalWidget(
281
            label=_("Member discount %"),
282
            description=_(
283
                "The discount percentage entered here, is applied to the prices for clients "
284
                "flagged as 'members', normally co-operative members or associates deserving "
285
                "of this discount"),
286
        )
287
    ),
288
    FixedPointField(
289
        'VAT',
290
        schemata="Accounting",
291
        default='19.00',
292
        accessor="getVAT",
293
        edit_accessor="getVAT",
294
        mutator="setVAT",
295
        widget=DecimalWidget(
296
            label=_("VAT %"),
297
            description=_(
298
                "Enter percentage value eg. 14.0. This percentage is applied system wide "
299
                "but can be overwrittem on individual items"),
300
        )
301
    ),
302
    StringField(
303
        'DecimalMark',
304
        schemata="Results Reports",
305
        vocabulary=DECIMAL_MARKS,
306
        default=".",
307
        accessor="getDecimalMark",
308
        edit_accessor="getDecimalMark",
309
        mutator="setDecimalMark",
310
        widget=SelectionWidget(
311
            label=_("Default decimal mark"),
312
            description=_("Preferred decimal mark for reports."),
313
            format='select',
314
        )
315
    ),
316
    StringField(
317
        'ScientificNotationReport',
318
        schemata="Results Reports",
319
        default='1',
320
        vocabulary=SCINOTATION_OPTIONS,
321
        accessor="getScientificNotationReport",
322
        edit_accessor="getScientificNotationReport",
323
        mutator="setScientificNotationReport",
324
        widget=SelectionWidget(
325
            label=_("Default scientific notation format for reports"),
326
            description=_("Preferred scientific notation format for reports"),
327
            format='select',
328
        )
329
    ),
330
    IntegerField(
331
        'MinimumResults',
332
        schemata="Results Reports",
333
        required=1,
334
        default=5,
335
        accessor="getMinimumResults",
336
        edit_accessor="getMinimumResults",
337
        mutator="setMinimumResults",
338
        widget=IntegerWidget(
339
            label=_("Minimum number of results for QC stats calculations"),
340
            description=_(
341
                "Using too few data points does not make statistical sense. "
342
                "Set an acceptable minimum number of results before QC statistics "
343
                "will be calculated and plotted"),
344
        )
345
    ),
346
    BooleanField(
347
        'CategoriseAnalysisServices',
348
        schemata="Analyses",
349
        default=False,
350
        accessor="getCategoriseAnalysisServices",
351
        edit_accessor="getCategoriseAnalysisServices",
352
        mutator="setCategoriseAnalysisServices",
353
        widget=BooleanWidget(
354
            label=_("Categorise analysis services"),
355
            description=_("Group analysis services by category in the LIMS tables, helpful when the list is long")
356
        ),
357
    ),
358
    BooleanField(
359
        "CategorizeSampleAnalyses",
360
        schemata="Analyses",
361
        default=False,
362
        accessor="getCategorizeSampleAnalyses",
363
        edit_accessor="getCategorizeSampleAnalyses",
364
        mutator="setCategorizeSampleAnalyses",
365
        widget=BooleanWidget(
366
            label=_("label_bikasetup_categorizesampleanalyses",
367
                    default="Categorize sample analyses"),
368
            description=_("description_bikasetup_categorizesampleanalyses",
369
                          "Group analyses by category for samples")
370
        ),
371
    ),
372
    BooleanField(
373
        "SampleAnalysesRequired",
374
        schemata="Analyses",
375
        default=True,
376
        accessor="getSampleAnalysesRequired",
377
        edit_accessor="getSampleAnalysesRequired",
378
        mutator="setSampleAnalysesRequired",
379
        widget=BooleanWidget(
380
            label=_("label_bikasetup_sampleanalysesrequired",
381
                    default="Require sample analyses"),
382
            description=_("description_bikasetup_sampleanalysesrequired",
383
                          "Analyses are required for sample registration")
384
        ),
385
    ),
386
    BooleanField(
387
        "AllowManualResultCaptureDate",
388
        schemata="Analyses",
389
        default=True,
390
        accessor="getAllowManualResultCaptureDate",
391
        edit_accessor="getAllowManualResultCaptureDate",
392
        mutator="setAllowManualResultCaptureDate",
393
        widget=BooleanWidget(
394
            label=_("label_bikasetup_allow_manual_result_capture_date",
395
                    default="Allow to set the result capture date"),
396
            description=_(
397
                "description_bikasetup_allow_manual_result_capture_date",
398
                default="If this option is activated, the result capture date "
399
                        "can be entered manually for analyses"),
400
        ),
401
    ),
402
    BooleanField(
403
        'EnableARSpecs',
404
        schemata="Analyses",
405
        default=False,
406
        accessor="getEnableARSpecs",
407
        edit_accessor="getEnableARSpecs",
408
        mutator="setEnableARSpecs",
409
        widget=BooleanWidget(
410
            label=_("Enable Sample Specifications"),
411
            description=_(
412
                "Analysis specifications which are edited directly on the "
413
                "Sample."),
414
        ),
415
    ),
416
    IntegerField(
417
        'ExponentialFormatThreshold',
418
        schemata="Analyses",
419
        required=1,
420
        default=7,
421
        accessor="getExponentialFormatThreshold",
422
        edit_accessor="getExponentialFormatThreshold",
423
        mutator="setExponentialFormatThreshold",
424
        widget=IntegerWidget(
425
            label=_("Exponential format threshold"),
426
            description=_(
427
                "Result values with at least this number of significant "
428
                "digits are displayed in scientific notation using the "
429
                "letter 'e' to indicate the exponent.  The precision can be "
430
                "configured in individual Analysis Services."),
431
        )
432
    ),
433
    BooleanField(
434
        "ImmediateResultsEntry",
435
        schemata="Analyses",
436
        default=False,
437
        accessor="getImmediateResultsEntry",
438
        edit_accessor="getImmediateResultsEntry",
439
        mutator="setImmediateResultsEntry",
440
        widget=BooleanWidget(
441
            label=_("label_bikasetup_immediateresultsentry",
442
                    default=u"Immediate results entry"),
443
            description=_(
444
                "description_bikasetup_immediateresultsentry",
445
                default=u"Allow the user to directly enter results after "
446
                "sample creation, e.g. to enter field results immediately, or "
447
                "lab results, when the automatic sample reception is "
448
                "activated."
449
            ),
450
        ),
451
    ),
452
    BooleanField(
453
        'EnableAnalysisRemarks',
454
        schemata="Analyses",
455
        default=False,
456
        accessor="getEnableAnalysisRemarks",
457
        edit_accessor="getEnableAnalysisRemarks",
458
        mutator="setEnableAnalysisRemarks",
459
        widget=BooleanWidget(
460
            label=_("Add a remarks field to all analyses"),
461
            description=_(
462
                "If enabled, a free text field will be displayed close to "
463
                "each analysis in results entry view"
464
            )
465
        ),
466
    ),
467
    BooleanField(
468
        "AutoVerifySamples",
469
        schemata="Analyses",
470
        default=True,
471
        accessor="getAutoVerifySamples",
472
        edit_accessor="getAutoVerifySamples",
473
        mutator="setAutoVerifySamples",
474
        widget=BooleanWidget(
475
            label=_("Automatic verification of samples"),
476
            description=_(
477
                "When enabled, the sample is automatically verified as soon as "
478
                "all results are verified. Otherwise, users with enough "
479
                "privileges have to manually verify the sample afterwards. "
480
                "Default: enabled"
481
            )
482
        )
483
    ),
484
    BooleanField(
485
        'SelfVerificationEnabled',
486
        schemata="Analyses",
487
        default=False,
488
        accessor="getSelfVerificationEnabled",
489
        edit_accessor="getSelfVerificationEnabled",
490
        mutator="setSelfVerificationEnabled",
491
        widget=BooleanWidget(
492
            label=_("Allow self-verification of results"),
493
            description=_(
494
                "If enabled, a user who submitted a result will also be able "
495
                "to verify it. This setting only take effect for those users "
496
                "with a role assigned that allows them to verify results "
497
                "(by default, managers, labmanagers and verifiers)."
498
                "This setting can be overrided for a given Analysis in "
499
                "Analysis Service edit view. By default, disabled."),
500
        ),
501
    ),
502
    IntegerField(
503
        'NumberOfRequiredVerifications',
504
        schemata="Analyses",
505
        default=1,
506
        vocabulary="_getNumberOfRequiredVerificationsVocabulary",
507
        accessor="getNumberOfRequiredVerifications",
508
        edit_accessor="getNumberOfRequiredVerifications",
509
        mutator="setNumberOfRequiredVerifications",
510
        widget=SelectionWidget(
511
            format="select",
512
            label=_("Number of required verifications"),
513
            description=_(
514
                "Number of required verifications before a given result being "
515
                "considered as 'verified'. This setting can be overrided for "
516
                "any Analysis in Analysis Service edit view. By default, 1"),
517
        ),
518
    ),
519
    StringField(
520
        'TypeOfmultiVerification',
521
        schemata="Analyses",
522
        default='self_multi_enabled',
523
        vocabulary=MULTI_VERIFICATION_TYPE,
524
        accessor="getTypeOfmultiVerification",
525
        edit_accessor="getTypeOfmultiVerification",
526
        mutator="setTypeOfmultiVerification",
527
        widget=SelectionWidget(
528
            label=_("Multi Verification type"),
529
            description=_(
530
                "Choose type of multiple verification for the same user."
531
                "This setting can enable/disable verifying/consecutively verifying"
532
                "more than once for the same user."),
533
            format='select',
534
        )
535
    ),
536
    StringField(
537
        'ResultsDecimalMark',
538
        schemata="Analyses",
539
        vocabulary=DECIMAL_MARKS,
540
        default=".",
541
        accessor="getResultsDecimalMark",
542
        edit_accessor="getResultsDecimalMark",
543
        mutator="setResultsDecimalMark",
544
        widget=SelectionWidget(
545
            label=_("Default decimal mark"),
546
            description=_("Preferred decimal mark for results"),
547
            format='select',
548
        )
549
    ),
550
    StringField(
551
        'ScientificNotationResults',
552
        schemata="Analyses",
553
        default='1',
554
        vocabulary=SCINOTATION_OPTIONS,
555
        accessor="getScientificNotationResults",
556
        edit_accessor="getScientificNotationResults",
557
        mutator="setScientificNotationResults",
558
        widget=SelectionWidget(
559
            label=_("Default scientific notation format for results"),
560
            description=_("Preferred scientific notation format for results"),
561
            format='select',
562
        )
563
    ),
564
    StringField(
565
        'WorksheetLayout',
566
        schemata="Appearance",
567
        default=DEFAULT_WORKSHEET_LAYOUT,
568
        vocabulary=getWorksheetLayouts(),
569
        accessor="getWorksheetLayout",
570
        edit_accessor="getWorksheetLayout",
571
        mutator="setWorksheetLayout",
572
        widget=SelectionWidget(
573
            label=_("Default layout in worksheet view"),
574
            description=_("Preferred layout of the results entry table "
575
                          "in the Worksheet view. Classic layout displays "
576
                          "the Samples in rows and the analyses "
577
                          "in columns. Transposed layout displays the "
578
                          "Samples in columns and the analyses "
579
                          "in rows."),
580
            format='select',
581
        )
582
    ),
583
    BooleanField(
584
        'DashboardByDefault',
585
        schemata="Appearance",
586
        default=True,
587
        accessor="getDashboardByDefault",
588
        edit_accessor="getDashboardByDefault",
589
        mutator="setDashboardByDefault",
590
        widget=BooleanWidget(
591
            label=_("Use Dashboard as default front page"),
592
            description=_("Select this to activate the dashboard as a default front page.")
593
        ),
594
    ),
595
    UIDReferenceField(
596
        "LandingPage",
597
        schemata="Appearance",
598
        required=0,
599
        allowed_types=(
600
            "Document",
601
            "Client",
602
            "ClientFolder",
603
            "Samples",
604
            "WorksheetFolder",
605
        ),
606
        mode="rw",
607
        multiValued=0,
608
        relationship="SetupLandingPage",
609
        accessor="getLandingPage",
610
        edit_accessor="getLandingPage",
611
        mutator="setLandingPage",
612
        widget=ReferenceWidget(
613
            label=_(
614
                "label_setup_landingpage",
615
                default="Landing Page"),
616
            description=_(
617
                "description_setup_landingpage",
618
                default="The landing page is shown for non-authenticated users "
619
                "if the Dashboard is not selected as the default front page. "
620
                "If no landing page is selected, the default frontpage is displayed."),
621
            catalog=["uid_catalog"],
622
            query={
623
                "is_active": True,
624
                "sort_on": "id",
625
                "sort_order": "ascending"
626
            },
627
            columns=[
628
                {"name": "Title", "label": _("Title")},
629
                {"name": "portal_type", "label": _("Type")},
630
            ],
631
632
        ),
633
    ),
634
    BooleanField(
635
        'PrintingWorkflowEnabled',
636
        schemata="Sampling",
637
        default=False,
638
        accessor="getPrintingWorkflowEnabled",
639
        edit_accessor="getPrintingWorkflowEnabled",
640
        mutator="setPrintingWorkflowEnabled",
641
        widget=BooleanWidget(
642
            label=_("Enable the Results Report Printing workflow"),
643
            description=_("Select this to allow the user to set an "
644
                          "additional 'Printed' status to those Analysis "
645
                          "Requests that have been Published. "
646
                          "Disabled by default.")
647
        ),
648
    ),
649
    BooleanField(
650
        'SamplingWorkflowEnabled',
651
        schemata="Sampling",
652
        default=False,
653
        accessor="getSamplingWorkflowEnabled",
654
        edit_accessor="getSamplingWorkflowEnabled",
655
        mutator="setSamplingWorkflowEnabled",
656
        widget=BooleanWidget(
657
            label=_("Enable Sampling"),
658
            description=_("Select this to activate the sample collection workflow steps.")
659
        ),
660
    ),
661
    BooleanField(
662
        'ScheduleSamplingEnabled',
663
        schemata="Sampling",
664
        default=False,
665
        accessor="getScheduleSamplingEnabled",
666
        edit_accessor="getScheduleSamplingEnabled",
667
        mutator="setScheduleSamplingEnabled",
668
        widget=BooleanWidget(
669
            label=_("Enable Sampling Scheduling"),
670
            description=_(
671
                "Select this to allow a Sampling Coordinator to" +
672
                " schedule a sampling. This functionality only takes effect" +
673
                " when 'Sampling workflow' is active")
674
        ),
675
    ),
676
    # NOTE: This is a Proxy Field which delegates to the SENAITE Registry!
677
    BooleanField(
678
        "DateSampledRequired",
679
        schemata="Sampling",
680
        default=True,
681
        accessor="getDateSampledRequired",
682
        edit_accessor="getDateSampledRequired",
683
        mutator="setDateSampledRequired",
684
        widget=BooleanWidget(
685
            label=_(
686
                "label_bikasetup_date_sampled_required",
687
                default="Date sampled required"
688
            ),
689
            description=_(
690
                "description_bikasetup_date_sampled_required",
691
                default="Select this to make DateSampled field required on "
692
                        "sample creation. This functionality only takes "
693
                        "effect when 'Sampling workflow' is not active"
694
            ),
695
        ),
696
    ),
697
    BooleanField(
698
        "AutoreceiveSamples",
699
        schemata="Sampling",
700
        default=False,
701
        accessor="getAutoreceiveSamples",
702
        edit_accessor="getAutoreceiveSamples",
703
        mutator="setAutoreceiveSamples",
704
        widget=BooleanWidget(
705
            label=_("Auto-receive samples"),
706
            description=_(
707
                "Select to receive the samples automatically when created by "
708
                "lab personnel and sampling workflow is disabled. Samples "
709
                "created by client contacts won't be received automatically"
710
            ),
711
        ),
712
    ),
713
    BooleanField(
714
        'ShowPartitions',
715
        schemata="Appearance",
716
        default=False,
717
        accessor="getShowPartitions",
718
        edit_accessor="getShowPartitions",
719
        mutator="setShowPartitions",
720
        widget=BooleanWidget(
721
            label=_("Display sample partitions to clients"),
722
            description=_(
723
                "Select to show sample partitions to client contacts. "
724
                "If deactivated, partitions won't be included in listings "
725
                "and no info message with links to the primary sample will "
726
                "be displayed to client contacts.")
727
        ),
728
    ),
729
    BooleanField(
730
        'SamplePreservationEnabled',
731
        schemata="Sampling",
732
        default=False,
733
        accessor="getSamplePreservationEnabled",
734
        edit_accessor="getSamplePreservationEnabled",
735
        mutator="setSamplePreservationEnabled",
736
        widget=BooleanWidget(
737
            label=_("Enable Sample Preservation"),
738
            description=_("")
739
        ),
740
    ),
741
    LinesField(
742
        "Workdays",
743
        schemata="Sampling",
744
        vocabulary=WEEKDAYS,
745
        default=tuple(map(str, range(7))),
746
        required=1,
747
        accessor="getWorkdays",
748
        edit_accessor="getWorkdays",
749
        mutator="setWorkdays",
750
        widget=InAndOutWidget(
751
            visible=True,
752
            label=_("Laboratory Workdays"),
753
            description=_("Only laboratory workdays are considered for the "
754
                          "analysis turnaround time calculation. "),
755
            format="checkbox",
756
        )
757
    ),
758
    DurationField(
759
        'DefaultTurnaroundTime',
760
        schemata="Sampling",
761
        required=1,
762
        default={"days": 5, "hours": 0, "minutes": 0},
763
        accessor="getDefaultTurnaroundTime",
764
        edit_accessor="getDefaultTurnaroundTime",
765
        mutator="setDefaultTurnaroundTime",
766
        widget=DurationWidget(
767
            label=_("Default turnaround time for analyses."),
768
            description=_(
769
                "This is the default maximum time allowed for performing "
770
                "analyses.  It is only used for analyses where the analysis "
771
                "service does not specify a turnaround time. "
772
                "Only laboratory workdays are considered."
773
            ),
774
        )
775
    ),
776
    DurationField(
777
        'DefaultSampleLifetime',
778
        schemata="Sampling",
779
        required=1,
780
        default={"days": 30, "hours": 0, "minutes": 0},
781
        accessor="getDefaultSampleLifetime",
782
        edit_accessor="getDefaultSampleLifetime",
783
        mutator="setDefaultSampleLifetime",
784
        widget=DurationWidget(
785
            label=_("Default sample retention period"),
786
            description=_(
787
                "The number of days before a sample expires and cannot be analysed "
788
                "any more. This setting can be overwritten per individual sample type "
789
                "in the sample types setup"),
790
        )
791
    ),
792
    # NOTE: This is a Proxy Field which delegates to the SENAITE Registry!
793
    StringField(
794
        "EmailFromSamplePublication",
795
        default_method='getEmailFromSamplePublication',
796
        schemata="Notifications",
797
        accessor="getEmailFromSamplePublication",
798
        edit_accessor="getEmailFromSamplePublication",
799
        mutator="setEmailFromSamplePublication",
800
        widget=StringWidget(
801
            label=_(
802
                "label_bikasetup_email_from_sample_publication",
803
                default="Publication 'From' address"
804
            ),
805
            description=_(
806
                "description_bikasetup_email_from_sample_publication",
807
                default="E-mail to use as the 'From' address for outgoing "
808
                        "e-mails when publishing results reports. This "
809
                        "address overrides the value set at portal's 'Mail "
810
                        "settings'."
811
            ),
812
        ),
813
        validators=("isEmail", )
814
    ),
815
    # NOTE: This is a Proxy Field which delegates to the SENAITE Registry!
816
    TextField(
817
        "EmailBodySamplePublication",
818
        default_content_type="text/html",
819
        default_output_type="text/x-html-safe",
820
        schemata="Notifications",
821
        # Needed to fetch the default value from the registry
822
        accessor="getEmailBodySamplePublication",
823
        edit_accessor="getEmailBodySamplePublication",
824
        mutator="setEmailBodySamplePublication",
825
        widget=RichWidget(
826
            label=_(
827
                "label_bikasetup_email_body_sample_publication",
828
                "Email body for Sample publication notifications"),
829
            description=_(
830
                "description_bikasetup_email_body_sample_publication",
831
                default="Set the email body text to be used by default when "
832
                "sending out result reports to the selected recipients. "
833
                "You can use reserved keywords: "
834
                "$client_name, $recipients, $lab_name, $lab_address"),
835
            default_mime_type="text/x-html",
836
            output_mime_type="text/x-html",
837
            allow_file_upload=False,
838
            rows=15,
839
        ),
840
    ),
841
    # NOTE: This is a Proxy Field which delegates to the SENAITE Registry!
842
    BooleanField(
843
        "AlwaysCCResponsiblesInReportEmail",
844
        schemata="Notifications",
845
        default=True,
846
        accessor="getAlwaysCCResponsiblesInReportEmail",
847
        edit_accessor="getAlwaysCCResponsiblesInReportEmail",
848
        mutator="setAlwaysCCResponsiblesInReportEmail",
849
        widget=BooleanWidget(
850
            label=_(
851
                "label_bikasetup_always_cc_responsibles_in_report_emails",
852
                default="Always send publication email to responsibles"),
853
            description=_(
854
                "description_bikasetup_always_cc_responsibles_in_report_emails",
855
                default="When selected, the responsible persons of all "
856
                "involved lab departments will receive publication emails."),
857
        ),
858
    ),
859
    BooleanField(
860
        'NotifyOnSampleRejection',
861
        schemata="Notifications",
862
        default=False,
863
        accessor="getNotifyOnSampleRejection",
864
        edit_accessor="getNotifyOnSampleRejection",
865
        mutator="setNotifyOnSampleRejection",
866
        widget=BooleanWidget(
867
            label=_("Email notification on Sample rejection"),
868
            description=_("Select this to activate automatic notifications "
869
                          "via email to the Client when a Sample is rejected.")
870
        ),
871
    ),
872
    TextField(
873
        "EmailBodySampleRejection",
874
        default_content_type='text/html',
875
        default_output_type='text/x-html-safe',
876
        schemata="Notifications",
877
        label=_("Email body for Sample Rejection notifications"),
878
        default="The sample $sample_link has been rejected because of the "
879
                "following reasons:"
880
                "<br/><br/>$reasons<br/><br/>"
881
                "For further information, please contact us under the "
882
                "following address.<br/><br/>"
883
                "$lab_address",
884
        accessor="getEmailBodySampleRejection",
885
        edit_accessor="getEmailBodySampleRejection",
886
        mutator="setEmailBodySampleRejection",
887
        widget=RichWidget(
888
            label=_("Email body for Sample Rejection notifications"),
889
            description=_(
890
                "Set the text for the body of the email to be sent to the "
891
                "Sample's client contact if the option 'Email notification on "
892
                "Sample rejection' is enabled. You can use reserved keywords: "
893
                "$sample_id, $sample_link, $reasons, $lab_address"),
894
            default_mime_type='text/x-rst',
895
            output_mime_type='text/x-html',
896
            allow_file_upload=False,
897
            rows=15,
898
        ),
899
    ),
900
    # NOTE: This is a Proxy Field which delegates to the SENAITE Registry!
901
    BooleanField(
902
        "InvalidationReasonRequired",
903
        schemata="Notifications",
904
        default=True,
905
        accessor="getInvalidationReasonRequired",
906
        edit_accessor="getInvalidationReasonRequired",
907
        mutator="setInvalidationReasonRequired",
908
        widget=BooleanWidget(
909
            label=_(
910
                "label_bikasetup_invalidation_reason_required",
911
                default="Invalidation reason required"
912
            ),
913
            description=_(
914
                "description_bikasetup_invalidation_reason_required",
915
                default="Specify whether providing a reason is mandatory when "
916
                        "invalidating a sample. If enabled, the '$reason' "
917
                        "placeholder in the sample invalidation notification "
918
                        "email body will be replaced with the entered reason."
919
            ),
920
        ),
921
    ),
922
    TextField(
923
        "EmailBodySampleInvalidation",
924
        default_content_type='text/html',
925
        default_output_type='text/x-html-safe',
926
        schemata="Notifications",
927
        default=
928
            "Some non-conformities have been detected in the results report "
929
            "published for Sample $sample_link. "
930
            "<br/><br/> "
931
            "A new Sample $retest_link has been created automatically, and the "
932
            "previous request has been invalidated. "
933
            "<br/><br/> "
934
            "The root cause is under investigation and corrective "
935
            "action has been initiated. "
936
            "<br/><br/> "
937
            "$lab_address",
938
        accessor="getEmailBodySampleInvalidation",
939
        edit_accessor="getEmailBodySampleInvalidation",
940
        mutator="setEmailBodySampleInvalidation",
941
        widget=RichWidget(
942
            label=_(
943
                "label_bikasetup_invalidation_email_body",
944
                default="Email body for Sample Invalidation notifications"
945
            ),
946
            description=_(
947
                "description_bikasetup_invalidation_email_body",
948
                default=
949
                "Define the template for the email body that will be "
950
                "automatically sent to primary contacts and laboratory "
951
                "managers when a sample is invalidated. The following "
952
                "placeholders are supported: "
953
                "<code title='The ID of the sample'>$sample_id</code>, "
954
                "<code title='The ID of the sample retest'>$retest_id</code>, "
955
                "<code title='The link to the retest'>$retest_link</code>, "
956
                "<code title='The reason(s) for invalidation'>$reason</code>, "
957
                "<code title='The address of the lab'>$lab_address</code>."
958
            ),
959
            default_mime_type='text/x-rst',
960
            output_mime_type='text/x-html',
961
            allow_file_upload=False,
962
            rows=15,
963
        ),
964
    ),
965
966
    StringField(
967
        "AutoPrintStickers",
968
        schemata="Sticker",
969
        vocabulary=STICKER_AUTO_OPTIONS,
970
        accessor="getAutoPrintStickers",
971
        edit_accessor="getAutoPrintStickers",
972
        mutator="setAutoPrintStickers",
973
        widget=SelectionWidget(
974
            format='select',
975
            label=_("Automatic Sticker Printing"),
976
            description=_(
977
                "Choose when stickers should be automatically printed:<br/>"
978
                "<ul>"
979
                "<li><strong>Register:</strong> Stickers are printed "
980
                " automatically when new samples are created.</li>"
981
                "<li><strong>Receive:</strong> Stickers are printed "
982
                " automatically when samples are received.</li>"
983
                "<li><strong>None:</strong> Disables automatic sticker "
984
                "printing.</li>"
985
                "</ul>"
986
            ),
987
        )
988
    ),
989
990
    StringField(
991
        "AutoStickerTemplate",
992
        schemata="Sticker",
993
        vocabulary_factory="senaite.core.vocabularies.stickers",
994
        accessor="getAutoStickerTemplate",
995
        edit_accessor="getAutoStickerTemplate",
996
        mutator="setAutoStickerTemplate",
997
        widget=SelectionWidget(
998
            format='select',
999
            label=_("Default Sticker Template"),
1000
            description=_(
1001
                "Select the default sticker template used for "
1002
                "automatic printing.<br/>"
1003
            ),
1004
        )
1005
    ),
1006
1007
    StringField(
1008
        "SmallStickerTemplate",
1009
        schemata="Sticker",
1010
        vocabulary_factory="senaite.core.vocabularies.stickers",
1011
        default="Code_128_1x48mm.pt",
1012
        accessor="getSmallStickerTemplate",
1013
        edit_accessor="getSmallStickerTemplate",
1014
        mutator="setSmallStickerTemplate",
1015
        widget=SelectionWidget(
1016
            format='select',
1017
            label=_("Small Sticker Template"),
1018
            description=_(
1019
                "Choose the default template for 'small' stickers.<br/>"
1020
                "<strong>Note:</strong> Sample-specific 'small' stickers are "
1021
                "configured based on their sample type."
1022
            ),
1023
        )
1024
    ),
1025
1026
    StringField(
1027
        "LargeStickerTemplate",
1028
        schemata="Sticker",
1029
        vocabulary_factory="senaite.core.vocabularies.stickers",
1030
        default="Code_128_1x72mm.pt",
1031
        accessor="getLargeStickerTemplate",
1032
        edit_accessor="getLargeStickerTemplate",
1033
        mutator="setLargeStickerTemplate",
1034
        widget=SelectionWidget(
1035
            format='select',
1036
            label=_("Large Sticker Template"),
1037
            description=_(
1038
                "Choose the default template for 'large' stickers.<br/>"
1039
                "<strong>Note:</strong> Sample-specific 'large' stickers are "
1040
                "configured based on their sample type."
1041
            ),
1042
        )
1043
    ),
1044
1045
    IntegerField(
1046
        "DefaultNumberOfCopies",
1047
        schemata="Sticker",
1048
        required=True,
1049
        default=1,
1050
        accessor="getDefaultNumberOfCopies",
1051
        edit_accessor="getDefaultNumberOfCopies",
1052
        mutator="setDefaultNumberOfCopies",
1053
        widget=IntegerWidget(
1054
            label=_("Default Number of Copies"),
1055
            description=_(
1056
                "Specify how many copies of each sticker should be printed "
1057
                "by default."
1058
            ),
1059
        )
1060
    ),
1061
1062
    IDFormattingField(
1063
        'IDFormatting',
1064
        schemata="ID Server",
1065
        accessor="getIDFormatting",
1066
        edit_accessor="getIDFormatting",
1067
        mutator="setIDFormatting",
1068
        default=[
1069
            {
1070
                'form': 'B-{seq:03d}',
1071
                'portal_type': 'Batch',
1072
                'prefix': 'batch',
1073
                'sequence_type': 'generated',
1074
                'split_length': 1
1075
            }, {
1076
                'form': 'D-{seq:03d}',
1077
                'portal_type': 'DuplicateAnalysis',
1078
                'prefix': 'duplicate',
1079
                'sequence_type': 'generated',
1080
                'split_length': 1
1081
            }, {
1082
                'form': 'I-{seq:03d}',
1083
                'portal_type': 'Invoice',
1084
                'prefix': 'invoice',
1085
                'sequence_type': 'generated',
1086
                'split_length': 1
1087
            }, {
1088
                'form': 'QC-{seq:03d}',
1089
                'portal_type': 'ReferenceSample',
1090
                'prefix': 'refsample',
1091
                'sequence_type': 'generated',
1092
                'split_length': 1
1093
            }, {
1094
                'form': 'SA-{seq:03d}',
1095
                'portal_type': 'ReferenceAnalysis',
1096
                'prefix': 'refanalysis',
1097
                'sequence_type': 'generated',
1098
                'split_length': 1
1099
            }, {
1100
                'form': 'WS-{seq:03d}',
1101
                'portal_type': 'Worksheet',
1102
                'prefix': 'worksheet',
1103
                'sequence_type': 'generated',
1104
                'split_length': 1
1105
            }, {
1106
                'form': '{sampleType}-{seq:04d}',
1107
                'portal_type': 'AnalysisRequest',
1108
                'prefix': 'analysisrequest',
1109
                'sequence_type': 'generated',
1110
                'split_length': 1
1111
            }, {
1112
                'form': '{parent_ar_id}-P{partition_count:02d}',
1113
                'portal_type': 'AnalysisRequestPartition',
1114
                'prefix': 'analysisrequestpartition',
1115
                'sequence_type': '',
1116
                'split-length': 1
1117
            }, {
1118
                'form': '{parent_base_id}-R{retest_count:02d}',
1119
                'portal_type': 'AnalysisRequestRetest',
1120
                'prefix': 'analysisrequestretest',
1121
                'sequence_type': '',
1122
                'split-length': 1
1123
            }, {
1124
                'form': '{parent_ar_id}-S{secondary_count:02d}',
1125
                'portal_type': 'AnalysisRequestSecondary',
1126
                'prefix': 'analysisrequestsecondary',
1127
                'sequence_type': '',
1128
                'split-length': 1
1129
            },
1130
        ],
1131
        widget=RecordsWidget(
1132
            label=_("Formatting Configuration"),
1133
            allowDelete=True,
1134
            description=_(
1135
                " <p>The ID Server provides unique sequential IDs "
1136
                "for objects such as Samples and Worksheets etc, based on a "
1137
                "format specified for each content type.</p>"
1138
                "<p>The format is constructed similarly to the Python format"
1139
                " syntax, using predefined variables per content type, and"
1140
                " advancing the IDs through a sequence number, 'seq' and its"
1141
                " padding as a number of digits, e.g. '03d' for a sequence of"
1142
                " IDs from 001 to 999.</p>"
1143
                "<p>Alphanumeric prefixes for IDs are included as is in the"
1144
                " formats, e.g. WS for Worksheet in WS-{seq:03d} produces"
1145
                " sequential Worksheet IDs: WS-001, WS-002, WS-003 etc.</p>"
1146
                "<p>For dynamic generation of alphanumeric and sequential IDs,"
1147
                " the wildcard {alpha} can be used. E.g WS-{alpha:2a3d}"
1148
                " produces WS-AA001, WS-AA002, WS-AB034, etc.</p>"
1149
                "<p>Variables that can be used include:"
1150
                "<table>"
1151
                "<tr>"
1152
                "<th style='width:150px'>Content Type</th><th>Variables</th>"
1153
                "</tr>"
1154
                "<tr><td>Client ID</td><td>{clientId}</td></tr>"
1155
                "<tr><td>Year</td><td>{year}</td></tr>"
1156
                "<tr><td>Sample ID</td><td>{sampleId}</td></tr>"
1157
                "<tr><td>Sample Type</td><td>{sampleType}</td></tr>"
1158
                "<tr><td>Sampling Date</td><td>{samplingDate}</td></tr>"
1159
                "<tr><td>Date Sampled</td><td>{dateSampled}</td></tr>"
1160
                "</table>"
1161
                "</p>"
1162
                "<p>Configuration Settings:"
1163
                "<ul>"
1164
                "<li>format:"
1165
                "<ul><li>a python format string constructed from predefined"
1166
                " variables like sampleId, clientId, sampleType.</li>"
1167
                "<li>special variable 'seq' must be positioned last in the"
1168
                "format string</li></ul></li>"
1169
                "<li>sequence type: [generated|counter]</li>"
1170
                "<li>context: if type counter, provides context the counting"
1171
                " function</li>"
1172
                "<li>counter type: [backreference|contained]</li>"
1173
                "<li>counter reference: a parameter to the counting"
1174
                " function</li>"
1175
                "<li>prefix: default prefix if none provided in format"
1176
                " string</li>"
1177
                "<li>split length: the number of parts to be included in the"
1178
                " prefix</li>"
1179
                "</ul></p>")
1180
        )
1181
    ),
1182
    StringField(
1183
        'IDServerValues',
1184
        schemata="ID Server",
1185
        accessor="getIDServerValuesHTML",
1186
        readonly=True,
1187
        widget=TextAreaWidget(
1188
            label=_("ID Server Values"),
1189
            cols=30,
1190
            rows=30,
1191
        ),
1192
    ),
1193
    LinesField(
1194
        "RejectionReasons",
1195
        schemata="Analyses",
1196
        # NOTE: accessors/mutators needed to display the proxied values!
1197
        accessor="getRejectionReasons",
1198
        edit_accessor="getRejectionReasons",
1199
        mutator="setRejectionReasons",
1200
        widget=LinesWidget(
1201
            label=_("Rejection Reasons"),
1202
            description=_("List of predefined rejection reasons. "
1203
                          "Enter one reason per line.")
1204
        ),
1205
    ),
1206
    IntegerField(
1207
        'DefaultNumberOfARsToAdd',
1208
        schemata="Analyses",
1209
        required=0,
1210
        default=4,
1211
        accessor="getDefaultNumberOfARsToAdd",
1212
        edit_accessor="getDefaultNumberOfARsToAdd",
1213
        mutator="setDefaultNumberOfARsToAdd",
1214
        widget=IntegerWidget(
1215
            label=_("Default count of Sample to add."),
1216
            description=_("Default value of the 'Sample count' when users click 'ADD' button to create new Samples"),
1217
        )
1218
    ),
1219
    IntegerField(
1220
        "MaxNumberOfSamplesAdd",
1221
        schemata="Analyses",
1222
        required=0,
1223
        default=10,
1224
        accessor="getMaxNumberOfSamplesAdd",
1225
        edit_accessor="getMaxNumberOfSamplesAdd",
1226
        mutator="setMaxNumberOfSamplesAdd",
1227
        widget=IntegerWidget(
1228
            label=_(
1229
                u"label_senaitesetup_maxnumberofsamplesadd",
1230
                default=u"Maximum value for 'Number of samples' field on "
1231
                        u"registration"
1232
            ),
1233
            description=_(
1234
                u"description_senaitesetup_maxnumberofsamplesadd",
1235
                default=u"Maximum number of samples that can be created in "
1236
                        u"accordance with the value set for the field 'Number "
1237
                        u"of samples' on the sample registration form"
1238
            ),
1239
        )
1240
    ),
1241
    # NOTE: This is a Proxy Field which delegates to senaite_setup DX
1242
    BooleanField(
1243
        "ShowLabNameInLogin",
1244
        schemata="Appearance",
1245
        default=False,
1246
        accessor="getShowLabNameInLogin",
1247
        edit_accessor="getShowLabNameInLogin",
1248
        mutator="setShowLabNameInLogin",
1249
        widget=BooleanWidget(
1250
            label=_(
1251
                u"title_senaitesetup_show_lab_name_in_login",
1252
                default=u"Display laboratory name in the login page"),
1253
            description=_(
1254
                u"description_senaitesetup_show_lab_name_in_login",
1255
                default=u"When selected, the laboratory name will be displayed"
1256
                        u"in the login page, above the access credentials."
1257
            ),
1258
        )
1259
    ),
1260
))
1261
1262
schema['title'].validators = ()
1263
schema['title'].widget.visible = False
1264
# Update the validation layer after change the validator in runtime
1265
schema['title']._validationLayer()
1266
1267
1268
class BikaSetup(folder.ATFolder):
1269
    """LIMS Setup
1270
    """
1271
    implements(IBikaSetup, IHideActionsMenu)
1272
1273
    schema = schema
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable schema does not seem to be defined.
Loading history...
1274
    security = ClassSecurityInfo()
1275
1276
    def setAutoLogOff(self, value):
1277
        """set session lifetime
1278
        """
1279
        value = int(value)
1280
        if value < 0:
1281
            value = 0
1282
        value = value * 60
1283
        acl = api.get_tool("acl_users")
1284
        session = acl.get("session")
1285
        if session:
1286
            session.timeout = value
1287
1288
    def getAutoLogOff(self):
1289
        """get session lifetime
1290
        """
1291
        acl = api.get_tool("acl_users")
1292
        session = acl.get("session")
1293
        if not session:
1294
            return 0
1295
        return session.timeout // 60
1296
1297
    def getAnalysisServicesVocabulary(self):
1298
        """
1299
        Get all active Analysis Services from Bika Setup and return them as Display List.
1300
        """
1301
        bsc = getToolByName(self, 'senaite_catalog_setup')
1302
        brains = bsc(portal_type='AnalysisService',
1303
                     is_active=True)
1304
        items = [(b.UID, b.Title) for b in brains]
1305
        items.insert(0, ("", ""))
1306
        items.sort(lambda x, y: cmp(x[1], y[1]))
1307
        return DisplayList(list(items))
1308
1309
    def getPrefixFor(self, portal_type):
1310
        """Return the prefix for a portal_type.
1311
           If not found, simply uses the portal_type itself
1312
        """
1313
        prefix = [p for p in self.getIDFormatting() if p['portal_type'] == portal_type]
1314
        if prefix:
1315
            return prefix[0]['prefix']
1316
        else:
1317
            return portal_type
1318
1319
    def getCountries(self):
1320
        items = geo.get_countries()
1321
        items = map(lambda country: (country.alpha_2, country.name), items)
1322
        return items
1323
1324
    def isRejectionWorkflowEnabled(self):
1325
        """Return true if the rejection workflow is enabled
1326
        """
1327
        return self.getEnableRejectionWorkflow()
1328
1329
    def getRejectionReasonsItems(self):
1330
        """Return the list of predefined rejection reasons
1331
1332
        .. deprecated::
1333
            This method now simply returns getRejectionReasons() as both
1334
            return the same format (simple list). Kept for backwards
1335
            compatibility.
1336
        """
1337
        return self.getRejectionReasons()
1338
1339
    def _getNumberOfRequiredVerificationsVocabulary(self):
1340
        """
1341
        Returns a DisplayList with the available options for the
1342
        multi-verification list: '1', '2', '3', '4'
1343
        :returns: DisplayList with the available options for the
1344
            multi-verification list
1345
        """
1346
        items = [(1, '1'), (2, '2'), (3, '3'), (4, '4')]
1347
        return IntDisplayList(list(items))
1348
1349
    def getEmailFromSamplePublication(self):
1350
        """Get the value from the senaite setup
1351
        """
1352
        setup = api.get_senaite_setup()
1353
        # setup is `None` during initial site content structure installation
1354
        if setup:
1355
            return setup.getEmailFromSamplePublication()
1356
1357
    def setEmailFromSamplePublication(self, value):
1358
        """Set the value in the senaite setup
1359
        """
1360
        setup = api.get_senaite_setup()
1361
        # setup is `None` during initial site content structure installation
1362
        if setup:
1363
            setup.setEmailFromSamplePublication(value)
1364
1365
    def getEmailBodySamplePublication(self):
1366
        """Get the value from the senaite setup
1367
        """
1368
        setup = api.get_senaite_setup()
1369
        # setup is `None` during initial site content structure installation
1370
        if setup:
1371
            return setup.getEmailBodySamplePublication()
1372
1373
    def setEmailBodySamplePublication(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.setEmailBodySamplePublication(value)
1380
1381
    def getAlwaysCCResponsiblesInReportEmail(self):
1382
        """Get the value from 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.getAlwaysCCResponsiblesInReportEmail()
1388
1389
    def setAlwaysCCResponsiblesInReportEmail(self, value):
1390
        """Set the value in the senaite setup
1391
        """
1392
        setup = api.get_senaite_setup()
1393
        # setup is `None` during initial site content structure installation
1394
        if setup:
1395
            setup.setAlwaysCCResponsiblesInReportEmail(value)
1396
1397
    def getEnableGlobalAuditlog(self):
1398
        """Get the value from the senaite setup
1399
        """
1400
        setup = api.get_senaite_setup()
1401
        # setup is `None` during initial site content structure installation
1402
        if setup:
1403
            return setup.getEnableGlobalAuditlog()
1404
        return False
1405
1406
    def setEnableGlobalAuditlog(self, value):
1407
        """Set the value in the senaite setup
1408
        """
1409
        setup = api.get_senaite_setup()
1410
        # setup is `None` during initial site content structure installation
1411
        if setup:
1412
            setup.setEnableGlobalAuditlog(value)
1413
1414
    def getImmediateResultsEntry(self):
1415
        """Get the value from the senaite setup
1416
        """
1417
        setup = api.get_senaite_setup()
1418
        # setup is `None` during initial site content structure installation
1419
        if setup:
1420
            return setup.getImmediateResultsEntry()
1421
        return False
1422
1423
    def setImmediateResultsEntry(self, value):
1424
        """Set the value in the senaite setup
1425
        """
1426
        setup = api.get_senaite_setup()
1427
        # setup is `None` during initial site content structure installation
1428
        if setup:
1429
            setup.setImmediateResultsEntry(value)
1430
1431
    def getCategorizeSampleAnalyses(self):
1432
        """Get the value from the senaite setup
1433
        """
1434
        setup = api.get_senaite_setup()
1435
        # setup is `None` during initial site content structure installation
1436
        if setup:
1437
            return setup.getCategorizeSampleAnalyses()
1438
        return False
1439
1440
    def setCategorizeSampleAnalyses(self, value):
1441
        """Set the value in the senaite setup
1442
        """
1443
        setup = api.get_senaite_setup()
1444
        # setup is `None` during initial site content structure installation
1445
        if setup:
1446
            setup.setCategorizeSampleAnalyses(value)
1447
1448
    def getSampleAnalysesRequired(self):
1449
        """Get the value from the senaite setup
1450
        """
1451
        setup = api.get_senaite_setup()
1452
        # setup is `None` during initial site content structure installation
1453
        if setup:
1454
            return setup.getSampleAnalysesRequired()
1455
        return False
1456
1457
    def setSampleAnalysesRequired(self, value):
1458
        """Set the value in the senaite setup
1459
        """
1460
        setup = api.get_senaite_setup()
1461
        # setup is `None` during initial site content structure installation
1462
        if setup:
1463
            setup.setSampleAnalysesRequired(value)
1464
1465
    def getAllowManualResultCaptureDate(self):
1466
        """Get the value from the senaite setup
1467
        """
1468
        setup = api.get_senaite_setup()
1469
        # setup is `None` during initial site content structure installation
1470
        if setup:
1471
            return setup.getAllowManualResultCaptureDate()
1472
        return False
1473
1474
    def setAllowManualResultCaptureDate(self, value):
1475
        """Set the value in the senaite setup
1476
        """
1477
        setup = api.get_senaite_setup()
1478
        # setup is `None` during initial site content structure installation
1479
        if setup:
1480
            setup.setAllowManualResultCaptureDate(value)
1481
1482
    def getShowLabNameInLogin(self):
1483
        """Get the value from the senaite setup
1484
        """
1485
        setup = api.get_senaite_setup()
1486
        # setup is `None` during initial site content structure installation
1487
        if setup:
1488
            return setup.getShowLabNameInLogin()
1489
        return False
1490
1491
    def setShowLabNameInLogin(self, value):
1492
        """Set the value in the senaite setup
1493
        """
1494
        setup = api.get_senaite_setup()
1495
        # setup is `None` during initial site content structure installation
1496
        if setup:
1497
            setup.setShowLabNameInLogin(value)
1498
1499
    def getDateSampledRequired(self):
1500
        """Get the value form the senaite setup
1501
        """
1502
        setup = api.get_senaite_setup()
1503
        # setup is `None` during initial site content structure installation
1504
        if setup:
1505
            return setup.getDateSampledRequired()
1506
        return self.getField("DateSampledRequired").default
1507
1508
    def setDateSampledRequired(self, value):
1509
        """Set the value in the senaite setup
1510
        """
1511
        setup = api.get_senaite_setup()
1512
        # setup is `None` during initial site content structure installation
1513
        if setup:
1514
            setup.setDateSampledRequired(value)
1515
1516
    def getInvalidationReasonRequired(self):
1517
        """Get the value form the senaite setup
1518
        """
1519
        setup = api.get_senaite_setup()
1520
        # setup is `None` during initial site content structure installation
1521
        if setup:
1522
            return setup.getInvalidationReasonRequired()
1523
        return self.getField("InvalidationReasonRequired").default
1524
1525
    def setInvalidationReasonRequired(self, value):
1526
        """Set the value in the senaite setup
1527
        """
1528
        setup = api.get_senaite_setup()
1529
        # setup is `None` during initial site content structure installation
1530
        if setup:
1531
            setup.setInvalidationReasonRequired(value)
1532
1533
    # Proxy methods for Security fields
1534
    def getRestrictWorksheetUsersAccess(self):
1535
        """Get the value from the senaite setup
1536
        """
1537
        setup = api.get_senaite_setup()
1538
        if setup:
1539
            return setup.getRestrictWorksheetUsersAccess()
1540
1541
    def setRestrictWorksheetUsersAccess(self, value):
1542
        """Set the value in the senaite setup
1543
        """
1544
        setup = api.get_senaite_setup()
1545
        if setup:
1546
            setup.setRestrictWorksheetUsersAccess(value)
1547
1548
    def getAllowToSubmitNotAssigned(self):
1549
        """Get the value from the senaite setup
1550
        """
1551
        setup = api.get_senaite_setup()
1552
        if setup:
1553
            return setup.getAllowToSubmitNotAssigned()
1554
1555
    def setAllowToSubmitNotAssigned(self, value):
1556
        """Set the value in the senaite setup
1557
        """
1558
        setup = api.get_senaite_setup()
1559
        if setup:
1560
            setup.setAllowToSubmitNotAssigned(value)
1561
1562
    def getRestrictWorksheetManagement(self):
1563
        """Get the value from the senaite setup
1564
        """
1565
        setup = api.get_senaite_setup()
1566
        if setup:
1567
            return setup.getRestrictWorksheetManagement()
1568
1569
    def setRestrictWorksheetManagement(self, value):
1570
        """Set the value in the senaite setup
1571
        """
1572
        setup = api.get_senaite_setup()
1573
        if setup:
1574
            setup.setRestrictWorksheetManagement(value)
1575
1576
    # Proxy methods for Accounting fields
1577
    def getShowPrices(self):
1578
        """Get the value from the senaite setup
1579
        """
1580
        setup = api.get_senaite_setup()
1581
        if setup:
1582
            return setup.getShowPrices()
1583
1584
    def setShowPrices(self, value):
1585
        """Set the value in the senaite setup
1586
        """
1587
        setup = api.get_senaite_setup()
1588
        if setup:
1589
            setup.setShowPrices(value)
1590
1591
    def getCurrency(self):
1592
        """Get the value from the senaite setup
1593
        """
1594
        setup = api.get_senaite_setup()
1595
        if setup:
1596
            return setup.getCurrency()
1597
1598
    def setCurrency(self, value):
1599
        """Set the value in the senaite setup
1600
        """
1601
        setup = api.get_senaite_setup()
1602
        if setup:
1603
            setup.setCurrency(value)
1604
1605
    def getDefaultCountry(self):
1606
        """Get the value from the senaite setup
1607
        """
1608
        setup = api.get_senaite_setup()
1609
        if setup:
1610
            return setup.getDefaultCountry()
1611
1612
    def setDefaultCountry(self, value):
1613
        """Set the value in the senaite setup
1614
        """
1615
        setup = api.get_senaite_setup()
1616
        if setup:
1617
            setup.setDefaultCountry(value)
1618
1619
    def getMemberDiscount(self):
1620
        """Get the value from the senaite setup
1621
        """
1622
        setup = api.get_senaite_setup()
1623
        if setup:
1624
            return setup.getMemberDiscount()
1625
1626
    def setMemberDiscount(self, value):
1627
        """Set the value in the senaite setup
1628
        """
1629
        setup = api.get_senaite_setup()
1630
        if setup:
1631
            setup.setMemberDiscount(value)
1632
1633
    def getVAT(self):
1634
        """Get the value from the senaite setup
1635
        """
1636
        setup = api.get_senaite_setup()
1637
        if setup:
1638
            return setup.getVAT()
1639
1640
    def setVAT(self, value):
1641
        """Set the value in the senaite setup
1642
        """
1643
        setup = api.get_senaite_setup()
1644
        if setup:
1645
            setup.setVAT(value)
1646
1647
    def getDecimalMark(self):
1648
        """Get the value from the senaite setup
1649
        """
1650
        setup = api.get_senaite_setup()
1651
        # setup is `None` during initial site content structure installation
1652
        if setup:
1653
            return setup.getDecimalMark()
1654
1655
    def setDecimalMark(self, value):
1656
        """Set the value in the senaite setup
1657
        """
1658
        setup = api.get_senaite_setup()
1659
        # setup is `None` during initial site content structure installation
1660
        if setup:
1661
            setup.setDecimalMark(value)
1662
1663
    def getScientificNotationReport(self):
1664
        """Get the value from the senaite setup
1665
        """
1666
        setup = api.get_senaite_setup()
1667
        # setup is `None` during initial site content structure installation
1668
        if setup:
1669
            return setup.getScientificNotationReport()
1670
1671
    def setScientificNotationReport(self, value):
1672
        """Set the value in the senaite setup
1673
        """
1674
        setup = api.get_senaite_setup()
1675
        # setup is `None` during initial site content structure installation
1676
        if setup:
1677
            setup.setScientificNotationReport(value)
1678
1679
    def getMinimumResults(self):
1680
        """Get the value from the senaite setup
1681
        """
1682
        setup = api.get_senaite_setup()
1683
        # setup is `None` during initial site content structure installation
1684
        if setup:
1685
            return setup.getMinimumResults()
1686
1687
    def setMinimumResults(self, value):
1688
        """Set the value in the senaite setup
1689
        """
1690
        setup = api.get_senaite_setup()
1691
        # setup is `None` during initial site content structure installation
1692
        if setup:
1693
            setup.setMinimumResults(value)
1694
1695
    def getCategoriseAnalysisServices(self):
1696
        """Get the value from the senaite setup
1697
        """
1698
        setup = api.get_senaite_setup()
1699
        # setup is `None` during initial site content structure installation
1700
        if setup:
1701
            return setup.getCategoriseAnalysisServices()
1702
1703
    def setCategoriseAnalysisServices(self, value):
1704
        """Set the value in the senaite setup
1705
        """
1706
        setup = api.get_senaite_setup()
1707
        # setup is `None` during initial site content structure installation
1708
        if setup:
1709
            setup.setCategoriseAnalysisServices(value)
1710
1711
    def getEnableARSpecs(self):
1712
        """Get the value from the senaite setup
1713
        """
1714
        setup = api.get_senaite_setup()
1715
        # setup is `None` during initial site content structure installation
1716
        if setup:
1717
            return setup.getEnableARSpecs()
1718
1719
    def setEnableARSpecs(self, value):
1720
        """Set the value in the senaite setup
1721
        """
1722
        setup = api.get_senaite_setup()
1723
        # setup is `None` during initial site content structure installation
1724
        if setup:
1725
            setup.setEnableARSpecs(value)
1726
1727
    def getExponentialFormatThreshold(self):
1728
        """Get the value from the senaite setup
1729
        """
1730
        setup = api.get_senaite_setup()
1731
        # setup is `None` during initial site content structure installation
1732
        if setup:
1733
            return setup.getExponentialFormatThreshold()
1734
1735
    def setExponentialFormatThreshold(self, value):
1736
        """Set the value in the senaite setup
1737
        """
1738
        setup = api.get_senaite_setup()
1739
        # setup is `None` during initial site content structure installation
1740
        if setup:
1741
            setup.setExponentialFormatThreshold(value)
1742
1743
    def getEnableAnalysisRemarks(self):
1744
        """Get the value from the senaite setup
1745
        """
1746
        setup = api.get_senaite_setup()
1747
        # setup is `None` during initial site content structure installation
1748
        if setup:
1749
            return setup.getEnableAnalysisRemarks()
1750
1751
    def setEnableAnalysisRemarks(self, value):
1752
        """Set the value in the senaite setup
1753
        """
1754
        setup = api.get_senaite_setup()
1755
        # setup is `None` during initial site content structure installation
1756
        if setup:
1757
            setup.setEnableAnalysisRemarks(value)
1758
1759
    def getAutoVerifySamples(self):
1760
        """Get the value from the senaite setup
1761
        """
1762
        setup = api.get_senaite_setup()
1763
        # setup is `None` during initial site content structure installation
1764
        if setup:
1765
            return setup.getAutoVerifySamples()
1766
1767
    def setAutoVerifySamples(self, value):
1768
        """Set the value in the senaite setup
1769
        """
1770
        setup = api.get_senaite_setup()
1771
        # setup is `None` during initial site content structure installation
1772
        if setup:
1773
            setup.setAutoVerifySamples(value)
1774
1775
    def getSelfVerificationEnabled(self):
1776
        """Get the value from the senaite setup
1777
        """
1778
        setup = api.get_senaite_setup()
1779
        # setup is `None` during initial site content structure installation
1780
        if setup:
1781
            return setup.getSelfVerificationEnabled()
1782
1783
    def setSelfVerificationEnabled(self, value):
1784
        """Set the value in the senaite setup
1785
        """
1786
        setup = api.get_senaite_setup()
1787
        # setup is `None` during initial site content structure installation
1788
        if setup:
1789
            setup.setSelfVerificationEnabled(value)
1790
1791
    def getNumberOfRequiredVerifications(self):
1792
        """Get the value from the senaite setup
1793
        """
1794
        setup = api.get_senaite_setup()
1795
        # setup is `None` during initial site content structure installation
1796
        if setup:
1797
            return setup.getNumberOfRequiredVerifications()
1798
1799
    def setNumberOfRequiredVerifications(self, value):
1800
        """Set the value in the senaite setup
1801
        """
1802
        setup = api.get_senaite_setup()
1803
        # setup is `None` during initial site content structure installation
1804
        if setup:
1805
            setup.setNumberOfRequiredVerifications(value)
1806
1807
    def getTypeOfmultiVerification(self):
1808
        """Get the value from the senaite setup
1809
        """
1810
        setup = api.get_senaite_setup()
1811
        # setup is `None` during initial site content structure installation
1812
        if setup:
1813
            return setup.getTypeOfmultiVerification()
1814
1815
    def setTypeOfmultiVerification(self, value):
1816
        """Set the value in the senaite setup
1817
        """
1818
        setup = api.get_senaite_setup()
1819
        # setup is `None` during initial site content structure installation
1820
        if setup:
1821
            setup.setTypeOfmultiVerification(value)
1822
1823
    def getResultsDecimalMark(self):
1824
        """Get the value from the senaite setup
1825
        """
1826
        setup = api.get_senaite_setup()
1827
        # setup is `None` during initial site content structure installation
1828
        if setup:
1829
            return setup.getResultsDecimalMark()
1830
1831
    def setResultsDecimalMark(self, value):
1832
        """Set the value in the senaite setup
1833
        """
1834
        setup = api.get_senaite_setup()
1835
        # setup is `None` during initial site content structure installation
1836
        if setup:
1837
            setup.setResultsDecimalMark(value)
1838
1839
    def getScientificNotationResults(self):
1840
        """Get the value from the senaite setup
1841
        """
1842
        setup = api.get_senaite_setup()
1843
        # setup is `None` during initial site content structure installation
1844
        if setup:
1845
            return setup.getScientificNotationResults()
1846
1847
    def setScientificNotationResults(self, value):
1848
        """Set the value in the senaite setup
1849
        """
1850
        setup = api.get_senaite_setup()
1851
        # setup is `None` during initial site content structure installation
1852
        if setup:
1853
            setup.setScientificNotationResults(value)
1854
1855
    def getDefaultNumberOfARsToAdd(self):
1856
        """Get the value from the senaite setup
1857
        """
1858
        setup = api.get_senaite_setup()
1859
        # setup is `None` during initial site content structure installation
1860
        if setup:
1861
            return setup.getDefaultNumberOfARsToAdd()
1862
1863
    def setDefaultNumberOfARsToAdd(self, value):
1864
        """Set the value in the senaite setup
1865
        """
1866
        setup = api.get_senaite_setup()
1867
        # setup is `None` during initial site content structure installation
1868
        if setup:
1869
            setup.setDefaultNumberOfARsToAdd(value)
1870
1871
    def getEnableRejectionWorkflow(self):
1872
        """Get the value from the senaite setup
1873
        """
1874
        setup = api.get_senaite_setup()
1875
        # setup is `None` during initial site content structure installation
1876
        if setup:
1877
            return setup.getEnableRejectionWorkflow()
1878
        return False
1879
1880
    def setEnableRejectionWorkflow(self, value):
1881
        """Set the value in the senaite setup
1882
        """
1883
        setup = api.get_senaite_setup()
1884
        # setup is `None` during initial site content structure installation
1885
        if setup:
1886
            setup.setEnableRejectionWorkflow(value)
1887
1888
    def getRejectionReasons(self):
1889
        """Get the rejection reasons from senaite setup
1890
        Returns a simple list of unicode strings
1891
        """
1892
        setup = api.get_senaite_setup()
1893
        # setup is `None` during initial site content structure installation
1894
        if setup:
1895
            return setup.getRejectionReasons()
1896
        return []
1897
1898
    def setRejectionReasons(self, value):
1899
        """Set the rejection reasons in senaite setup
1900
        Accepts a simple list of strings
1901
        """
1902
        setup = api.get_senaite_setup()
1903
        # setup is `None` during initial site content structure installation
1904
        if setup:
1905
            setup.setRejectionReasons(value)
1906
1907
    def getMaxNumberOfSamplesAdd(self):
1908
        """Get the value from the senaite setup
1909
        """
1910
        setup = api.get_senaite_setup()
1911
        # setup is `None` during initial site content structure installation
1912
        if setup:
1913
            return setup.getMaxNumberOfSamplesAdd()
1914
1915
    def setMaxNumberOfSamplesAdd(self, value):
1916
        """Set the value in the senaite setup
1917
        """
1918
        setup = api.get_senaite_setup()
1919
        # setup is `None` during initial site content structure installation
1920
        if setup:
1921
            setup.setMaxNumberOfSamplesAdd(value)
1922
1923
    def getWorksheetLayout(self):
1924
        """Get the value from the senaite setup
1925
        """
1926
        setup = api.get_senaite_setup()
1927
        # setup is `None` during initial site content structure installation
1928
        if setup:
1929
            return setup.getWorksheetLayout()
1930
1931
    def setWorksheetLayout(self, value):
1932
        """Set the value in the senaite setup
1933
        """
1934
        setup = api.get_senaite_setup()
1935
        # setup is `None` during initial site content structure installation
1936
        if setup:
1937
            setup.setWorksheetLayout(value)
1938
1939
    def getDashboardByDefault(self):
1940
        """Get the value from the senaite setup
1941
        """
1942
        setup = api.get_senaite_setup()
1943
        # setup is `None` during initial site content structure installation
1944
        if setup:
1945
            return setup.getDashboardByDefault()
1946
1947
    def setDashboardByDefault(self, value):
1948
        """Set the value in the senaite setup
1949
        """
1950
        setup = api.get_senaite_setup()
1951
        # setup is `None` during initial site content structure installation
1952
        if setup:
1953
            setup.setDashboardByDefault(value)
1954
1955
    def getLandingPage(self):
1956
        """Get the value from the senaite setup
1957
        """
1958
        setup = api.get_senaite_setup()
1959
        # setup is `None` during initial site content structure installation
1960
        if setup:
1961
            return setup.getLandingPage()
1962
1963
    def setLandingPage(self, value):
1964
        """Set the value in the senaite setup
1965
        """
1966
        setup = api.get_senaite_setup()
1967
        # setup is `None` during initial site content structure installation
1968
        if setup:
1969
            setup.setLandingPage(value)
1970
1971
    def getShowPartitions(self):
1972
        """Get the value from the senaite setup
1973
        """
1974
        setup = api.get_senaite_setup()
1975
        # setup is `None` during initial site content structure installation
1976
        if setup:
1977
            return setup.getShowPartitions()
1978
1979
    def setShowPartitions(self, value):
1980
        """Set the value in the senaite setup
1981
        """
1982
        setup = api.get_senaite_setup()
1983
        # setup is `None` during initial site content structure installation
1984
        if setup:
1985
            setup.setShowPartitions(value)
1986
1987
    def getPrintingWorkflowEnabled(self):
1988
        """Get the value from the senaite setup
1989
        """
1990
        setup = api.get_senaite_setup()
1991
        # setup is `None` during initial site content structure installation
1992
        if setup:
1993
            return setup.getPrintingWorkflowEnabled()
1994
1995
    def setPrintingWorkflowEnabled(self, value):
1996
        """Set the value in the senaite setup
1997
        """
1998
        setup = api.get_senaite_setup()
1999
        # setup is `None` during initial site content structure installation
2000
        if setup:
2001
            setup.setPrintingWorkflowEnabled(value)
2002
2003
    def getSamplingWorkflowEnabled(self):
2004
        """Get the value from the senaite setup
2005
        """
2006
        setup = api.get_senaite_setup()
2007
        # setup is `None` during initial site content structure installation
2008
        if setup:
2009
            return setup.getSamplingWorkflowEnabled()
2010
2011
    def setSamplingWorkflowEnabled(self, value):
2012
        """Set the value in the senaite setup
2013
        """
2014
        setup = api.get_senaite_setup()
2015
        # setup is `None` during initial site content structure installation
2016
        if setup:
2017
            setup.setSamplingWorkflowEnabled(value)
2018
2019
    def getScheduleSamplingEnabled(self):
2020
        """Get the value from the senaite setup
2021
        """
2022
        setup = api.get_senaite_setup()
2023
        # setup is `None` during initial site content structure installation
2024
        if setup:
2025
            return setup.getScheduleSamplingEnabled()
2026
2027
    def setScheduleSamplingEnabled(self, value):
2028
        """Set the value in the senaite setup
2029
        """
2030
        setup = api.get_senaite_setup()
2031
        # setup is `None` during initial site content structure installation
2032
        if setup:
2033
            setup.setScheduleSamplingEnabled(value)
2034
2035
    def getAutoreceiveSamples(self):
2036
        """Get the value from the senaite setup
2037
        """
2038
        setup = api.get_senaite_setup()
2039
        # setup is `None` during initial site content structure installation
2040
        if setup:
2041
            return setup.getAutoreceiveSamples()
2042
2043
    def setAutoreceiveSamples(self, value):
2044
        """Set the value in the senaite setup
2045
        """
2046
        setup = api.get_senaite_setup()
2047
        # setup is `None` during initial site content structure installation
2048
        if setup:
2049
            setup.setAutoreceiveSamples(value)
2050
2051
    def getSamplePreservationEnabled(self):
2052
        """Get the value from the senaite setup
2053
        """
2054
        setup = api.get_senaite_setup()
2055
        # setup is `None` during initial site content structure installation
2056
        if setup:
2057
            return setup.getSamplePreservationEnabled()
2058
2059
    def setSamplePreservationEnabled(self, value):
2060
        """Set the value in the senaite setup
2061
        """
2062
        setup = api.get_senaite_setup()
2063
        # setup is `None` during initial site content structure installation
2064
        if setup:
2065
            setup.setSamplePreservationEnabled(value)
2066
2067
    def getWorkdays(self):
2068
        """Get the value from the senaite setup
2069
        """
2070
        setup = api.get_senaite_setup()
2071
        # setup is `None` during initial site content structure installation
2072
        if setup:
2073
            return setup.getWorkdays()
2074
2075
    def setWorkdays(self, value):
2076
        """Set the value in the senaite setup
2077
        """
2078
        setup = api.get_senaite_setup()
2079
        # setup is `None` during initial site content structure installation
2080
        if setup:
2081
            setup.setWorkdays(value)
2082
2083
    def getDefaultTurnaroundTime(self):
2084
        """Get the value from the senaite setup
2085
        """
2086
        from senaite.core.api import dtime
2087
        setup = api.get_senaite_setup()
2088
        # setup is `None` during initial site content structure installation
2089
        if setup:
2090
            value = setup.getDefaultTurnaroundTime()
2091
            # Convert timedelta to dict for AT form
2092
            return dtime.timedelta_to_dict(value, default={})
2093
        return {}
2094
2095
    def setDefaultTurnaroundTime(self, value):
2096
        """Set the value in the senaite setup
2097
        """
2098
        from senaite.core.api import dtime
2099
        setup = api.get_senaite_setup()
2100
        # setup is `None` during initial site content structure installation
2101
        if setup:
2102
            # Convert dict to timedelta for DX storage
2103
            td_value = dtime.to_timedelta(value, default=None)
2104
            if td_value is not None:
2105
                setup.setDefaultTurnaroundTime(td_value)
2106
2107
    def getDefaultSampleLifetime(self):
2108
        """Get the value from the senaite setup
2109
        """
2110
        from senaite.core.api import dtime
2111
        setup = api.get_senaite_setup()
2112
        # setup is `None` during initial site content structure installation
2113
        if setup:
2114
            value = setup.getDefaultSampleLifetime()
2115
            # Convert timedelta to dict for AT form
2116
            return dtime.timedelta_to_dict(value, default={})
2117
        return {}
2118
2119
    def setDefaultSampleLifetime(self, value):
2120
        """Set the value in the senaite setup
2121
        """
2122
        from senaite.core.api import dtime
2123
        setup = api.get_senaite_setup()
2124
        # setup is `None` during initial site content structure installation
2125
        if setup:
2126
            # Convert dict to timedelta for DX storage
2127
            td_value = dtime.to_timedelta(value, default=None)
2128
            if td_value is not None:
2129
                setup.setDefaultSampleLifetime(td_value)
2130
2131
    def getNotifyOnSampleRejection(self):
2132
        """Get the value from the senaite setup
2133
        """
2134
        setup = api.get_senaite_setup()
2135
        # setup is `None` during initial site content structure installation
2136
        if setup:
2137
            return setup.getNotifyOnSampleRejection()
2138
2139
    def setNotifyOnSampleRejection(self, value):
2140
        """Set the value in the senaite setup
2141
        """
2142
        setup = api.get_senaite_setup()
2143
        # setup is `None` during initial site content structure installation
2144
        if setup:
2145
            setup.setNotifyOnSampleRejection(value)
2146
2147
    def getEmailBodySampleRejection(self):
2148
        """Get the value from the senaite setup
2149
        """
2150
        setup = api.get_senaite_setup()
2151
        # setup is `None` during initial site content structure installation
2152
        if setup:
2153
            return setup.getEmailBodySampleRejection()
2154
2155
    def setEmailBodySampleRejection(self, value):
2156
        """Set the value in the senaite setup
2157
        """
2158
        setup = api.get_senaite_setup()
2159
        # setup is `None` during initial site content structure installation
2160
        if setup:
2161
            setup.setEmailBodySampleRejection(value)
2162
2163
    def getEmailBodySampleInvalidation(self):
2164
        """Get the value from the senaite setup
2165
        """
2166
        setup = api.get_senaite_setup()
2167
        # setup is `None` during initial site content structure installation
2168
        if setup:
2169
            return setup.getEmailBodySampleInvalidation()
2170
2171
    def setEmailBodySampleInvalidation(self, value):
2172
        """Set the value in the senaite setup
2173
        """
2174
        setup = api.get_senaite_setup()
2175
        # setup is `None` during initial site content structure installation
2176
        if setup:
2177
            setup.setEmailBodySampleInvalidation(value)
2178
2179
    def getAutoPrintStickers(self):
2180
        """Get the value from the senaite setup
2181
        """
2182
        setup = api.get_senaite_setup()
2183
        # setup is `None` during initial site content structure installation
2184
        if setup:
2185
            return setup.getAutoPrintStickers()
2186
2187
    def setAutoPrintStickers(self, value):
2188
        """Set the value in the senaite setup
2189
        """
2190
        setup = api.get_senaite_setup()
2191
        # setup is `None` during initial site content structure installation
2192
        if setup:
2193
            setup.setAutoPrintStickers(value)
2194
2195
    def getAutoStickerTemplate(self):
2196
        """Get the value from the senaite setup
2197
        """
2198
        setup = api.get_senaite_setup()
2199
        # setup is `None` during initial site content structure installation
2200
        if setup:
2201
            return setup.getAutoStickerTemplate()
2202
2203
    def setAutoStickerTemplate(self, value):
2204
        """Set the value in the senaite setup
2205
        """
2206
        setup = api.get_senaite_setup()
2207
        # setup is `None` during initial site content structure installation
2208
        if setup:
2209
            setup.setAutoStickerTemplate(value)
2210
2211
    def getSmallStickerTemplate(self):
2212
        """Get the value from the senaite setup
2213
        """
2214
        setup = api.get_senaite_setup()
2215
        # setup is `None` during initial site content structure installation
2216
        if setup:
2217
            return setup.getSmallStickerTemplate()
2218
2219
    def setSmallStickerTemplate(self, value):
2220
        """Set the value in the senaite setup
2221
        """
2222
        setup = api.get_senaite_setup()
2223
        # setup is `None` during initial site content structure installation
2224
        if setup:
2225
            setup.setSmallStickerTemplate(value)
2226
2227
    def getLargeStickerTemplate(self):
2228
        """Get the value from the senaite setup
2229
        """
2230
        setup = api.get_senaite_setup()
2231
        # setup is `None` during initial site content structure installation
2232
        if setup:
2233
            return setup.getLargeStickerTemplate()
2234
2235
    def setLargeStickerTemplate(self, value):
2236
        """Set the value in the senaite setup
2237
        """
2238
        setup = api.get_senaite_setup()
2239
        # setup is `None` during initial site content structure installation
2240
        if setup:
2241
            setup.setLargeStickerTemplate(value)
2242
2243
    def getDefaultNumberOfCopies(self):
2244
        """Get the value from the senaite setup
2245
        """
2246
        setup = api.get_senaite_setup()
2247
        # setup is `None` during initial site content structure installation
2248
        if setup:
2249
            return setup.getDefaultNumberOfCopies()
2250
2251
    def setDefaultNumberOfCopies(self, value):
2252
        """Set the value in the senaite setup
2253
        """
2254
        setup = api.get_senaite_setup()
2255
        # setup is `None` during initial site content structure installation
2256
        if setup:
2257
            setup.setDefaultNumberOfCopies(value)
2258
2259
    def getIDFormatting(self):
2260
        """Get the value from the senaite setup
2261
        """
2262
        setup = api.get_senaite_setup()
2263
        # setup is `None` during initial site content structure installation
2264
        if setup:
2265
            return setup.getIDFormatting()
2266
        return []
2267
2268
    def setIDFormatting(self, value):
2269
        """Set the value in the senaite setup
2270
        """
2271
        setup = api.get_senaite_setup()
2272
        # setup is `None` during initial site content structure installation
2273
        if setup:
2274
            setup.setIDFormatting(value)
2275
2276
    def getIDServerValuesHTML(self):
2277
        """Get the value from the senaite setup
2278
        """
2279
        setup = api.get_senaite_setup()
2280
        # setup is `None` during initial site content structure installation
2281
        if setup:
2282
            return setup.getIDServerValuesHTML()
2283
        return ""
2284
2285
2286
registerType(BikaSetup, PROJECTNAME)
2287