diff --git a/changelog.d/25.added.md b/changelog.d/25.added.md new file mode 100644 index 0000000..3945dc7 --- /dev/null +++ b/changelog.d/25.added.md @@ -0,0 +1 @@ +Add support for fiscal positions diff --git a/docs/managers/fiscal-position-tax-mapping.md b/docs/managers/fiscal-position-tax-mapping.md new file mode 100644 index 0000000..b52b72b --- /dev/null +++ b/docs/managers/fiscal-position-tax-mapping.md @@ -0,0 +1,162 @@ +# Fiscal Position Tax Mappings + +*Added in version 0.2.4.* + +This page documents how to use the manager and record objects +for fiscal position tax mappings. + +## Details + +| Name | Value | +|-----------------|--------------------------------| +| Odoo Modules | Accounting | +| Odoo Model Name | `account.fiscal.position.tax` | +| Manager | `fiscal_position_tax_mappings` | +| Record Type | `FiscalPositionTaxMapping` | + +## Manager + +The fiscal position tax mapping manager is available as the `fiscal_position_tax_mappings` +attribute on the Odoo client object. + +```python +>>> from openstack_odooclient import Client as OdooClient +>>> odoo_client = OdooClient( +... hostname="localhost", +... port=8069, +... protocol="jsonrpc", +... database="odoodb", +... user="test-user", +... password="", +... ) +>>> odoo_client.fiscal_position_tax_mappings.get(1234) +FiscalPositionTaxMapping(record={'id': 1234, ...}, fields=None) +``` + +For more information on how to use managers, refer to [Managers](index.md). + +## Record + +The fiscal position tax mapping manager returns `FiscalPositionTaxMapping` record objects. + +To import the record class for type hinting purposes: + +```python +from openstack_odooclient import FiscalPositionTaxMapping +``` + +The record class currently implements the following fields and methods. + +For more information on attributes and methods common to all record types, +see [Record Attributes and Methods](index.md#attributes-and-methods). + +### `company_id` + +```python +company_id: int +``` + +The ID for the [company](company.md) this fiscal position tax mapping +is associated with. + +### `company_name` + +```python +company_name: str +``` + +The name of the [company](company.md) this fiscal position tax mapping +is associated with. + +### `company` + +```python +company: Company +``` + +The [company](company.md) this fiscal position tax mapping +is associated with. + +This fetches the full record from Odoo once, +and caches it for subsequent accesses. + +### `position_id` + +```python +position_id: int +``` + +The ID for the [fiscal position](fiscal-position.md) this mapping is part of. + +### `position_name` + +```python +position_name: str +``` + +The name of the [fiscal position](fiscal-position.md) this mapping is part of. + +### `position` + +```python +position: FiscalPosition +``` + +The [fiscal position](fiscal-position.md) this mapping is part of. + +This fetches the full record from Odoo once, +and caches it for subsequent accesses. + +### `tax_src_id` + +```python +tax_src_id: int +``` + +The ID of the [tax](tax.md) to be overridden on products. + +### `tax_src_name` + +```python +tax_src_name: str +``` + +The name of the [tax](tax.md) to be overridden on products. + +### `tax_src` + +```python +tax_src: Tax +``` + +The [tax](tax.md) to be overridden on products. + +This fetches the full record from Odoo once, +and caches it for subsequent accesses. + +### `tax_dest_id` + +```python +tax_dest_id: int | None +``` + +The ID of the [tax](tax.md) to override the source tax with, if set. + +### `tax_dest_name` + +```python +tax_dest_name: str | None +``` + +The name of the [tax](tax.md) to override the source tax with, if set. + +### `tax_dest` + +```python +tax_dest: Tax | None +``` + +The [tax](tax.md) to override the source tax with, if set. + +This fetches the full record from Odoo once, +and caches it for subsequent accesses. diff --git a/docs/managers/fiscal-position.md b/docs/managers/fiscal-position.md new file mode 100644 index 0000000..d78bcd7 --- /dev/null +++ b/docs/managers/fiscal-position.md @@ -0,0 +1,119 @@ +# Fiscal Positions + +*Added in version 0.2.4.* + +This page documents how to use the manager and record objects +for fiscal positions. + +## Details + +| Name | Value | +|-----------------|---------------------------| +| Odoo Modules | Accounting, Point of Sale | +| Odoo Model Name | `account.fiscal.position` | +| Manager | `fiscal_positions` | +| Record Type | `FiscalPosition` | + +## Manager + +The fiscal position manager is available as the `fiscal_positions` +attribute on the Odoo client object. + +```python +>>> from openstack_odooclient import Client as OdooClient +>>> odoo_client = OdooClient( +... hostname="localhost", +... port=8069, +... protocol="jsonrpc", +... database="odoodb", +... user="test-user", +... password="", +... ) +>>> odoo_client.fiscal_positions.get(1234) +FiscalPosition(record={'id': 1234, ...}, fields=None) +``` + +For more information on how to use managers, refer to [Managers](index.md). + +## Record + +The fiscal position manager returns `FiscalPosition` record objects. + +To import the record class for type hinting purposes: + +```python +from openstack_odooclient import FiscalPosition +``` + +The record class currently implements the following fields and methods. + +For more information on attributes and methods common to all record types, +see [Record Attributes and Methods](index.md#attributes-and-methods). + +### `active` + +```python +active: bool +``` + +Whether or not this fiscal position is active (enabled). + +### `company_id` + +```python +company_id: int +``` + +The ID for the [company](company.md) this fiscal position is associated with. + +### `company_name` + +```python +company_name: str +``` + +The name of the [company](company.md) this fiscal position is associated with. + +### `company` + +```python +company: Company +``` + +The [company](company.md) this fiscal position is associated with. + +This fetches the full record from Odoo once, +and caches it for subsequent accesses. + +### `name` + +```python +name: str +``` + +The name of the fiscal position. + +Not guaranteed to be unique. + +### `tax_ids` + +```python +tax_ids: list[int] +``` + +The list of IDs for the [tax mappings](fiscal-position-tax-mapping.md) that will be applied +to sale orders and invoices for partners using this +fiscal position. + +### `taxes` + +```python +taxes: list[FiscalPositionTaxMapping] +``` + +The list of [tax mappings](fiscal-position-tax-mapping.md) that will be applied +to sale orders and invoices for partners using this +fiscal position. + +This fetches the full records from Odoo once, +and caches them for subsequent accesses. diff --git a/docs/managers/partner.md b/docs/managers/partner.md index 17ff4b8..a456823 100644 --- a/docs/managers/partner.md +++ b/docs/managers/partner.md @@ -307,6 +307,39 @@ if it has a parent. This fetches the full record from Odoo once, and caches it for subsequent accesses. +### `property_account_position_id` + +```python +property_account_position_id: int | None +``` + +The ID for the [fiscal position](fiscal-position.md) this partner uses, if it uses one. + +*Added in version 0.2.4.* + +### `property_account_position_name` + +```python +property_account_position_name: str | None +``` + +The name of the [fiscal position](fiscal-position.md) this partner uses, if it uses one. + +*Added in version 0.2.4.* + +### `property_account_position` + +```python +property_account_position: FiscalPosition | None +``` + +The [fiscal position](fiscal-position.md) this partner uses, if it uses one. + +This fetches the full record from Odoo once, +and caches it for subsequent accesses. + +*Added in version 0.2.4.* + ### `property_product_pricelist_id` ```python diff --git a/mkdocs.yml b/mkdocs.yml index 30fcc52..3d99159 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -41,6 +41,8 @@ nav: - managers/credit-transaction.md - managers/currency.md - managers/customer-group.md + - managers/fiscal-position.md + - managers/fiscal-position-tax-mapping.md - managers/grant.md - managers/grant-type.md - managers/partner.md diff --git a/openstack_odooclient/__init__.py b/openstack_odooclient/__init__.py index 07ddd3d..521abd6 100644 --- a/openstack_odooclient/__init__.py +++ b/openstack_odooclient/__init__.py @@ -44,6 +44,11 @@ from .managers.credit_type import CreditType, CreditTypeManager from .managers.currency import Currency, CurrencyManager from .managers.customer_group import CustomerGroup, CustomerGroupManager +from .managers.fiscal_position import FiscalPosition, FiscalPositionManager +from .managers.fiscal_position_tax_mapping import ( + FiscalPositionTaxMapping, + FiscalPositionTaxMappingManager, +) from .managers.grant import Grant, GrantManager from .managers.grant_type import GrantType, GrantTypeManager from .managers.partner import Partner, PartnerManager @@ -108,6 +113,10 @@ "CustomerGroup", "CustomerGroupManager", "FieldAlias", + "FiscalPosition", + "FiscalPositionManager", + "FiscalPositionTaxMapping", + "FiscalPositionTaxMappingManager", "Grant", "GrantManager", "GrantType", diff --git a/openstack_odooclient/client.py b/openstack_odooclient/client.py index c68149e..4dda6dc 100644 --- a/openstack_odooclient/client.py +++ b/openstack_odooclient/client.py @@ -25,6 +25,10 @@ from .managers.credit_type import CreditTypeManager from .managers.currency import CurrencyManager from .managers.customer_group import CustomerGroupManager +from .managers.fiscal_position import FiscalPositionManager +from .managers.fiscal_position_tax_mapping import ( + FiscalPositionTaxMappingManager, +) from .managers.grant import GrantManager from .managers.grant_type import GrantTypeManager from .managers.partner import PartnerManager @@ -112,6 +116,12 @@ class Client(ClientBase): customer_groups: CustomerGroupManager """OpenStack customer group manager.""" + fiscal_positions: FiscalPositionManager + """Fiscal position manager.""" + + fiscal_position_tax_mappings: FiscalPositionTaxMappingManager + """Fiscal position tax mapping manager.""" + grants: GrantManager """OpenStack grant manager.""" diff --git a/openstack_odooclient/managers/fiscal_position.py b/openstack_odooclient/managers/fiscal_position.py new file mode 100644 index 0000000..b73ebac --- /dev/null +++ b/openstack_odooclient/managers/fiscal_position.py @@ -0,0 +1,75 @@ +# Copyright (C) 2026 Catalyst Cloud Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +from typing import Annotated + +from ..base.record.base import RecordBase +from ..base.record.types import ModelRef +from ..base.record_manager.base import RecordManagerBase + + +class FiscalPosition(RecordBase["FiscalPositionManager"]): + active: bool + """Whether or not this fiscal position is active (enabled).""" + + company_id: Annotated[int, ModelRef("company_id", Company)] + """The ID for the company this fiscal position is associated with.""" + + company_name: Annotated[str, ModelRef("company_id", Company)] + """The name of the company this fiscal position is associated with.""" + + company: Annotated[Company, ModelRef("company_id", Company)] + """The company this fiscal position is associated with. + + This fetches the full record from Odoo once, + and caches it for subsequent accesses. + """ + + name: str + """The name of the fiscal position. + + Not guaranteed to be unique. + """ + + tax_ids: Annotated[ + list[int], + ModelRef("tax_ids", FiscalPositionTaxMapping), + ] + """The list of IDs for the tax mappings that will be applied to + sale orders and invoices for partners using this fiscal position. + """ + + taxes: Annotated[ + list[FiscalPositionTaxMapping], + ModelRef("tax_ids", FiscalPositionTaxMapping), + ] + """The list of tax mappings that will be applied to + sale orders and invoices for partners using this fiscal position. + + This fetches the full records from Odoo once, + and caches them for subsequent accesses. + """ + + +class FiscalPositionManager(RecordManagerBase[FiscalPosition]): + env_name = "account.fiscal.position" + record_class = FiscalPosition + + +# NOTE(callumdickinson): Import here to make sure circular imports work. +from .company import Company # noqa: E402 +from .fiscal_position_tax_mapping import FiscalPositionTaxMapping # noqa: E402 diff --git a/openstack_odooclient/managers/fiscal_position_tax_mapping.py b/openstack_odooclient/managers/fiscal_position_tax_mapping.py new file mode 100644 index 0000000..650aad3 --- /dev/null +++ b/openstack_odooclient/managers/fiscal_position_tax_mapping.py @@ -0,0 +1,97 @@ +# Copyright (C) 2026 Catalyst Cloud Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +from typing import Annotated + +from ..base.record.base import RecordBase +from ..base.record.types import ModelRef +from ..base.record_manager.base import RecordManagerBase + + +class FiscalPositionTaxMapping(RecordBase["FiscalPositionTaxMappingManager"]): + company_id: Annotated[int, ModelRef("company_id", Company)] + """The ID for the company this fiscal position tax mapping + is associated with. + """ + + company_name: Annotated[str, ModelRef("company_id", Company)] + """The name of the company this fiscal position tax mapping + is associated with. + """ + + company: Annotated[Company, ModelRef("company_id", Company)] + """The company this fiscal position tax mapping + is associated with. + + This fetches the full record from Odoo once, + and caches it for subsequent accesses. + """ + + position_id: Annotated[int, ModelRef("position_id", FiscalPosition)] + """The ID for the fiscal position this mapping is part of.""" + + position_name: Annotated[str, ModelRef("position_id", FiscalPosition)] + """The name of the fiscal position this mapping is part of.""" + + position: Annotated[ + FiscalPosition, + ModelRef("position_id", FiscalPosition), + ] + """The fiscal position this mapping is part of. + + This fetches the full record from Odoo once, + and caches it for subsequent accesses. + """ + + tax_src_id: Annotated[int, ModelRef("tax_src_id", Tax)] + """The ID of the tax to be overridden on products.""" + + tax_src_name: Annotated[str, ModelRef("tax_src_id", Tax)] + """The name of the tax to be overridden on products.""" + + tax_src: Annotated[Tax, ModelRef("tax_src_id", Tax)] + """The tax to be overridden on products. + + This fetches the full record from Odoo once, + and caches it for subsequent accesses. + """ + + tax_dest_id: Annotated[int | None, ModelRef("tax_dest_id", Tax)] + """The ID of the tax to override the source tax with, if set.""" + + tax_dest_name: Annotated[str | None, ModelRef("tax_dest_id", Tax)] + """The name of the tax to override the source tax with, if set.""" + + tax_dest: Annotated[Tax | None, ModelRef("tax_dest_id", Tax)] + """The tax to override the source tax with, if set. + + This fetches the full record from Odoo once, + and caches it for subsequent accesses. + """ + + +class FiscalPositionTaxMappingManager( + RecordManagerBase[FiscalPositionTaxMapping], +): + env_name = "account.fiscal.position.tax" + record_class = FiscalPositionTaxMapping + + +# NOTE(callumdickinson): Import here to make sure circular imports work. +from .company import Company # noqa: E402 +from .fiscal_position import FiscalPosition # noqa: E402 +from .tax import Tax # noqa: E402 diff --git a/openstack_odooclient/managers/partner.py b/openstack_odooclient/managers/partner.py index a8a76c8..fcfab34 100644 --- a/openstack_odooclient/managers/partner.py +++ b/openstack_odooclient/managers/partner.py @@ -215,6 +215,36 @@ class Partner(RecordBase["PartnerManager"]): and caches it for subsequent accesses. """ + property_account_position_id: Annotated[ + int | None, + ModelRef("property_account_position_id", FiscalPosition), + ] + """The ID for the fiscal position this partner uses, if it uses one. + + *Added in version 0.2.4.* + """ + + property_account_position_name: Annotated[ + str | None, + ModelRef("property_account_position_id", FiscalPosition), + ] + """The name of the fiscal position this partner uses, if it uses one. + + *Added in version 0.2.4.* + """ + + property_account_position: Annotated[ + FiscalPosition | None, + ModelRef("property_account_position_id", FiscalPosition), + ] + """The fiscal position this partner uses, if it uses one. + + This fetches the full record from Odoo once, + and caches it for subsequent accesses. + + *Added in version 0.2.4.* + """ + property_product_pricelist_id: Annotated[ int | None, ModelRef("property_product_pricelist", Pricelist), @@ -281,6 +311,7 @@ class PartnerManager(RecordManagerBase[Partner]): # NOTE(callumdickinson): Import here to make sure circular imports work. from .company import Company # noqa: E402 from .customer_group import CustomerGroup # noqa: E402 +from .fiscal_position import FiscalPosition # noqa: E402 from .pricelist import Pricelist # noqa: E402 from .project import Project # noqa: E402 from .project_contact import ProjectContact # noqa: E402