ajera 0.1.3__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 (38) hide show
  1. ajera-0.1.3/PKG-INFO +203 -0
  2. ajera-0.1.3/README.md +191 -0
  3. ajera-0.1.3/pyproject.toml +31 -0
  4. ajera-0.1.3/src/ajera/__init__.py +5 -0
  5. ajera-0.1.3/src/ajera/cli/__init__.py +95 -0
  6. ajera-0.1.3/src/ajera/cli/__main__.py +4 -0
  7. ajera-0.1.3/src/ajera/cli/commands/__init__.py +0 -0
  8. ajera-0.1.3/src/ajera/cli/commands/activities.py +31 -0
  9. ajera-0.1.3/src/ajera/cli/commands/bank_accounts.py +18 -0
  10. ajera-0.1.3/src/ajera/cli/commands/clients.py +153 -0
  11. ajera-0.1.3/src/ajera/cli/commands/companies.py +18 -0
  12. ajera-0.1.3/src/ajera/cli/commands/contacts.py +153 -0
  13. ajera-0.1.3/src/ajera/cli/commands/departments.py +18 -0
  14. ajera-0.1.3/src/ajera/cli/commands/employees.py +212 -0
  15. ajera-0.1.3/src/ajera/cli/commands/invoice_formats.py +18 -0
  16. ajera-0.1.3/src/ajera/cli/commands/ledger.py +101 -0
  17. ajera-0.1.3/src/ajera/cli/commands/projects.py +350 -0
  18. ajera-0.1.3/src/ajera/cli/commands/rate_tables.py +18 -0
  19. ajera-0.1.3/src/ajera/cli/commands/vendors.py +407 -0
  20. ajera-0.1.3/src/ajera/cli/context.py +37 -0
  21. ajera-0.1.3/src/ajera/cli/group.py +72 -0
  22. ajera-0.1.3/src/ajera/cli/options.py +27 -0
  23. ajera-0.1.3/src/ajera/cli/output.py +30 -0
  24. ajera-0.1.3/src/ajera/client.py +2049 -0
  25. ajera-0.1.3/src/ajera/schemas/client.py +614 -0
  26. ajera-0.1.3/src/ajera/schemas/contact.py +568 -0
  27. ajera-0.1.3/src/ajera/schemas/deduction.py +92 -0
  28. ajera-0.1.3/src/ajera/schemas/employee.py +858 -0
  29. ajera-0.1.3/src/ajera/schemas/fringe.py +92 -0
  30. ajera-0.1.3/src/ajera/schemas/generic.py +82 -0
  31. ajera-0.1.3/src/ajera/schemas/ledger.py +322 -0
  32. ajera-0.1.3/src/ajera/schemas/project.py +1416 -0
  33. ajera-0.1.3/src/ajera/schemas/project_summary.py +825 -0
  34. ajera-0.1.3/src/ajera/schemas/project_v2.py +1171 -0
  35. ajera-0.1.3/src/ajera/schemas/reference.py +704 -0
  36. ajera-0.1.3/src/ajera/schemas/session.py +96 -0
  37. ajera-0.1.3/src/ajera/schemas/vendor.py +691 -0
  38. ajera-0.1.3/src/ajera/schemas/vendor_invoice.py +624 -0
