|
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 decimal import Decimal |
|
22
|
|
|
|
|
23
|
|
|
from AccessControl import ClassSecurityInfo |
|
24
|
|
|
from ComputedAttribute import ComputedAttribute |
|
25
|
|
|
from bika.lims import api |
|
26
|
|
|
from bika.lims import senaiteMessageFactory as _ |
|
27
|
|
|
from bika.lims.interfaces import IDeactivable |
|
28
|
|
|
from plone.supermodel import model |
|
29
|
|
|
from plone.autoform import directives |
|
30
|
|
|
from Products.CMFCore import permissions |
|
31
|
|
|
from senaite.core.catalog import SETUP_CATALOG |
|
32
|
|
|
from senaite.core.content.base import Container |
|
33
|
|
|
from senaite.core.interfaces import IHavePrice |
|
34
|
|
|
from senaite.core.interfaces import ILabProduct |
|
35
|
|
|
from z3c.form.interfaces import IEditForm |
|
36
|
|
|
from zope import schema |
|
37
|
|
|
from zope.interface import implementer |
|
38
|
|
|
|
|
39
|
|
|
|
|
40
|
|
|
def default_vat_factory(): |
|
41
|
|
|
"""Returns the default VAT value if any |
|
42
|
|
|
""" |
|
43
|
|
|
setup_vat = api.get_setup().getVAT() or "0.00" |
|
44
|
|
|
return api.safe_unicode(setup_vat) |
|
45
|
|
|
|
|
46
|
|
|
|
|
47
|
|
|
class ILabProductSchema(model.Schema): |
|
48
|
|
|
"""LabProduct Schema interface |
|
49
|
|
|
""" |
|
50
|
|
|
|
|
51
|
|
|
title = schema.TextLine( |
|
52
|
|
|
title=_( |
|
53
|
|
|
"title_labproduct_title", |
|
54
|
|
|
default="Name" |
|
55
|
|
|
), |
|
56
|
|
|
required=True, |
|
57
|
|
|
) |
|
58
|
|
|
|
|
59
|
|
|
description = schema.Text( |
|
60
|
|
|
title=_( |
|
61
|
|
|
"title_labproduct_description", |
|
62
|
|
|
default="Description" |
|
63
|
|
|
), |
|
64
|
|
|
required=False, |
|
65
|
|
|
) |
|
66
|
|
|
|
|
67
|
|
|
labproduct_volume = schema.TextLine( |
|
68
|
|
|
title=_( |
|
69
|
|
|
"title_labproduct_volume", |
|
70
|
|
|
default="Volume" |
|
71
|
|
|
), |
|
72
|
|
|
required=False, |
|
73
|
|
|
) |
|
74
|
|
|
|
|
75
|
|
|
labproduct_unit = schema.TextLine( |
|
76
|
|
|
title=_( |
|
77
|
|
|
"title_labproduct_unit", |
|
78
|
|
|
default="Unit" |
|
79
|
|
|
), |
|
80
|
|
|
required=False, |
|
81
|
|
|
) |
|
82
|
|
|
|
|
83
|
|
|
directives.widget("labproduct_price", klass="numeric") |
|
84
|
|
|
labproduct_price = schema.TextLine( |
|
85
|
|
|
title=_( |
|
86
|
|
|
"title_labproduct_price", |
|
87
|
|
|
default="Price (excluding VAT)" |
|
88
|
|
|
), |
|
89
|
|
|
description=_( |
|
90
|
|
|
"description_labproduct_price", |
|
91
|
|
|
default="Please provide the price excluding VAT" |
|
92
|
|
|
), |
|
93
|
|
|
required=True, |
|
94
|
|
|
) |
|
95
|
|
|
|
|
96
|
|
|
directives.widget("labproduct_vat", klass="numeric") |
|
97
|
|
|
labproduct_vat = schema.TextLine( |
|
98
|
|
|
title=_( |
|
99
|
|
|
"title_labproduct_vat", |
|
100
|
|
|
default="VAT %" |
|
101
|
|
|
), |
|
102
|
|
|
description=_( |
|
103
|
|
|
"description_labproduct_vat", |
|
104
|
|
|
default="Please provide the VAT in percent that is added to the " |
|
105
|
|
|
"labproduct price" |
|
106
|
|
|
), |
|
107
|
|
|
defaultFactory=default_vat_factory, |
|
108
|
|
|
required=False, |
|
109
|
|
|
) |
|
110
|
|
|
|
|
111
|
|
|
directives.mode(labproduct_vat_amount="display") |
|
112
|
|
|
directives.mode(IEditForm, labproduct_vat_amount="hidden") |
|
113
|
|
|
labproduct_vat_amount = schema.TextLine( |
|
114
|
|
|
title=_( |
|
115
|
|
|
"title_labproduct_vat_amount", |
|
116
|
|
|
default="VAT" |
|
117
|
|
|
), |
|
118
|
|
|
readonly=True) |
|
119
|
|
|
|
|
120
|
|
|
directives.mode(labproduct_total_price="display") |
|
121
|
|
|
directives.mode(IEditForm, labproduct_total_price="hidden") |
|
122
|
|
|
labproduct_total_price = schema.TextLine( |
|
123
|
|
|
title=_( |
|
124
|
|
|
"title_labproduct_total_price", |
|
125
|
|
|
default="Total Price" |
|
126
|
|
|
), |
|
127
|
|
|
readonly=True) |
|
128
|
|
|
|
|
129
|
|
|
|
|
130
|
|
|
@implementer(ILabProduct, ILabProductSchema, IHavePrice, IDeactivable) |
|
131
|
|
|
class LabProduct(Container): |
|
132
|
|
|
"""LabProduct content |
|
133
|
|
|
""" |
|
134
|
|
|
# Catalogs where this type will be catalogued |
|
135
|
|
|
_catalogs = [SETUP_CATALOG] |
|
136
|
|
|
|
|
137
|
|
|
security = ClassSecurityInfo() |
|
138
|
|
|
|
|
139
|
|
|
@security.protected(permissions.View) |
|
140
|
|
|
def getVolume(self): |
|
141
|
|
|
accessor = self.accessor("labproduct_volume") |
|
142
|
|
|
value = accessor(self) or "" |
|
143
|
|
|
return api.to_utf8(value) |
|
144
|
|
|
|
|
145
|
|
|
@security.protected(permissions.ModifyPortalContent) |
|
146
|
|
|
def setVolume(self, value): |
|
147
|
|
|
mutator = self.mutator("labproduct_volume") |
|
148
|
|
|
mutator(self, api.safe_unicode(value)) |
|
149
|
|
|
|
|
150
|
|
|
# BBB: AT schema field property |
|
151
|
|
|
Volume = property(getVolume, setVolume) |
|
152
|
|
|
|
|
153
|
|
|
@security.protected(permissions.View) |
|
154
|
|
|
def getUnit(self): |
|
155
|
|
|
accessor = self.accessor("labproduct_unit") |
|
156
|
|
|
value = accessor(self) or "" |
|
157
|
|
|
return api.to_utf8(value) |
|
158
|
|
|
|
|
159
|
|
|
@security.protected(permissions.ModifyPortalContent) |
|
160
|
|
|
def setUnit(self, value): |
|
161
|
|
|
mutator = self.mutator("labproduct_unit") |
|
162
|
|
|
mutator(self, api.safe_unicode(value)) |
|
163
|
|
|
|
|
164
|
|
|
# BBB: AT schema field property |
|
165
|
|
|
Unit = property(getUnit, setUnit) |
|
166
|
|
|
|
|
167
|
|
|
@security.protected(permissions.View) |
|
168
|
|
|
def getPrice(self): |
|
169
|
|
|
accessor = self.accessor("labproduct_price") |
|
170
|
|
|
value = accessor(self) or "" |
|
171
|
|
|
return api.to_utf8(str(value)) |
|
172
|
|
|
|
|
173
|
|
|
@security.protected(permissions.ModifyPortalContent) |
|
174
|
|
|
def setPrice(self, value): |
|
175
|
|
|
mutator = self.mutator("labproduct_price") |
|
176
|
|
|
mutator(self, api.safe_unicode(value)) |
|
177
|
|
|
|
|
178
|
|
|
# BBB: AT schema field property |
|
179
|
|
|
Price = property(getPrice, setPrice) |
|
180
|
|
|
|
|
181
|
|
|
@security.protected(permissions.View) |
|
182
|
|
|
def getVAT(self): |
|
183
|
|
|
accessor = self.accessor("labproduct_vat") |
|
184
|
|
|
value = accessor(self) or "" |
|
185
|
|
|
return api.to_utf8(str(value)) |
|
186
|
|
|
|
|
187
|
|
|
@security.protected(permissions.ModifyPortalContent) |
|
188
|
|
|
def setVAT(self, value): |
|
189
|
|
|
mutator = self.mutator("labproduct_vat") |
|
190
|
|
|
mutator(self, api.safe_unicode(value)) |
|
191
|
|
|
|
|
192
|
|
|
# BBB: AT schema field property |
|
193
|
|
|
VAT = property(getVAT, setVAT) |
|
194
|
|
|
|
|
195
|
|
|
@security.protected(permissions.View) |
|
196
|
|
|
def getVATAmount(self): |
|
197
|
|
|
""" Compute VATAmount |
|
198
|
|
|
""" |
|
199
|
|
|
try: |
|
200
|
|
|
vatamount = self.getTotalPrice() - Decimal(self.getPrice()) |
|
201
|
|
|
except Exception: |
|
202
|
|
|
vatamount = Decimal('0.00') |
|
203
|
|
|
return vatamount.quantize(Decimal('0.00')) |
|
204
|
|
|
|
|
205
|
|
|
labproduct_vat_amount = ComputedAttribute(getVATAmount, 1) |
|
206
|
|
|
|
|
207
|
|
|
# BBB: AT schema (computed) field property |
|
208
|
|
|
VATAmount = property(getVATAmount) |
|
209
|
|
|
|
|
210
|
|
|
def getTotalPrice(self): |
|
211
|
|
|
""" compute total price """ |
|
212
|
|
|
price = Decimal(self.getPrice() or '0.00') |
|
213
|
|
|
vat = Decimal(self.getVAT()) |
|
214
|
|
|
vat = vat and vat / 100 or 0 |
|
215
|
|
|
price = price + (price * vat) |
|
216
|
|
|
return price.quantize(Decimal('0.00')) |
|
217
|
|
|
|
|
218
|
|
|
labproduct_total_price = ComputedAttribute(getTotalPrice, 1) |
|
219
|
|
|
|
|
220
|
|
|
# BBB: AT schema (computed) field property |
|
221
|
|
|
TotalPrice = property(getTotalPrice) |
|
222
|
|
|
|