diff --git a/changelog.d/12.added.md b/changelog.d/12.added.md new file mode 100644 index 0000000..0cebcb3 --- /dev/null +++ b/changelog.d/12.added.md @@ -0,0 +1 @@ +Add/fix missing/incorrect field definitions for records diff --git a/docs/managers/product.md b/docs/managers/product.md index 0dc38c4..ecdca41 100644 --- a/docs/managers/product.md +++ b/docs/managers/product.md @@ -254,6 +254,16 @@ 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 product is active (enabled). + +*Added in version 0.2.0.* + ### `categ_id` ```python @@ -311,14 +321,15 @@ and caches it for subsequent accesses. ### `default_code` ```python -default_code: str +default_code: str | Literal[False] ``` -The Default Code for this product. +The Default Code for this product, if set. In the OpenStack Integration add-on, this is used to store the rated unit for the service product. +*Changed in version 0.2.0*: Made `default_code` optional. ### `description` @@ -346,7 +357,6 @@ The list price of the product. This becomes the unit price of the product on invoices. - ### `name` ```python @@ -355,6 +365,16 @@ name: str The name of the product. +### `sale_ok` + +```python +sale_ok: bool +``` + +Whether or not this product is sellable. + +*Added in version 0.2.0.* + ### `uom_id` ```python diff --git a/docs/managers/sale-order.md b/docs/managers/sale-order.md index 7d12bc0..8f8c169 100644 --- a/docs/managers/sale-order.md +++ b/docs/managers/sale-order.md @@ -35,6 +35,39 @@ For more information on how to use managers, refer to [Managers](index.md). The following manager methods are also available, in addition to the standard methods. +### `action_cancel` + +```python +action_cancel( + sale_order: int | SaleOrder, +) -> None +``` + +Cancel the given sale order. + +```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.sale_orders.action_cancel( +... sale_order=1234, # ID or object +... ) +``` + +*Added in version 0.2.0.* + +#### Parameters + +| Name | Type | Description | Default | +|--------------|-------------------|--------------------------|------------| +| `sale_order` | `int | SaleOrder` | The sale order to cancel | (required) | + ### `action_confirm` ```python @@ -187,6 +220,39 @@ display_name: str The display name of the sale order. +### `invoice_count` + +```python +invoice_count: int +``` + +The number of [invoices (account moves)](account-move.md) generated from the sale order. + +*Added in version 0.2.0.* + +### `invoice_ids` + +```python +invoice_ids: list[int] +``` + +A list of IDs for [invoices (account moves)](account-move.md) generated from the sale order. + +*Added in version 0.2.0.* + +### `invoices` + +```python +invoices: list[AccountMove] +``` + +The [invoices (account moves)](account-move.md) generated from the sale order. + +This fetches the full records from Odoo once, +and caches them for subsequent accesses. + +*Added in version 0.2.0.* + ### `invoice_status` ```python @@ -337,6 +403,30 @@ Values: * ``done`` - Finalised and settled sale order, cannot be modified * ``cancel`` - Cancelled sale order, can be deleted in most cases +### `action_cancel` + +```python +action_cancel() -> None +``` + +Cancel this sale order. + +```python +>>> from openstack_odooclient import Client as OdooClient +>>> odoo_client = OdooClient( +... hostname="localhost", +... port=8069, +... protocol="jsonrpc", +... database="odoodb", +... user="test-user", +... password="", +... ) +>>> sale_order = odoo_client.sale_orders.get(1234) +>>> sale_order.action_cancel() +``` + +*Added in version 0.2.0.* + ### `action_confirm` ```python diff --git a/openstack_odooclient/base/record.py b/openstack_odooclient/base/record.py index b97fa79..8be05c4 100644 --- a/openstack_odooclient/base/record.py +++ b/openstack_odooclient/base/record.py @@ -19,7 +19,7 @@ from dataclasses import dataclass from datetime import date, datetime -from types import MappingProxyType +from types import MappingProxyType, UnionType from typing import ( TYPE_CHECKING, Annotated, @@ -392,7 +392,8 @@ def _getattr_model_ref( # The following is for decoding a singular model ref value. # Check if the model ref is optional, and if it is, # return the desired value for when the value is empty. - if get_type_origin(attr_type) is Union: + attr_type_origin = get_type_origin(attr_type) + if attr_type_origin is Union or attr_type_origin is UnionType: unsupported_union = ( "Only unions of the format Optional[T], " "Union[T, type(None)] or Union[T, Literal[False]] " diff --git a/openstack_odooclient/base/record_manager.py b/openstack_odooclient/base/record_manager.py index b2afded..25b4019 100644 --- a/openstack_odooclient/base/record_manager.py +++ b/openstack_odooclient/base/record_manager.py @@ -360,7 +360,7 @@ def get( :param optional: Return ``None`` if not found, defaults to ``False`` :raises RecordNotFoundError: Record with the given ID not found :return: List of records - :rtype: Record | list[str, Any] + :rtype: Record | dict[str, Any] """ try: return self.list( diff --git a/openstack_odooclient/managers/product.py b/openstack_odooclient/managers/product.py index d57eaae..032c6ed 100644 --- a/openstack_odooclient/managers/product.py +++ b/openstack_odooclient/managers/product.py @@ -27,6 +27,12 @@ class Product(RecordBase["ProductManager"]): + active: bool + """Whether or not this product is active (enabled). + + *Added in version 0.2.0.* + """ + categ_id: Annotated[int, ModelRef("categ_id", ProductCategory)] """The ID for the category this product is under.""" @@ -53,11 +59,13 @@ class Product(RecordBase["ProductManager"]): and caches it for subsequent accesses. """ - default_code: str - """The Default Code for this product. + default_code: str | Literal[False] + """The Default Code for this product, if set. In the OpenStack Integration add-on, this is used to store the rated unit for the service product. + + *Changed in version 0.2.0*: Made `default_code` optional. """ description: str @@ -75,6 +83,12 @@ class Product(RecordBase["ProductManager"]): name: str """The name of the product.""" + sale_ok: bool + """Whether or not this product is sellable. + + *Added in version 0.2.0.* + """ + uom_id: Annotated[int, ModelRef("uom_id", Uom)] """The ID for the Unit of Measure for this product.""" diff --git a/openstack_odooclient/managers/sale_order.py b/openstack_odooclient/managers/sale_order.py index a2ee550..15f1830 100644 --- a/openstack_odooclient/managers/sale_order.py +++ b/openstack_odooclient/managers/sale_order.py @@ -54,6 +54,30 @@ class SaleOrder(RecordBase["SaleOrderManager"]): display_name: str """The display name of the sale order.""" + invoice_count: int + """The number of invoices generated from the sale order. + + *Added in version 0.2.0.* + """ + + invoice_ids: Annotated[list[int], ModelRef("invoice_ids", AccountMove)] + """A list of IDs for invoices generated from the sale order. + + *Added in version 0.2.0.* + """ + + invoices: Annotated[ + list[AccountMove], + ModelRef("invoice_ids", AccountMove), + ] + """The invoices generated from the sale order. + + This fetches the full records from Odoo once, + and caches them for subsequent accesses. + + *Added in version 0.2.0.* + """ + invoice_status: Literal["no", "to invoice", "invoiced", "upselling"] """The current invoicing status of this sale order. @@ -145,6 +169,13 @@ class SaleOrder(RecordBase["SaleOrderManager"]): * ``cancel`` - Cancelled sale order, can be deleted in most cases """ + def action_cancel(self) -> None: + """Cancel this sale order. + + *Added in version 0.2.0.* + """ + self._env.action_cancel(self.id) + def action_confirm(self) -> None: """Confirm this sale order.""" self._env.action_confirm(self.id) @@ -158,6 +189,22 @@ class SaleOrderManager(NamedRecordManagerBase[SaleOrder]): env_name = "sale.order" record_class = SaleOrder + def action_cancel(self, sale_order: int | SaleOrder) -> None: + """Cancel the given sale order. + + *Added in version 0.2.0.* + + :param sale_order: The sale order to cancel + :type sale_order: int | SaleOrder + """ + self._env.action_cancel( + ( + sale_order.id + if isinstance(sale_order, SaleOrder) + else sale_order + ), + ) + def action_confirm(self, sale_order: int | SaleOrder) -> None: """Confirm the given sale order. @@ -188,6 +235,7 @@ def create_invoices(self, sale_order: int | SaleOrder) -> None: # NOTE(callumdickinson): Import here to avoid circular imports. +from .account_move import AccountMove # noqa: E402 from .currency import Currency # noqa: E402 from .partner import Partner # noqa: E402 from .project import Project # noqa: E402