PyOdoo 0.5.6__tar.gz → 0.5.8__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. {PyOdoo-0.5.6 → PyOdoo-0.5.8}/PKG-INFO +2 -1
  2. {PyOdoo-0.5.6 → PyOdoo-0.5.8}/PyOdoo.egg-info/PKG-INFO +2 -1
  3. {PyOdoo-0.5.6 → PyOdoo-0.5.8}/PyOdoo.egg-info/SOURCES.txt +1 -0
  4. {PyOdoo-0.5.6 → PyOdoo-0.5.8}/pyodoo/compare_type.py +10 -0
  5. {PyOdoo-0.5.6 → PyOdoo-0.5.8}/pyodoo/constants.py +1 -1
  6. {PyOdoo-0.5.6 → PyOdoo-0.5.8}/pyodoo/samples/contacts.py +5 -0
  7. {PyOdoo-0.5.6 → PyOdoo-0.5.8}/pyodoo/v12/model.py +29 -0
  8. {PyOdoo-0.5.6 → PyOdoo-0.5.8}/setup.cfg +1 -0
  9. PyOdoo-0.5.8/tests/test_compare_types.py +393 -0
  10. {PyOdoo-0.5.6 → PyOdoo-0.5.8}/tests/test_contacts.py +73 -47
  11. {PyOdoo-0.5.6 → PyOdoo-0.5.8}/LICENSE +0 -0
  12. {PyOdoo-0.5.6 → PyOdoo-0.5.8}/PyOdoo.egg-info/dependency_links.txt +0 -0
  13. {PyOdoo-0.5.6 → PyOdoo-0.5.8}/PyOdoo.egg-info/top_level.txt +0 -0
  14. {PyOdoo-0.5.6 → PyOdoo-0.5.8}/README.md +0 -0
  15. {PyOdoo-0.5.6 → PyOdoo-0.5.8}/pyodoo/__init__.py +0 -0
  16. {PyOdoo-0.5.6 → PyOdoo-0.5.8}/pyodoo/active_status_choice.py +0 -0
  17. {PyOdoo-0.5.6 → PyOdoo-0.5.8}/pyodoo/boolean_operator.py +0 -0
  18. {PyOdoo-0.5.6 → PyOdoo-0.5.8}/pyodoo/filter.py +0 -0
  19. {PyOdoo-0.5.6 → PyOdoo-0.5.8}/pyodoo/message_subtype.py +0 -0
  20. {PyOdoo-0.5.6 → PyOdoo-0.5.8}/pyodoo/samples/__init__.py +0 -0
  21. {PyOdoo-0.5.6 → PyOdoo-0.5.8}/pyodoo/samples/awaitable.py +0 -0
  22. {PyOdoo-0.5.6 → PyOdoo-0.5.8}/pyodoo/samples/contacts_async.py +0 -0
  23. {PyOdoo-0.5.6 → PyOdoo-0.5.8}/pyodoo/v12/__init__.py +0 -0
  24. {PyOdoo-0.5.6 → PyOdoo-0.5.8}/pyodoo/v12/api.py +0 -0
  25. {PyOdoo-0.5.6 → PyOdoo-0.5.8}/pyproject.toml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PyOdoo
3
- Version: 0.5.6
3
+ Version: 0.5.8
4
4
  Summary: API for Odoo
5
5
  Home-page: http://www.muflone.com/pyodoo/
6
6
  Author: Fabio Castelli
@@ -13,6 +13,7 @@ Classifier: Operating System :: OS Independent
13
13
  Classifier: Programming Language :: Python :: 3
14
14
  Classifier: Programming Language :: Python :: 3.9
15
15
  Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
16
17
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
17
18
  Description-Content-Type: text/markdown
18
19
  License-File: LICENSE
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PyOdoo
3
- Version: 0.5.6
3
+ Version: 0.5.8
4
4
  Summary: API for Odoo
5
5
  Home-page: http://www.muflone.com/pyodoo/
6
6
  Author: Fabio Castelli
@@ -13,6 +13,7 @@ Classifier: Operating System :: OS Independent
13
13
  Classifier: Programming Language :: Python :: 3
14
14
  Classifier: Programming Language :: Python :: 3.9
15
15
  Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
16
17
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
17
18
  Description-Content-Type: text/markdown
18
19
  License-File: LICENSE
@@ -20,4 +20,5 @@ pyodoo/samples/contacts_async.py
20
20
  pyodoo/v12/__init__.py
21
21
  pyodoo/v12/api.py
22
22
  pyodoo/v12/model.py
23
+ tests/test_compare_types.py
23
24
  tests/test_contacts.py
@@ -32,3 +32,13 @@ class CompareType(object):
32
32
  NOT_IN = 'not in'
33
33
  CONTAINS = 'ilike'
34
34
  CONTAINS_NOT = 'not ilike'
