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
|
|
|
|