Passed
Push — 2.x ( 4f65d0...ad582b )
by Jordi
06:22
created

ISupplierSchema.validate_iban()   B

Complexity

Conditions 6

Size

Total Lines 31
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 22
dl 0
loc 31
rs 8.4186
c 0
b 0
f 0
cc 6
nop 1
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-2024 by it's authors.
19
# Some rights reserved, see README and LICENSE.
20
21
from AccessControl import ClassSecurityInfo
22
from bika.lims import senaiteMessageFactory as _
23
from bika.lims.interfaces import IDeactivable
24
from bika.lims.validators import country_dic
25
from bika.lims.validators import letter_dic
26
from bika.lims.validators import _toIntList
27
from bika.lims.validators import _sumLists
28
from senaite.core.i18n import translate
29
from senaite.core.catalog import SETUP_CATALOG
30
from senaite.core.content.organization import IOrganizationSchema
31
from senaite.core.content.organization import Organization
32
from senaite.core.interfaces import ISupplier
33
from plone.supermodel import model
34
from Products.CMFCore import permissions
35
from Products.CMFPlone.utils import safe_unicode
36
from zope import schema
37
from zope.interface import Invalid
38
from zope.interface import implementer
39
from zope.interface import invariant
40
41
42
class ISupplierSchema(IOrganizationSchema):
43
    """Schema interface
44
    """
45
46
    lab_account_number = schema.TextLine(
47
        title=_(
48
            u"title_supplier_lab_contact_number",
49
            default=u"Lab Account Number",
50
        ),
51
        required=False,
52
    )
53
54
    remarks = schema.Text(
55
        title=_(
56
            u"title_supplier_remarks",
57
            default=u"Remarks",
58
        ),
59
        required=False,
60
    )
61
62
    website = schema.TextLine(
63
        title=_(
64
            u"title_supplier_website",
65
            default=u"Website",
66
        ),
67
        required=False,
68
    )
69
70
    model.fieldset(
71
        "bank_details",
72
        label=_(
73
            u"Accounting",
74
            default=u"Bank Details"
75
        ),
76
        fields=[
77
            "nib",
78
            "iban",
79
            "swift_code",
80
        ]
81
    )
82
83
    nib = schema.TextLine(
84
        title=_(
85
            u"title_supplier_nib",
86
            default=u"NIB",
87
        ),
88
        description=_(
89
            u"description_supplier_nib",
90
            default=u"National Identification Bank Account Number",
91
        ),
92
        required=False,
93
    )
94
95
    iban = schema.TextLine(
96
        title=_(
97
            u"title_supplier_iban",
98
            default=u"IBAN",
99
        ),
100
        description=_(
101
            u"description_supplier_iban",
102
            default=u"International Bank Account Number",
103
        ),
104
        required=False,
105
    )
106
107
    swift_code = schema.TextLine(
108
        title=_(
109
            u"title_supplier_swift_code",
110
            default=u"SWIFT code",
111
        ),
112
        required=False,
113
    )
114
115
    @invariant
116
    def validate_nib(data):
117
        """Checks NIB field for float value if exist
118
        """
119
        src_nib = getattr(data, "nib", None)
120
        if not src_nib:
121
            return
122
123
        LEN_NIB = 21
124
        table = (73, 17, 89, 38, 62, 45, 53, 15, 50, 5, 49, 34, 81, 76, 27, 90,
125
                 9, 30, 3)
126
127
        # convert to entire numbers list
128
        nib = _toIntList(src_nib)
129
130
        # checking the length of the number
131
        if len(nib) != LEN_NIB:
132
            msg = translate(_('Incorrect NIB number: %s' % src_nib))
133
            raise Invalid(msg)
134
135
        # last numbers algorithm validator
136
        left_part = nib[-2] * 10 + nib[-1]
137
        right_part = 98 - _sumLists(table, nib[:-2]) % 97
138
        if left_part != right_part:
139
            raise Invalid("Invalid NIB number")
140
141
    @invariant
142
    def validate_iban(data):
143
        """Checks IBAN field for float value if exist
144
        """
145
        iban = getattr(data, "iban", None)
146
        if not iban:
147
            return
148
149
        # remove spaces from formatted
150
        IBAN = ''.join(c for c in iban if c.isalnum())