35
+ NOT_CONTAINS = 'not ilike'
36
+ LIKE = 'like'
37
+ NOT_LIKE = 'not like'
38
+ ILIKE = 'ilike'
39
+ NOT_ILIKE = 'not ilike'
40
+ RAW_LIKE = '=like'
41
+ RAW_ILIKE = '=ilike'
42
+ UNSET_OR_EQUAL = '=?'
43
+ CHILD_OF = 'child_of'
44
+ PARENT_OF = 'parent_of'
@@ -19,7 +19,7 @@
19
19
  ##
20
20
 
21
21
  APP_NAME = 'PyOdoo'
22
- APP_VERSION = '0.5.6'
22
+ APP_VERSION = '0.5.8'
23
23
  APP_DESCRIPTION = 'API for Odoo'
24
24
  APP_DOMAIN = 'pyodoo'
25
25
  APP_ID = f'{APP_DOMAIN}.muflone.com'
@@ -72,6 +72,11 @@ results = model.filter(filters=filters,
72
72
  fields=('id', 'name', 'country_id'))
73
73
  print('filter', results)
74
74
 
75
+ # Find first record by filters
76
+ results = model.first(filters=filters,
77
+ fields=('id', 'name', 'country_id'))
78
+ print('first', results)
79
+
75
80
  # Get all the record
76
81
  results = model.all(fields=('id', 'name'),
77
82
  limit=1000)
@@ -315,6 +315,35 @@ class Model(object):
315
315
  options=options)
316
316
  return results
317
317
 
