Files
idoklad/documentation/datumy-utc.md
T
JiriUhlirandClaude Opus 5 225161e4ca revize endpointu: cteni NullableProperty, filtr v UTC, attachments
Kontrola vsech 180 operaci proti falesnemu iDoklad API a round trip
331 modelu SDK v obou smerech.

- SdkNullablePropertyConverter doplnuje cteni NullableProperty<T>.
  Konvertor SDK umi jen zapis, takze PATCH s takovou polozkou koncil
  prazdnou 500 uz pri cteni tela. Tykalo se 29 modelu. Zapis zustava
  na konvertoru SDK, odchozi payload se nemeni.
- Datum uvnitr NullableProperty se normalizuje na UTC stejne jako
  zbytek serializace.
- ListModifiers parsuje datum ve filtru s AdjustToUniversal a
  AssumeUniversal. Filtr se zonou se drive posouval o offset.
- POST /attachments kontroluje FileName a FileBytes, SDK na ne sahalo
  bez kontroly na null a vracelo neosetrenou 500.
- ExceptionHandlingMiddleware ma posledni zachyt, zadny request uz
  nekonci prazdnou 500.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 11:13:21 +02:00

74 lines
3.3 KiB
Markdown

# Datumy v requestech a UTC kontrola SDK
## Problém
`POST /issued-invoices` (a stejně tak další Post/Patch endpointy) končil chybou HTTP 500
s prázdným tělem. V logu kontejneru:
```text
IdokladSdk.Exceptions.IdokladValidationException: Model is not valid.
DateTime must be in UTC format.
DateTime must be in UTC format.
DateTime must be in UTC format.
DateTime must be in UTC format
at IdokladSdk.Clients.BaseClient.ValidateModel[T](T model)
at IdokladSdk.Clients.BaseClient.PostAsync[TPostModel,TGetModel](String resource, TPostModel model, ...)
```
Chyby byly čtyři, protože model faktury má čtyři datumy: `DateOfIssue`, `DateOfMaturity`,
`DateOfTaxing` a `DateOfVatApplication`.
Příčina:
- Klient posílá datum bez časové zóny, například `"DateOfIssue":"2026-08-25"`.
- Newtonsoft s výchozím `DateTimeZoneHandling.RoundtripKind` takovou hodnotu načte jako
`DateTimeKind.Unspecified`.
- SDK před odesláním requestu validuje model a u každé položky `DateTime` vyžaduje `Kind == Utc`.
Validace tedy selhala ještě předtím, než se cokoli odeslalo do iDokladu.
- `IdokladValidationException` dědí z `System.ComponentModel.DataAnnotations.ValidationException`,
ne z `IdokladBaseException`. `ExceptionHandlingMiddleware` ji proto nezachytil a request skončil
jako neošetřená 500 bez těla. Z odpovědi nebylo poznat, co je špatně.
Ověřeno, že s tím nesouvisí změna serializace z [serializace-odpovedi.md](serializace-odpovedi.md).
Deserializace stejného JSONu dává `Kind=Unspecified` shodně s původním `DefaultContractResolver`
i s novým `SdkContractResolver`.
## Řešení
| Soubor | Změna |
| --- | --- |
| `Program.cs` | `SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Utc` |
| `Infrastructure/ExceptionHandlingMiddleware.cs` | nový `catch (IdokladValidationException)` mapovaný na HTTP 400 |
Detaily:
- `DateTimeZoneHandling.Utc` u hodnoty bez zóny pouze nastaví `Kind` na `Utc`, hodnotu neposouvá.
Z `"2026-08-25"` vznikne `2026-08-25T00:00:00Z`, takže datum zůstává stejné.
- Hodnota, která zónu nese (například `"2026-08-25T10:00:00+02:00"`), se přepočítá do UTC.
To je požadované chování, iDoklad pracuje s UTC.
- Nastavení platí pro celý serializer, tedy pro všechny agendy, ne jen pro vydané faktury.
- Nový `catch` vrací `problem+json` se statusem 400, zprávou ze SDK a seznamem vadných vlastností
v poli `invalidProperties`. Validační chyba už neskončí jako prázdná 500.
## Ověření
1. `dotnet build -c Release` bez chyb.
2. Deserializace původního JSONu z logu klienta:
- před změnou všechny čtyři datumy `Kind=Unspecified`
- po změně všechny čtyři `Kind=Utc` a shodná hodnota data
3. Běh služby lokálně, `POST /issued-invoices` se stejným tělem, jaké dřív padalo:
- dříve: HTTP 500, prázdné tělo, v logu `DateTime must be in UTC format`
- nyní: HTTP 401 `invalid_client`, tedy request prošel validací a došel až k přihlášení
do iDokladu (test běžel s neplatnými credentials)
4. Model s chybějícími povinnými poli vrací HTTP 400 se seznamem chyb.
Po nasazení musí projít:
```text
POST https://services.csbot.cz/apps/idoklad/issued-invoices
```
s datumy ve tvaru `2026-08-25`.
Navazující revize všech ostatních endpointů: [revize-endpointu.md](revize-endpointu.md).