ajera-0.1.3/PKG-INFO ADDED
@@ -0,0 +1,203 @@
1
+ Metadata-Version: 2.3
2
+ Name: ajera
3
+ Version: 0.1.3
4
+ Summary: Deltek Ajera Python API Client
5
+ Author: SB&O Inc
6
+ Author-email: SB&O Inc <contact@sboinc.com>
7
+ Requires-Dist: click>=8.0.0
8
+ Requires-Dist: requests>=2.0,<3
9
+ Requires-Dist: pydantic>=2.10.0
10
+ Requires-Python: >=3.12
11
+ Description-Content-Type: text/markdown
12
+
13
+ [![CI](https://github.com/sbo-inc/ajera/actions/workflows/ci.yaml/badge.svg?branch=main)](https://github.com/sbo-inc/ajera/actions/workflows/ci.yaml)
14
+
15
+ # Deltek Ajera Python client
16
+
17
+ A typed Python client and command-line interface for the [Deltek Ajera](https://www.deltek.com/en/project-based-erp/ajera) API.
18
+
19
+ Ajera exposes a single JSON-RPC style endpoint; this package wraps it in an ergonomic, fully type-hinted client built on [Pydantic](https://docs.pydantic.dev/) models, plus an `ajera` CLI for quick access from the terminal. Responses are validated and normalized into predictable Python objects so you can work with employees, projects, vendors, invoices, and general-ledger data without hand-rolling request payloads.
20
+
21
+ ## Features
22
+
23
+ - **Typed models** - every response is parsed into Pydantic models with descriptive fields.
24
+ - **Python client and CLI** - use it as a library or straight from the shell via `ajera`.
25
+ - **Sensible defaults** - handles session tokens and per-method API versions for you.
26
+ - **Read and write** - list, get, update, and create across the supported APIs.
27
+
28
+ ## Installation
29
+
30
+ ```bash
31
+ pip install ajera
32
+ # or, with uv:
33
+ uv add ajera
34
+ ```
35
+
36
+ Requires Python 3.12+.
37
+
38
+ ## Configuration
39
+
40
+ Credentials are read from environment variables (or can be passed directly to `AjeraClient`):
41
+
42
+ | Variable | Description |
43
+ | --- | --- |
44
+ | `AJERA_API_URL` | The Ajera API endpoint URL for your tenant. |
45
+ | `AJERA_API_USERNAME` | API username. |
46
+ | `AJERA_API_PASSWORD` | API password. |
47
+
48
+ ```bash
49
+ export AJERA_API_URL="https://ajera.com/V0000000/AjeraAPI.ashx?..."
50
+ export AJERA_API_USERNAME="your-username"
51
+ export AJERA_API_PASSWORD="your-password"
52
+ ```
53
+
54
+ For setting up an API user and generating credentials, see the [Deltek Ajera Learning Hub API docs](https://learning.deltek.com/bundle/ajera/page/Content/api_setting_up_api_user.htm).
55
+
56
+ ## Quick start
57
+
58
+ ### Python
59
+
60
+ ```python
61
+ from ajera import AjeraClient
62
+
63
+ # Reads AJERA_API_URL / AJERA_API_USERNAME / AJERA_API_PASSWORD from the
64
+ # environment, or pass url=, username=, password= explicitly.
65
+ client = AjeraClient()
66
+
67
+ for employee in client.list_employees():
68
+ print(employee.employee_key, employee.first_name, employee.last_name)
69
+ ```
70
+
71
+ ### CLI
72
+
73
+ ```console
74
+ $ ajera employees list
75
+ [
76
+ {
77
+ "employee_key": 42,
78
+ "first_name": "Ada",
79
+ "last_name": "Lovelace",
80
+ ...
81
+ },
82
+ ...
83
+ ]
84
+ ```
85
+
86
+ > **Note:** List commands backed by an active/inactive status return only **active** records by
87
+ > default. Pass `--status` to override - e.g. `--status Inactive`, or
88
+ > `--status Active --status Inactive` to include both.
89
+
90
+ ## Reference Documentation
91
+
92
+ This client adheres (to the extent possible) to the API documentation provided by Deltek Ajera, which can be found at:
93
+
94
+ https://help.deltek.com/product/Ajera/api/index.html
95
+
96
+ ## API reference
97
+
98
+ Each section below maps a CLI command group to the Ajera API(s) it is built on. The Python client
99
+ exposes the same operations as `client.<method>()` (e.g. `client.list_employees()`,
100
+ `client.get_projects(...)`).
101
+
102
+ ### Employees
103
+
104
+ Docs: [Employees API](https://help.deltek.com/product/Ajera/api/employees.html) · [List Methods API](https://help.deltek.com/product/Ajera/api/list_methods.html)
105
+
106
+ `pays`, `payroll-taxes`, and `wage-tables` come from the List Methods API; the rest come from the Employees API.
107
+
108
+ | Command | Description |
109
+ | --- | --- |
110
+ | `ajera employees list` | List employees. |
111
+ | `ajera employees get <id>...` | Get one or more employees by ID. |
112
+ | `ajera employees update <key> [options]` | Update simple fields on one employee. |
113
+ | `ajera employees types` | List employee types. |
114
+ | `ajera employees deductions` | List deductions. |
115
+ | `ajera employees fringes` | List fringes. |
116
+ | `ajera employees pays` | List pay types. |
117
+ | `ajera employees payroll-taxes` | List payroll taxes. |
118
+ | `ajera employees wage-tables` | List wage tables. |
119
+
120
+ ### Clients
121
+
122
+ Docs: [Clients API](https://help.deltek.com/product/Ajera/api/clients.html)
123
+
124
+ | Command | Description |
125
+ | --- | --- |
126
+ | `ajera clients list` | List clients. |
127
+ | `ajera clients get <id>...` | Get one or more clients by ID. |
128
+ | `ajera clients update <key> [options]` | Update simple fields on one client. |
129
+ | `ajera clients types` | List client types. |
130
+
131
+ ### Contacts
132
+
133
+ Docs: [Contacts API](https://help.deltek.com/product/Ajera/api/contacts.html)
134
+
135
+ | Command | Description |
136
+ | --- | --- |
137
+ | `ajera contacts list` | List contacts. |
138
+ | `ajera contacts get <id>...` | Get one or more contacts by ID. |
139
+ | `ajera contacts update <key> [options]` | Update simple fields on one contact. |
140
+ | `ajera contacts types` | List contact types. |
141
+
142
+ ### Vendors
143
+
144
+ Docs: [Vendors API](https://help.deltek.com/product/Ajera/api/vendors.html) · [Vendor Invoices API (v2)](https://help.deltek.com/product/Ajera/api/version2/vendor_invoices.html)
145
+
146
+ The `invoices` subcommands come from the Vendor Invoices (v2) API; the rest come from the Vendors API.
147
+
148
+ | Command | Description |
149
+ | --- | --- |
150
+ | `ajera vendors list` | List vendors. |
151
+ | `ajera vendors get <id>...` | Get one or more vendors by ID. |
152
+ | `ajera vendors update <key> [options]` | Update simple fields on one vendor. |
153
+ | `ajera vendors types` | List vendor types. |
154
+ | `ajera vendors invoices list` | List vendor invoices, optionally filtered. |
155
+ | `ajera vendors invoices get <key>...` | Get one or more vendor invoices, with their line items. |
156
+ | `ajera vendors invoices create [options]` | Create a vendor invoice with a single line item. |
157
+
158
+ ### Projects
159
+
160
+ Docs: [Projects API (v2)](https://help.deltek.com/product/Ajera/api/version2/projects.html) · [Projects API (v1)](https://help.deltek.com/product/Ajera/api/projects.html) · [List Methods API](https://help.deltek.com/product/Ajera/api/list_methods.html)
161
+
162
+ `list`, `get`, `update`, and `create` use the v2 Projects API; `totals`, `types`, and `templates`
163
+ use the v1 Projects API; `chargeable-phases` comes from the List Methods API.
164
+
165
+ | Command | Description |
166
+ | --- | --- |
167
+ | `ajera projects list` | List projects, optionally filtered. |
168
+ | `ajera projects get <id>...` | Get one or more projects by ID. |
169
+ | `ajera projects create <description> [options]` | Create a new project. |
170
+ | `ajera projects update <key> [options]` | Update simple fields on one project. |
171
+ | `ajera projects totals <id>` | Get a project's financial totals. |
172
+ | `ajera projects types` | List project types. |
173
+ | `ajera projects templates list` | List project templates, optionally filtered. |
174
+ | `ajera projects templates get <id>...` | Get one or more project templates by ID. |
175
+ | `ajera projects chargeable-phases <project-key>` | List the chargeable phases of a project. |
176
+
177
+ ### General Ledger
178
+
179
+ Docs: [GL Accounts API](https://help.deltek.com/product/Ajera/api/gl_accounts.html) · [List Methods API](https://help.deltek.com/product/Ajera/api/list_methods.html)
180
+
181
+ `account-groups` comes from the List Methods API; `list` and `get` come from the GL Accounts API.
182
+
183
+ | Command | Description |
184
+ | --- | --- |
185
+ | `ajera ledger list` | List general ledger accounts. |
186
+ | `ajera ledger get [id]...` | Get general ledger account details, with calculated amounts. |
187
+ | `ajera ledger account-groups` | List general ledger account groups. |
188
+
189
+ ### Reference lists
190
+
191
+ Docs: [List Methods API](https://help.deltek.com/product/Ajera/api/list_methods.html)
192
+
193
+ Lightweight lookup lists. (Other List Methods endpoints are grouped with their domain - see
194
+ `employees`, `ledger`, and `projects` above.)
195
+
196
+ | Command | Description |
197
+ | --- | --- |
198
+ | `ajera activities` | List activities. |
199
+ | `ajera bank-accounts` | List bank accounts. |
200
+ | `ajera companies` | List companies. |
201
+ | `ajera departments` | List departments. |
202
+ | `ajera invoice-formats` | List invoice formats. |
203
+ | `ajera rate-tables` | List rate tables. |
ajera-0.1.3/README.md ADDED
@@ -0,0 +1,191 @@
1
+ [![CI](https://github.com/sbo-inc/ajera/actions/workflows/ci.yaml/badge.svg?branch=main)](https://github.com/sbo-inc/ajera/actions/workflows/ci.yaml)
2
+
3
+ # Deltek Ajera Python client
4
+
5
+ A typed Python client and command-line interface for the [Deltek Ajera](https://www.deltek.com/en/project-based-erp/ajera) API.
6
+
7
+ Ajera exposes a single JSON-RPC style endpoint; this package wraps it in an ergonomic, fully type-hinted client built on [Pydantic](https://docs.pydantic.dev/) models, plus an `ajera` CLI for quick access from the terminal. Responses are validated and normalized into predictable Python objects so you can work with employees, projects, vendors, invoices, and general-ledger data without hand-rolling request payloads.
8
+
9
+ ## Features
10
+
11
+ - **Typed models** - every response is parsed into Pydantic models with descriptive fields.
12
+ - **Python client and CLI** - use it as a library or straight from the shell via `ajera`.
13
+ - **Sensible defaults** - handles session tokens and per-method API versions for you.
14
+ - **Read and write** - list, get, update, and create across the supported APIs.
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ pip install ajera
20
+ # or, with uv:
21
+ uv add ajera
22
+ ```
23
+
24
+ Requires Python 3.12+.
25
+
26
+ ## Configuration
27
+
28
+ Credentials are read from environment variables (or can be passed directly to `AjeraClient`):
29
+
30
+ | Variable | Description |
31
+ | --- | --- |
32
+ | `AJERA_API_URL` | The Ajera API endpoint URL for your tenant. |
33
+ | `AJERA_API_USERNAME` | API username. |
34
+ | `AJERA_API_PASSWORD` | API password. |
35
+
36
+ ```bash
37
+ export AJERA_API_URL="https://ajera.com/V0000000/AjeraAPI.ashx?..."
38
+ export AJERA_API_USERNAME="your-username"
39
+ export AJERA_API_PASSWORD="your-password"
40
+ ```
41
+
42
+ For setting up an API user and generating credentials, see the [Deltek Ajera Learning Hub API docs](https://learning.deltek.com/bundle/ajera/page/Content/api_setting_up_api_user.htm).
43
+
44
+ ## Quick start
45
+
46
+ ### Python
47
+
48
+ ```python
49
+ from ajera import AjeraClient
50
+
51
+ # Reads AJERA_API_URL / AJERA_API_USERNAME / AJERA_API_PASSWORD from the
52
+ # environment, or pass url=, username=, password= explicitly.
53
+ client = AjeraClient()
54
+
55
+ for employee in client.list_employees():
56
+ print(employee.employee_key, employee.first_name, employee.last_name)
57
+ ```
58
+
59
+ ### CLI
60
+
61
+ ```console
62
+ $ ajera employees list
63
+ [
64
+ {
65
+ "employee_key": 42,
66
+ "first_name": "Ada",
67
+ "last_name": "Lovelace",
68
+ ...
69
+ },
70
+ ...
71
+ ]
72
+ ```
73
+
74
+ > **Note:** List commands backed by an active/inactive status return only **active** records by
75
+ > default. Pass `--status` to override - e.g. `--status Inactive`, or
76
+ > `--status Active --status Inactive` to include both.
77
+
78
+ ## Reference Documentation
79
+
80
+ This client adheres (to the extent possible) to the API documentation provided by Deltek Ajera, which can be found at:
81
+
82
+ https://help.deltek.com/product/Ajera/api/index.html
83
+
84
+ ## API reference
85
+
86
+ Each section below maps a CLI command group to the Ajera API(s) it is built on. The Python client
87
+ exposes the same operations as `client.<method>()` (e.g. `client.list_employees()`,
88
+ `client.get_projects(...)`).
89
+
90
+ ### Employees
91
+
92
+ Docs: [Employees API](https://help.deltek.com/product/Ajera/api/employees.html) · [List Methods API](https://help.deltek.com/product/Ajera/api/list_methods.html)
93
+
94
+ `pays`, `payroll-taxes`, and `wage-tables` come from the List Methods API; the rest come from the Employees API.
95
+
96
+ | Command | Description |
97
+ | --- | --- |
98
+ | `ajera employees list` | List employees. |
99
+ | `ajera employees get <id>...` | Get one or more employees by ID. |
100
+ | `ajera employees update <key> [options]` | Update simple fields on one employee. |
101
+ | `ajera employees types` | List employee types. |
102
+ | `ajera employees deductions` | List deductions. |
103
+ | `ajera employees fringes` | List fringes. |
104
+ | `ajera employees pays` | List pay types. |
105
+ | `ajera employees payroll-taxes` | List payroll taxes. |
106
+ | `ajera employees wage-tables` | List wage tables. |
107
+
108
+ ### Clients
109
+
110
+ Docs: [Clients API](https://help.deltek.com/product/Ajera/api/clients.html)
111
+
112
+ | Command | Description |
113
+ | --- | --- |
114
+ | `ajera clients list` | List clients. |
115
+ | `ajera clients get <id>...` | Get one or more clients by ID. |
116
+ | `ajera clients update <key> [options]` | Update simple fields on one client. |
117
+ | `ajera clients types` | List client types. |
118
+
119
+ ### Contacts
120
+
121
+ Docs: [Contacts API](https://help.deltek.com/product/Ajera/api/contacts.html)
122
+
123
+ | Command | Description |
124
+ | --- | --- |
125
+ | `ajera contacts list` | List contacts. |
126
+ | `ajera contacts get <id>...` | Get one or more contacts by ID. |
127
+ | `ajera contacts update <key> [options]` | Update simple fields on one contact. |
128
+ | `ajera contacts types` | List contact types. |
129
+
130
+ ### Vendors
131
+
132
+ Docs: [Vendors API](https://help.deltek.com/product/Ajera/api/vendors.html) · [Vendor Invoices API (v2)](https://help.deltek.com/product/Ajera/api/version2/vendor_invoices.html)
133
+
134
+ The `invoices` subcommands come from the Vendor Invoices (v2) API; the rest come from the Vendors API.
135
+
136
+ | Command | Description |
137
+ | --- | --- |
138
+ | `ajera vendors list` | List vendors. |
139
+ | `ajera vendors get <id>...` | Get one or more vendors by ID. |
140
+ | `ajera vendors update <key> [options]` | Update simple fields on one vendor. |
141
+ | `ajera vendors types` | List vendor types. |
142
+ | `ajera vendors invoices list` | List vendor invoices, optionally filtered. |
143
+ | `ajera vendors invoices get <key>...` | Get one or more vendor invoices, with their line items. |
144
+ | `ajera vendors invoices create [options]` | Create a vendor invoice with a single line item. |
145
+
146
+ ### Projects
147
+
148
+ Docs: [Projects API (v2)](https://help.deltek.com/product/Ajera/api/version2/projects.html) · [Projects API (v1)](https://help.deltek.com/product/Ajera/api/projects.html) · [List Methods API](https://help.deltek.com/product/Ajera/api/list_methods.html)
149
+
150
+ `list`, `get`, `update`, and `create` use the v2 Projects API; `totals`, `types`, and `templates`
151
+ use the v1 Projects API; `chargeable-phases` comes from the List Methods API.
152
+
153
+ | Command | Description |
154
+ | --- | --- |
155
+ | `ajera projects list` | List projects, optionally filtered. |
156
+ | `ajera projects get <id>...` | Get one or more projects by ID. |
157
+ | `ajera projects create <description> [options]` | Create a new project. |
158
+ | `ajera projects update <key> [options]` | Update simple fields on one project. |
159
+ | `ajera projects totals <id>` | Get a project's financial totals. |
160
+ | `ajera projects types` | List project types. |
161
+ | `ajera projects templates list` | List project templates, optionally filtered. |
162
+ | `ajera projects templates get <id>...` | Get one or more project templates by ID. |
163
+ | `ajera projects chargeable-phases <project-key>` | List the chargeable phases of a project. |
164
+
165
+ ### General Ledger
166
+
167
+ Docs: [GL Accounts API](https://help.deltek.com/product/Ajera/api/gl_accounts.html) · [List Methods API](https://help.deltek.com/product/Ajera/api/list_methods.html)
168
+
169
+ `account-groups` comes from the List Methods API; `list` and `get` come from the GL Accounts API.
170
+
171
+ | Command | Description |
172
+ | --- | --- |
173
+ | `ajera ledger list` | List general ledger accounts. |
174
+ | `ajera ledger get [id]...` | Get general ledger account details, with calculated amounts. |
175
+ | `ajera ledger account-groups` | List general ledger account groups. |
176
+
177
+ ### Reference lists
178
+
179
+ Docs: [List Methods API](https://help.deltek.com/product/Ajera/api/list_methods.html)
180
+
181
+ Lightweight lookup lists. (Other List Methods endpoints are grouped with their domain - see
182
+ `employees`, `ledger`, and `projects` above.)
183
+
184
+ | Command | Description |
185
+ | --- | --- |
186
+ | `ajera activities` | List activities. |
187
+ | `ajera bank-accounts` | List bank accounts. |
188
+ | `ajera companies` | List companies. |
189
+ | `ajera departments` | List departments. |
190
+ | `ajera invoice-formats` | List invoice formats. |
191
+ | `ajera rate-tables` | List rate tables. |
@@ -0,0 +1,31 @@
1
+ [project]
2
+ name = "ajera"
3
+ version = "0.1.3"
4
+ description = "Deltek Ajera Python API Client"
5
+ readme = "README.md"
6
+ authors = [{ name = "SB&O Inc", email = "contact@sboinc.com" }]
7
+ requires-python = ">=3.12"
8
+ dependencies = [
9
+ "click >= 8.0.0",
10
+ "requests >=2.0, <3",
11
+ "pydantic >= 2.10.0",
12
+ ]
13
+
14
+ [dependency-groups]
15
+ dev = [
16
+ "mypy",
17
+ "ruff",
18
+ "pytest",
19
+ ]
20
+
21
+ [tool.pytest.ini_options]
22
+ markers = [
23
+ "integration: marks tests that require a live Ajera API instance",
24
+ ]
25
+
26
+ [project.scripts]
27
+ ajera = "ajera.cli:main"
28
+
29
+ [build-system]
30
+ requires = ["uv_build<0.9.0"]
31
+ build-backend = "uv_build"
@@ -0,0 +1,5 @@
1
+ from ajera.client import AjeraClient
2
+
3
+ __all__ = [
4
+ "AjeraClient",
5
+ ]
@@ -0,0 +1,95 @@
1
+ import logging
2
+ import sys
3
+
4
+ import click
5
+ import requests
6
+
7
+ from ajera.cli.commands import (
8
+ activities,
9
+ bank_accounts,
10
+ clients,
11
+ companies,
12
+ contacts,
13
+ departments,
14
+ employees,
15
+ invoice_formats,
16
+ ledger,
17
+ projects,
18
+ rate_tables,
19
+ vendors,
20
+ )
21
+ from ajera.cli.context import ClientContext
22
+ from ajera.cli.group import CommonClickGroup
23
+
24
+ CLI_DOCS = """
25
+ Deltek Ajera API command-line interface.
26
+
27
+ Connection settings are read from environment variables:
28
+
29
+ \b
30
+ - AJERA_API_URL to define the API endpoint.
31
+ - AJERA_API_USERNAME / AJERA_API_PASSWORD for authentication.
32
+ - AJERA_API_HEADERS as a JSON object of extra request headers.
33
+ """
34
+
35
+
36
+ @click.group(
37
+ cls=CommonClickGroup,
38
+ context_settings={"help_option_names": ["-h", "--help"]},
39
+ help=CLI_DOCS,
40
+ )
41
+ @click.option(
42
+ "--log",
43
+ is_flag=True,
44
+ default=False,
45
+ help="Enable request logging at INFO level.",
46
+ )
47
+ @click.version_option(package_name="ajera", prog_name="ajera")
48
+ @click.pass_context
49
+ def cli(ctx: click.Context, log: bool) -> None:
50
+ ctx.obj = ClientContext(log=log)
51
+
52
+
53
+ # Register all domain-specific command groups.
54
+ # Runtime gating is handled by AJERA_CLI_DISABLE.
55
+ for module in (
56
+ employees,
57
+ clients,
58
+ contacts,
59
+ vendors,
60
+ projects,
61
+ ledger,
62
+ activities,
63
+ companies,
64
+ departments,
65
+ rate_tables,
66
+ bank_accounts,
67
+ invoice_formats,
68
+ ):
69
+ cli.add_command(module.group)
70
+
71
+
72
+ def main() -> None:
73
+ """
74
+ Console-script entry point.
75
+
76
+ Translates expected errors into a non-zero exit with a readable message
77
+ instead of a traceback.
78
+ """
79
+ try:
80
+ cli(standalone_mode=False)
81
+ except click.ClickException as exc:
82
+ exc.show()
83
+ sys.exit(exc.exit_code)
84
+ except click.exceptions.Abort:
85
+ sys.exit(1)
86
+ except requests.HTTPError as exc:
87
+ click.echo(f"Error: {exc}", err=True)
88
+ sys.exit(1)
89
+ except Exception as exc: # noqa: BLE001
90
+ logging.getLogger("ajera").debug("CLI error", exc_info=True)
91
+ click.echo(f"Error: {exc}", err=True)
92
+ sys.exit(1)
93
+
94
+
95
+ __all__ = ["cli", "main"]
@@ -0,0 +1,4 @@
1
+ from ajera.cli import main
2
+
3
+ if __name__ == "__main__":
4
+ main()
File without changes
@@ -0,0 +1,31 @@
1
+ import click
2
+
3
+ from ajera.cli.context import ClientContext
4
+ from ajera.cli.options import status_option
5
+ from ajera.cli.output import render
6
+
7
+
8
+ @click.command(name="activities")
9
+ @status_option
10
+ @click.option(
11
+ "--description-like",
12
+ "filter_by_description_like",
13
+ type=str,
14
+ default=None,
15
+ help="Filter where the description contains this substring.",
16
+ )
17
+ @click.pass_obj
18
+ def group(
19
+ ctx: ClientContext,
20
+ filter_by_status: tuple[str, ...],
21
+ filter_by_description_like: str | None,
22
+ ) -> None:
23
+ """
24
+ List activities (active only by default).
25
+ """
26
+ render(
27
+ ctx.client.list_activities(
28
+ filter_by_status=list(filter_by_status),
29
+ filter_by_description_like=filter_by_description_like,
30
+ )
31
+ )
@@ -0,0 +1,18 @@
1
+ import click
2
+
3
+ from ajera.cli.context import ClientContext
4
+ from ajera.cli.options import status_option
5
+ from ajera.cli.output import render
6
+
7
+
8
+ @click.command(name="bank-accounts")
9
+ @status_option
10
+ @click.pass_obj
11
+ def group(
12
+ ctx: ClientContext,
13
+ filter_by_status: tuple[str, ...],
14
+ ) -> None:
15
+ """
16
+ List bank accounts (active only by default).
17
+ """
18
+ render(ctx.client.list_bank_accounts(filter_by_status=list(filter_by_status)))