318
+ def first(self,
319
+ filters: list[Union[BooleanOperator, Filter]],
320
+ is_active: ActiveStatusChoice = ActiveStatusChoice.NOT_SET,
321
+ fields: tuple[str, ...] = None,
322
+ options: dict[str, Any] = None,
323
+ limit: Optional[int] = None,
324
+ offset: Optional[int] = None,
325
+ order: Optional[str] = None) -> Optional[dict[str, Any]]:
326
+ """
327
+ Find the first row from a model using some filters
328
+
329
+ :param filters: List of filters used for searching the data
330
+ :param is_active: Additional filter for active field
331
+ :param fields: Tuple with the fields to include in the response
332
+ :param options: Dictionary with options to use
333
+ :param limit: Maximum number of results count
334
+ :param offset: Starting record number to fetch the data
335
+ :param order: Ordering clause
336
+ :return: List of dictionaries with the requested fields
337
+ """
338
+ results = self.filter(filters=filters,
339
+ is_active=is_active,
340
+ fields=fields,
341
+ options=options,
342
+ limit=limit,
343
+ offset=offset,
344
+ order=order)
345
+ return results[0] if results else None
346
+
318
347
  def count(self,
319
348
  filters: list[Union[BooleanOperator, Filter]],
320
349
  is_active: ActiveStatusChoice = ActiveStatusChoice.NOT_SET,
@@ -17,6 +17,7 @@ classifiers =
17
17
  Programming Language :: Python :: 3
18
18
  Programming Language :: Python :: 3.9
19
19
  Programming Language :: Python :: 3.10
20
+ Programming Language :: Python :: 3.11
20
21
  Topic :: Software Development :: Libraries :: Python Modules
21
22
 
22
23
  [options]
@@ -0,0 +1,393 @@
1
+ ##
2
+ # Project: PyOdoo
3
+ # Description: API for Odoo
4
+ # Author: Fabio Castelli (Muflone) <muflone@muflone.com>
5
+ # Copyright: 2021-2023 Fabio Castelli
6
+ # License: GPL-3+
7
+ # This program is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU General Public License as published by
9
+ # the Free Software Foundation, either version 3 of the License, or
10
+ # (at your option) any later version.
11
+ #
12
+ # This program is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU General Public License
18
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
19
+ ##
20
+
21
+ import unittest
22
+
23
+ from pyodoo import CompareType, Filter
24
+
25
+ import utility
26
+
27
+
28
+ class TestCaseCompareTypes(unittest.TestCase):
29
+ @classmethod
30
+ def setUpClass(cls):
31
+ """
32
+ Model object preparation
33
+ """
34
+ cls.model = utility.get_model_from_demo(model_name='product.template')
35
+ cls.model.authenticate()
36
+
37
+ def test_01_filter_equal(self) -> None:
38
+ """
39
+ Search some rows using filters using compare EQUAL
40
+ """
41
+ filters = [Filter(field='name',
42
+ compare_type=CompareType.EQUAL,
43
+ value='Mozzarella Sandwich')]
44
+ results = self.model.search(filters=filters)
45
+ # Check if the results are not None
46
+ self.assertIsNotNone(results)
47
+ # Check if the results list has exactly one item
48
+ self.assertEqual(len(results), 1)
49
+ # Check if the results list contains the Mozzarella Sandwich
50
+ self.assertIn(75, results)
51
+
52
+ def test_02_filter_not_equal(self) -> None:
53
+ """
54
+ Search some rows using filters using compare NOT_EQUAL
55
+ """
56
+ filters = [Filter(field='name',
57
+ compare_type=CompareType.CONTAINS,
58
+ value='Sandwich'),
59
+ Filter(field='name',
60
+ compare_type=CompareType.NOT_EQUAL,
61
+ value='Mozzarella Sandwich')]
62
+ results = self.model.search(filters=filters)
63
+ # Check if the results are not None
64
+ self.assertIsNotNone(results)
65
+ # Check if the results list has exactly three items
66
+ self.assertEqual(len(results), 3)
67
+ # Check if the results list doesn't contain the Mozzarella Sandwich
68
+ self.assertNotIn(75, results)
69
+
70
+ def test_03_filter_greater(self) -> None:
71
+ """
72
+ Search some rows using filters using compare GREATER
73
+ """
74
+ filters = [Filter(field='name',
75
+ compare_type=CompareType.CONTAINS,
76
+ value='Sandwich'),
77
+ Filter(field='name',
78
+ compare_type=CompareType.GREATER,
79
+ value='Mozzarella Sandwich')]
80
+ results = self.model.search(filters=filters)
81
+ # Check if the results are not None
82
+ self.assertIsNotNone(results)
83
+ # Check if the results list has exactly one item
84
+ self.assertEqual(len(results), 1)
85
+ # Check if the results list doesn't contain the Mozzarella Sandwich
86
+ self.assertNotIn(75, results)
87
+
88
+ def test_04_filter_greater_equal(self) -> None:
89
+ """
90
+ Search some rows using filters using compare GREATER_EQ
91
+ """
92
+ filters = [Filter(field='name',
93
+ compare_type=CompareType.CONTAINS,
94
+ value='Sandwich'),
95
+ Filter(field='name',
96
+ compare_type=CompareType.GREATER_EQ,
97
+ value='Mozzarella Sandwich')]
98
+ results = self.model.search(filters=filters)
99
+ # Check if the results are not None
100
+ self.assertIsNotNone(results)
101
+ # Check if the results list has exactly two items
102
+ self.assertEqual(len(results), 2)
103
+ # Check if the results list contains the Mozzarella Sandwich
104
+ self.assertIn(75, results)
105
+
106
+ def test_05_filter_lower(self) -> None:
107
+ """
108
+ Search some rows using filters using compare LOWER
109
+ """
110
+ filters = [Filter(field='name',
111
+ compare_type=CompareType.CONTAINS,
112
+ value='Sandwich'),
113
+ Filter(field='name',
114
+ compare_type=CompareType.LOWER,
115
+ value='Mozzarella Sandwich')]
116
+ results = self.model.search(filters=filters)
117
+ # Check if the results are not None
118
+ self.assertIsNotNone(results)
119
+ # Check if the results list has exactly two items
120
+ self.assertEqual(len(results), 2)
121
+ # Check if the results list doesn't contain the Mozzarella Sandwich
122
+ self.assertNotIn(75, results)
123
+
124
+ def test_06_filter_lower_equal(self) -> None:
125
+ """
126
+ Search some rows using filters using compare LOWER_EQ
127
+ """
128
+ filters = [Filter(field='name',
129
+ compare_type=CompareType.CONTAINS,
130
+ value='Sandwich'),
131
+ Filter(field='name',
132
+ compare_type=CompareType.LOWER_EQ,
133
+ value='Mozzarella Sandwich')]
134
+ results = self.model.search(filters=filters)
135
+ # Check if the results are not None
136
+ self.assertIsNotNone(results)
137
+ # Check if the results list has exactly three items
138
+ self.assertEqual(len(results), 3)
139
+ # Check if the results list contains the Mozzarella Sandwich
140
+ self.assertIn(75, results)
141
+
142
+ def test_07_filter_in(self) -> None:
143
+ """
144
+ Search some rows using filters using compare IN
145
+ """
146
+ filters = [Filter(field='name',
147
+ compare_type=CompareType.CONTAINS,
148
+ value='Sandwich'),
149
+ Filter(field='name',
150
+ compare_type=CompareType.IN,
151
+ value=('Club Sandwich', 'Mozzarella Sandwich'))]
152
+ results = self.model.search(filters=filters)
153
+ # Check if the results are not None
154
+ self.assertIsNotNone(results)
155
+ # Check if the results list has exactly two items
156
+ self.assertEqual(len(results), 2)
157
+ # Check if the results list contains the Club Sandwich
158
+ self.assertIn(76, results)
159
+ # Check if the results list contains the Mozzarella Sandwich
160
+ self.assertIn(75, results)
161
+
162
+ def test_08_filter_not_in(self) -> None:
163
+ """
164
+ Search some rows using filters using compare NOT_IN
165
+ """
166
+ filters = [Filter(field='name',
167
+ compare_type=CompareType.CONTAINS,
168
+ value='Sandwich'),
169
+ Filter(field='name',
170
+ compare_type=CompareType.NOT_IN,
171
+ value=('Club Sandwich', 'Mozzarella Sandwich'))]
172
+ results = self.model.search(filters=filters)
173
+ # Check if the results are not None
174
+ self.assertIsNotNone(results)
175
+ # Check if the results list has exactly two items
176
+ self.assertEqual(len(results), 2)
177
+ # Check if the results list doesn't contain the Club Sandwich
178
+ self.assertNotIn(76, results)
179
+ # Check if the results list doesn't contain the Mozzarella Sandwich
180
+ self.assertNotIn(75, results)
181
+
182
+ def test_09_filter_contains(self) -> None:
183
+ """
184
+ Search some rows using filters using compare CONTAINS
185
+ """
186
+ filters = [Filter(field='name',
187
+ compare_type=CompareType.CONTAINS,
188
+ value='Sandwich')]
189
+ results = self.model.search(filters=filters)
190
+ # Check if the results are not None
191
+ self.assertIsNotNone(results)
192
+ # Check if the results list has exactly four items
193
+ self.assertEqual(len(results), 4)
194
+ # Check if the results list contains the Mozzarella Sandwich
195
+ self.assertIn(75, results)
196
+
197
+ def test_10_filter_contains_not(self) -> None:
198
+ """
199
+ Search some rows using filters using compare CONTAINS_NOT
200
+ """
201
+ filters = [Filter(field='name',
202
+ compare_type=CompareType.CONTAINS,
203
+ value='Sandwich'),
204
+ Filter(field='name',
205
+ compare_type=CompareType.CONTAINS_NOT,
206
+ value='mozzarella')]
207
+ results = self.model.search(filters=filters)
208
+ # Check if the results are not None
209
+ self.assertIsNotNone(results)
210
+ # Check if the results list has exactly three items
211
+ self.assertEqual(len(results), 3)
212
+ # Check if the results list doesn't contain the Mozzarella Sandwich
213
+ self.assertNotIn(75, results)
214
+
215
+ def test_11_filter_not_contains(self) -> None:
216
+ """
217
+ Search some rows using filters using compare NOT_CONTAINS
218
+ """
219
+ filters = [Filter(field='name',
220
+ compare_type=CompareType.CONTAINS,
221
+ value='Sandwich'),
222
+ Filter(field='name',
223
+ compare_type=CompareType.NOT_CONTAINS,
224
+ value='mozzarella')]
225
+ results = self.model.search(filters=filters)
226
+ # Check if the results are not None
227
+ self.assertIsNotNone(results)
228
+ # Check if the results list has exactly three items
229
+ self.assertEqual(len(results), 3)
230
+ # Check if the results list doesn't contain the Mozzarella Sandwich
231
+ self.assertNotIn(75, results)
232
+
233
+ def test_12_filter_like(self) -> None:
234
+ """
235
+ Search some rows using filters using compare LIKE
236
+ """
237
+ filters = [Filter(field='name',
238
+ compare_type=CompareType.CONTAINS,
239
+ value='Sandwich'),
240
+ Filter(field='name',
241
+ compare_type=CompareType.LIKE,
242
+ value='Mozzarella')]
243
+ results = self.model.search(filters=filters)
244
+ # Check if the results are not None
245
+ self.assertIsNotNone(results)
246
+ # Check if the results list has exactly one item
247
+ self.assertEqual(len(results), 1)
248
+ # Check if the results list contains the Mozzarella Sandwich
249
+ self.assertIn(75, results)
250
+
251
+ def test_13_filter_not_like(self) -> None:
252
+ """
253
+ Search some rows using filters using compare NOT_LIKE
254
+ """
255
+ filters = [Filter(field='name',
256
+ compare_type=CompareType.CONTAINS,
257
+ value='Sandwich'),
258
+ Filter(field='name',
259
+ compare_type=CompareType.NOT_LIKE,
260
+ value='Mozzarella')]
261
+ results = self.model.search(filters=filters)
262
+ # Check if the results are not None
263
+ self.assertIsNotNone(results)
264
+ # Check if the results list has exactly three items
265
+ self.assertEqual(len(results), 3)
266
+ # Check if the results list doesn't contain the Mozzarella Sandwich
267
+ self.assertNotIn(75, results)
268
+
269
+ def test_14_filter_ilike(self) -> None:
270
+ """
271
+ Search some rows using filters using compare ILIKE
272
+ """
273
+ filters = [Filter(field='name',
274
+ compare_type=CompareType.CONTAINS,
275
+ value='Sandwich'),
276
+ Filter(field='name',
277
+ compare_type=CompareType.ILIKE,
278
+ value='mozzarella')]
279
+ results = self.model.search(filters=filters)
280
+ # Check if the results are not None
281
+ self.assertIsNotNone(results)
282
+ # Check if the results list has exactly one item
283
+ self.assertEqual(len(results), 1)
284
+ # Check if the results list contains the Mozzarella Sandwich
285
+ self.assertIn(75, results)
286
+
287
+ def test_15_filter_not_ilike(self) -> None:
288
+ """
289
+ Search some rows using filters using compare NOT_ILIKE
290
+ """
291
+ filters = [Filter(field='name',
292
+ compare_type=CompareType.CONTAINS,
293
+ value='Sandwich'),
294
+ Filter(field='name',
295
+ compare_type=CompareType.NOT_ILIKE,
296
+ value='mozzarella')]
297
+ results = self.model.search(filters=filters)
298
+ # Check if the results are not None
299
+ self.assertIsNotNone(results)
300
+ # Check if the results list has exactly three items
301
+ self.assertEqual(len(results), 3)
302
+ # Check if the results list doesn't contain the Mozzarella Sandwich
303
+ self.assertNotIn(75, results)
304
+
305
+ def test_16_filter_raw_like(self) -> None:
306
+ """
307
+ Search some rows using filters using compare RAW_LIKE
308
+ """
309
+ filters = [Filter(field='name',
310
+ compare_type=CompareType.CONTAINS,
311
+ value='Sandwich'),
312
+ Filter(field='name',
313
+ compare_type=CompareType.RAW_LIKE,
314
+ value='Mozzarella %')]
315
+ results = self.model.search(filters=filters)
316
+ # Check if the results are not None
317
+ self.assertIsNotNone(results)
318
+ # Check if the results list has exactly one item
319
+ self.assertEqual(len(results), 1)
320
+ # Check if the results list contains the Mozzarella Sandwich
321
+ self.assertIn(75, results)
322
+
323
+ def test_17_filter_raw_ilike(self) -> None:
324
+ """
325
+ Search some rows using filters using compare RAW_ILIKE
326
+ """
327
+ filters = [Filter(field='name',
328
+ compare_type=CompareType.CONTAINS,
329
+ value='Sandwich'),
330
+ Filter(field='name',
331
+ compare_type=CompareType.RAW_ILIKE,
332
+ value='mozzarella %')]
333
+ results = self.model.search(filters=filters)
334
+ # Check if the results are not None
335
+ self.assertIsNotNone(results)
336
+ # Check if the results list has exactly one item
337
+ self.assertEqual(len(results), 1)
338
+ # Check if the results list contains the Mozzarella Sandwich
339
+ self.assertIn(75, results)
340
+
341
+ def test_18_filter_unset_or_equal(self) -> None:
342
+ """
343
+ Search some rows using filters using compare UNSET_OR_EQUAL
344
+ """
345
+ filters = [Filter(field='name',
346
+ compare_type=CompareType.CONTAINS,
347
+ value='Sandwich'),
348
+ Filter(field='uom_id',
349
+ compare_type=CompareType.UNSET_OR_EQUAL,
350
+ value=1)]
351
+ results = self.model.search(filters=filters)
352
+ # Check if the results are not None
353
+ self.assertIsNotNone(results)
354
+ # Check if the results list has exactly four items
355
+ self.assertEqual(len(results), 4)
356
+ # Check if the results list contains the Mozzarella Sandwich
357
+ self.assertIn(75, results)
358
+
359
+ def test_19_filter_child_of(self) -> None:
360
+ """
361
+ Search some rows using filters using compare CHILD_OF
362
+ """
363
+ filters = [Filter(field='name',
364
+ compare_type=CompareType.CONTAINS,
365
+ value='Sandwich'),
366
+ Filter(field='categ_id',
367
+ compare_type=CompareType.CHILD_OF,
368
+ value='PoS')]
369
+ results = self.model.search(filters=filters)
370
+ # Check if the results are not None
371
+ self.assertIsNotNone(results)
372
+ # Check if the results list has exactly four items
373
+ self.assertEqual(len(results), 4)
374
+ # Check if the results list contains the Mozzarella Sandwich
375
+ self.assertIn(75, results)
376
+
377
+ def test_20_filter_parent_of(self) -> None:
378
+ """
379
+ Search some rows using filters using compare PARENT_OF
380
+ """
381
+ filters = [Filter(field='name',
382
+ compare_type=CompareType.CONTAINS,
383
+ value='Sandwich'),
384
+ Filter(field='categ_id',
385
+ compare_type=CompareType.PARENT_OF,
386
+ value='Food')]
387
+ results = self.model.search(filters=filters)
388
+ # Check if the results are not None
389
+ self.assertIsNotNone(results)
390
+ # Check if the results list has exactly four items
391
+ self.assertEqual(len(results), 4)
392
+ # Check if the results list contains the Mozzarella Sandwich
393
+ self.assertIn(75, results)
@@ -29,6 +29,8 @@ from pyodoo import (ActiveStatusChoice,
29
29
  from pyodoo.constants import APP_AUTHOR_EMAIL, APP_NAME, APP_VERSION
30
30
  from pyodoo.v12 import Model
31
31
 
32
+ import utility
33
+
32
34
 
33
35
  class TestCaseContacts(unittest.TestCase):
34
36
  @classmethod
@@ -36,24 +38,7 @@ class TestCaseContacts(unittest.TestCase):
36
38
  """
37
39
  Model object preparation
38
40
  """
39
- # Get the free public server credentials
40
- try:
41
- info = xmlrpc.client.ServerProxy(
42
- uri='https://demo.odoo.com/start').start()
43
- except xmlrpc.client.Fault:
44
- # Sometimes the free public server start page is not available
45
- # Use a specif instance (this may be very mutable)
46
- info = {'host': 'https://demo4.odoo.com',
47
- 'database': 'demo_160_1676157153',
48
- 'user': 'admin',
49
- 'password': 'admin'}
50
- cls.model = Model(model_name='res.partner',
51
- endpoint=info['host'],
52
- database=info['database'],
53
- username=info['user'],
54
- password=info['password'],
55
- language='en_US',
56
- authenticate=False)
41
+ cls.model = utility.get_model_from_demo(model_name='res.partner')
57
42
 
58
43
  def test_01_authenticate(self) -> None:
59
44
  """
@@ -117,7 +102,7 @@ class TestCaseContacts(unittest.TestCase):
117
102
  """
118
103
  Search some rows using filters
119
104
  """
120
- # Filters by name and excluding an explicit ID
105
+ # Filters by active and exclude an explicit name
121
106
  filters = [BooleanOperator.AND,
122
107
  Filter(field='active',
123
108
  compare_type=CompareType.EQUAL,
@@ -231,7 +216,7 @@ class TestCaseContacts(unittest.TestCase):
231
216
  """
232
217
  Find multiple rows using some filters
233
218
  """
234
- # Filters by name and excluding an explicit ID
219
+ # Filters by active and exclude an explicit name
235
220
  filters = [BooleanOperator.AND,
236
221
  Filter(field='active',
237
222
  compare_type=CompareType.EQUAL,
@@ -252,7 +237,7 @@ class TestCaseContacts(unittest.TestCase):
252
237
  """
253
238
  Find multiple rows using some filters and order
254
239
  """
255
- # Filters by name and excluding an explicit ID
240
+ # Filters by active and exclude an explicit name
256
241
  filters = [BooleanOperator.AND,
257
242
  Filter(field='active',
258
243
  compare_type=CompareType.EQUAL,
@@ -270,7 +255,48 @@ class TestCaseContacts(unittest.TestCase):
270
255
  # Check if the results list is not empty
271
256
  self.assertGreater(len(results), 0)
272
257
 
273
- def test_14_get(self) -> None:
258
+ def test_14_first(self) -> None:
259
+ """
260
+ Find a single row using some filters
261
+ """
262
+ # Filters by active and exclude an explicit name
263
+ filters = [BooleanOperator.AND,
264
+ Filter(field='active',
265
+ compare_type=CompareType.EQUAL,
266
+ value=False),
267
+ BooleanOperator.NOT,
268
+ Filter(field='name',
269
+ compare_type=CompareType.CONTAINS,
270
+ value='OdooBot'),
271
+ ]
272
+ results = self.model.first(filters=filters,
273
+ fields=('id', 'name', 'type', 'street'))
274
+ # Check if the results type is None or dict
275
+ self.assertIn(type(results), (None, dict))
276
+ # Check if the results contains 4 values, for the 4 fields
277
+ self.assertEqual(len(results), 4)
278
+
279
+ def test_15_first_none(self) -> None:
280
+ """
281
+ Find a single row which returns None
282
+ """
283
+ # Filters by active and exclude an explicit name
284
+ filters = [BooleanOperator.AND,
285
+ Filter(field='active',
286
+ compare_type=CompareType.EQUAL,
287
+ value=False),
288
+ BooleanOperator.NOT,
289
+ Filter(field='active',
290
+ compare_type=CompareType.EQUAL,
291
+ value=True),
292
+ ]
293
+ results = self.model.first(filters=filters,
294
+ fields=('id', 'name', 'type', 'street'),
295
+ offset=1000)
296
+ # Check if the results type is None
297
+ self.assertEqual(results, None)
298
+
299
+ def test_16_get(self) -> None:
274
300
  """
275
301
  Get a single row using ID
276
302
  """
@@ -290,7 +316,7 @@ class TestCaseContacts(unittest.TestCase):
290
316
  self.assertEqual(results['type'], 'contact')
291
317
  self.assertGreater(len(results['street']), 0)
292
318
 
293
- def test_15_get_many(self) -> None:
319
+ def test_17_get_many(self) -> None:
294
320
  """
295
321
  Get multiple rows using ID
296
322
  """
@@ -308,7 +334,7 @@ class TestCaseContacts(unittest.TestCase):
308
334
  self.assertEqual(results[0]['type'], 'contact')
309
335
  self.assertGreater(len(results[0]['street']), 0)
310
336
 
311
- def test_16_create(self) -> None:
337
+ def test_18_create(self) -> None:
312
338
  """
313
339
  Create a new row
314
340
  """
@@ -319,7 +345,7 @@ class TestCaseContacts(unittest.TestCase):
319
345
  # Check if the results is not empty
320
346
  self.assertGreater(results, 0)
321
347
 
322
- def test_17_many_to_many_add(self) -> None:
348
+ def test_19_many_to_many_add(self) -> None:
323
349
  """
324
350
  Add a record to a Many-to-Many relationship
325
351
  """
@@ -340,7 +366,7 @@ class TestCaseContacts(unittest.TestCase):
340
366
  # Check if we have results
341
367
  self.assertTrue(results)
342
368
 
343
- def test_18_many_to_many_create(self) -> None:
369
+ def test_20_many_to_many_create(self) -> None:
344
370
  """
345
371
  Create a new record and add it to a Many-to-Many relationship
346
372
  """
@@ -358,7 +384,7 @@ class TestCaseContacts(unittest.TestCase):
358
384
  # Check if we have results
359
385
  self.assertTrue(results)
360
386
 
361
- def test_19_many_to_many_update(self) -> None:
387
+ def test_21_many_to_many_update(self) -> None:
362
388
  """
363
389
  Update an existing record from a Many-to-Many relationship
364
390
  """
@@ -390,7 +416,7 @@ class TestCaseContacts(unittest.TestCase):
390
416
  # Check if we have results
391
417
  self.assertTrue(results)
392
418
 
393
- def test_20_many_to_many_delete(self) -> None:
419
+ def test_22_many_to_many_delete(self) -> None:
394
420
  """
395
421
  Delete an existing child record from a Many-to-Many relationship
396
422
  and delete the record itself
@@ -430,7 +456,7 @@ class TestCaseContacts(unittest.TestCase):
430
456
  # We catched a different error, re-raise it
431
457
  raise error
432
458
 
433
- def test_21_many_to_many_remove(self) -> None:
459
+ def test_23_many_to_many_remove(self) -> None:
434
460
  """
435
461
  Remove an existing child record from a Many-to-Many relationship
436
462
  """
@@ -460,7 +486,7 @@ class TestCaseContacts(unittest.TestCase):
460
486
  # Check if we have results
461
487
  self.assertTrue(results)
462
488
 
463
- def test_22_many_to_many_clear(self) -> None:
489
+ def test_24_many_to_many_clear(self) -> None:
464
490
  """
465
491
  Clear any existing children records from a Many-to-Many relationship
466
492
  """
@@ -476,7 +502,7 @@ class TestCaseContacts(unittest.TestCase):
476
502
  # Check if we have results
477
503
  self.assertTrue(results)
478
504
 
479
- def test_23_many_to_many_replace(self) -> None:
505
+ def test_25_many_to_many_replace(self) -> None:
480
506
  """
481
507
  Replace any existing children records from a Many-to-Many relationship
482
508
  """
@@ -503,7 +529,7 @@ class TestCaseContacts(unittest.TestCase):
503
529
  # Check if we have results
504
530
  self.assertTrue(results)
505
531
 
506
- def test_24_update(self) -> None:
532
+ def test_26_update(self) -> None:
507
533
  """
508
534
  Update the newly created rows
509
535
  """
@@ -532,7 +558,7 @@ class TestCaseContacts(unittest.TestCase):
532
558
  # Check the field street
533
559
  self.assertEqual(results_updated['street'], 'TEST TEST TEST')
534
560
 
535
- def test_25_update_many(self) -> None:
561
+ def test_27_update_many(self) -> None:
536
562
  """
537
563
  Update all the newly created rows at once
538
564
  """
@@ -547,7 +573,7 @@ class TestCaseContacts(unittest.TestCase):
547
573
  self.model.update(entity_id=results,
548
574
  values={'street': 'TEST TEST TEST 2'})
549
575
 
550
- def test_26_count(self) -> None:
576
+ def test_28_count(self) -> None:
551
577
  """
552
578
  Count the newly created rows.
553
579
  """
@@ -559,7 +585,7 @@ class TestCaseContacts(unittest.TestCase):
559
585
  self.assertIsNotNone(results)
560
586
  self.assertGreater(results, 0)
561
587
 
562
- def test_27_get_model_data_reference(self) -> None:
588
+ def test_29_get_model_data_reference(self) -> None:
563
589
  """
564
590
  Get a reference row from ir.model.data
565
591
  """
@@ -571,7 +597,7 @@ class TestCaseContacts(unittest.TestCase):
571
597
  self.assertIsInstance(results, dict)
572
598
  self.assertGreater(results['res_id'], 0)
573
599
 
574
- def test_28_get_message_subtype_id_activity(self) -> None:
600
+ def test_30_get_message_subtype_id_activity(self) -> None:
575
601
  """
576
602
  Get a Message subtype ID
577
603
  """
@@ -582,7 +608,7 @@ class TestCaseContacts(unittest.TestCase):
582
608
  self.assertIsInstance(results, int)
583
609
  self.assertGreater(results, 0)
584
610
 
585
- def test_29_get_message_subtype_id_comment(self) -> None:
611
+ def test_31_get_message_subtype_id_comment(self) -> None:
586
612
  """
587
613
  Get a Message subtype ID
588
614
  """
@@ -593,7 +619,7 @@ class TestCaseContacts(unittest.TestCase):
593
619
  self.assertIsInstance(results, int)
594
620
  self.assertGreater(results, 0)
595
621
 
596
- def test_30_get_message_subtype_id_note(self) -> None:
622
+ def test_32_get_message_subtype_id_note(self) -> None:
597
623
  """
598
624
  Get a Message subtype ID
599
625
  """
@@ -604,7 +630,7 @@ class TestCaseContacts(unittest.TestCase):
604
630
  self.assertIsInstance(results, int)
605
631
  self.assertGreater(results, 0)
606
632
 
607
- def test_31_post_message_activity(self) -> None:
633
+ def test_33_post_message_activity(self) -> None:
608
634
  """
609
635
  Post a new message as activity
610
636
  """
@@ -623,7 +649,7 @@ class TestCaseContacts(unittest.TestCase):
623
649
  self.assertIsNotNone(entity_id)
624
650
  self.assertGreater(entity_id, 0)
625
651
 
626
- def test_32_post_message_comment(self) -> None:
652
+ def test_34_post_message_comment(self) -> None:
627
653
  """
628
654
  Post a new message as comment
629
655
  """
@@ -642,7 +668,7 @@ class TestCaseContacts(unittest.TestCase):
642
668
  self.assertIsNotNone(entity_id)
643
669
  self.assertGreater(entity_id, 0)
644
670
 
645
- def test_33_post_message_note(self) -> None:
671
+ def test_35_post_message_note(self) -> None:
646
672
  """
647
673
  Post a new message as note
648
674
  """
@@ -661,7 +687,7 @@ class TestCaseContacts(unittest.TestCase):
661
687
  self.assertIsNotNone(entity_id)
662
688
  self.assertGreater(entity_id, 0)
663
689
 
664
- def test_34_delete(self) -> None:
690
+ def test_36_delete(self) -> None:
665
691
  """
666
692
  Delete the newly created rows.
667
693
  This test may be skipped in the case there's an active PoS session
@@ -694,7 +720,7 @@ class TestCaseContacts(unittest.TestCase):
694
720
  # We catched a different error, re-raise it
695
721
  raise error
696
722
 
697
- def test_35_delete_many(self) -> None:
723
+ def test_37_delete_many(self) -> None:
698
724
  """
699
725
  Delete the newly created rows.
700
726
  This test may be skipped in the case there's an active PoS session
@@ -720,7 +746,7 @@ class TestCaseContacts(unittest.TestCase):
720
746
  # We catched a different error, re-raise it
721
747
  raise error
722
748
 
723
- def test_36_language(self) -> None:
749
+ def test_38_language(self) -> None:
724
750
  """
725
751
  Get the current default language, change and restore it
726
752
  """
@@ -737,7 +763,7 @@ class TestCaseContacts(unittest.TestCase):
737
763
  results = self.model.language
738
764
  self.assertEqual(results, original_language)
739
765
 
740
- def test_37_get_fields(self) -> None:
766
+ def test_39_get_fields(self) -> None:
741
767
  """
742
768
  Get the model fields
743
769
  """
@@ -745,7 +771,7 @@ class TestCaseContacts(unittest.TestCase):
745
771
  # Check if we have results
746
772
  self.assertIsNotNone(results)
747
773
 
748
- def test_38_get_fields_attributes(self) -> None:
774
+ def test_40_get_fields_attributes(self) -> None:
749
775
  """
750
776
  Get the model fields
751
777
  """
@@ -758,7 +784,7 @@ class TestCaseContacts(unittest.TestCase):
758
784
  self.assertIn('string', field_name)
759
785
  self.assertIn('type', field_name)
760
786
 
761
- def test_39_get_model(self) -> None:
787
+ def test_41_get_model(self) -> None:
762
788
  """
763
789
  Get a new Model object
764
790
  """
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes