odoo-easy 0.1.0__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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Antonio Maminiaina
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,154 @@
1
+ Metadata-Version: 2.4
2
+ Name: odoo-easy
3
+ Version: 0.1.0
4
+ Summary: Lightweight Python wrapper for the Odoo XML-RPC API
5
+ License-Expression: MIT
6
+ Project-URL: Homepage, https://github.com/atovoman/odoo-easy
7
+ Project-URL: Repository, https://github.com/atovoman/odoo-easy
8
+ Project-URL: Issues, https://github.com/atovoman/odoo-easy/issues
9
+ Keywords: odoo,xmlrpc,erp,api,wrapper
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.8
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Topic :: Software Development :: Libraries
19
+ Requires-Python: >=3.8
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Dynamic: license-file
23
+
24
+ # odoo-easy
25
+
26
+ A lightweight Python wrapper for the Odoo XML-RPC API. Stop writing boilerplate — connect and query Odoo in 3 lines.
27
+
28
+ ## Installation
29
+
30
+ ```bash
31
+ pip install odoo-easy
32
+ ```
33
+
34
+ ## Quick Start
35
+
36
+ ```python
37
+ from odoo_connect import OdooClient
38
+
39
+ odoo = OdooClient("https://mycompany.odoo.com", "my-database")
40
+ odoo.login("admin@example.com", "your-password")
41
+
42
+ # List products
43
+ products = odoo.search_read("product.template", fields=["name", "list_price"])
44
+ ```
45
+
46
+ ## Usage
47
+
48
+ ### Connect
49
+
50
+ ```python
51
+ from odoo_connect import OdooClient
52
+
53
+ odoo = OdooClient("https://mycompany.odoo.com", "my-database")
54
+ odoo.login("admin@example.com", "your-password")
55
+ ```
56
+
57
+ ### Search records
58
+
59
+ ```python
60
+ # Get all IDs
61
+ ids = odoo.search("product.template")
62
+
63
+ # With filters
64
+ ids = odoo.search("product.template", filters=[["type", "=", "product"]])
65
+
66
+ # With limit
67
+ ids = odoo.search("product.template", limit=10)
68
+ ```
69
+
70
+ ### Search and read in one call
71
+
72
+ ```python
73
+ products = odoo.search_read(
74
+ "product.template",
75
+ filters=[["sale_ok", "=", True]],
76
+ fields=["name", "list_price", "categ_id"],
77
+ limit=50
78
+ )
79
+ # Returns: [{"id": 1, "name": "My Product", "list_price": 99.0, ...}, ...]
80
+ ```
81
+
82
+ ### Read by IDs
83
+
84
+ ```python
85
+ records = odoo.read("res.partner", ids=[1, 2, 3], fields=["name", "email"])
86
+ ```
87
+
88
+ ### Create a record
89
+
90
+ ```python
91
+ new_id = odoo.create("product.template", {
92
+ "name": "New Product",
93
+ "list_price": 49.99,
94
+ "type": "product",
95
+ })
96
+ print(f"Created product with ID: {new_id}")
97
+ ```
98
+
99
+ ### Update records
100
+
101
+ ```python
102
+ odoo.write("product.template", ids=[1, 2, 3], values={
103
+ "list_price": 59.99
104
+ })
105
+ ```
106
+
107
+ ### Delete records
108
+
109
+ ```python
110
+ odoo.unlink("product.template", ids=[99])
111
+ ```
112
+
113
+ ### Count records
114
+
115
+ ```python
116
+ total = odoo.count("product.template", filters=[["active", "=", True]])
117
+ print(f"Total active products: {total}")
118
+ ```
119
+
120
+ ### Inspect model fields
121
+
122
+ ```python
123
+ fields = odoo.get_fields("product.template", attributes=["string", "type"])
124
+ ```
125
+
126
+ ### Get server version
127
+
128
+ ```python
129
+ info = odoo.version()
130
+ print(info) # {'server_version': '18.0', ...}
131
+ ```
132
+
133
+ ## Error Handling
134
+
135
+ ```python
136
+ from odoo_connect import OdooClient, OdooAuthError, OdooError
137
+
138
+ try:
139
+ odoo = OdooClient("https://mycompany.odoo.com", "my-database")
140
+ odoo.login("wrong@example.com", "badpassword")
141
+ except OdooAuthError as e:
142
+ print(f"Authentication failed: {e}")
143
+ except OdooError as e:
144
+ print(f"Odoo error: {e}")
145
+ ```
146
+
147
+ ## Compatibility
148
+
149
+ - Python 3.8+
150
+ - Odoo 14, 15, 16, 17, 18
151
+
152
+ ## License
153
+
154
+ MIT
@@ -0,0 +1,131 @@
1
+ # odoo-easy
2
+
3
+ A lightweight Python wrapper for the Odoo XML-RPC API. Stop writing boilerplate — connect and query Odoo in 3 lines.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install odoo-easy
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```python
14
+ from odoo_connect import OdooClient
15
+
16
+ odoo = OdooClient("https://mycompany.odoo.com", "my-database")
17
+ odoo.login("admin@example.com", "your-password")
18
+
19
+ # List products
20
+ products = odoo.search_read("product.template", fields=["name", "list_price"])
21
+ ```
22
+
23
+ ## Usage
24
+
25
+ ### Connect
26
+
27
+ ```python
28
+ from odoo_connect import OdooClient
29
+
30
+ odoo = OdooClient("https://mycompany.odoo.com", "my-database")
31
+ odoo.login("admin@example.com", "your-password")
32
+ ```
33
+
34
+ ### Search records
35
+
36
+ ```python
37
+ # Get all IDs
38
+ ids = odoo.search("product.template")
39
+
40
+ # With filters
41
+ ids = odoo.search("product.template", filters=[["type", "=", "product"]])
42
+
43
+ # With limit
44
+ ids = odoo.search("product.template", limit=10)
45
+ ```
46
+
47
+ ### Search and read in one call
48
+
49
+ ```python
50
+ products = odoo.search_read(
51
+ "product.template",
52
+ filters=[["sale_ok", "=", True]],
53
+ fields=["name", "list_price", "categ_id"],
54
+ limit=50
55
+ )
56
+ # Returns: [{"id": 1, "name": "My Product", "list_price": 99.0, ...}, ...]
57
+ ```
58
+
59
+ ### Read by IDs
60
+
61
+ ```python
62
+ records = odoo.read("res.partner", ids=[1, 2, 3], fields=["name", "email"])
63
+ ```
64
+
65
+ ### Create a record
66
+
67
+ ```python
68
+ new_id = odoo.create("product.template", {
69
+ "name": "New Product",
70
+ "list_price": 49.99,
71
+ "type": "product",
72
+ })
73
+ print(f"Created product with ID: {new_id}")
74
+ ```
75
+
76
+ ### Update records
77
+
78
+ ```python
79
+ odoo.write("product.template", ids=[1, 2, 3], values={
80
+ "list_price": 59.99
81
+ })
82
+ ```
83
+
84
+ ### Delete records
85
+
86
+ ```python
87
+ odoo.unlink("product.template", ids=[99])
88
+ ```
89
+
90
+ ### Count records
91
+
92
+ ```python
93
+ total = odoo.count("product.template", filters=[["active", "=", True]])
94
+ print(f"Total active products: {total}")
95
+ ```
96
+
97
+ ### Inspect model fields
98
+
99
+ ```python
100
+ fields = odoo.get_fields("product.template", attributes=["string", "type"])
101
+ ```
102
+
103
+ ### Get server version
104
+
105
+ ```python
106
+ info = odoo.version()
107
+ print(info) # {'server_version': '18.0', ...}
108
+ ```
109
+
110
+ ## Error Handling
111
+
112
+ ```python
113
+ from odoo_connect import OdooClient, OdooAuthError, OdooError
114
+
115
+ try:
116
+ odoo = OdooClient("https://mycompany.odoo.com", "my-database")
117
+ odoo.login("wrong@example.com", "badpassword")
118
+ except OdooAuthError as e:
119
+ print(f"Authentication failed: {e}")
120
+ except OdooError as e:
121
+ print(f"Odoo error: {e}")
122
+ ```
123
+
124
+ ## Compatibility
125
+
126
+ - Python 3.8+
127
+ - Odoo 14, 15, 16, 17, 18
128
+
129
+ ## License
130
+
131
+ MIT
@@ -0,0 +1,5 @@
1
+ from .client import OdooClient
2
+ from .exceptions import OdooAuthError, OdooError, OdooRPCError
3
+
4
+ __version__ = "0.1.0"
5
+ __all__ = ["OdooClient", "OdooAuthError", "OdooError", "OdooRPCError"]
@@ -0,0 +1,231 @@
1
+ import xmlrpc.client
2
+ from .exceptions import OdooAuthError, OdooRPCError
3
+
4
+
5
+ class OdooClient:
6
+ """
7
+ A lightweight Python wrapper for the Odoo XML-RPC API.
8
+
9
+ Usage:
10
+ from odoo_connect import OdooClient
11
+
12
+ odoo = OdooClient("https://mycompany.odoo.com", "my-database")
13
+ odoo.login("admin@example.com", "password")
14
+
15
+ products = odoo.search_read("product.template", fields=["name", "list_price"])
16
+ """
17
+
18
+ def __init__(self, url: str, db: str):
19
+ """
20
+ Initialize the Odoo client.
21
+
22
+ Args:
23
+ url: The base URL of your Odoo instance (e.g. "https://mycompany.odoo.com")
24
+ db: The database name
25
+ """
26
+ self.url = url.rstrip("/")
27
+ self.db = db
28
+ self.uid = None
29
+ self._password = None
30
+
31
+ self._common = xmlrpc.client.ServerProxy(f"{self.url}/xmlrpc/2/common")
32
+ self._models = xmlrpc.client.ServerProxy(f"{self.url}/xmlrpc/2/object")
33
+
34
+ # ------------------------------------------------------------------
35
+ # Auth
36
+ # ------------------------------------------------------------------
37
+
38
+ def login(self, username: str, password: str) -> int:
39
+ """
40
+ Authenticate against the Odoo instance.
41
+
42
+ Args:
43
+ username: Your Odoo login (usually an email)
44
+ password: Your Odoo password or API key
45
+
46
+ Returns:
47
+ The user ID (uid)
48
+
49
+ Raises:
50
+ OdooAuthError: If authentication fails
51
+ """
52
+ try:
53
+ uid = self._common.authenticate(self.db, username, password, {})
54
+ except Exception as e:
55
+ raise OdooRPCError(f"Connection error: {e}") from e
56
+
57
+ if not uid:
58
+ raise OdooAuthError(
59
+ f"Login failed for user '{username}' on database '{self.db}'. "
60
+ "Check your credentials."
61
+ )
62
+
63
+ self.uid = uid
64
+ self._password = password
65
+ return uid
66
+
67
+ # ------------------------------------------------------------------
68
+ # Internal helper
69
+ # ------------------------------------------------------------------
70
+
71
+ def _execute(self, model: str, method: str, *args, **kwargs):
72
+ """Low-level execute_kw wrapper."""
73
+ if not self.uid:
74
+ raise OdooAuthError("Not logged in. Call login() first.")
75
+ try:
76
+ return self._models.execute_kw(
77
+ self.db, self.uid, self._password,
78
+ model, method, args, kwargs
79
+ )
80
+ except xmlrpc.client.Fault as e:
81
+ raise OdooRPCError(f"Odoo error on {model}.{method}: {e.faultString}") from e
82
+
83
+ # ------------------------------------------------------------------
84
+ # CRUD operations
85
+ # ------------------------------------------------------------------
86
+
87
+ def search(self, model: str, filters: list = None, limit: int = None, offset: int = 0) -> list:
88
+ """
89
+ Search for record IDs matching the given filters.
90
+
91
+ Args:
92
+ model: Odoo model name (e.g. "product.template")
93
+ filters: Domain filter list (e.g. [["active", "=", True]])
94
+ limit: Max number of results (None = no limit)
95
+ offset: Number of records to skip
96
+
97
+ Returns:
98
+ List of matching record IDs
99
+ """
100
+ domain = filters or []
101
+ kwargs = {"offset": offset}
102
+ if limit is not None:
103
+ kwargs["limit"] = limit
104
+ return self._execute(model, "search", domain, **kwargs)
105
+
106
+ def search_read(self, model: str, filters: list = None, fields: list = None,
107
+ limit: int = None, offset: int = 0) -> list:
108
+ """
109
+ Search and return records with their field values.
110
+
111
+ Args:
112
+ model: Odoo model name
113
+ filters: Domain filter list
114
+ fields: List of field names to return (None = all fields)
115
+ limit: Max number of results
116
+ offset: Number of records to skip
117
+
118
+ Returns:
119
+ List of dicts with the requested field values
120
+ """
121
+ domain = filters or []
122
+ kwargs = {"offset": offset}
123
+ if fields:
124
+ kwargs["fields"] = fields
125
+ if limit is not None:
126
+ kwargs["limit"] = limit
127
+ return self._execute(model, "search_read", domain, **kwargs)
128
+
129
+ def read(self, model: str, ids: list, fields: list = None) -> list:
130
+ """
131
+ Read specific records by their IDs.
132
+
133
+ Args:
134
+ model: Odoo model name
135
+ ids: List of record IDs to read
136
+ fields: List of field names to return (None = all fields)
137
+
138
+ Returns:
139
+ List of dicts with the requested field values
140
+ """
141
+ kwargs = {}
142
+ if fields:
143
+ kwargs["fields"] = fields
144
+ return self._execute(model, "read", ids, **kwargs)
145
+
146
+ def create(self, model: str, values: dict) -> int:
147
+ """
148
+ Create a new record.
149
+
150
+ Args:
151
+ model: Odoo model name
152
+ values: Dict of field values for the new record
153
+
154
+ Returns:
155
+ ID of the newly created record
156
+ """
157
+ return self._execute(model, "create", values)
158
+
159
+ def write(self, model: str, ids: list, values: dict) -> bool:
160
+ """
161
+ Update existing records.
162
+
163
+ Args:
164
+ model: Odoo model name
165
+ ids: List of record IDs to update
166
+ values: Dict of field values to update
167
+
168
+ Returns:
169
+ True if successful
170
+ """
171
+ return self._execute(model, "write", ids, values)
172
+
173
+ def unlink(self, model: str, ids: list) -> bool:
174
+ """
175
+ Delete records.
176
+
177
+ Args:
178
+ model: Odoo model name
179
+ ids: List of record IDs to delete
180
+
181
+ Returns:
182
+ True if successful
183
+ """
184
+ return self._execute(model, "unlink", ids)
185
+
186
+ def count(self, model: str, filters: list = None) -> int:
187
+ """
188
+ Count records matching the given filters.
189
+
190
+ Args:
191
+ model: Odoo model name
192
+ filters: Domain filter list
193
+
194
+ Returns:
195
+ Number of matching records
196
+ """
197
+ domain = filters or []
198
+ return self._execute(model, "search_count", domain)
199
+
200
+ # ------------------------------------------------------------------
201
+ # Extras
202
+ # ------------------------------------------------------------------
203
+
204
+ def get_fields(self, model: str, attributes: list = None) -> dict:
205
+ """
206
+ Get the field definitions for a model.
207
+
208
+ Args:
209
+ model: Odoo model name
210
+ attributes: List of attributes to return (e.g. ["string", "type"])
211
+
212
+ Returns:
213
+ Dict of field definitions
214
+ """
215
+ kwargs = {}
216
+ if attributes:
217
+ kwargs["attributes"] = attributes
218
+ return self._execute(model, "fields_get", [], **kwargs)
219
+
220
+ def version(self) -> dict:
221
+ """
222
+ Get the Odoo server version info.
223
+
224
+ Returns:
225
+ Dict with version information
226
+ """
227
+ return self._common.version()
228
+
229
+ def __repr__(self):
230
+ status = f"uid={self.uid}" if self.uid else "not logged in"
231
+ return f"OdooClient(url='{self.url}', db='{self.db}', {status})"
@@ -0,0 +1,13 @@
1
+ class OdooError(Exception):
2
+ """Base exception for odoo-connect."""
3
+ pass
4
+
5
+
6
+ class OdooAuthError(OdooError):
7
+ """Raised when authentication fails."""
8
+ pass
9
+
10
+
11
+ class OdooRPCError(OdooError):
12
+ """Raised when an XML-RPC call fails."""
13
+ pass
@@ -0,0 +1,154 @@
1
+ Metadata-Version: 2.4
2
+ Name: odoo-easy
3
+ Version: 0.1.0
4
+ Summary: Lightweight Python wrapper for the Odoo XML-RPC API
5
+ License-Expression: MIT
6
+ Project-URL: Homepage, https://github.com/atovoman/odoo-easy
7
+ Project-URL: Repository, https://github.com/atovoman/odoo-easy
8
+ Project-URL: Issues, https://github.com/atovoman/odoo-easy/issues
9
+ Keywords: odoo,xmlrpc,erp,api,wrapper
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.8
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Topic :: Software Development :: Libraries
19
+ Requires-Python: >=3.8
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Dynamic: license-file
23
+
24
+ # odoo-easy
25
+
26
+ A lightweight Python wrapper for the Odoo XML-RPC API. Stop writing boilerplate — connect and query Odoo in 3 lines.
27
+
28
+ ## Installation
29
+
30
+ ```bash
31
+ pip install odoo-easy
32
+ ```
33
+
34
+ ## Quick Start
35
+
36
+ ```python
37
+ from odoo_connect import OdooClient
38
+
39
+ odoo = OdooClient("https://mycompany.odoo.com", "my-database")
40
+ odoo.login("admin@example.com", "your-password")
41
+
42
+ # List products
43
+ products = odoo.search_read("product.template", fields=["name", "list_price"])
44
+ ```
45
+
46
+ ## Usage
47
+
48
+ ### Connect
49
+
50
+ ```python
51
+ from odoo_connect import OdooClient
52
+
53
+ odoo = OdooClient("https://mycompany.odoo.com", "my-database")
54
+ odoo.login("admin@example.com", "your-password")
55
+ ```
56
+
57
+ ### Search records
58
+
59
+ ```python
60
+ # Get all IDs
61
+ ids = odoo.search("product.template")
62
+
63
+ # With filters
64
+ ids = odoo.search("product.template", filters=[["type", "=", "product"]])
65
+
66
+ # With limit
67
+ ids = odoo.search("product.template", limit=10)
68
+ ```
69
+
70
+ ### Search and read in one call
71
+
72
+ ```python
73
+ products = odoo.search_read(
74
+ "product.template",
75
+ filters=[["sale_ok", "=", True]],
76
+ fields=["name", "list_price", "categ_id"],
77
+ limit=50
78
+ )
79
+ # Returns: [{"id": 1, "name": "My Product", "list_price": 99.0, ...}, ...]
80
+ ```
81
+
82
+ ### Read by IDs
83
+
84
+ ```python
85
+ records = odoo.read("res.partner", ids=[1, 2, 3], fields=["name", "email"])
86
+ ```
87
+
88
+ ### Create a record
89
+
90
+ ```python
91
+ new_id = odoo.create("product.template", {
92
+ "name": "New Product",
93
+ "list_price": 49.99,
94
+ "type": "product",
95
+ })
96
+ print(f"Created product with ID: {new_id}")
97
+ ```
98
+
99
+ ### Update records
100
+
101
+ ```python
102
+ odoo.write("product.template", ids=[1, 2, 3], values={
103
+ "list_price": 59.99
104
+ })
105
+ ```
106
+
107
+ ### Delete records
108
+
109
+ ```python
110
+ odoo.unlink("product.template", ids=[99])
111
+ ```
112
+
113
+ ### Count records
114
+
115
+ ```python
116
+ total = odoo.count("product.template", filters=[["active", "=", True]])
117
+ print(f"Total active products: {total}")
118
+ ```
119
+
120
+ ### Inspect model fields
121
+
122
+ ```python
123
+ fields = odoo.get_fields("product.template", attributes=["string", "type"])
124
+ ```
125
+
126
+ ### Get server version
127
+
128
+ ```python
129
+ info = odoo.version()
130
+ print(info) # {'server_version': '18.0', ...}
131
+ ```
132
+
133
+ ## Error Handling
134
+
135
+ ```python
136
+ from odoo_connect import OdooClient, OdooAuthError, OdooError
137
+
138
+ try:
139
+ odoo = OdooClient("https://mycompany.odoo.com", "my-database")
140
+ odoo.login("wrong@example.com", "badpassword")
141
+ except OdooAuthError as e:
142
+ print(f"Authentication failed: {e}")
143
+ except OdooError as e:
144
+ print(f"Odoo error: {e}")
145
+ ```
146
+
147
+ ## Compatibility
148
+
149
+ - Python 3.8+
150
+ - Odoo 14, 15, 16, 17, 18
151
+
152
+ ## License
153
+
154
+ MIT
@@ -0,0 +1,10 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ odoo_connect/__init__.py
5
+ odoo_connect/client.py
6
+ odoo_connect/exceptions.py
7
+ odoo_easy.egg-info/PKG-INFO
8
+ odoo_easy.egg-info/SOURCES.txt
9
+ odoo_easy.egg-info/dependency_links.txt
10
+ odoo_easy.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ odoo_connect
@@ -0,0 +1,28 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "odoo-easy"
7
+ version = "0.1.0"
8
+ description = "Lightweight Python wrapper for the Odoo XML-RPC API"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ requires-python = ">=3.8"
12
+ keywords = ["odoo", "xmlrpc", "erp", "api", "wrapper"]
13
+ classifiers = [
14
+ "Development Status :: 3 - Alpha",
15
+ "Intended Audience :: Developers",
16
+ "Programming Language :: Python :: 3",
17
+ "Programming Language :: Python :: 3.8",
18
+ "Programming Language :: Python :: 3.9",
19
+ "Programming Language :: Python :: 3.10",
20
+ "Programming Language :: Python :: 3.11",
21
+ "Programming Language :: Python :: 3.12",
22
+ "Topic :: Software Development :: Libraries",
23
+ ]
24
+
25
+ [project.urls]
26
+ Homepage = "https://github.com/atovoman/odoo-easy"
27
+ Repository = "https://github.com/atovoman/odoo-easy"
28
+ Issues = "https://github.com/atovoman/odoo-easy/issues"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+