Repository navigation
Conversation
Migrate packaging from setup.py/setup.cfg to a PEP 621 pyproject.toml with the hatchling backend, and adopt uv for dependency/environment management. Remove Pipfile, Pipfile.lock, requirements.txt, tox.ini, MANIFEST.in and the dead .pyup.yml. Drop Python 2 support (requires-python >=3.9) and refresh the locked dependencies so transitive packages (urllib3 etc.) resolve to current, non-vulnerable versions. Make the test suite runnable offline: the live integration tests now skip unless PROCOUNTOR_RUN_INTEGRATION=1 and credentials are present, and a new mocked unit-test suite covers client URL/header/token/response logic and api_methods delegation (43 passing, 41 skipped). Add a GitHub Actions CI workflow (ruff + pytest on Python 3.9-3.13) and modernize the PyPI publish workflow to build with uv. Add the previously empty LICENSE (MIT). Minor hardening: fix a bare except, a dict string-concatenation in _get_token's error path, and two test modules that called unittest.main() without importing unittest. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Non-breaking follow-ups to the tooling modernization: * docs/source/conf.py now reads the version from the package instead of a hardcoded 0.0.1. * CONTRIBUTING.rst updated for the uv-based workflow (drop virtualenv/setup.py/ tox/travis/bumpversion references, Python 2.7/3.6 support note). * Replace the mutable default argument in Client._create_endpoint with a None sentinel. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Client.__init__ stored the API key as a 1-tuple (`(api_key,)`), almost certainly an accidental trailing comma. The form-encoded token request happened to produce identical output, so this is wire-identical, but the public `client.api_key` attribute is now the string it is documented to be. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Fix eight write methods (update_company, update_user, update_business_partner, update_dimension, create_dimension_item, update_dimension_item, post_payment, payments_direct_bank_transfers) that passed the request body into request()'s `headers` positional argument, so no JSON body was ever sent. They now forward the body as keyword arguments like the other write methods. This changes what these methods send on the wire and is covered by new regression tests. Add type hints across the package and a py.typed marker so the types are exposed to downstream users (PEP 561). To make the mixin types honest without TYPE_CHECKING stubs, the HTTP transport/authentication is extracted into a new procountor.transport.BaseClient that ApiMethods now inherits from; the public procountor.client.Client (and all its methods/attributes) is unchanged, and Client is additionally exported from the top-level package. Wire mypy + types-requests into the dev dependencies and the CI lint job, and update the package author/maintainer metadata. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* get_payments queried the "invoices" endpoint instead of "payments". * create_dimension_item, update_dimension_item and confirm_invoice had a leading slash in the endpoint path, producing a double slash in the URL. * Add regression tests for all three and a Usage/Installation section to the README. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Procountor API exposes ~192 operations; the named helpers cover only a
subset. Rather than hand-maintain wrappers that drift from the spec, add
generic verbs on the transport layer so any endpoint is reachable while the
client keeps handling auth and token refresh:
client.get("payrolls/salaryslips", salaryPeriodId=123)
client.post("products", json={"name": "Widget"})
client.put("invoices/123/notes", json={"note": "ok"})
client.delete("attachments/42")
request() now accepts an explicit `json` body so top-level JSON arrays can be
sent (the kwargs path can only build an object).
Three methods target endpoints that no longer exist in the current API
(pay_invoice -> /invoices/pay, send_one_time_pass -> /users/otp,
update_dimension -> PUT /dimensions); they now emit a DeprecationWarning.
Document the generic verbs and the supported/latest/dated api_version cycle in
the README, pointing at https://dev.procountor.com/.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Convert the 60 simple endpoint helpers to call get/post/put/delete instead of
request() directly, making it explicit that they are thin convenience wrappers.
This is wire-identical: get(endpoint) == request("GET", endpoint) and
post(endpoint, json=data) == request("POST", endpoint, **data).
post_attachment (multipart upload with custom headers) and
get_invoice_paymentevents (sends a GET body) keep calling request() directly to
preserve their exact behaviour.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
get_bank_statements now accepts date/datetime objects (or strings) for its date-only startDate/endDate parameters, formatting them to yyyy-MM-dd via a small _as_date_string helper. The conversion lives in the named wrapper, not the transport layer, on purpose: the correct format is per-parameter (some Procountor fields are date-only, others are full datetime), so only the method that knows a field's semantics should normalise it. A blanket transport-level conversion would silently send a full datetime to a date-only field. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Procountor removed PUT /dimensions (around version 25.11); updating a dimension
is now PUT /dimensions/{dimensionId}. Give update_dimension a dimensionId
argument and point it at the correct endpoint, replacing the earlier
deprecation. Signature change is acceptable for the upcoming 3.0.0.
The default-dimension endpoints added in 25.11
(/products|businesspartners|persons/{id}/defaults/dimensions[/{dimensionId}])
are intentionally left to the generic verbs rather than dedicated helpers.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Per the Procountor OpenAPI description, previousId, orderById, size and page on
GET /invoices/{invoiceId}/paymentevents are query parameters. The method was
sending them as a JSON body on the GET request; build them into the query
string instead. post_attachment is now the only method that calls request()
directly (multipart upload).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
bump-my-version creates an annotated tag, so the commit and tag can be pushed together with `git push --follow-tags` instead of separate `git push` and `git push --tags` commands. Clarify the release steps in CONTRIBUTING.rst and README.rst. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
astral-sh/setup-uv publishes major aliases only up to v7; v8 exists only as exact tags (v8.2.0). Pin the exact version so the workflows resolve. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
samaij
approved these changes
Jun 11, 2026
Replace the "TODO: This text" placeholder docstring on _handleResponse with a proper description of the returned dict shape. Remove the long commented-out, never-finished test_invoice draft in test_010_invoices.py (it no longer matches the current response shape and references a now-deprecated method); git history keeps it. The remaining "write tests for X" markers are left as they flag real missing integration tests. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This is a large maintenance pass to bring the package up to date and keep it healthy. It is backwards compatible for the public surface (
from procountor.client import Client, the constructor, all method names, properties, and attributes are unchanged) except for a handful of intentional correctness fixes that send different (correct) data on the wire — these are why this is slated for a 3.0.0 release.No version bump or release is included here; that's a follow-up once this is reviewed.
Tooling / packaging
setup.py/setup.cfg/Pipfile/requirements.txt/tox.ini→ a single PEP 621pyproject.toml(hatchling backend), and adopted uv for env/lock management. Removed the dead.pyup.ymlandMANIFEST.in.LICENSE(MIT).Tests
PROCOUNTOR_RUN_INTEGRATION=1(+ credentials), and a new mocked unit-test suite is the CI gate. 67 unit tests pass, 41 integration skip.Code quality
py.typedmarker (PEP 561). To keep the types honest, the HTTP transport/auth was extracted intoprocountor.transport.BaseClient, whichApiMethodsnow inherits (MRO:Client → ApiMethods → BaseClient).Clientis now importable from the top level:from procountor import Client.Generic verbs (the strategic bit)
The Procountor API has ~192 operations; the named helpers cover ~58. Rather than hand-maintain wrappers that drift from the spec, added generic
get/post/put/deleteverbs so any endpoint is reachable while the client keeps handling auth + token refresh:request()also gained an explicitjsonbody param (so top-level JSON arrays work). The named helpers were converted to thin wrappers over these verbs (wire-identical) to make it obvious they're just convenience.Bug fixes (these change what is sent on the wire)
update_company,update_user,update_business_partner,create_dimension_item,update_dimension_item,post_payment,payments_direct_bank_transfers,put_products_to_bank_statement) passed the body intorequest()'sheadersarg, so no JSON body was ever sent. Fixed + regression tests.get_paymentsqueried/invoicesinstead of/payments.get_invoice_paymenteventssent its args as a GET JSON body; they are query params (previousId,orderById,size,page).create_dimension_item,update_dimension_item,confirm_invoice) produced double-slash URLs.update_dimensionnow takes adimensionIdand targetsPUT /dimensions/{id}(Procountor removedPUT /dimensionsaround 25.11).api_keywas stored as a stray 1-tuple; now the documented string (wire-identical).Deprecations
pay_invoiceandsend_one_time_passnow emitDeprecationWarning— their endpoints (/invoices/pay,/users/otp) no longer exist in the current API. They still function.Ergonomics
get_bank_statementsacceptsdate/datetime(or strings) for its date-only params. Date normalization deliberately lives in the named wrapper, not the transport — the correct format is per-parameter, so only the method that knows a field's semantics should convert it.Docs
supported/latest/datedapi_versioncycle, pointing at https://dev.procountor.com/.git push --follow-tags.🤖 Generated with Claude Code