Skip to content

katana_public_api_client.domain.base

katana_public_api_client.domain.base

Base domain model for Katana entities.

This module provides the foundation for Pydantic domain models that represent business entities from the Katana Manufacturing ERP system.

Domain models are separate from the generated API request/response models and are optimized for: - ETL and data processing - Business logic - Data validation - JSON schema generation - Clean, ergonomic APIs

Classes

KatanaBaseModel

Bases: BaseModel

Base class for all Pydantic domain models.

Provides: - Immutability by default (frozen=True) - Automatic validation - JSON schema generation - Easy serialization for ETL - Common timestamp fields

Example
class ProductDomain(KatanaBaseModel):
    id: int
    name: str
    sku: str

    @computed_field
    @property
    def display_name(self) -> str:
        return f"{self.name} ({self.sku})"
Functions
model_dump_for_etl()

Export to ETL-friendly format.

Removes None values and uses field aliases for cleaner output.

Returns:

  • dict[str, Any]

    Dictionary with all non-None fields

Example
variant = KatanaVariant(id=123, sku="ABC-001", sales_price=99.99)
data = variant.model_dump_for_etl()
# {"id": 123, "sku": "ABC-001", "sales_price": 99.99}
Source code in katana_public_api_client/domain/base.py
def model_dump_for_etl(self) -> dict[str, Any]:
    """Export to ETL-friendly format.

    Removes None values and uses field aliases for cleaner output.

    Returns:
        Dictionary with all non-None fields

    Example:
        ```python
        variant = KatanaVariant(id=123, sku="ABC-001", sales_price=99.99)
        data = variant.model_dump_for_etl()
        # {"id": 123, "sku": "ABC-001", "sales_price": 99.99}
        ```
    """
    return self.model_dump(exclude_none=True, by_alias=True)
to_dict_with_computed()

Export including computed fields.

Unlike model_dump(), this includes @computed_field properties.

Returns:

  • dict[str, Any]

    Dictionary with all fields including computed ones

Source code in katana_public_api_client/domain/base.py
def to_dict_with_computed(self) -> dict[str, Any]:
    """Export including computed fields.

    Unlike model_dump(), this includes @computed_field properties.

    Returns:
        Dictionary with all fields including computed ones
    """
    # Pydantic v2 automatically includes computed fields in model_dump
    return self.model_dump(mode="python", exclude_none=True)
to_warehouse_json()

Export as JSON for data warehouse.

Returns:

  • str

    JSON string with all non-None fields

Example
variant = KatanaVariant(id=123, sku="ABC-001")
json_str = variant.to_warehouse_json()
# '{"id":123,"sku":"ABC-001"}'
Source code in katana_public_api_client/domain/base.py
def to_warehouse_json(self) -> str:
    """Export as JSON for data warehouse.

    Returns:
        JSON string with all non-None fields

    Example:
        ```python
        variant = KatanaVariant(id=123, sku="ABC-001")
        json_str = variant.to_warehouse_json()
        # '{"id":123,"sku":"ABC-001"}'
        ```
    """
    return self.model_dump_json(exclude_none=True, by_alias=True)

Functions

decimal_str_to_float(value)

Coalesce a Katana decimal field to float.

Several Katana read fields arrive as fixed-precision decimal strings (e.g. "12.00000000000") rather than JSON numbers. Domain models expose them as float | None; this normalizes the string (or an already-numeric int/float) to float. None and the empty string (which the wire occasionally uses for "no value") map to None; this mirrors tool_result_utils.float_or_none so the two layers agree.

Source code in katana_public_api_client/domain/base.py
def decimal_str_to_float(value: str | int | float | None) -> float | None:
    """Coalesce a Katana decimal field to ``float``.

    Several Katana read fields arrive as fixed-precision decimal **strings**
    (e.g. ``"12.00000000000"``) rather than JSON numbers. Domain models expose
    them as ``float | None``; this normalizes the string (or an already-numeric
    ``int``/``float``) to ``float``. ``None`` and the empty string (which the
    wire occasionally uses for "no value") map to ``None``; this mirrors
    ``tool_result_utils.float_or_none`` so the two layers agree.
    """
    if value is None or value == "":
        return None
    return float(value)