easybill_client/generated/async/easybill_generated_async/models/position.py
claudi caacb339dd Add unit tests for authentication and webhook parsing
- Implement tests for basic and bearer authentication headers in `test_auth.py`.
- Create tests for the `EasybillWebhookParser` in `test_webhooks.py`, covering JSON and form-encoded payloads, as well as a generic parse and acknowledgement method.
2026-04-17 10:20:12 +02:00

287 lines
16 KiB
Python

# coding: utf-8
"""
easybill REST API
The first version of the easybill REST API. [CHANGELOG](https://api.easybill.de/rest/v1/CHANGELOG.md) ## Authentication You can choose between two available methods: `Basic Auth` or `Bearer Token`. In each HTTP request, one of the following HTTP headers is required: ``` # Basic Auth Authorization: Basic base64_encode('<email>:<api_key>') # Bearer Token Authorization: Bearer <api_key> ``` ## Limitations ### Request Limit * PLUS: 10 requests per minute * BUSINESS: 60 requests per minute If the limit is exceeded, you will receive the HTTP error: `429 Too Many Requests` ### Result Limit All result lists are limited to 100 by default. This limit can be increased by the query parameter `limit` to a maximum of 1000. ## Query filter Many list resources can be filtered. In `/documents` you can filter e.g. by number with `/documents?number=111028654`. If you want to filter multiple numbers, you can either enter them separated by commas `/documents?number=111028654,222006895` or as an array `/documents?number[]=111028654&number[]=222006895`. **Warning**: The maximum size of an HTTP request line in bytes is 4094. If this limit is exceeded, you will receive the HTTP error: `414 Request-URI Too Large` ### Escape commas in query You can escape commans in query `name=Patrick\\, Peter` if you submit the header `X-Easybill-Escape: true` in your request. ## Property login_id This is the login of your admin or employee account. ## Date and Date-Time format Please use the timezone `Europe/Berlin`. * **date** = *Y-m-d* = `2016-12-31` * **date-time** = *Y-m-d H:i:s* = `2016-12-31 03:13:37` Date or datetime can be `null` because the attributes have been added later and the entry is older.
The version of the OpenAPI document: 1.96.0
Generated by OpenAPI Generator (https://openapi-generator.tech)
Do not edit the class manually.
""" # noqa: E501
from __future__ import annotations
import pprint
import re # noqa: F401
import json
from pydantic import BaseModel, ConfigDict, Field, StrictBool, StrictFloat, StrictInt, StrictStr, field_validator
from typing import Any, ClassVar, Dict, List, Optional, Union
from easybill_generated_async.models.position_export_identifier_extended import PositionExportIdentifierExtended
from typing import Optional, Set
from typing_extensions import Self
from pydantic_core import to_jsonable_python
class Position(BaseModel):
"""
Position
""" # noqa: E501
id: Optional[StrictInt] = None
type: Optional[StrictStr] = 'PRODUCT'
number: StrictStr
description: StrictStr = Field(description="The positions name or description")
document_note: Optional[StrictStr] = Field(default=None, description="This field can be used in the document text areas with the liquid placeholder {{document.item_notes}}. Every note is only displayed once for every kind of product. This is useful if you want to add something like an instruction.")
note: Optional[StrictStr] = Field(default='null', description="Note for internal use")
unit: Optional[StrictStr] = 'null'
export_identifier: Optional[StrictStr] = Field(default='null', description="The FAS-Account is the four-digit revenue account, in which the revenue will be entered when doing the export to your tax consultant. In case you want to split your revenue to several revenue accounts, please talk to your tax consultant before, to guarantee an unobstructed use of the interface. For every revenue element, there are number ranges, which can be used. Please avoid using combinations of numbers, which can not be used by your tax consultant.")
export_identifier_extended: Optional[PositionExportIdentifierExtended] = None
login_id: Optional[StrictInt] = None
price_type: Optional[StrictStr] = 'NETTO'
vat_percent: Optional[Union[StrictFloat, StrictInt]] = 19.0
sale_price: Union[StrictFloat, StrictInt] = Field(description="Price in cents (e.g. \"150\" = 1.50€)")
sale_price2: Optional[Union[StrictFloat, StrictInt]] = Field(default=None, description="Price for customers of group 2 in cents (e.g. \"150\" = 1.50€)")
sale_price3: Optional[Union[StrictFloat, StrictInt]] = Field(default=None, description="Price for customers of group 3 in cents (e.g. \"150\" = 1.50€)")
sale_price4: Optional[Union[StrictFloat, StrictInt]] = Field(default=None, description="Price for customers of group 4 in cents (e.g. \"150\" = 1.50€)")
sale_price5: Optional[Union[StrictFloat, StrictInt]] = Field(default=None, description="Price for customers of group 5 in cents (e.g. \"150\" = 1.50€)")
sale_price6: Optional[Union[StrictFloat, StrictInt]] = Field(default=None, description="Price for customers of group 6 in cents (e.g. \"150\" = 1.50€)")
sale_price7: Optional[Union[StrictFloat, StrictInt]] = Field(default=None, description="Price for customers of group 7 in cents (e.g. \"150\" = 1.50€)")
sale_price8: Optional[Union[StrictFloat, StrictInt]] = Field(default=None, description="Price for customers of group 8 in cents (e.g. \"150\" = 1.50€)")
sale_price9: Optional[Union[StrictFloat, StrictInt]] = Field(default=None, description="Price for customers of group 9 in cents (e.g. \"150\" = 1.50€)")
sale_price10: Optional[Union[StrictFloat, StrictInt]] = Field(default=None, description="Price for customers of group 10 in cents (e.g. \"150\" = 1.50€)")
cost_price: Optional[Union[StrictFloat, StrictInt]] = Field(default=None, description="Price in cents (e.g. \"150\" = 1.50€)")
export_cost1: Optional[StrictStr] = 'null'
export_cost2: Optional[StrictStr] = 'null'
group_id: Optional[StrictInt] = None
stock: Optional[StrictStr] = Field(default='NO', description="Activates stock management for this position")
stock_count: Optional[StrictInt] = Field(default=None, description="Current stock count")
stock_limit_notify: Optional[StrictBool] = Field(default=False, description="Notify when stock_count is lower than stock_limit")
stock_limit_notify_frequency: Optional[StrictStr] = Field(default='ALWAYS', description="Notify frequency when stock_count is lower than stock_limit (ALWAYS, ONCE)")
stock_limit: Optional[StrictInt] = None
quantity: Optional[Union[StrictFloat, StrictInt]] = Field(default=None, description="Used as the default quantity when adding this position to a document")
archived: Optional[StrictBool] = False
__properties: ClassVar[List[str]] = ["id", "type", "number", "description", "document_note", "note", "unit", "export_identifier", "export_identifier_extended", "login_id", "price_type", "vat_percent", "sale_price", "sale_price2", "sale_price3", "sale_price4", "sale_price5", "sale_price6", "sale_price7", "sale_price8", "sale_price9", "sale_price10", "cost_price", "export_cost1", "export_cost2", "group_id", "stock", "stock_count", "stock_limit_notify", "stock_limit_notify_frequency", "stock_limit", "quantity", "archived"]
@field_validator('type')
def type_validate_enum(cls, value):
"""Validates the enum"""
if value is None:
return value
if value not in set(['PRODUCT', 'SERVICE', 'TEXT']):
raise ValueError("must be one of enum values ('PRODUCT', 'SERVICE', 'TEXT')")
return value
@field_validator('price_type')
def price_type_validate_enum(cls, value):
"""Validates the enum"""
if value is None:
return value
if value not in set(['BRUTTO', 'NETTO']):
raise ValueError("must be one of enum values ('BRUTTO', 'NETTO')")
return value
@field_validator('stock')
def stock_validate_enum(cls, value):
"""Validates the enum"""
if value is None:
return value
if value not in set(['YES', 'NO']):
raise ValueError("must be one of enum values ('YES', 'NO')")
return value
@field_validator('stock_limit_notify_frequency')
def stock_limit_notify_frequency_validate_enum(cls, value):
"""Validates the enum"""
if value is None:
return value
if value not in set(['ALWAYS', 'ONCE']):
raise ValueError("must be one of enum values ('ALWAYS', 'ONCE')")
return value
model_config = ConfigDict(
validate_by_name=True,
validate_by_alias=True,
validate_assignment=True,
protected_namespaces=(),
)
def to_str(self) -> str:
"""Returns the string representation of the model using alias"""
return pprint.pformat(self.model_dump(by_alias=True))
def to_json(self) -> str:
"""Returns the JSON representation of the model using alias"""
return json.dumps(to_jsonable_python(self.to_dict()))
@classmethod
def from_json(cls, json_str: str) -> Optional[Self]:
"""Create an instance of Position from a JSON string"""
return cls.from_dict(json.loads(json_str))
def to_dict(self) -> Dict[str, Any]:
"""Return the dictionary representation of the model using alias.
This has the following differences from calling pydantic's
`self.model_dump(by_alias=True)`:
* `None` is only added to the output dict for nullable fields that
were set at model initialization. Other fields with value `None`
are ignored.
* OpenAPI `readOnly` fields are excluded.
* OpenAPI `readOnly` fields are excluded.
* OpenAPI `readOnly` fields are excluded.
"""
excluded_fields: Set[str] = set([
"id",
"login_id",
"stock_count",
])
_dict = self.model_dump(
by_alias=True,
exclude=excluded_fields,
exclude_none=True,
)
# override the default output from pydantic by calling `to_dict()` of export_identifier_extended
if self.export_identifier_extended:
_dict['export_identifier_extended'] = self.export_identifier_extended.to_dict()
# set to None if note (nullable) is None
# and model_fields_set contains the field
if self.note is None and "note" in self.model_fields_set:
_dict['note'] = None
# set to None if unit (nullable) is None
# and model_fields_set contains the field
if self.unit is None and "unit" in self.model_fields_set:
_dict['unit'] = None
# set to None if export_identifier (nullable) is None
# and model_fields_set contains the field
if self.export_identifier is None and "export_identifier" in self.model_fields_set:
_dict['export_identifier'] = None
# set to None if sale_price2 (nullable) is None
# and model_fields_set contains the field
if self.sale_price2 is None and "sale_price2" in self.model_fields_set:
_dict['sale_price2'] = None
# set to None if sale_price3 (nullable) is None
# and model_fields_set contains the field
if self.sale_price3 is None and "sale_price3" in self.model_fields_set:
_dict['sale_price3'] = None
# set to None if sale_price4 (nullable) is None
# and model_fields_set contains the field
if self.sale_price4 is None and "sale_price4" in self.model_fields_set:
_dict['sale_price4'] = None
# set to None if sale_price5 (nullable) is None
# and model_fields_set contains the field
if self.sale_price5 is None and "sale_price5" in self.model_fields_set:
_dict['sale_price5'] = None
# set to None if sale_price6 (nullable) is None
# and model_fields_set contains the field
if self.sale_price6 is None and "sale_price6" in self.model_fields_set:
_dict['sale_price6'] = None
# set to None if sale_price7 (nullable) is None
# and model_fields_set contains the field
if self.sale_price7 is None and "sale_price7" in self.model_fields_set:
_dict['sale_price7'] = None
# set to None if sale_price8 (nullable) is None
# and model_fields_set contains the field
if self.sale_price8 is None and "sale_price8" in self.model_fields_set:
_dict['sale_price8'] = None
# set to None if sale_price9 (nullable) is None
# and model_fields_set contains the field
if self.sale_price9 is None and "sale_price9" in self.model_fields_set:
_dict['sale_price9'] = None
# set to None if sale_price10 (nullable) is None
# and model_fields_set contains the field
if self.sale_price10 is None and "sale_price10" in self.model_fields_set:
_dict['sale_price10'] = None
# set to None if cost_price (nullable) is None
# and model_fields_set contains the field
if self.cost_price is None and "cost_price" in self.model_fields_set:
_dict['cost_price'] = None
# set to None if export_cost1 (nullable) is None
# and model_fields_set contains the field
if self.export_cost1 is None and "export_cost1" in self.model_fields_set:
_dict['export_cost1'] = None
# set to None if export_cost2 (nullable) is None
# and model_fields_set contains the field
if self.export_cost2 is None and "export_cost2" in self.model_fields_set:
_dict['export_cost2'] = None
# set to None if group_id (nullable) is None
# and model_fields_set contains the field
if self.group_id is None and "group_id" in self.model_fields_set:
_dict['group_id'] = None
# set to None if quantity (nullable) is None
# and model_fields_set contains the field
if self.quantity is None and "quantity" in self.model_fields_set:
_dict['quantity'] = None
return _dict
@classmethod
def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]:
"""Create an instance of Position from a dict"""
if obj is None:
return None
if not isinstance(obj, dict):
return cls.model_validate(obj)
_obj = cls.model_validate({
"id": obj.get("id"),
"type": obj.get("type") if obj.get("type") is not None else 'PRODUCT',
"number": obj.get("number"),
"description": obj.get("description"),
"document_note": obj.get("document_note"),
"note": obj.get("note") if obj.get("note") is not None else 'null',
"unit": obj.get("unit") if obj.get("unit") is not None else 'null',
"export_identifier": obj.get("export_identifier") if obj.get("export_identifier") is not None else 'null',
"export_identifier_extended": PositionExportIdentifierExtended.from_dict(obj["export_identifier_extended"]) if obj.get("export_identifier_extended") is not None else None,
"login_id": obj.get("login_id"),
"price_type": obj.get("price_type") if obj.get("price_type") is not None else 'NETTO',
"vat_percent": obj.get("vat_percent") if obj.get("vat_percent") is not None else 19.0,
"sale_price": obj.get("sale_price"),
"sale_price2": obj.get("sale_price2"),
"sale_price3": obj.get("sale_price3"),
"sale_price4": obj.get("sale_price4"),
"sale_price5": obj.get("sale_price5"),
"sale_price6": obj.get("sale_price6"),
"sale_price7": obj.get("sale_price7"),
"sale_price8": obj.get("sale_price8"),
"sale_price9": obj.get("sale_price9"),
"sale_price10": obj.get("sale_price10"),
"cost_price": obj.get("cost_price"),
"export_cost1": obj.get("export_cost1") if obj.get("export_cost1") is not None else 'null',
"export_cost2": obj.get("export_cost2") if obj.get("export_cost2") is not None else 'null',
"group_id": obj.get("group_id"),
"stock": obj.get("stock") if obj.get("stock") is not None else 'NO',
"stock_count": obj.get("stock_count"),
"stock_limit_notify": obj.get("stock_limit_notify") if obj.get("stock_limit_notify") is not None else False,
"stock_limit_notify_frequency": obj.get("stock_limit_notify_frequency") if obj.get("stock_limit_notify_frequency") is not None else 'ALWAYS',
"stock_limit": obj.get("stock_limit"),
"quantity": obj.get("quantity"),
"archived": obj.get("archived") if obj.get("archived") is not None else False
})
return _obj