151
152
        IBAN = IBAN[4:] + IBAN[:4]
153
        country = IBAN[-4:-2]
154
155
        if country not in country_dic:
156
            msg = translate(_('Unknown IBAN country %s' % country))
157
            raise Invalid(msg)
158
159
        length_c, name_c = country_dic[country]
160
161
        if len(IBAN) != length_c:
162
            diff = len(IBAN) - length_c
163
            warn_len = ('short by %i' % -diff) if diff < 0 \
164
                else ('too long by %i' % diff)
165
            msg = translate(
166
                _('Wrong IBAN length by %s: %s' % (warn_len, iban)))
167
            raise Invalid(msg)
168
        # Validating procedure
169
        elif int("".join(str(letter_dic[x]) for x in IBAN)) % 97 != 1:
170
            msg = translate(_('Incorrect IBAN number: %s' % iban))
171
            raise Invalid(msg)
172
173
174
@implementer(ISupplier, ISupplierSchema, IDeactivable)
175
class Supplier(Organization):
176
    """A container for Supplier
177
    """
178
    # Catalogs where this type will be catalogued
179
    _catalogs = [SETUP_CATALOG]
180
181
    security = ClassSecurityInfo()
182
183
    @security.protected(permissions.View)
184
    def getRemarks(self):
185
        accessor = self.accessor("remarks")
186
        value = accessor(self) or ""
187
        return value.encode("utf-8")
188
189
    @security.protected(permissions.ModifyPortalContent)
190
    def setRemarks(self, value):
191
        mutator = self.mutator("remarks")
192
        mutator(self, safe_unicode(value))
193
194
    # BBB: AT schema field property
195
    Remarks = property(getRemarks, setRemarks)
196
197
    @security.protected(permissions.View)
198
    def getWebsite(self):
199
        accessor = self.accessor("website")
200
        value = accessor(self) or ""
201
        return value.encode("utf-8")
202
203
    @security.protected(permissions.ModifyPortalContent)
204
    def setWebsite(self, value):
205
        mutator = self.mutator("website")
206
        mutator(self, safe_unicode(value))
207
208
    # BBB: AT schema field property
209
    Website = property(getWebsite, setWebsite)
210
211
    @security.protected(permissions.View)
212
    def getNIB(self):
213
        accessor = self.accessor("nib")
214
        value = accessor(self) or ""
215
        return value.encode("utf-8")
216
217
    @security.protected(permissions.ModifyPortalContent)
218
    def setNIB(self, value):
219
        mutator = self.mutator("nib")
220
        mutator(self, safe_unicode(value))
221
222
    # BBB: AT schema field property
223
    NIB = property(getNIB, setNIB)
224
225
    @security.protected(permissions.View)
226
    def getIBAN(self):
227
        accessor = self.accessor("iban")
228
        value = accessor(self) or ""
229
        return value.encode("utf-8")
230
231
    @security.protected(permissions.ModifyPortalContent)
232
    def setIBAN(self, value):
233
        mutator = self.mutator("iban")
234
        mutator(self, safe_unicode(value))
235
236
    # BBB: AT schema field property
237
    IBAN = property(getIBAN, setIBAN)
238
239
    @security.protected(permissions.View)
240
    def getSwiftCode(self):
241
        accessor = self.accessor("swift_code")
242
        value = accessor(self) or ""
243
        return value.encode("utf-8")
244
245
    @security.protected(permissions.ModifyPortalContent)
246
    def setSwiftCode(self, value):
247
        mutator = self.mutator("swift_code")
248
        mutator(self, safe_unicode(value))
249
250
    # BBB: AT schema field property
251
    SWIFTcode = property(getSwiftCode, setSwiftCode)
252
253
    @security.protected(permissions.View)
254
    def getLabAccountNumber(self):
255
        accessor = self.accessor("lab_account_number")
256
        value = accessor(self) or ""
257
        return value.encode("utf-8")
258
259
    @security.protected(permissions.ModifyPortalContent)
260
    def setLabAccountNumber(self, value):
261
        mutator = self.mutator("lab_account_number")
262
        mutator(self, safe_unicode(value))
263
264
    # BBB: AT schema field property
265
    LabAccountNumber = property(getLabAccountNumber, setLabAccountNumber)
266