3
This commit is contained in:
+225
-81
@@ -1,10 +1,33 @@
|
|||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
from typing import Any
|
from typing import Any, Optional, Type
|
||||||
|
|
||||||
from fastapi import Body, Depends, FastAPI, Header, HTTPException, Query, Request
|
from fastapi import Body, Depends, FastAPI, Header, HTTPException, Query, Request
|
||||||
|
from pydantic import BaseModel, ConfigDict, Field, create_model
|
||||||
|
|
||||||
from app.models import CompanyData, CreateCompanyResponse
|
from app.models import (
|
||||||
|
BusinessCaseData,
|
||||||
|
CompanyData,
|
||||||
|
CreateResponse,
|
||||||
|
DetailResponse,
|
||||||
|
EmailData,
|
||||||
|
EventData,
|
||||||
|
InvoiceData,
|
||||||
|
LeadData,
|
||||||
|
LetterData,
|
||||||
|
ListResponse,
|
||||||
|
MeetingData,
|
||||||
|
OfferData,
|
||||||
|
PersonData,
|
||||||
|
PhoneCallData,
|
||||||
|
PriceListData,
|
||||||
|
ProductData,
|
||||||
|
ProjectData,
|
||||||
|
SalesOrderData,
|
||||||
|
SimpleResponse,
|
||||||
|
TaskData,
|
||||||
|
WebhookData,
|
||||||
|
)
|
||||||
from app.raynet_client import (
|
from app.raynet_client import (
|
||||||
RaynetAuthError,
|
RaynetAuthError,
|
||||||
RaynetClient,
|
RaynetClient,
|
||||||
@@ -19,8 +42,7 @@ APP_NAME = os.getenv("APP_NAME", "raynet")
|
|||||||
APP_VERSION = os.getenv("APP_VERSION", "1.0.0")
|
APP_VERSION = os.getenv("APP_VERSION", "1.0.0")
|
||||||
ROOT_PATH = os.getenv("ROOT_PATH", "")
|
ROOT_PATH = os.getenv("ROOT_PATH", "")
|
||||||
|
|
||||||
# Popis se zobrazí nahoře ve Swaggeru (/docs) – přihlašovací údaje jsou
|
# Zobrazí se nahoře ve Swaggeru (/docs).
|
||||||
# společné pro celé připojení a posílají se v hlavičkách u KAŽDÉHO requestu.
|
|
||||||
API_DESCRIPTION = """
|
API_DESCRIPTION = """
|
||||||
Stateless proxy nad **RAYNET CRM API v2**.
|
Stateless proxy nad **RAYNET CRM API v2**.
|
||||||
|
|
||||||
@@ -29,19 +51,24 @@ Stateless proxy nad **RAYNET CRM API v2**.
|
|||||||
Každý endpoint vyžaduje tři hlavičky. Jsou stejné pro všechna volání:
|
Každý endpoint vyžaduje tři hlavičky. Jsou stejné pro všechna volání:
|
||||||
|
|
||||||
| Hlavička | Co to je | Kde to vzít |
|
| Hlavička | Co to je | Kde to vzít |
|
||||||
|-------------------|--------------------|-------------|
|
|-----------------------|--------------------|-------------|
|
||||||
| **`X-Api-Key`** | API klíč (secret) | V RAYNET CRM: **Nastavení → Klíč k API** (vygeneruj / zkopíruj). |
|
| **`X-Api-Key`** | API klíč (secret) | V RAYNET CRM: **Nastavení → Klíč k API**. |
|
||||||
| **`X-Raynet-Email`** | Email uživatele | Přihlašovací email do RAYNET (tvoří dvojici s API klíčem pro Basic Auth). |
|
| **`X-Raynet-Email`** | Email uživatele | Přihlašovací email (tvoří s API klíčem Basic Auth). |
|
||||||
| **`X-Instance-Name`** | Název instance | Identifikátor tvé RAYNET instance (subdoména účtu). |
|
| **`X-Instance-Name`** | Název instance | Identifikátor RAYNET instance (subdoména účtu). |
|
||||||
|
|
||||||
`X-Api-Key` je **secret** – nikdy se neloguje ani nevrací z endpointů.
|
`X-Api-Key` je **secret** – nikdy se neloguje ani nevrací z endpointů.
|
||||||
|
|
||||||
## Endpointy
|
## Endpointy
|
||||||
|
|
||||||
- Typované zkratky pro firmy: `POST/GET/PUT/DELETE /company...`
|
Typované entity (vidíš strukturu objektu i odpovědi ve Swaggeru) – pro každou
|
||||||
- Generický průchod na celé API: `/api/{resource}` – podporuje libovolnou
|
platí `POST` (vytvořit), `GET` (seznam), `GET /{id}` (detail), `PUT /{id}`
|
||||||
RAYNET entitu (`company`, `person`, `lead`, `businessCase`, `activity`,
|
(úprava), `DELETE /{id}`:
|
||||||
`product`, `offer`, `order`, `project`, ...).
|
|
||||||
|
`company`, `person`, `lead`, `businessCase`, `task`, `product`, `offer`,
|
||||||
|
`salesOrder`, `invoice`.
|
||||||
|
|
||||||
|
Generický průchod na **zbytek API** (např. `userAccount`, `project`,
|
||||||
|
`priceList`, `email`, `file`, číselníky): `/api/{resource}`.
|
||||||
|
|
||||||
Plná dokumentace RAYNET: <https://app.raynetcrm.com/api/doc/index-en.html>
|
Plná dokumentace RAYNET: <https://app.raynetcrm.com/api/doc/index-en.html>
|
||||||
"""
|
"""
|
||||||
@@ -58,15 +85,9 @@ app = FastAPI(
|
|||||||
# Sdílená credentials dependency – 3 hlavičky pro celé připojení.
|
# Sdílená credentials dependency – 3 hlavičky pro celé připojení.
|
||||||
# --------------------------------------------------------------------------- #
|
# --------------------------------------------------------------------------- #
|
||||||
def get_client(
|
def get_client(
|
||||||
x_api_key: str = Header(
|
x_api_key: str = Header(..., alias="X-Api-Key", description="RAYNET API klíč (secret)."),
|
||||||
..., alias="X-Api-Key", description="RAYNET API klíč (secret)."
|
x_raynet_email: str = Header(..., alias="X-Raynet-Email", description="Email uživatele RAYNET."),
|
||||||
),
|
x_instance_name: str = Header(..., alias="X-Instance-Name", description="Název RAYNET instance."),
|
||||||
x_raynet_email: str = Header(
|
|
||||||
..., alias="X-Raynet-Email", description="Email uživatele RAYNET."
|
|
||||||
),
|
|
||||||
x_instance_name: str = Header(
|
|
||||||
..., alias="X-Instance-Name", description="Název RAYNET instance."
|
|
||||||
),
|
|
||||||
):
|
):
|
||||||
"""Vytvoří RaynetClient z hlaviček a po dokončení requestu uvolní session."""
|
"""Vytvoří RaynetClient z hlaviček a po dokončení requestu uvolní session."""
|
||||||
client = RaynetClient(
|
client = RaynetClient(
|
||||||
@@ -113,76 +134,138 @@ def version():
|
|||||||
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------------- #
|
# --------------------------------------------------------------------------- #
|
||||||
# Typované zkratky pro firmy (company)
|
# Generátor typovaných CRUD endpointů pro entitu
|
||||||
# --------------------------------------------------------------------------- #
|
# --------------------------------------------------------------------------- #
|
||||||
@app.post("/company", response_model=CreateCompanyResponse, tags=["company"])
|
def _make_patch_model(model: Type[BaseModel]) -> Type[BaseModel]:
|
||||||
def create_company(
|
"""Z Insert modelu vyrobí model pro částečnou úpravu (vše volitelné)."""
|
||||||
company: CompanyData,
|
fields = {
|
||||||
client: RaynetClient = Depends(get_client),
|
fname: (Optional[finfo.annotation], None)
|
||||||
):
|
for fname, finfo in model.model_fields.items()
|
||||||
"""Vytvoří firmu v RAYNET CRM (validovaná data dle CompanyInsertDto)."""
|
}
|
||||||
data = company.model_dump(exclude_none=True)
|
return create_model(
|
||||||
result = _run(client.create_company, data)
|
f"{model.__name__}Patch",
|
||||||
return CreateCompanyResponse(id=result.get("id"), success=True, raw=result)
|
__config__=ConfigDict(extra="allow"),
|
||||||
|
**fields,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@app.get("/company", tags=["company"])
|
def register_crud(path: str, model: Type[BaseModel], tag: str) -> None:
|
||||||
def list_companies(
|
"""Zaregistruje typované CRUD endpointy pro danou RAYNET entitu."""
|
||||||
client: RaynetClient = Depends(get_client),
|
patch_model = _make_patch_model(model)
|
||||||
offset: int = Query(0, ge=0),
|
|
||||||
limit: int = Query(20, ge=1, le=1000),
|
|
||||||
fulltext: str | None = Query(None, description="Fulltextové hledání"),
|
|
||||||
):
|
|
||||||
"""Vrátí seznam firem s podporou stránkování a fulltextu."""
|
|
||||||
params = {"offset": offset, "limit": limit}
|
|
||||||
if fulltext:
|
|
||||||
params["fulltext"] = fulltext
|
|
||||||
return _run(client.list_companies, **params)
|
|
||||||
|
|
||||||
|
@app.post(
|
||||||
|
f"/{path}",
|
||||||
|
response_model=CreateResponse,
|
||||||
|
tags=[tag],
|
||||||
|
operation_id=f"{path}_create",
|
||||||
|
summary=f"Vytvořit ({path})",
|
||||||
|
)
|
||||||
|
def _create(payload: model, client: RaynetClient = Depends(get_client)): # type: ignore[valid-type]
|
||||||
|
data = payload.model_dump(mode="json", exclude_none=True)
|
||||||
|
res = _run(client.create_record, path, data)
|
||||||
|
return CreateResponse(success=res.get("success", True), id=res.get("id"), data=res.get("data"))
|
||||||
|
|
||||||
@app.get("/company/{company_id}", tags=["company"])
|
@app.get(
|
||||||
def get_company(company_id: int, client: RaynetClient = Depends(get_client)):
|
f"/{path}",
|
||||||
"""Vrátí detail firmy."""
|
response_model=ListResponse,
|
||||||
return _run(client.get_company, company_id)
|
tags=[tag],
|
||||||
|
operation_id=f"{path}_list",
|
||||||
|
summary=f"Seznam ({path})",
|
||||||
@app.put("/company/{company_id}", tags=["company"])
|
)
|
||||||
def update_company(
|
def _list(
|
||||||
company_id: int,
|
|
||||||
data: dict = Body(..., description="Pole firmy ke změně"),
|
|
||||||
client: RaynetClient = Depends(get_client),
|
|
||||||
):
|
|
||||||
"""Upraví firmu."""
|
|
||||||
return _run(client.update_company, company_id, data)
|
|
||||||
|
|
||||||
|
|
||||||
@app.delete("/company/{company_id}", tags=["company"])
|
|
||||||
def delete_company(company_id: int, client: RaynetClient = Depends(get_client)):
|
|
||||||
"""Smaže firmu."""
|
|
||||||
return _run(client.delete_company, company_id)
|
|
||||||
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------------- #
|
|
||||||
# Generický průchod na CELÉ RAYNET API
|
|
||||||
# resource = libovolná entita: company, person, lead, businessCase, activity,
|
|
||||||
# product, offer, order, project, ...
|
|
||||||
# --------------------------------------------------------------------------- #
|
|
||||||
@app.get("/api/{resource}", tags=["generic"])
|
|
||||||
def api_list(
|
|
||||||
resource: str,
|
|
||||||
request: Request,
|
request: Request,
|
||||||
client: RaynetClient = Depends(get_client),
|
client: RaynetClient = Depends(get_client),
|
||||||
|
offset: int = Query(0, ge=0, description="Posun ve výsledcích"),
|
||||||
|
limit: int = Query(50, ge=1, le=1000, description="Počet záznamů"),
|
||||||
|
fulltext: Optional[str] = Query(None, description="Fulltextové hledání"),
|
||||||
):
|
):
|
||||||
"""Seznam záznamů entity. Všechny query parametry se předávají do RAYNET
|
# Předáme všechny query parametry do RAYNET; doplníme výchozí offset/limit.
|
||||||
(např. `offset`, `limit`, `fulltext`, `name`, ...)."""
|
params = dict(request.query_params)
|
||||||
|
params.setdefault("offset", offset)
|
||||||
|
params.setdefault("limit", limit)
|
||||||
|
res = _run(client.list_records, path, params)
|
||||||
|
return ListResponse(
|
||||||
|
success=res.get("success", True),
|
||||||
|
totalCount=res.get("totalCount"),
|
||||||
|
data=res.get("data") or [],
|
||||||
|
)
|
||||||
|
|
||||||
|
@app.get(
|
||||||
|
f"/{path}/{{record_id}}",
|
||||||
|
response_model=DetailResponse,
|
||||||
|
tags=[tag],
|
||||||
|
operation_id=f"{path}_detail",
|
||||||
|
summary=f"Detail ({path})",
|
||||||
|
)
|
||||||
|
def _detail(record_id: int, client: RaynetClient = Depends(get_client)):
|
||||||
|
res = _run(client.get_record, path, record_id)
|
||||||
|
return DetailResponse(success=res.get("success", True), data=res.get("data"))
|
||||||
|
|
||||||
|
@app.put(
|
||||||
|
f"/{path}/{{record_id}}",
|
||||||
|
response_model=SimpleResponse,
|
||||||
|
tags=[tag],
|
||||||
|
operation_id=f"{path}_update",
|
||||||
|
summary=f"Upravit ({path})",
|
||||||
|
)
|
||||||
|
def _update(
|
||||||
|
record_id: int,
|
||||||
|
payload: patch_model, # type: ignore[valid-type]
|
||||||
|
client: RaynetClient = Depends(get_client),
|
||||||
|
):
|
||||||
|
data = payload.model_dump(mode="json", exclude_none=True)
|
||||||
|
res = _run(client.update_record, path, record_id, data)
|
||||||
|
return SimpleResponse(success=res.get("success", True))
|
||||||
|
|
||||||
|
@app.delete(
|
||||||
|
f"/{path}/{{record_id}}",
|
||||||
|
response_model=SimpleResponse,
|
||||||
|
tags=[tag],
|
||||||
|
operation_id=f"{path}_delete",
|
||||||
|
summary=f"Smazat ({path})",
|
||||||
|
)
|
||||||
|
def _delete(record_id: int, client: RaynetClient = Depends(get_client)):
|
||||||
|
res = _run(client.delete_record, path, record_id)
|
||||||
|
return SimpleResponse(success=res.get("success", True))
|
||||||
|
|
||||||
|
|
||||||
|
# Registrace typovaných entit (resource path = tag).
|
||||||
|
ENTITIES: list[tuple[str, Type[BaseModel]]] = [
|
||||||
|
("company", CompanyData),
|
||||||
|
("person", PersonData),
|
||||||
|
("lead", LeadData),
|
||||||
|
("businessCase", BusinessCaseData),
|
||||||
|
("offer", OfferData),
|
||||||
|
("salesOrder", SalesOrderData),
|
||||||
|
("invoice", InvoiceData),
|
||||||
|
("product", ProductData),
|
||||||
|
("priceList", PriceListData),
|
||||||
|
("project", ProjectData),
|
||||||
|
("task", TaskData),
|
||||||
|
("email", EmailData),
|
||||||
|
("event", EventData),
|
||||||
|
("meeting", MeetingData),
|
||||||
|
("phoneCall", PhoneCallData),
|
||||||
|
("letter", LetterData),
|
||||||
|
("webhook", WebhookData),
|
||||||
|
]
|
||||||
|
for _path, _model in ENTITIES:
|
||||||
|
register_crud(_path, _model, _path)
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Generický průchod na zbytek RAYNET API (netypované entity)
|
||||||
|
# resource = libovolná entita: userAccount, project, priceList, email, file, ...
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
@app.get("/api/{resource}", tags=["generic"])
|
||||||
|
def api_list(resource: str, request: Request, client: RaynetClient = Depends(get_client)):
|
||||||
|
"""Seznam záznamů entity. Query parametry se předávají do RAYNET."""
|
||||||
params = dict(request.query_params)
|
params = dict(request.query_params)
|
||||||
return _run(client.list_records, resource, params or None)
|
return _run(client.list_records, resource, params or None)
|
||||||
|
|
||||||
|
|
||||||
@app.get("/api/{resource}/{record_id}", tags=["generic"])
|
@app.get("/api/{resource}/{record_id}", tags=["generic"])
|
||||||
def api_detail(
|
def api_detail(resource: str, record_id: str, client: RaynetClient = Depends(get_client)):
|
||||||
resource: str, record_id: str, client: RaynetClient = Depends(get_client)
|
|
||||||
):
|
|
||||||
"""Detail jednoho záznamu."""
|
"""Detail jednoho záznamu."""
|
||||||
return _run(client.get_record, resource, record_id)
|
return _run(client.get_record, resource, record_id)
|
||||||
|
|
||||||
@@ -209,8 +292,69 @@ def api_update(
|
|||||||
|
|
||||||
|
|
||||||
@app.delete("/api/{resource}/{record_id}", tags=["generic"])
|
@app.delete("/api/{resource}/{record_id}", tags=["generic"])
|
||||||
def api_delete(
|
def api_delete(resource: str, record_id: str, client: RaynetClient = Depends(get_client)):
|
||||||
resource: str, record_id: str, client: RaynetClient = Depends(get_client)
|
|
||||||
):
|
|
||||||
"""Smaže záznam."""
|
"""Smaže záznam."""
|
||||||
return _run(client.delete_record, resource, record_id)
|
return _run(client.delete_record, resource, record_id)
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Vnořené zdroje a speciální akce
|
||||||
|
# Pokrývá např.: /company/{id}/address/, /company/{id}/lock,
|
||||||
|
# /company/{id}/merge/{srcId}/, /invoice/{id}/cancel, /invoice/{id}/pdfExport,
|
||||||
|
# /invoice/{id}/payment/, /offer/{id}/item/ atd.
|
||||||
|
# `sub` je zbytek cesty za /{resource}/{id}/ – uveď přesně jak chce RAYNET
|
||||||
|
# (vč. koncového lomítka u kolekcí, např. "address/" nebo "payment/5/").
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
@app.get("/api/{resource}/{record_id}/{sub:path}", tags=["generic"])
|
||||||
|
def api_sub_get(
|
||||||
|
resource: str, record_id: str, sub: str, request: Request,
|
||||||
|
client: RaynetClient = Depends(get_client),
|
||||||
|
):
|
||||||
|
"""GET vnořeného zdroje / akce (např. pdfExport, seznam adres)."""
|
||||||
|
return _run(client.call, "GET", f"/{resource}/{record_id}/{sub}", dict(request.query_params) or None)
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/api/{resource}/{record_id}/{sub:path}", tags=["generic"])
|
||||||
|
def api_sub_post(
|
||||||
|
resource: str, record_id: str, sub: str,
|
||||||
|
data: Optional[dict] = Body(None, description="Volitelné tělo (akce nemusí mít žádné)"),
|
||||||
|
client: RaynetClient = Depends(get_client),
|
||||||
|
):
|
||||||
|
"""POST vnořeného zdroje / akce (např. lock, cancel, setPrimary, update)."""
|
||||||
|
return _run(client.call, "POST", f"/{resource}/{record_id}/{sub}", None, data)
|
||||||
|
|
||||||
|
|
||||||
|
@app.put("/api/{resource}/{record_id}/{sub:path}", tags=["generic"])
|
||||||
|
def api_sub_put(
|
||||||
|
resource: str, record_id: str, sub: str,
|
||||||
|
data: Optional[dict] = Body(None, description="Tělo nového vnořeného záznamu"),
|
||||||
|
client: RaynetClient = Depends(get_client),
|
||||||
|
):
|
||||||
|
"""PUT vnořeného zdroje (create v kolekci, např. address/, payment/, item/)."""
|
||||||
|
return _run(client.call, "PUT", f"/{resource}/{record_id}/{sub}", None, data)
|
||||||
|
|
||||||
|
|
||||||
|
@app.delete("/api/{resource}/{record_id}/{sub:path}", tags=["generic"])
|
||||||
|
def api_sub_delete(
|
||||||
|
resource: str, record_id: str, sub: str,
|
||||||
|
client: RaynetClient = Depends(get_client),
|
||||||
|
):
|
||||||
|
"""DELETE vnořeného zdroje (např. address/5/, payment/3/)."""
|
||||||
|
return _run(client.call, "DELETE", f"/{resource}/{record_id}/{sub}")
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Raw escape-hatch – dosáhne na JAKOUKOLI cestu RAYNET API libovolnou metodou.
|
||||||
|
# Pro speciální případy mimo výše uvedené vzory (např. PUT /invoice/creditNote).
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
class RawRequest(BaseModel):
|
||||||
|
method: str = Field(..., examples=["GET", "POST", "PUT", "DELETE"])
|
||||||
|
path: str = Field(..., description="Cesta za base URL, např. /invoice/creditNote", examples=["/company/"])
|
||||||
|
params: Optional[dict] = Field(default=None, description="Query parametry")
|
||||||
|
data: Optional[dict] = Field(default=None, description="JSON tělo")
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/raw", tags=["generic"], summary="Raw volání libovolného RAYNET endpointu")
|
||||||
|
def raw_call(req: RawRequest, client: RaynetClient = Depends(get_client)):
|
||||||
|
"""Univerzální volání – pokrývá 100 % RAYNET API včetně netypických cest."""
|
||||||
|
return _run(client.call, req.method, req.path, req.params, req.data)
|
||||||
|
|||||||
+360
-13
@@ -1,7 +1,12 @@
|
|||||||
"""Pydantic modely pro RAYNET endpointy.
|
"""Pydantic modely pro RAYNET endpointy.
|
||||||
|
|
||||||
Modely odpovídají RAYNET CRM API v2 (CompanyInsertDto). Slouží zároveň jako
|
Modely odpovídají RAYNET CRM API v2 (Insert DTO jednotlivých entit) a slouží
|
||||||
zdroj pro Swagger/OpenAPI dokumentaci.
|
zároveň jako zdroj pro Swagger/OpenAPI dokumentaci – ve Swaggeru je tak vidět
|
||||||
|
přesná struktura objektů, povinná pole i tvar odpovědí.
|
||||||
|
|
||||||
|
POZN.: Názvy polí jsou v ``camelCase`` (tak je očekává RAYNET JSON tělo).
|
||||||
|
Nevyjmenovaná pole jsou povolená (``extra="allow"``), takže lze poslat i pole,
|
||||||
|
která zde nejsou explicitně uvedená.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
@@ -10,6 +15,9 @@ from typing import Optional
|
|||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Číselníkové enumy (company)
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
class Rating(str, Enum):
|
class Rating(str, Enum):
|
||||||
A = "A"
|
A = "A"
|
||||||
B = "B"
|
B = "B"
|
||||||
@@ -35,6 +43,9 @@ class TaxPayer(str, Enum):
|
|||||||
NO = "NO"
|
NO = "NO"
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Sdílené pod-objekty
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
class Address(BaseModel):
|
class Address(BaseModel):
|
||||||
name: Optional[str] = Field(default=None, examples=["Sídlo klienta"])
|
name: Optional[str] = Field(default=None, examples=["Sídlo klienta"])
|
||||||
street: Optional[str] = Field(default=None, examples=["Francouzská 6167/5"])
|
street: Optional[str] = Field(default=None, examples=["Francouzská 6167/5"])
|
||||||
@@ -64,21 +75,21 @@ class CompanyAddress(BaseModel):
|
|||||||
contactInfo: Optional[ContactInfo] = None
|
contactInfo: Optional[ContactInfo] = None
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Entitní modely (request body pro create) – CamelCase dle RAYNET API
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
class CompanyData(BaseModel):
|
class CompanyData(BaseModel):
|
||||||
"""Tělo požadavku pro vytvoření firmy (CompanyInsertDto).
|
"""Firma / klient (RAYNET CompanyInsertDto). Povinná: name, rating, state, role."""
|
||||||
|
|
||||||
Povinná pole: ``name``, ``rating``, ``state``, ``role``.
|
model_config = {"extra": "allow"}
|
||||||
"""
|
|
||||||
|
|
||||||
# Povinná pole
|
|
||||||
name: str = Field(..., examples=["ACME s.r.o."])
|
name: str = Field(..., examples=["ACME s.r.o."])
|
||||||
rating: Rating = Field(..., examples=[Rating.A])
|
rating: Rating = Field(..., examples=[Rating.A])
|
||||||
state: CompanyState = Field(..., examples=[CompanyState.A_POTENTIAL])
|
state: CompanyState = Field(..., examples=[CompanyState.A_POTENTIAL])
|
||||||
role: CompanyRole = Field(..., examples=[CompanyRole.A_SUBSCRIBER])
|
role: CompanyRole = Field(..., examples=[CompanyRole.A_SUBSCRIBER])
|
||||||
|
|
||||||
# Volitelná pole
|
regNumber: Optional[str] = Field(default=None, examples=["12345678"], description="IČO")
|
||||||
regNumber: Optional[str] = Field(default=None, examples=["12345678"])
|
taxNumber: Optional[str] = Field(default=None, examples=["CZ12345678"], description="DIČ")
|
||||||
taxNumber: Optional[str] = Field(default=None, examples=["CZ12345678"])
|
|
||||||
taxNumber2: Optional[str] = None
|
taxNumber2: Optional[str] = None
|
||||||
taxPayer: Optional[TaxPayer] = None
|
taxPayer: Optional[TaxPayer] = None
|
||||||
bankAccount: Optional[str] = None
|
bankAccount: Optional[str] = None
|
||||||
@@ -95,11 +106,347 @@ class CompanyData(BaseModel):
|
|||||||
tags: Optional[list[str]] = None
|
tags: Optional[list[str]] = None
|
||||||
customFields: Optional[dict] = None
|
customFields: Optional[dict] = None
|
||||||
|
|
||||||
# Umožní i pole, která zde nejsou explicitně vyjmenovaná (RAYNET jich má víc).
|
|
||||||
|
class PersonData(BaseModel):
|
||||||
|
"""Kontaktní osoba (RAYNET PersonInsertDto). Povinné: lastName."""
|
||||||
|
|
||||||
model_config = {"extra": "allow"}
|
model_config = {"extra": "allow"}
|
||||||
|
|
||||||
|
lastName: str = Field(..., description="Příjmení")
|
||||||
|
titleBefore: Optional[str] = None
|
||||||
|
firstName: Optional[str] = None
|
||||||
|
titleAfter: Optional[str] = None
|
||||||
|
securityLevel: Optional[int] = None
|
||||||
|
owner: Optional[int] = None
|
||||||
|
category: Optional[int] = None
|
||||||
|
personClassification1: Optional[int] = None
|
||||||
|
personClassification2: Optional[int] = None
|
||||||
|
personClassification3: Optional[int] = None
|
||||||
|
salutation: Optional[str] = None
|
||||||
|
birthday: Optional[str] = None
|
||||||
|
language: Optional[int] = None
|
||||||
|
maritalStatus: Optional[int] = None
|
||||||
|
gender: Optional[str] = None
|
||||||
|
contactInfo: Optional[dict] = None
|
||||||
|
socialNetworkContact: Optional[dict] = None
|
||||||
|
privateAddress: Optional[dict] = None
|
||||||
|
notice: Optional[str] = None
|
||||||
|
relationship: Optional[dict] = None
|
||||||
|
tags: Optional[list[str]] = None
|
||||||
|
keyman: Optional[bool] = None
|
||||||
|
originLead: Optional[int] = None
|
||||||
|
|
||||||
class CreateCompanyResponse(BaseModel):
|
|
||||||
id: Optional[int] = Field(default=None, description="ID nově vytvořené firmy")
|
class LeadData(BaseModel):
|
||||||
|
"""Lead / poptávka (RAYNET LeadInsertDto). Povinné: topic, priority."""
|
||||||
|
|
||||||
|
model_config = {"extra": "allow"}
|
||||||
|
|
||||||
|
topic: str = Field(..., description="Předmět")
|
||||||
|
priority: str = Field(..., description="Priorita (číselník)")
|
||||||
|
companyName: Optional[str] = None
|
||||||
|
regNumber: Optional[str] = None
|
||||||
|
firstName: Optional[str] = None
|
||||||
|
lastName: Optional[str] = None
|
||||||
|
titleBefore: Optional[str] = None
|
||||||
|
titleAfter: Optional[str] = None
|
||||||
|
securityLevel: Optional[int] = None
|
||||||
|
owner: Optional[int] = None
|
||||||
|
contactSource: Optional[int] = None
|
||||||
|
category: Optional[int] = None
|
||||||
|
notice: Optional[str] = None
|
||||||
|
leadPhase: Optional[int] = None
|
||||||
|
tags: Optional[str] = None
|
||||||
|
territory: Optional[int] = None
|
||||||
|
leadPerson: Optional[bool] = None
|
||||||
|
contactInfo: Optional[dict] = None
|
||||||
|
address: Optional[dict] = None
|
||||||
|
socialNetworkContact: Optional[dict] = None
|
||||||
|
customFields: Optional[dict] = None
|
||||||
|
notificationMessage: Optional[str] = None
|
||||||
|
notificationEmailAddresses: Optional[list[str]] = None
|
||||||
|
|
||||||
|
|
||||||
|
class BusinessCaseData(BaseModel):
|
||||||
|
"""Obchodní případ (RAYNET BusinessCaseInsertDto). Povinné: name, company."""
|
||||||
|
|
||||||
|
model_config = {"extra": "allow"}
|
||||||
|
|
||||||
|
name: str = Field(..., description="Předmět")
|
||||||
|
company: int = Field(..., description="ID klienta")
|
||||||
|
securityLevel: Optional[int] = None
|
||||||
|
owner: Optional[int] = None
|
||||||
|
person: Optional[int] = None
|
||||||
|
project: Optional[int] = None
|
||||||
|
totalAmount: Optional[float] = None
|
||||||
|
estimatedValue: Optional[float] = None
|
||||||
|
probability: Optional[int] = None
|
||||||
|
validFrom: Optional[str] = None
|
||||||
|
description: Optional[str] = None
|
||||||
|
currency: Optional[int] = None
|
||||||
|
exchangeRate: Optional[float] = None
|
||||||
|
category: Optional[int] = None
|
||||||
|
source: Optional[int] = None
|
||||||
|
businessCaseClassification1: Optional[int] = None
|
||||||
|
businessCaseClassification2: Optional[int] = None
|
||||||
|
businessCaseClassification3: Optional[int] = None
|
||||||
|
businessCasePhase: Optional[int] = None
|
||||||
|
originalLead: Optional[int] = None
|
||||||
|
tags: Optional[list[str]] = None
|
||||||
|
customFields: Optional[dict] = None
|
||||||
|
|
||||||
|
|
||||||
|
class TaskData(BaseModel):
|
||||||
|
"""Úkol = aktivita typu task (RAYNET TaskInsertDto, endpoint /task/).
|
||||||
|
|
||||||
|
Povinné: title, priority, owner, resolver, deadline.
|
||||||
|
"""
|
||||||
|
|
||||||
|
model_config = {"extra": "allow"}
|
||||||
|
|
||||||
|
title: str = Field(..., description="Předmět")
|
||||||
|
priority: str = Field(..., description="Priorita")
|
||||||
|
owner: int = Field(..., description="ID kontaktní osoby – vlastník úkolu")
|
||||||
|
resolver: int = Field(..., description="ID kontaktní osoby – řešitel úkolu")
|
||||||
|
deadline: str = Field(..., description="Deadline (datetime)")
|
||||||
|
category: Optional[int] = None
|
||||||
|
person: Optional[int] = None
|
||||||
|
company: Optional[int] = None
|
||||||
|
businessCase: Optional[int] = None
|
||||||
|
offer: Optional[int] = None
|
||||||
|
salesOrder: Optional[int] = None
|
||||||
|
project: Optional[int] = None
|
||||||
|
activity: Optional[int] = None
|
||||||
|
lead: Optional[int] = None
|
||||||
|
securityLevel: Optional[int] = None
|
||||||
|
scheduledFrom: Optional[str] = None
|
||||||
|
scheduledTill: Optional[str] = None
|
||||||
|
completed: Optional[str] = None
|
||||||
|
description: Optional[str] = None
|
||||||
|
solution: Optional[str] = None
|
||||||
|
tags: Optional[list[str]] = None
|
||||||
|
|
||||||
|
|
||||||
|
class ProductData(BaseModel):
|
||||||
|
"""Produkt (RAYNET ProductInsertDto). Povinné: code, name."""
|
||||||
|
|
||||||
|
model_config = {"extra": "allow"}
|
||||||
|
|
||||||
|
code: str = Field(..., description="Kód")
|
||||||
|
name: str = Field(..., description="Název")
|
||||||
|
unit: Optional[str] = None
|
||||||
|
description: Optional[str] = None
|
||||||
|
taxRate: Optional[float] = None
|
||||||
|
category: Optional[int] = Field(default=None, description="ID z číselníku ProductCategory")
|
||||||
|
productLine: Optional[int] = Field(default=None, description="ID z číselníku ProductLine")
|
||||||
|
cost: Optional[float] = None
|
||||||
|
price: Optional[float] = None
|
||||||
|
tags: Optional[list[str]] = None
|
||||||
|
|
||||||
|
|
||||||
|
class OfferData(BaseModel):
|
||||||
|
"""Nabídka (RAYNET OfferInsertDto). Povinné: name, company, businessCase."""
|
||||||
|
|
||||||
|
model_config = {"extra": "allow"}
|
||||||
|
|
||||||
|
name: str = Field(..., description="Předmět")
|
||||||
|
company: int = Field(..., description="ID klienta")
|
||||||
|
businessCase: int = Field(..., description="ID obchodního případu")
|
||||||
|
securityLevel: Optional[int] = None
|
||||||
|
owner: Optional[int] = None
|
||||||
|
person: Optional[int] = None
|
||||||
|
totalAmount: Optional[float] = None
|
||||||
|
estimatedValue: Optional[float] = None
|
||||||
|
validFrom: Optional[str] = None
|
||||||
|
validTill: Optional[str] = None
|
||||||
|
expirationDate: Optional[str] = None
|
||||||
|
description: Optional[str] = None
|
||||||
|
category: Optional[int] = None
|
||||||
|
offerStatus: Optional[int] = None
|
||||||
|
|
||||||
|
|
||||||
|
class SalesOrderData(BaseModel):
|
||||||
|
"""Objednávka (RAYNET SalesOrderInsertDto, endpoint /salesOrder/).
|
||||||
|
|
||||||
|
Povinné: name, company, businessCase.
|
||||||
|
"""
|
||||||
|
|
||||||
|
model_config = {"extra": "allow"}
|
||||||
|
|
||||||
|
name: str = Field(..., description="Předmět")
|
||||||
|
company: int = Field(..., description="ID klienta")
|
||||||
|
businessCase: int = Field(..., description="ID obchodního případu")
|
||||||
|
securityLevel: Optional[int] = None
|
||||||
|
owner: Optional[int] = None
|
||||||
|
person: Optional[int] = None
|
||||||
|
offer: Optional[int] = None
|
||||||
|
totalAmount: Optional[float] = None
|
||||||
|
estimatedValue: Optional[float] = None
|
||||||
|
validFrom: Optional[str] = None
|
||||||
|
validTill: Optional[str] = None
|
||||||
|
expirationDate: Optional[str] = None
|
||||||
|
requestDeliveryDate: Optional[str] = None
|
||||||
|
description: Optional[str] = None
|
||||||
|
category: Optional[int] = None
|
||||||
|
salesOrderStatus: Optional[int] = None
|
||||||
|
deliveryAddress: Optional[dict] = None
|
||||||
|
|
||||||
|
|
||||||
|
class InvoiceData(BaseModel):
|
||||||
|
"""Faktura (RAYNET InvoiceInsertDto, endpoint /invoice/).
|
||||||
|
|
||||||
|
Povinné: company, currency, dueDate, issueDate, invoiceType, paymentType,
|
||||||
|
taxableSupplyDate.
|
||||||
|
"""
|
||||||
|
|
||||||
|
model_config = {"extra": "allow"}
|
||||||
|
|
||||||
|
company: int = Field(..., description="ID klienta, kterému se fakturuje")
|
||||||
|
currency: int = Field(..., description="ID měny (číselník Currency)")
|
||||||
|
dueDate: str = Field(..., description="Datum splatnosti")
|
||||||
|
issueDate: str = Field(..., description="Datum vystavení")
|
||||||
|
invoiceType: str = Field(..., description="Typ faktury")
|
||||||
|
paymentType: str = Field(..., description="Způsob úhrady")
|
||||||
|
taxableSupplyDate: str = Field(..., description="Datum zdanitelného plnění")
|
||||||
|
securityLevel: Optional[int] = None
|
||||||
|
title: Optional[str] = None
|
||||||
|
constantSymbol: Optional[str] = None
|
||||||
|
specificSymbol: Optional[str] = None
|
||||||
|
paymentTermDays: Optional[int] = None
|
||||||
|
businessCase: Optional[int] = None
|
||||||
|
note: Optional[str] = None
|
||||||
|
deliveryNote: Optional[str] = None
|
||||||
|
privateNote: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
class ProjectData(BaseModel):
|
||||||
|
"""Projekt (RAYNET ProjectInsertDto). Povinné: name, company."""
|
||||||
|
|
||||||
|
model_config = {"extra": "allow"}
|
||||||
|
|
||||||
|
name: str = Field(..., description="Předmět")
|
||||||
|
company: int = Field(..., description="ID klienta")
|
||||||
|
securityLevel: Optional[int] = None
|
||||||
|
owner: Optional[int] = None
|
||||||
|
person: Optional[int] = None
|
||||||
|
totalAmount: Optional[int] = None
|
||||||
|
avgValueTotalAmount: Optional[float] = None
|
||||||
|
minValueTotalAmount: Optional[float] = None
|
||||||
|
maxValueTotalAmount: Optional[float] = None
|
||||||
|
validFrom: Optional[str] = None
|
||||||
|
validTill: Optional[str] = None
|
||||||
|
scheduledEnd: Optional[str] = None
|
||||||
|
description: Optional[str] = None
|
||||||
|
category: Optional[int] = None
|
||||||
|
projectStatus: Optional[int] = None
|
||||||
|
tags: Optional[list[str]] = None
|
||||||
|
customFields: Optional[dict] = None
|
||||||
|
|
||||||
|
|
||||||
|
class PriceListData(BaseModel):
|
||||||
|
"""Ceník (RAYNET PriceListInsertDto). Povinné: name, code, currency, validFrom."""
|
||||||
|
|
||||||
|
model_config = {"extra": "allow"}
|
||||||
|
|
||||||
|
name: str = Field(..., description="Název ceníku")
|
||||||
|
code: str = Field(..., description="Kód ceníku")
|
||||||
|
currency: int = Field(..., description="ID měny (číselník Currency)")
|
||||||
|
validFrom: str = Field(..., description="Platnost od")
|
||||||
|
securityLevel: Optional[int] = None
|
||||||
|
owner: Optional[int] = None
|
||||||
|
category: Optional[int] = None
|
||||||
|
validTill: Optional[str] = None
|
||||||
|
description: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
# --- Aktivity (sdílí strukturu: title, priority, owner povinné) ----------- #
|
||||||
|
class _ActivityBase(BaseModel):
|
||||||
|
"""Společná pole aktivit RAYNET (task/email/event/meeting/phoneCall/letter)."""
|
||||||
|
|
||||||
|
model_config = {"extra": "allow"}
|
||||||
|
|
||||||
|
title: str = Field(..., description="Předmět")
|
||||||
|
priority: str = Field(..., description="Priorita")
|
||||||
|
owner: int = Field(..., description="ID kontaktní osoby – vlastník")
|
||||||
|
category: Optional[int] = None
|
||||||
|
person: Optional[int] = None
|
||||||
|
company: Optional[int] = None
|
||||||
|
businessCase: Optional[int] = None
|
||||||
|
offer: Optional[int] = None
|
||||||
|
salesOrder: Optional[int] = None
|
||||||
|
project: Optional[int] = None
|
||||||
|
activity: Optional[int] = None
|
||||||
|
lead: Optional[int] = None
|
||||||
|
securityLevel: Optional[int] = None
|
||||||
|
scheduledFrom: Optional[str] = None
|
||||||
|
scheduledTill: Optional[str] = None
|
||||||
|
completed: Optional[str] = None
|
||||||
|
description: Optional[str] = None
|
||||||
|
tags: Optional[list[str]] = None
|
||||||
|
|
||||||
|
|
||||||
|
class EmailData(_ActivityBase):
|
||||||
|
"""E-mailová aktivita (RAYNET EmailInsertDto)."""
|
||||||
|
|
||||||
|
|
||||||
|
class EventData(_ActivityBase):
|
||||||
|
"""Událost (RAYNET EventInsertDto)."""
|
||||||
|
|
||||||
|
|
||||||
|
class MeetingData(_ActivityBase):
|
||||||
|
"""Schůzka (RAYNET MeetingInsertDto)."""
|
||||||
|
|
||||||
|
solution: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
class PhoneCallData(_ActivityBase):
|
||||||
|
"""Telefonát (RAYNET PhonecallInsertDto)."""
|
||||||
|
|
||||||
|
solution: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
class LetterData(_ActivityBase):
|
||||||
|
"""Dopis (RAYNET LetterInsertDto)."""
|
||||||
|
|
||||||
|
|
||||||
|
class WebhookData(BaseModel):
|
||||||
|
"""Webhook (RAYNET WebhookInsertDto). Povinné: url, events."""
|
||||||
|
|
||||||
|
model_config = {"extra": "allow"}
|
||||||
|
|
||||||
|
url: str = Field(..., description="Cílová adresa (http/https)")
|
||||||
|
events: list[str] = Field(
|
||||||
|
...,
|
||||||
|
description="Typy událostí: record.created, record.updated, record.deleted",
|
||||||
|
)
|
||||||
|
secretToken: Optional[str] = Field(
|
||||||
|
default=None,
|
||||||
|
description="Token zaslaný v hlavičce X-RAYNETCRM-Token",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Obálky odpovědí (RAYNET vrací data zabalená do success/data/totalCount)
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
class CreateResponse(BaseModel):
|
||||||
|
success: bool = True
|
||||||
|
id: Optional[int] = Field(default=None, description="ID nově vytvořeného záznamu")
|
||||||
|
data: Optional[dict] = Field(default=None, description="Surová data z RAYNET, pokud byla vrácena")
|
||||||
|
|
||||||
|
|
||||||
|
class DetailResponse(BaseModel):
|
||||||
|
success: bool = True
|
||||||
|
data: Optional[dict] = Field(
|
||||||
|
default=None,
|
||||||
|
description="Objekt záznamu vč. serverových polí (id, rowInfo, ...)",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ListResponse(BaseModel):
|
||||||
|
success: bool = True
|
||||||
|
totalCount: Optional[int] = Field(default=None, description="Celkový počet záznamů")
|
||||||
|
data: list[dict] = Field(default_factory=list, description="Pole záznamů")
|
||||||
|
|
||||||
|
|
||||||
|
class SimpleResponse(BaseModel):
|
||||||
success: bool = True
|
success: bool = True
|
||||||
raw: Optional[dict] = Field(default=None, description="Surová odpověď RAYNET API")
|
|
||||||
|
|||||||
@@ -36,17 +36,55 @@ Příklady entit: `company`, `person`, `lead`, `businessCase`, `activity`,
|
|||||||
|
|
||||||
Veřejně přes AppFactory reverse proxy: `https://services.csbot.cz/apps/<app-id>/...`
|
Veřejně přes AppFactory reverse proxy: `https://services.csbot.cz/apps/<app-id>/...`
|
||||||
|
|
||||||
### Typované zkratky pro firmy
|
### Typované entity
|
||||||
|
|
||||||
|
Pro každou z těchto entit existuje plné CRUD se **strukturou objektu i odpovědí
|
||||||
|
viditelnou ve Swaggeru** (request modely, povinná pole, validace):
|
||||||
|
|
||||||
|
| Cesta | Entita | Povinná pole |
|
||||||
|
|---------------------------|--------------------|--------------|
|
||||||
|
| `/company` | Firma / klient | `name`, `rating`, `state`, `role` |
|
||||||
|
| `/person` | Kontaktní osoba | `lastName` |
|
||||||
|
| `/lead` | Lead / poptávka | `topic`, `priority` |
|
||||||
|
| `/businessCase` | Obchodní případ | `name`, `company` |
|
||||||
|
| `/offer` | Nabídka | `name`, `company`, `businessCase` |
|
||||||
|
| `/salesOrder` | Objednávka | `name`, `company`, `businessCase` |
|
||||||
|
| `/invoice` | Faktura | `company`, `currency`, `dueDate`, `issueDate`, `invoiceType`, `paymentType`, `taxableSupplyDate` |
|
||||||
|
| `/product` | Produkt | `code`, `name` |
|
||||||
|
| `/priceList` | Ceník | `name`, `code`, `currency`, `validFrom` |
|
||||||
|
| `/project` | Projekt | `name`, `company` |
|
||||||
|
| `/task` | Úkol | `title`, `priority`, `owner`, `resolver`, `deadline` |
|
||||||
|
| `/email` | E-mail (aktivita) | `title`, `priority`, `owner` |
|
||||||
|
| `/event` | Událost | `title`, `priority`, `owner` |
|
||||||
|
| `/meeting` | Schůzka | `title`, `priority`, `owner` |
|
||||||
|
| `/phoneCall` | Telefonát | `title`, `priority`, `owner` |
|
||||||
|
| `/letter` | Dopis | `title`, `priority`, `owner` |
|
||||||
|
| `/webhook` | Webhook | `url`, `events` |
|
||||||
|
|
||||||
|
Operace u každé entity:
|
||||||
|
|
||||||
| Metoda | Cesta | Popis |
|
| Metoda | Cesta | Popis |
|
||||||
|--------|-------------------------|-------------------------------|
|
|--------|--------------------|--------------------------------------|
|
||||||
| POST | `/company` | Vytvoří firmu (validace) |
|
| POST | `/{entita}` | Vytvoří záznam (validovaný objekt) |
|
||||||
| GET | `/company` | Seznam firem (offset/limit/fulltext) |
|
| GET | `/{entita}` | Seznam (`offset`, `limit`, `fulltext` + libovolné RAYNET filtry) |
|
||||||
| GET | `/company/{id}` | Detail firmy |
|
| GET | `/{entita}/{id}` | Detail |
|
||||||
| PUT | `/company/{id}` | Úprava firmy |
|
| PUT | `/{entita}/{id}` | Částečná úprava (všechna pole volitelná) |
|
||||||
| DELETE | `/company/{id}` | Smazání firmy |
|
| DELETE | `/{entita}/{id}` | Smazání |
|
||||||
|
|
||||||
### Generický průchod na celé API
|
Enumy firmy: `rating` = `A`/`B`/`C`; `state` = `A_POTENTIAL`/`B_ACTUAL`/`C_DEFERRED`/`D_UNATTRACTIVE`;
|
||||||
|
`role` = `A_SUBSCRIBER`/`B_PARTNER`/`C_SUPPLIER`/`D_RIVAL`; `taxPayer` = `YES`/`NO`.
|
||||||
|
|
||||||
|
> Pole jsou v `camelCase` (tak je očekává RAYNET). Nevyjmenovaná pole jsou
|
||||||
|
> povolená (`extra="allow"`), takže lze poslat i pole, která model neuvádí.
|
||||||
|
|
||||||
|
### Generický průchod na zbytek API
|
||||||
|
|
||||||
|
Pro entity bez typovaného modelu – zejména **uživatelé** (`userAccount`),
|
||||||
|
soubory (`file`), DMS dokumenty/složky, GDPR, hromadné e-maly a všechny
|
||||||
|
**číselníky** (`currency`, `legalForm`, `paymentTerm`, `taxRate`, `territory`,
|
||||||
|
`contactSource`, `companyCategory`, `productCategory`, `leadPhase`,
|
||||||
|
`businessCasePhase`, `offerStatus`, `salesOrderStatus`, `projectStatus`,
|
||||||
|
`maritalStatus`, `employeesNumber`, `economyActivity`, klasifikace 1/2/3, …):
|
||||||
|
|
||||||
| Metoda | Cesta | Mapuje se na RAYNET |
|
| Metoda | Cesta | Mapuje se na RAYNET |
|
||||||
|--------|-----------------------------|-------------------------------|
|
|--------|-----------------------------|-------------------------------|
|
||||||
@@ -56,19 +94,33 @@ Veřejně přes AppFactory reverse proxy: `https://services.csbot.cz/apps/<app-i
|
|||||||
| PUT | `/api/{resource}/{id}` | `POST /{resource}/{id}/` (update) |
|
| PUT | `/api/{resource}/{id}` | `POST /{resource}/{id}/` (update) |
|
||||||
| DELETE | `/api/{resource}/{id}` | `DELETE /{resource}/{id}/` |
|
| DELETE | `/api/{resource}/{id}` | `DELETE /{resource}/{id}/` |
|
||||||
|
|
||||||
## Vytvoření firmy – povinná pole
|
### Vnořené zdroje a speciální akce
|
||||||
|
|
||||||
| Pole | Typ | Hodnoty |
|
Pro kolekce a akce nad záznamem (`sub` = zbytek cesty, přesně dle RAYNET vč.
|
||||||
|----------|--------|------------------------------------------------------------|
|
koncového lomítka u kolekcí):
|
||||||
| `name` | string | název firmy |
|
|
||||||
| `rating` | enum | `A`, `B`, `C` |
|
|
||||||
| `state` | enum | `A_POTENTIAL`, `B_ACTUAL`, `C_DEFERRED`, `D_UNATTRACTIVE` |
|
|
||||||
| `role` | enum | `A_SUBSCRIBER`, `B_PARTNER`, `C_SUPPLIER`, `D_RIVAL` |
|
|
||||||
|
|
||||||
Volitelná: `regNumber` (IČO), `taxNumber` (DIČ), `taxPayer` (`YES`/`NO`),
|
| Metoda | Cesta | Příklad cíle |
|
||||||
`bankAccount`, `notice`, `owner`, `category`, `legalForm`, `paymentTerm`,
|
|--------|----------------------------------------|--------------|
|
||||||
`turnover`, `territory`, `addresses`, `tags`, `customFields` a další dle
|
| GET | `/api/{resource}/{id}/{sub}` | `/invoice/{id}/pdfExport`, `/company/{id}/relationship/` |
|
||||||
RAYNET `CompanyInsertDto`.
|
| POST | `/api/{resource}/{id}/{sub}` | `/invoice/{id}/cancel`, `/company/{id}/lock`, `/company/{id}/merge/{srcId}/` |
|
||||||
|
| PUT | `/api/{resource}/{id}/{sub}` | `/company/{id}/address/`, `/invoice/{id}/payment/`, `/offer/{id}/item/` |
|
||||||
|
| DELETE | `/api/{resource}/{id}/{sub}` | `/company/{id}/address/5/` |
|
||||||
|
|
||||||
|
### Raw – 100 % pokrytí
|
||||||
|
|
||||||
|
Pro jakoukoli cestu / metodu (i netypické, např. `PUT /invoice/creditNote`):
|
||||||
|
|
||||||
|
```text
|
||||||
|
POST /raw
|
||||||
|
{ "method": "PUT", "path": "/invoice/creditNote", "params": {...}, "data": {...} }
|
||||||
|
```
|
||||||
|
|
||||||
|
### Tvar odpovědí
|
||||||
|
|
||||||
|
- Create → `{ "success": true, "id": 123, "data": {...} }`
|
||||||
|
- Detail → `{ "success": true, "data": { ...objekt vč. id, rowInfo } }`
|
||||||
|
- Seznam → `{ "success": true, "totalCount": N, "data": [ ... ] }`
|
||||||
|
- Update / Delete → `{ "success": true }`
|
||||||
|
|
||||||
### Příklad těla `POST /company`
|
### Příklad těla `POST /company`
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user