diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 3d2ac0b..10f3091 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.1.0" + ".": "0.2.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index f63bd2d..1ab0ec9 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 12 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/azure/partnermax-f1de4d57d6db51b99f89340dcc6f5e6526e3b6a2fe412ac6284558b7b7519b5d.yml -openapi_spec_hash: 8b7784a9e9cab8307b6ff497331dda11 -config_hash: e0912dd3f20745785d7b0adc0bb61b3f +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/azure/partnermax-52b9912a508298f3c622752ebf2b3a23da0c923955e1c5effe76912f7ed00b44.yml +openapi_spec_hash: 549792bed17760a971f104f9268072b2 +config_hash: bf921bb2f4db038ffed15141c74cd018 diff --git a/CHANGELOG.md b/CHANGELOG.md index 8219d67..8bd52aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## 0.2.0 (2026-05-17) + +Full Changelog: [v0.1.0...v0.2.0](https://github.com/DealerMax-app/partnermax-python/compare/v0.1.0...v0.2.0) + +### Features + +* **api:** api update ([c6bbbc9](https://github.com/DealerMax-app/partnermax-python/commit/c6bbbc935dac5ffe3836ea5a1e0a05695e257564)) + + +### Chores + +* update SDK settings ([330cce0](https://github.com/DealerMax-app/partnermax-python/commit/330cce0d4b5c07c78814f22e0021e25aa9373bf6)) +* update SDK settings ([7f94790](https://github.com/DealerMax-app/partnermax-python/commit/7f94790da7d611485d22af9a3917411cdd84c9fb)) + ## 0.1.0 (2026-05-17) Full Changelog: [v0.0.2...v0.1.0](https://github.com/DealerMax-app/partnermax-python/compare/v0.0.2...v0.1.0) diff --git a/README.md b/README.md index dd9c5d9..0068947 100644 --- a/README.md +++ b/README.md @@ -145,7 +145,6 @@ nlt_settings = client.dealers.nlt_settings.update( "low_eur": 0, "medium_eur": 3000, }, - vat_treatment="private", ) print(nlt_settings.down_payment_tiers) ``` diff --git a/pyproject.toml b/pyproject.toml index 9ca1dfd..b261929 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "partnermax" -version = "0.1.0" +version = "0.2.0" description = "The official Python library for the partnermax API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/partnermax/_client.py b/src/partnermax/_client.py index c0bd379..432abce 100644 --- a/src/partnermax/_client.py +++ b/src/partnermax/_client.py @@ -153,7 +153,10 @@ def __init__( @cached_property def keys(self) -> KeysResource: - """Human session login (cookie JWT) and API key lifecycle management.""" + """API key lifecycle management — issue, list, revoke. + + The partner authenticates every request with `X-Api-Key` (preferred) or `Authorization: Bearer `; the server identifies the partner from the key and scopes all reads/writes to dealers owned by that partner. + """ from .resources.keys import KeysResource return KeysResource(self) @@ -403,7 +406,10 @@ def __init__( @cached_property def keys(self) -> AsyncKeysResource: - """Human session login (cookie JWT) and API key lifecycle management.""" + """API key lifecycle management — issue, list, revoke. + + The partner authenticates every request with `X-Api-Key` (preferred) or `Authorization: Bearer `; the server identifies the partner from the key and scopes all reads/writes to dealers owned by that partner. + """ from .resources.keys import AsyncKeysResource return AsyncKeysResource(self) @@ -567,7 +573,10 @@ def __init__(self, client: Partnermax) -> None: @cached_property def keys(self) -> keys.KeysResourceWithRawResponse: - """Human session login (cookie JWT) and API key lifecycle management.""" + """API key lifecycle management — issue, list, revoke. + + The partner authenticates every request with `X-Api-Key` (preferred) or `Authorization: Bearer `; the server identifies the partner from the key and scopes all reads/writes to dealers owned by that partner. + """ from .resources.keys import KeysResourceWithRawResponse return KeysResourceWithRawResponse(self._client.keys) @@ -588,7 +597,10 @@ def __init__(self, client: AsyncPartnermax) -> None: @cached_property def keys(self) -> keys.AsyncKeysResourceWithRawResponse: - """Human session login (cookie JWT) and API key lifecycle management.""" + """API key lifecycle management — issue, list, revoke. + + The partner authenticates every request with `X-Api-Key` (preferred) or `Authorization: Bearer `; the server identifies the partner from the key and scopes all reads/writes to dealers owned by that partner. + """ from .resources.keys import AsyncKeysResourceWithRawResponse return AsyncKeysResourceWithRawResponse(self._client.keys) @@ -609,7 +621,10 @@ def __init__(self, client: Partnermax) -> None: @cached_property def keys(self) -> keys.KeysResourceWithStreamingResponse: - """Human session login (cookie JWT) and API key lifecycle management.""" + """API key lifecycle management — issue, list, revoke. + + The partner authenticates every request with `X-Api-Key` (preferred) or `Authorization: Bearer `; the server identifies the partner from the key and scopes all reads/writes to dealers owned by that partner. + """ from .resources.keys import KeysResourceWithStreamingResponse return KeysResourceWithStreamingResponse(self._client.keys) @@ -630,7 +645,10 @@ def __init__(self, client: AsyncPartnermax) -> None: @cached_property def keys(self) -> keys.AsyncKeysResourceWithStreamingResponse: - """Human session login (cookie JWT) and API key lifecycle management.""" + """API key lifecycle management — issue, list, revoke. + + The partner authenticates every request with `X-Api-Key` (preferred) or `Authorization: Bearer `; the server identifies the partner from the key and scopes all reads/writes to dealers owned by that partner. + """ from .resources.keys import AsyncKeysResourceWithStreamingResponse return AsyncKeysResourceWithStreamingResponse(self._client.keys) diff --git a/src/partnermax/_version.py b/src/partnermax/_version.py index 4354263..8ebf0ab 100644 --- a/src/partnermax/_version.py +++ b/src/partnermax/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "partnermax" -__version__ = "0.1.0" # x-release-please-version +__version__ = "0.2.0" # x-release-please-version diff --git a/src/partnermax/resources/dealers/nlt_settings.py b/src/partnermax/resources/dealers/nlt_settings.py index 9e792a1..cd6e5fc 100644 --- a/src/partnermax/resources/dealers/nlt_settings.py +++ b/src/partnermax/resources/dealers/nlt_settings.py @@ -85,7 +85,6 @@ def update( *, agency_markup_percent: float, down_payment_tiers: DownPaymentTiersParam, - vat_treatment: Literal["private", "business"], currency: Literal["EUR"] | Omit = omit, idempotency_key: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -100,9 +99,17 @@ def update( / medium / high). Down-payment tiers MUST be in strictly ascending order. Changes propagate to the cross-network AI surfaces within five minutes. - The displayed canon for an offer is computed as: - `displayed_canon = base_canon × (1 + agency_markup_percent / 100) × adjustment_for_down_payment_tier × vat_multiplier` - where `vat_multiplier = 1.22` if `vat_treatment = "private"`, else `1.0`. + The displayed monthly canon for an offer is computed as: + + ``` + listino_imponibile = prezzo_listino / 1.22 + provvigione = listino_imponibile × (agency_markup_percent / 100) + canon = base_canon + provvigione / durata - anticipo_eur / durata + if offer.vat_treatment == "private": canon *= 1.22 + ``` + + VAT treatment is a property of each offer (`NltOfferSummary.vat_treatment`), not + of the dealer. Args: down_payment_tiers: Three down-payment scenarios shown to consumers, in strictly ascending order @@ -125,7 +132,6 @@ def update( { "agency_markup_percent": agency_markup_percent, "down_payment_tiers": down_payment_tiers, - "vat_treatment": vat_treatment, "currency": currency, }, nlt_setting_update_params.NltSettingUpdateParams, @@ -198,7 +204,6 @@ async def update( *, agency_markup_percent: float, down_payment_tiers: DownPaymentTiersParam, - vat_treatment: Literal["private", "business"], currency: Literal["EUR"] | Omit = omit, idempotency_key: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -213,9 +218,17 @@ async def update( / medium / high). Down-payment tiers MUST be in strictly ascending order. Changes propagate to the cross-network AI surfaces within five minutes. - The displayed canon for an offer is computed as: - `displayed_canon = base_canon × (1 + agency_markup_percent / 100) × adjustment_for_down_payment_tier × vat_multiplier` - where `vat_multiplier = 1.22` if `vat_treatment = "private"`, else `1.0`. + The displayed monthly canon for an offer is computed as: + + ``` + listino_imponibile = prezzo_listino / 1.22 + provvigione = listino_imponibile × (agency_markup_percent / 100) + canon = base_canon + provvigione / durata - anticipo_eur / durata + if offer.vat_treatment == "private": canon *= 1.22 + ``` + + VAT treatment is a property of each offer (`NltOfferSummary.vat_treatment`), not + of the dealer. Args: down_payment_tiers: Three down-payment scenarios shown to consumers, in strictly ascending order @@ -238,7 +251,6 @@ async def update( { "agency_markup_percent": agency_markup_percent, "down_payment_tiers": down_payment_tiers, - "vat_treatment": vat_treatment, "currency": currency, }, nlt_setting_update_params.NltSettingUpdateParams, diff --git a/src/partnermax/resources/keys.py b/src/partnermax/resources/keys.py index 7167eb9..8f9e42b 100644 --- a/src/partnermax/resources/keys.py +++ b/src/partnermax/resources/keys.py @@ -26,7 +26,10 @@ class KeysResource(SyncAPIResource): - """Human session login (cookie JWT) and API key lifecycle management.""" + """API key lifecycle management — issue, list, revoke. + + The partner authenticates every request with `X-Api-Key` (preferred) or `Authorization: Bearer `; the server identifies the partner from the key and scopes all reads/writes to dealers owned by that partner. + """ @cached_property def with_raw_response(self) -> KeysResourceWithRawResponse: @@ -88,8 +91,10 @@ def issue( The key material is returned in plaintext in the response and is never retrievable again — store it securely on - first receipt. Can be called with a cookie session (from /v1/auth/login) or with - an existing API key that has `can_issue_keys` capability. + first receipt. Must be called with an existing API key that has the + `can_issue_keys` capability (the initial key issued by DealerMAX support has + this capability by default; rotated keys inherit it unless explicitly scoped + down). Args: label: Human-readable identifier for this key, used for safe logging. @@ -159,7 +164,10 @@ def revoke( class AsyncKeysResource(AsyncAPIResource): - """Human session login (cookie JWT) and API key lifecycle management.""" + """API key lifecycle management — issue, list, revoke. + + The partner authenticates every request with `X-Api-Key` (preferred) or `Authorization: Bearer `; the server identifies the partner from the key and scopes all reads/writes to dealers owned by that partner. + """ @cached_property def with_raw_response(self) -> AsyncKeysResourceWithRawResponse: @@ -221,8 +229,10 @@ async def issue( The key material is returned in plaintext in the response and is never retrievable again — store it securely on - first receipt. Can be called with a cookie session (from /v1/auth/login) or with - an existing API key that has `can_issue_keys` capability. + first receipt. Must be called with an existing API key that has the + `can_issue_keys` capability (the initial key issued by DealerMAX support has + this capability by default; rotated keys inherit it unless explicitly scoped + down). Args: label: Human-readable identifier for this key, used for safe logging. diff --git a/src/partnermax/types/dealer_detail.py b/src/partnermax/types/dealer_detail.py index 3cd6009..392abd7 100644 --- a/src/partnermax/types/dealer_detail.py +++ b/src/partnermax/types/dealer_detail.py @@ -39,6 +39,11 @@ class DealerDetail(DealerSummary): metadata: Optional[Dict[str, str]] = None nlt_settings: Optional[NltSettings] = None + """Dealer-level NLT economics. + + VAT treatment is NOT a dealer-level field — it is a property of the offer (see + `NltOfferSummary.vat_treatment`). + """ partner_id: Optional[str] = None diff --git a/src/partnermax/types/dealers/nlt/nlt_offer_summary.py b/src/partnermax/types/dealers/nlt/nlt_offer_summary.py index b3e94bb..42bdf44 100644 --- a/src/partnermax/types/dealers/nlt/nlt_offer_summary.py +++ b/src/partnermax/types/dealers/nlt/nlt_offer_summary.py @@ -33,6 +33,13 @@ class NltOfferSummary(BaseModel): segment: Literal["A", "B", "C", "D", "E", "SUV", "VAN"] + vat_treatment: Literal["private", "business"] + """VAT treatment of this offer (not the dealer). + + `private` → `monthly_canon_from_eur` is VAT-inclusive (×1.22). `business` → + VAT-exclusive. Sourced from `nlt_offerte.solo_privati`. + """ + canonical_url: Optional[str] = None """Consumer-facing URL on the dealer's public site.""" diff --git a/src/partnermax/types/dealers/nlt_setting_update_params.py b/src/partnermax/types/dealers/nlt_setting_update_params.py index 7ab2317..ec2d5ce 100644 --- a/src/partnermax/types/dealers/nlt_setting_update_params.py +++ b/src/partnermax/types/dealers/nlt_setting_update_params.py @@ -19,8 +19,6 @@ class NltSettingUpdateParams(TypedDict, total=False): (low < medium < high). """ - vat_treatment: Required[Literal["private", "business"]] - currency: Literal["EUR"] idempotency_key: Annotated[str, PropertyInfo(alias="Idempotency-Key")] diff --git a/src/partnermax/types/dealers/nlt_settings.py b/src/partnermax/types/dealers/nlt_settings.py index 4642719..2154da0 100644 --- a/src/partnermax/types/dealers/nlt_settings.py +++ b/src/partnermax/types/dealers/nlt_settings.py @@ -10,6 +10,11 @@ class NltSettings(BaseModel): + """Dealer-level NLT economics. + + VAT treatment is NOT a dealer-level field — it is a property of the offer (see `NltOfferSummary.vat_treatment`). + """ + agency_markup_percent: float """Markup applied on top of the network base canon, in percent. Hard cap at 10%.""" @@ -25,6 +30,3 @@ class NltSettings(BaseModel): """ effective_from: datetime - - vat_treatment: Literal["private", "business"] - """private = display VAT-inclusive (×1.22). business = display VAT-exclusive.""" diff --git a/tests/api_resources/dealers/test_nlt_settings.py b/tests/api_resources/dealers/test_nlt_settings.py index 7071218..69f4d38 100644 --- a/tests/api_resources/dealers/test_nlt_settings.py +++ b/tests/api_resources/dealers/test_nlt_settings.py @@ -70,7 +70,6 @@ def test_method_update(self, client: Partnermax) -> None: "low_eur": 0, "medium_eur": 3000, }, - vat_treatment="private", ) assert_matches_type(NltSettings, nlt_setting, path=["response"]) @@ -85,7 +84,6 @@ def test_method_update_with_all_params(self, client: Partnermax) -> None: "low_eur": 0, "medium_eur": 3000, }, - vat_treatment="private", currency="EUR", idempotency_key="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) @@ -102,7 +100,6 @@ def test_raw_response_update(self, client: Partnermax) -> None: "low_eur": 0, "medium_eur": 3000, }, - vat_treatment="private", ) assert response.is_closed is True @@ -121,7 +118,6 @@ def test_streaming_response_update(self, client: Partnermax) -> None: "low_eur": 0, "medium_eur": 3000, }, - vat_treatment="private", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -143,7 +139,6 @@ def test_path_params_update(self, client: Partnermax) -> None: "low_eur": 0, "medium_eur": 3000, }, - vat_treatment="private", ) @@ -205,7 +200,6 @@ async def test_method_update(self, async_client: AsyncPartnermax) -> None: "low_eur": 0, "medium_eur": 3000, }, - vat_treatment="private", ) assert_matches_type(NltSettings, nlt_setting, path=["response"]) @@ -220,7 +214,6 @@ async def test_method_update_with_all_params(self, async_client: AsyncPartnermax "low_eur": 0, "medium_eur": 3000, }, - vat_treatment="private", currency="EUR", idempotency_key="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", ) @@ -237,7 +230,6 @@ async def test_raw_response_update(self, async_client: AsyncPartnermax) -> None: "low_eur": 0, "medium_eur": 3000, }, - vat_treatment="private", ) assert response.is_closed is True @@ -256,7 +248,6 @@ async def test_streaming_response_update(self, async_client: AsyncPartnermax) -> "low_eur": 0, "medium_eur": 3000, }, - vat_treatment="private", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -278,5 +269,4 @@ async def test_path_params_update(self, async_client: AsyncPartnermax) -> None: "low_eur": 0, "medium_eur": 3000, }, - vat_treatment="private", )