diff --git a/.claude/harvest/woa-rs/HARVEST.md b/.claude/harvest/woa-rs/HARVEST.md index 1e3b073..ea6533d 100644 --- a/.claude/harvest/woa-rs/HARVEST.md +++ b/.claude/harvest/woa-rs/HARVEST.md @@ -1,46 +1,65 @@ -# HARVEST — WoA sink-in substrate v1 +# HARVEST — WoA sink-in substrate v2 -`WoA` (Flask-SQLAlchemy, `/home/user/WoA/models.py`, READ-ONLY corpus) transcoded -through the ruff → OGAR pipeline into the V3 transpile substrate. Produced for -MISSION "OGAR V3: Criticals testen + Python-Arm real machen (WoA 1:1)" as the -concrete SPEC-5 Part B/C/D reality-check artifact set. +`WoA` (Flask-SQLAlchemy, `/home/user/WoA/models.py` **+** `/home/user/WoA/woa/models_shop.py`, +READ-ONLY corpus) transcoded through the ruff → OGAR pipeline into the V3 transpile substrate. +Produced for MISSION "OGAR V3: Criticals testen + Python-Arm real machen (WoA 1:1)" as the concrete +SPEC-B1-3 reality-check artifact set (v2 of the SPEC-5 Part B/C/D harvest). + +**v1 → v2 change:** the corpus grew from the monolithic `models.py` (139 classes) to +`models.py` (139) **+** `woa/models_shop.py` (12) = **151** classes, merged before minting via a +second `ruff_sqlalchemy_spo::extract_file` call and `ModelGraph.models.extend()` (same `"woa"` +namespace, so shop→core associations resolve against the combined corpus). The emitted `models.py` +was also regenerated through the FIXED `emit_python` (Batch-1 Item 1: `emit_python_prelude()` + +`Optional[...]` nullability) — the v2 module now **imports**, not just `py_compile`s (closes v1 gap +#3, see below). ## Pipeline (real code path, nothing hand-written) ``` -/home/user/WoA/models.py - │ ruff_sqlalchemy_spo::extract_file (frontend, /tmp/wt-gr crates/ruff_sqlalchemy_spo, - │ branch claude/spo-python-main) +/home/user/WoA/models.py + /home/user/WoA/woa/models_shop.py + │ ruff_sqlalchemy_spo::extract_file (x2, same "woa" namespace) (frontend, /tmp/wt-gr + │ crates/ruff_sqlalchemy_spo, + │ branch claude/spo-python-main) ▼ -ruff_spo_triplet::ModelGraph +ruff_spo_triplet::ModelGraph (merged: primary.models.extend(shop.models)) │ ogar-from-ruff::sqlalchemy::lift_model_graph_sqlalchemy ▼ Vec (schema stratum: attributes / associations) │ ogar-from-ruff::mint::compile_graph_sqlalchemy:: ▼ Vec (Class + 16-byte rail Facet, classid via WoaPort) - │ ogar-from-ruff::emit::emit_python -> models.py - │ (association walk, classid-annotated) -> woa_graph.spo + │ ogar-from-ruff::emit::emit_python_prelude() + emit::emit_python -> models.py + │ (association walk, classid-annotated) -> woa_graph.spo │ ogar-adapter-postgres-ddl::emit_facet_table_ddl + emit_postgres_ddl -> woa_facet.sql ▼ -this directory +this directory (+ ogar_runtime.py shipped alongside, see "Import gate" below) ``` -Driver: a scratch Cargo binary at `/tmp/harvest-woa` (NOT committed anywhere — -path-deps + a local `[patch]` block only, per the mission's leitplanken). See -"Reproduction" below for the exact commands to regenerate. +Driver: a scratch Cargo binary at `/tmp/harvest-woa` (NOT committed anywhere — path-deps + a local +`[patch]` block only, per the mission's leitplanken). See "Reproduction" below for the exact +commands to regenerate. -## Metrics +## Metrics (v2, rerun-measured — not hand-estimated, quoted from the driver's own stderr) -| Metric | Value | -|---|---| -| `db.Model` classes harvested | **139** (`ruff_sqlalchemy_spo` corpus-gate test asserts `>= 139`; WoA's own `grep -c 'class.*db.Model'` on `models.py` also gives 139 — exact match, no discrepancy) | -| Attributes (`Attribute` after FK-dedup) | **1961** | -| Associations (`db.relationship`, `BelongsTo`/`HasMany`) | **107** | -| Aliased (resolve through `WOA_ALIASES` convergence pins, concept ≠ 0) | **6** | -| Bootstrap (unmapped model name → classid `0x0000_0003`, concept = 0) | **133** | +``` +harvested 139 models from /home/user/WoA/models.py +harvested 12 shop models from /home/user/WoA/woa/models_shop.py +total 151 models +TimesheetActivity -> classid 0x01030003 (concept 0x0103, app 0x0003) +dangling .spo edges: 0/112 +done: 151 classes, 2154 attrs, 112 assocs, 6 aliased, 145 bootstrap, 0 dangling +``` + +| Metric | v1 (139) | v2 (151) | +|---|---|---| +| `db.Model` classes harvested | 139 | **151** (139 `models.py` + 12 `woa/models_shop.py`) | +| Attributes (`Attribute` after FK-dedup) | 1961 | **2154** | +| Associations (`db.relationship`, `BelongsTo`/`HasMany`) | 107 | **112** | +| Aliased (resolve through `WOA_ALIASES` convergence pins, concept ≠ 0) | 6 | **6** (unchanged — no shop model matches a `WOA_ALIASES` pin) | +| Bootstrap (unmapped model name → classid `0x0000_0003`, concept = 0) | 133 | **145** (133 + all 12 new shop models — verified: every `Shop*` class emits `CLASSID: ClassVar[int] = 0x00000000`) | +| Dangling `.spo` edges | 0/107 | **0/112** (shop associations also resolve within the combined 151-class corpus) | -### The 6 aliased classes (WOA_ALIASES convergence pins, `ogar-vocab/src/ports.rs`) +### The 6 aliased classes (WOA_ALIASES convergence pins, `ogar-vocab/src/ports.rs`) — unchanged in v2 | Class | classid | concept | Same concept converges with | |---|---|---|---| @@ -51,14 +70,13 @@ path-deps + a local `[patch]` block only, per the mission's leitplanken). See | `RecurringInvoice` | `0x0202_0003` | `0x0202` | same concept as `WorkOrder` — both are "billing document" shaped | | `TaxRate` | `0x0203_0003` | `0x0203` | | -The remaining 133 models mint the **bootstrap** classid `0x0000_0003` -(concept `0x0000`, WoA app prefix `0x0003`) — `WOA_ALIASES` is a small, -deliberately curated table (per `ports.rs` docs, a handful of named -convergence pins), not a name-for-name mirror of all 139 WoA models. This is -the honest, measured state (matches `sqlalchemy.rs`'s own doc comment on -`compile_graph_sqlalchemy`, which names this exact caveat for -`TimesheetActivity`) — no attempt was made to mint new aliases (mint -governance: no new Codebook concepts without an Operator ruling). +The 12 new `models_shop.py` classes (`ShopProduct`, `ShopCategory`, `ShopOrder`, `ShopOrderItem`, +`ShopCartSession`, `ShopPage`, `ShopShippingMethod`, `ShopPaymentMethod`, `ShopCustomer`, +`ShopPaymentConfig`, `ShopSaasConfig`, `ShopProductReview`) all mint the bootstrap classid +`0x0000_0003` — verified directly against the emitted module (`grep -A2 'class Shop' models.py` +shows `CLASSID: ClassVar[int] = 0x00000000` for all twelve). `ShopCustomer` does **not** collide +with the aliased `Customer` — they remain distinct classes with distinct (bootstrap vs. aliased) +classids, per mint governance (no new codebook aliases/mints without an Operator ruling). ### Convergence spot-check (mission-mandated) @@ -66,121 +84,108 @@ governance: no new Codebook concepts without an Operator ruling). TimesheetActivity -> classid 0x01030003 (concept 0x0103, app 0x0003) ``` -Matches the fixture in `ogar-from-ruff/src/mint.rs`'s own doc comment on +Unchanged from v1 — `TimesheetActivity` lives in the primary `models.py`, unaffected by the +`models_shop.py` merge. Matches the fixture in `ogar-from-ruff/src/mint.rs`'s own doc comment on `compile_graph_sqlalchemy` and its test -`compile_graph_sqlalchemy_timesheet_activity_converges_via_alias_not_bootstrap` -byte-for-byte. `TimesheetActivity`'s harvested shape also matches WoA source -`models.py:1746-1753` exactly: `id` (required), `beschreibung` (required), -`created_at` (nullable — no `nullable=False` in source), `timesheet_id` -FK-shadowed by the `timesheet: ToOne["TimeSheet"]` association (not -double-projected). +`compile_graph_sqlalchemy_timesheet_activity_converges_via_alias_not_bootstrap` byte-for-byte. + +## Import gate (v2 closes v1 gap #3 — proof, not just `py_compile`) + +`py_compile` only proves the module *parses*; it does not evaluate class-body annotation +expressions in a way that would raise `NameError` on an unresolved name. v1's `models.py` failed a +real `import` (`OgInt`/`OgStr`/etc. were referenced but never imported). v2 fixes this at the +source (Batch-1 Item 1's `emit_python_prelude()`), and this harvest now runs the import gate +directly in this directory (with `ogar_runtime.py` shipped alongside, since a real consumer may not +have it on `sys.path` yet): + +```sh +$ cd .claude/harvest/woa-rs # (or woa-sinkin-substrate on the WoA side) +$ python3 -m py_compile models.py +$ python3 -c "import models; print('IMPORT_OK', len(models.__dict__))" +IMPORT_OK 176 +``` + +Both commands exit 0. `ogar_runtime.py` (the reference wrapper-contract shipped from +`ogar-from-ruff/python/ogar_runtime.py`, Item 1) sits next to `models.py` in this directory so the +gate is standalone — no external `sys.path` setup required. A real consumer may instead supply its +own `ogar_runtime` (with real Arrow/Decimal/etc.-backed types) on `sys.path` and drop this reference +copy; either way the generated module's `from ogar_runtime import (...)` contract is now closed. ## Gates run -- `python3 -m py_compile models.py` → **PASS** (module parses; see "Known - gaps" below for what this gate does NOT prove). -- `cargo test -p ruff_sqlalchemy_spo` corpus gate (`WOA_SRC=/home/user/WoA/models.py`) - already asserts `>= 139` models upstream in the frontend crate itself (not - re-run here — this harvest's own driver run independently reproduces the - same 139). +- `python3 -m py_compile models.py` → **PASS**. +- `python3 -c "import models"` → **PASS** (176 names in `models.__dict__`, i.e. no `NameError` — + v1 gap #3 is closed; see "Import gate" above for the exact transcript). +- `cargo build --quiet` (the scratch driver, against the CURRENT `ogar-from-ruff` post Item 1/2) → + **PASS**, no `[patch]`/type-identity failures (see "Build caveat" note in the Reproduction + section — the same `/tmp/wt-gr` local checkout that satisfied v1 still resolves cleanly here, + since ogar-from-ruff's `ruff_spo_triplet`/`ruff_spo_address` git deps are patched by *source URL*, + not by matching the `branch =` string). ## Reproduction ```sh -# 1. Scratch driver (path deps + local-only [patch], never committed): -mkdir -p /tmp/harvest-woa/src -cat > /tmp/harvest-woa/Cargo.toml <<'TOML' -[package] -name = "harvest-woa" -version = "0.1.0" -edition = "2021" -publish = false -[[bin]] -name = "harvest-woa" -path = "src/main.rs" -[dependencies] -ruff_sqlalchemy_spo = { path = "/tmp/wt-gr/crates/ruff_sqlalchemy_spo" } -ogar-from-ruff = { path = "/workspace/ogar/crates/ogar-from-ruff" } -ogar-vocab = { path = "/workspace/ogar/crates/ogar-vocab" } -ogar-adapter-postgres-ddl = { path = "/workspace/ogar/crates/ogar-adapter-postgres-ddl" } -[patch."https://github.com/AdaWorldAPI/ruff"] -ruff_spo_triplet = { path = "/tmp/wt-gr/crates/ruff_spo_triplet" } -ruff_spo_address = { path = "/tmp/wt-gr/crates/ruff_spo_address" } -TOML -cp /workspace/ogar/rust-toolchain.toml /tmp/harvest-woa/rust-toolchain.toml # pins 1.95.0 -# (src/main.rs: extract_file -> compile_graph_sqlalchemy:: -> emit_python -# + a classid-keyed association walk for woa_graph.spo + the postgres-ddl adapter -# calls for woa_facet.sql — see this harvest's provenance pins below for the -# exact commit this was run against.) - +# Driver Cargo.toml path-deps point at the B1-integration worktree (/tmp/wt-b1int, branch +# claude/b1-integration — the SHA d679f850-approved B1-1+B1-2 stand this v2 was regenerated +# against), not /workspace/ogar (session-specific worktree substitution, documented as an +# authorized mission deviation; NOT committed — this Cargo.toml lives only at /tmp/harvest-woa). cd /tmp/harvest-woa -cargo run --quiet -- /home/user/WoA/models.py /tmp/harvest-woa/out +cargo run --quiet -- /home/user/WoA/models.py /tmp/harvest-woa/out /home/user/WoA/woa/models_shop.py python3 -m py_compile /tmp/harvest-woa/out/models.py +cp /tmp/wt-b1int/crates/ogar-from-ruff/python/ogar_runtime.py /tmp/harvest-woa/out/ogar_runtime.py +cd /tmp/harvest-woa/out && python3 -c "import models" ``` ## Provenance | Input | Pin | |---|---| -| WoA (`AdaWorldAPI/WoA`, `models.py`, READ-ONLY) | `4427b3d715d841290ea9108e3bf94b22d0cb72c2` (last commit touching `models.py`) | -| ruff (`AdaWorldAPI/ruff`, `ruff_sqlalchemy_spo`) | `/tmp/wt-gr`, branch `claude/spo-python-main`, HEAD `66db5c417eddf6017e924706031a23b019c17e81` | -| OGAR (`ogar-from-ruff`, `ogar-vocab`, `ogar-adapter-postgres-ddl`) | `/workspace/ogar`, branch `claude/v3-criticals-woa-parity`, HEAD `d5479ce382136b3aedad3c122e13d3aab6f9695e` | +| WoA (`AdaWorldAPI/WoA`, `models.py` + `woa/models_shop.py`, READ-ONLY) | `438dd8c429ed5db5188118f50490ad24485c92d3` (last commit touching both files — `backup-update: sync Live-/opt/woa/ Snapshot 2026-06-28 10:58:40`) | +| ruff (`AdaWorldAPI/ruff`, `ruff_sqlalchemy_spo`) | `/tmp/wt-gr`, branch `claude/spo-python-main`, HEAD `66db5c417eddf6017e924706031a23b019c17e81` (unchanged from v1 — `/tmp/wt-gr` still exists and still hosts `ruff_sqlalchemy_spo` + `ruff_spo_{triplet,address}`) | +| OGAR (`ogar-from-ruff`, `ogar-vocab`, `ogar-adapter-postgres-ddl`) | `/tmp/wt-b1int`, branch `claude/b1-integration`, HEAD `d679f8504389bb743b08d34ae7352511d0a34b4b` (B1-1 `emit_python` prelude+`Optional[]` + B1-2 FK-dedup consolidation, approved) | ## Known gaps (honest, not papered over) -1. **Behavior = names only.** `Model::functions` (methods) are harvested - name-only by `ruff_sqlalchemy_spo::functions` (v0, per its module doc) — - no params, no `reads`/`writes`, no `body_source`. `emit_python` therefore - emits **zero** methods; the generated dataclasses are schema-only mirrors - of WoA's `db.Model` classes, not behavior-complete ports. This is the same - gap the mission's Critical #1 (`behavior`) names for `lift_actions` - generally (`ogar-from-ruff/src/lib.rs:495`) — this SQLAlchemy harvest - doesn't lift `ActionDef`/`KausalSpec` at all (v0 doesn't wire it), so the - gap is total rather than partial here. -2. **`woa/models_shop.py` (12 more `db.Model` classes) is out of scope for - this v1.** Only the monolithic `/home/user/WoA/models.py` (139 classes) - was harvested, per the mission's explicit instruction. Follow-up: run - `ruff_sqlalchemy_spo::extract_file` a second time against - `woa/models_shop.py` (or `extract_with` over the `woa/` tree) and merge - the two `ModelGraph`s before minting, so shop-domain FKs into the core 139 - resolve rather than dangling. -3. **`emit_python`'s import list is incomplete — `models.py` does not - `import` (nor is `ogar_sdk.py` shipped alongside).** The header only - imports `OgScalar, ToOne, ToMany` from `ogar_sdk` (mirroring the PR #154 - Python-emit precedent's own header), but `emit_python`'s - `og_scalar_type()` mapping emits bare names — `OgInt`, `OgStr`, `OgBool`, - `OgDateTime`, `OgFloat`, `OgMoney`, `OgDate`, `OgBytes`, `OgSelection`, - `OgJson` — that are **never imported**. `python3 -m py_compile` still - passes because compiling to bytecode does not evaluate the class-body - annotation expressions at import time in a way that raises — it is a - syntax-level gate only, not proof the module is importable/runnable. - Actually running `import models` would raise `NameError` on the first - annotated attribute. This is `ogar-from-ruff::emit_python`'s existing gap - (not introduced by this harvest), reported per the mission's instruction - to name the "emit_python required-Lücke" honestly rather than - hand-patching the generated output. -4. **`woa_graph.spo` target-classid resolution: measured, not assumed.** An - association's target (`Association::class_name`, e.g. `TimeSheet` for - `TimesheetActivity.timesheet`) is looked up by name against the 139 - harvested+minted classes; a miss (renamed target, target lives in - `models_shop.py`, or a `class_name` string that doesn't match any - harvested class) is written as `[--------]` rather than silently guessed. - Measured over the actual 107-edge output: **0 of 107 edges are - `[--------]`** — every association target in `models.py` resolves within - the same 139-class corpus (the file is self-contained; nothing points - into the excluded `models_shop.py`). This is a stronger result than - expected going in and is called out explicitly so it isn't mistaken for - an unverified assumption. -5. **`PostgreSQL` DDL is the relational (`emit_postgres_ddl`) shape, not yet - wired to the facet table.** `woa_facet.sql` emits the two pieces - side-by-side (facet table via `emit_facet_table_ddl("facet")`, then one - `CREATE TABLE` per class via `emit_postgres_ddl`) but does **not** attempt - the dual-write / parity-checker wiring the Operator's ruling (b, refined) - describes — that is explicitly a "follow-up, NOT built here" per - `ogar-adapter-postgres-ddl`'s own module doc. -6. **Falsifizierer #1 (WP-Parity)-style field-by-field diff against the WoA - source was not run for all 139 classes** — only the `TimesheetActivity` - fixture (§ "Convergence spot-check" above) was checked field-for-field - against source. A full N/N-columns-typed parity metric (mission's - D-PARITY-PROBE-style measure) across all 139 classes is a reasonable - next step but out of scope for this harvest pass. +1. **Behavior = names only.** Unchanged from v1 — `Model::functions` (methods) are harvested + name-only by `ruff_sqlalchemy_spo::functions` (v0). `emit_python` emits **zero** methods for + either the 139 core or the 12 shop classes; this v2 pass does not touch the mission's Critical + #1 (`behavior`) gap at all. +2. **`woa/models_shop.py` gap — RESOLVED in v2.** v1 gap #2 ("`woa/models_shop.py` out of scope") + is closed: the driver now runs `ruff_sqlalchemy_spo::extract_file` a second time against + `woa/models_shop.py` and merges (`ModelGraph.models.extend()`) before minting, in the same + `"woa"` namespace as the primary corpus. All 12 shop classes are present in the emitted + `models.py` (verified by name, see the aliased-classes section above) and the corpus total is + the expected 139 + 12 = 151. +3. **`emit_python`'s import list — RESOLVED in v2.** v1 gap #3 ("`models.py` does not `import`") is + closed by Batch-1 Item 1 (`emit_python_prelude()` emits `from ogar_runtime import (OgScalar, + OgStr, OgInt, OgFloat, OgMoney, OgBool, OgDate, OgDateTime, OgBytes, OgSelection, OgJson, ToOne, + ToMany)` plus `from __future__ import annotations`, `from dataclasses import dataclass`, + `from typing import ClassVar, Optional`) and this harvest shipping `ogar_runtime.py` (Item 1's + reference wrapper-contract) alongside `models.py`. Verified by *running* `import models`, not + just `py_compile` — see "Import gate" above for the exact transcript (`IMPORT_OK 176`). +4. **`woa_graph.spo` target-classid resolution: re-measured for v2, still 0 dangling.** An + association's target is looked up by name against all 151 harvested+minted classes (up from + 139); a miss is written as `[--------]` rather than silently guessed. Measured over the actual + 112-edge v2 output (up from 107 in v1): **0 of 112 edges are `[--------]`** — every association + target, including any shop→core references introduced by `models_shop.py`, resolves within the + combined 151-class corpus. Quoted directly from the driver's own stderr + (`dangling .spo edges: 0/112`), not hand-counted. +5. **`PostgreSQL` DDL is the relational (`emit_postgres_ddl`) shape, not yet wired to the facet + table.** Unchanged from v1 — `woa_facet.sql` emits the two pieces side-by-side but does not + attempt the dual-write / parity-checker wiring from the Operator's ruling (b, refined); that + remains a documented follow-up, not built here. +6. **Falsifizierer #1 (WP-Parity)-style field-by-field diff against the WoA source was not run for + all 151 classes.** Unchanged scope from v1 — only the `TimesheetActivity` fixture (§ "Convergence + spot-check" above) is checked field-for-field against source; a full N/N-columns-typed parity + metric across all 151 classes remains a reasonable next step, out of scope for this harvest pass. +7. **Build/dependency note (report, not a gap in the artifact):** the driver's Cargo.toml points at + `/tmp/wt-b1int` (not `/workspace/ogar`) for `ogar-from-ruff`/`ogar-vocab`/ + `ogar-adapter-postgres-ddl` — an authorized session-specific substitution (the approved, + integrated B1-1+B1-2 stand lives in that worktree, on branch `claude/b1-integration`, not yet + merged to `/workspace/ogar`'s working branch at harvest time). The `[patch."https://github.com/ + AdaWorldAPI/ruff"]` block still resolves `ruff_spo_triplet`/`ruff_spo_address` to `/tmp/wt-gr` + for both consumers (`ruff_sqlalchemy_spo` and, transitively, `ogar-from-ruff`, which now declares + `branch = "main"` for those deps rather than the old convergence branch name) — Cargo's `[patch]` + keys on the git source URL, not the declared branch string, so the override still applies and + `cargo build` succeeded with no type-identity failures. This Cargo.toml is not committed anywhere + (scratch driver only, per mission leitplanken). diff --git a/.claude/harvest/woa-rs/PARKED.md b/.claude/harvest/woa-rs/PARKED.md index 80e1187..cbf2e5c 100644 --- a/.claude/harvest/woa-rs/PARKED.md +++ b/.claude/harvest/woa-rs/PARKED.md @@ -1,7 +1,8 @@ # PARKED — belongs in `AdaWorldAPI/woa-rs` This directory is the ruff → OGAR transcode of `AdaWorldAPI/WoA`'s -Flask-SQLAlchemy `models.py` (139 `db.Model` classes) into the OGAR V3 +Flask-SQLAlchemy `models.py` (139 `db.Model` classes) **+ `woa/models_shop.py` +(12 `db.Model` classes)** — **151** total (v2) — into the OGAR V3 sink-in substrate. Its real home is `AdaWorldAPI/woa-rs` (the Rust port repo this session's WoA-side context — `/home/user/WoA/.claude/CLAUDE.md` §4 "Zwei-Welten-Pattern" — names as the sibling repo, read-only from the @@ -31,18 +32,21 @@ copying in, since this session cannot read that repo to confirm. ## What's in this harvest See `HARVEST.md` (same directory) for the full metrics, provenance, gates, -and — importantly — the honestly-documented gaps (behavior is names-only, -`models_shop.py`'s 12 classes are out of scope, `emit_python`'s import list -is incomplete, and others). Read `HARVEST.md` before relying on `models.py` +and — importantly — the honestly-documented gaps. v2 RESOLVES two v1 gaps: +`models_shop.py`'s 12 classes are now harvested (merged pre-mint), and +`emit_python`'s import list is closed (`emit_python_prelude()` + shipped +`ogar_runtime.py` — the module now *imports*, verified by running +`python3 -c "import models"`, not just `py_compile`). Behavior remains +names-only (unchanged gap). Read `HARVEST.md` before relying on `models.py` here as anything more than a structural (schema-stratum) reality check. ## Provenance | Input | Pin | |---|---| -| WoA (`AdaWorldAPI/WoA`, `models.py`, READ-ONLY) | `4427b3d715d841290ea9108e3bf94b22d0cb72c2` | +| WoA (`AdaWorldAPI/WoA`, `models.py` + `woa/models_shop.py`, READ-ONLY) | `438dd8c429ed5db5188118f50490ad24485c92d3` | | ruff (`AdaWorldAPI/ruff`, `ruff_sqlalchemy_spo`) | `/tmp/wt-gr`, branch `claude/spo-python-main`, `66db5c417eddf6017e924706031a23b019c17e81` | -| OGAR (`ogar-from-ruff` / `ogar-vocab` / `ogar-adapter-postgres-ddl`) | `/workspace/ogar`, branch `claude/v3-criticals-woa-parity`, `d5479ce382136b3aedad3c122e13d3aab6f9695e` | +| OGAR (`ogar-from-ruff` / `ogar-vocab` / `ogar-adapter-postgres-ddl`) | `/tmp/wt-b1int`, branch `claude/b1-integration`, `d679f8504389bb743b08d34ae7352511d0a34b4b` | Regenerate with the scratch driver documented in `HARVEST.md`'s "Reproduction" section (not itself committed anywhere, per the mission's diff --git a/.claude/harvest/woa-rs/models.py b/.claude/harvest/woa-rs/models.py index 5a179ad..4159513 100644 --- a/.claude/harvest/woa-rs/models.py +++ b/.claude/harvest/woa-rs/models.py @@ -1,17 +1,23 @@ -"""WoA sink-in substrate v1 — generated, do not hand-edit. +"""WoA sink-in substrate v2 — generated, do not hand-edit. -Source: /home/user/WoA/models.py (READ-ONLY corpus, 139 `db.Model` classes harvested) -Pipeline: ruff_sqlalchemy_spo::extract_file -> ogar-from-ruff::lift_model_graph_sqlalchemy +Sources: /home/user/WoA/models.py + /home/user/WoA/woa/models_shop.py (READ-ONLY corpus, 151 `db.Model` classes harvested) +Pipeline: ruff_sqlalchemy_spo::extract_file (x2, .models.extend() merge) + -> ogar-from-ruff::lift_model_graph_sqlalchemy -> ogar-from-ruff::mint::compile_graph_sqlalchemy:: - -> ogar-from-ruff::emit::emit_python -Metrics: 139 classes, 1961 attributes, 107 associations, 6 aliased (WOA_ALIASES convergence pin) / 133 bootstrap (classid 0x0000_0003). + -> ogar-from-ruff::emit::emit_python (+ emit_python_prelude) +Metrics: 151 classes, 2154 attributes, 112 associations, 6 aliased (WOA_ALIASES convergence pin) / 145 bootstrap (classid 0x0000_0003). +Dangling .spo edges: 0/112. TimesheetActivity -> classid 0x01030003 (concept 0x0103, app 0x0003) """ +from __future__ import annotations from dataclasses import dataclass -from typing import ClassVar - -from ogar_sdk import OgScalar, ToOne, ToMany +from typing import ClassVar, Optional +from ogar_runtime import ( + OgScalar, OgStr, OgInt, OgFloat, OgMoney, OgBool, + OgDate, OgDateTime, OgBytes, OgSelection, OgJson, + ToOne, ToMany, +) @dataclass @@ -21,57 +27,57 @@ class Tenant: id: OgInt name: OgStr slug: OgStr - max_users: OgInt - aktiv: OgBool - logo_path: OgStr - logo_mail_path: OgStr - created_at: OgDateTime - branche: OgStr - is_test: OgBool - is_anbieter: OgBool - mod_tresor: OgBool - mod_stundenzettel: OgBool - mod_multitimer: OgBool - mod_fahrtenbuch: OgBool - mod_wiedervorlage: OgBool - mod_notizbuch: OgBool - mod_wartung: OgBool - mod_abo: OgBool - mod_inventar: OgBool - mod_abnahme: OgBool - mod_gobd: OgBool - mod_referral: OgBool - mod_kaltakquise: OgBool - mod_woa_service: OgBool - mod_erp: OgBool - mod_dms: OgBool - mod_rustdesk: OgBool - mod_rustdesk_server: OgBool - erp_gobd_festschreibung: OgBool - erp_mod_stammdaten: OgBool - erp_mod_fibu: OgBool - erp_mod_bank: OgBool - erp_mod_steuer: OgBool - erp_mod_lager: OgBool - erp_mod_dms: OgBool - erp_mod_reporting: OgBool - erp_mod_pos: OgBool - erp_mod_zugferd: OgBool - erp_mod_lohn: OgBool - erp_mod_shop: OgBool - erp_mod_crm: OgBool - erp_mod_stammdaten_gesperrt: OgBool - erp_mod_fibu_gesperrt: OgBool - erp_mod_bank_gesperrt: OgBool - erp_mod_steuer_gesperrt: OgBool - erp_mod_lager_gesperrt: OgBool - erp_mod_dms_gesperrt: OgBool - erp_mod_reporting_gesperrt: OgBool - erp_mod_pos_gesperrt: OgBool - erp_mod_zugferd_gesperrt: OgBool - erp_mod_crm_gesperrt: OgBool - erp_mod_lohn_gesperrt: OgBool - erp_mod_shop_gesperrt: OgBool + max_users: Optional[OgInt] + aktiv: Optional[OgBool] + logo_path: Optional[OgStr] + logo_mail_path: Optional[OgStr] + created_at: Optional[OgDateTime] + branche: Optional[OgStr] + is_test: Optional[OgBool] + is_anbieter: Optional[OgBool] + mod_tresor: Optional[OgBool] + mod_stundenzettel: Optional[OgBool] + mod_multitimer: Optional[OgBool] + mod_fahrtenbuch: Optional[OgBool] + mod_wiedervorlage: Optional[OgBool] + mod_notizbuch: Optional[OgBool] + mod_wartung: Optional[OgBool] + mod_abo: Optional[OgBool] + mod_inventar: Optional[OgBool] + mod_abnahme: Optional[OgBool] + mod_gobd: Optional[OgBool] + mod_referral: Optional[OgBool] + mod_kaltakquise: Optional[OgBool] + mod_woa_service: Optional[OgBool] + mod_erp: Optional[OgBool] + mod_dms: Optional[OgBool] + mod_rustdesk: Optional[OgBool] + mod_rustdesk_server: Optional[OgBool] + erp_gobd_festschreibung: Optional[OgBool] + erp_mod_stammdaten: Optional[OgBool] + erp_mod_fibu: Optional[OgBool] + erp_mod_bank: Optional[OgBool] + erp_mod_steuer: Optional[OgBool] + erp_mod_lager: Optional[OgBool] + erp_mod_dms: Optional[OgBool] + erp_mod_reporting: Optional[OgBool] + erp_mod_pos: Optional[OgBool] + erp_mod_zugferd: Optional[OgBool] + erp_mod_lohn: Optional[OgBool] + erp_mod_shop: Optional[OgBool] + erp_mod_crm: Optional[OgBool] + erp_mod_stammdaten_gesperrt: Optional[OgBool] + erp_mod_fibu_gesperrt: Optional[OgBool] + erp_mod_bank_gesperrt: Optional[OgBool] + erp_mod_steuer_gesperrt: Optional[OgBool] + erp_mod_lager_gesperrt: Optional[OgBool] + erp_mod_dms_gesperrt: Optional[OgBool] + erp_mod_reporting_gesperrt: Optional[OgBool] + erp_mod_pos_gesperrt: Optional[OgBool] + erp_mod_zugferd_gesperrt: Optional[OgBool] + erp_mod_crm_gesperrt: Optional[OgBool] + erp_mod_lohn_gesperrt: Optional[OgBool] + erp_mod_shop_gesperrt: Optional[OgBool] @dataclass class User: @@ -80,13 +86,13 @@ class User: id: OgInt username: OgStr password_hash: OgStr - firstname: OgStr - lastname: OgStr - email: OgStr - phone: OgStr - ma_rabatt: OgFloat - is_admin: OgBool - is_superadmin: OgBool + firstname: Optional[OgStr] + lastname: Optional[OgStr] + email: Optional[OgStr] + phone: Optional[OgStr] + ma_rabatt: Optional[OgFloat] + is_admin: Optional[OgBool] + is_superadmin: Optional[OgBool] perm_dashboard_umsaetze: OgBool perm_buchhaltung: OgBool perm_statistik: OgBool @@ -107,20 +113,20 @@ class User: perm_erp_dms: OgBool perm_erp_compliance: OgBool perm_erp_lohn: OgBool - failed_attempts: OgInt - locked_until: OgDateTime - scan_import_mode: OgStr - scan_import_username: OgStr - scan_import_pw_set_at: OgDateTime - scan_import_host: OgStr - scan_import_port: OgInt + failed_attempts: Optional[OgInt] + locked_until: Optional[OgDateTime] + scan_import_mode: Optional[OgStr] + scan_import_username: Optional[OgStr] + scan_import_pw_set_at: Optional[OgDateTime] + scan_import_host: Optional[OgStr] + scan_import_port: Optional[OgInt] vpn_enabled: OgBool - vpn_ip: OgStr - vpn_pubkey: OgStr - vpn_created_at: OgDateTime + vpn_ip: Optional[OgStr] + vpn_pubkey: Optional[OgStr] + vpn_created_at: Optional[OgDateTime] samba_enabled: OgBool - samba_pw_set_at: OgDateTime - created_at: OgDateTime + samba_pw_set_at: Optional[OgDateTime] + created_at: Optional[OgDateTime] tenant: ToOne["Tenant"] tresor_customer: ToOne["Customer"] @@ -129,46 +135,46 @@ class Customer: """Rail class `Customer` — classid 0x02040003 (concept 0x0204, app 0x0003).""" CLASSID: ClassVar[int] = 0x02040003 id: OgInt - tenant_id: OgInt - kdnr: OgStr - quick_token: OgStr - quick_token_ts: OgStr - firma: OgStr - anrede: OgStr - vorname: OgStr - nachname: OgStr - mail_anrede: OgStr - strasse: OgStr - adresszusatz: OgStr - plz: OgStr - ort: OgStr - email: OgStr - telefon: OgStr - tresor_pw_hash: OgStr - tresor_pw_set_at: OgDateTime - tresor_pw_failed: OgInt - tresor_pw_locked_until: OgDateTime - zahlungsziel: OgInt - skonto_prozent: OgFloat - skonto_tage: OgInt - stundensatz: OgFloat - fahrt_km: OgFloat - fahrt_kosten: OgFloat - notizen: OgStr - aktiv: OgBool - kundentyp: OgStr - referral_code: OgStr - sepa_iban: OgStr - sepa_bic: OgStr - sepa_kontoinhaber: OgStr - sepa_mandat_ref: OgStr - sepa_mandat_datum: OgDate - sepa_mandat_typ: OgStr - sepa_mandat_status: OgStr - sepa_letzte_lastschrift: OgDate - sepa_pre_notification_tage: OgInt - preis_gruppe_id: OgInt - created_at: OgDateTime + tenant_id: Optional[OgInt] + kdnr: Optional[OgStr] + quick_token: Optional[OgStr] + quick_token_ts: Optional[OgStr] + firma: Optional[OgStr] + anrede: Optional[OgStr] + vorname: Optional[OgStr] + nachname: Optional[OgStr] + mail_anrede: Optional[OgStr] + strasse: Optional[OgStr] + adresszusatz: Optional[OgStr] + plz: Optional[OgStr] + ort: Optional[OgStr] + email: Optional[OgStr] + telefon: Optional[OgStr] + tresor_pw_hash: Optional[OgStr] + tresor_pw_set_at: Optional[OgDateTime] + tresor_pw_failed: Optional[OgInt] + tresor_pw_locked_until: Optional[OgDateTime] + zahlungsziel: Optional[OgInt] + skonto_prozent: Optional[OgFloat] + skonto_tage: Optional[OgInt] + stundensatz: Optional[OgFloat] + fahrt_km: Optional[OgFloat] + fahrt_kosten: Optional[OgFloat] + notizen: Optional[OgStr] + aktiv: Optional[OgBool] + kundentyp: Optional[OgStr] + referral_code: Optional[OgStr] + sepa_iban: Optional[OgStr] + sepa_bic: Optional[OgStr] + sepa_kontoinhaber: Optional[OgStr] + sepa_mandat_ref: Optional[OgStr] + sepa_mandat_datum: Optional[OgDate] + sepa_mandat_typ: Optional[OgStr] + sepa_mandat_status: Optional[OgStr] + sepa_letzte_lastschrift: Optional[OgDate] + sepa_pre_notification_tage: Optional[OgInt] + preis_gruppe_id: Optional[OgInt] + created_at: Optional[OgDateTime] workorders: ToMany["WorkOrder"] @dataclass @@ -176,15 +182,15 @@ class Project: """Rail class `Project` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt + tenant_id: Optional[OgInt] name: OgStr - projekt_nr: OgStr - beschreibung: OgStr - status: OgStr - sort_order: OgInt - erstellt_am: OgDateTime - erstellt_von_id: OgInt - abgeschlossen_am: OgDateTime + projekt_nr: Optional[OgStr] + beschreibung: Optional[OgStr] + status: Optional[OgStr] + sort_order: Optional[OgInt] + erstellt_am: Optional[OgDateTime] + erstellt_von_id: Optional[OgInt] + abgeschlossen_am: Optional[OgDateTime] customer: ToOne["Customer"] notes: ToOne["ProjectNote"] @@ -193,11 +199,11 @@ class ProjectNote: """Rail class `ProjectNote` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - titel: OgStr - inhalt_text: OgScalar - inhalt_zeichnung: OgScalar - erstellt_am: OgDateTime - erstellt_von_id: OgInt + titel: Optional[OgStr] + inhalt_text: Optional[OgScalar] + inhalt_zeichnung: Optional[OgScalar] + erstellt_am: Optional[OgDateTime] + erstellt_von_id: Optional[OgInt] project: ToOne["Project"] @dataclass @@ -205,11 +211,11 @@ class ErpArticleCategory: """Rail class `ErpArticleCategory` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt + tenant_id: Optional[OgInt] name: OgStr - icon: OgStr - sort_order: OgInt - created_at: OgDateTime + icon: Optional[OgStr] + sort_order: Optional[OgInt] + created_at: Optional[OgDateTime] parent: ToMany["ErpArticleCategory"] @dataclass @@ -217,14 +223,14 @@ class ErpStorageLocation: """Rail class `ErpStorageLocation` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt + tenant_id: Optional[OgInt] name: OgStr - kuerzel: OgStr - icon: OgStr - beschreibung: OgStr - sort_order: OgInt - aktiv: OgBool - created_at: OgDateTime + kuerzel: Optional[OgStr] + icon: Optional[OgStr] + beschreibung: Optional[OgStr] + sort_order: Optional[OgInt] + aktiv: Optional[OgBool] + created_at: Optional[OgDateTime] parent: ToMany["ErpStorageLocation"] @dataclass @@ -232,115 +238,115 @@ class Article: """Rail class `Article` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt - artikelnr: OgStr - ean: OgStr + tenant_id: Optional[OgInt] + artikelnr: Optional[OgStr] + ean: Optional[OgStr] beschreibung: OgStr - kategorie: OgStr - category_id: OgInt - storage_location_id: OgInt - einheit: OgStr - hersteller: OgStr - hersteller_anr: OgStr - bild_url: OgStr - bestand: OgFloat - mindestbestand: OgFloat - preis_netto: OgMoney - ek_preis: OgMoney - mwst_satz: OgFloat - tax_rate_id: OgInt - lieferant: OgStr - lieferant_anr: OgStr - notizen: OgStr - typ: OgStr - aktiv: OgBool - dauer_minuten: OgInt - preis_schema_id: OgInt - vk_preis_manuell: OgBool - listenpreis: OgMoney - uvp: OgMoney - gewicht_kg: OgMoney - herkunftsland: OgStr - zolltarifnr: OgStr - langbeschreibung: OgStr - matchcode: OgStr - warengruppe: OgStr - warengruppe_nr: OgStr - gefahrgut: OgBool - gefahrgut_un_nr: OgStr - gefahrgut_klasse: OgStr - auslaufartikel: OgBool - auslaufdatum: OgDate - deeplink: OgStr - shop_active: OgBool - shop_product_type: OgStr - shop_category_id: OgInt - shop_long_desc_html: OgStr - shop_saas_meta_json: OgScalar - shop_saas_package_id: OgInt - shop_digital_path: OgStr - shop_meta_title: OgStr - shop_meta_description: OgStr - shop_slug: OgStr - shop_payment_methods_csv: OgStr + kategorie: Optional[OgStr] + category_id: Optional[OgInt] + storage_location_id: Optional[OgInt] + einheit: Optional[OgStr] + hersteller: Optional[OgStr] + hersteller_anr: Optional[OgStr] + bild_url: Optional[OgStr] + bestand: Optional[OgFloat] + mindestbestand: Optional[OgFloat] + preis_netto: Optional[OgMoney] + ek_preis: Optional[OgMoney] + mwst_satz: Optional[OgFloat] + tax_rate_id: Optional[OgInt] + lieferant: Optional[OgStr] + lieferant_anr: Optional[OgStr] + notizen: Optional[OgStr] + typ: Optional[OgStr] + aktiv: Optional[OgBool] + dauer_minuten: Optional[OgInt] + preis_schema_id: Optional[OgInt] + vk_preis_manuell: Optional[OgBool] + listenpreis: Optional[OgMoney] + uvp: Optional[OgMoney] + gewicht_kg: Optional[OgMoney] + herkunftsland: Optional[OgStr] + zolltarifnr: Optional[OgStr] + langbeschreibung: Optional[OgStr] + matchcode: Optional[OgStr] + warengruppe: Optional[OgStr] + warengruppe_nr: Optional[OgStr] + gefahrgut: Optional[OgBool] + gefahrgut_un_nr: Optional[OgStr] + gefahrgut_klasse: Optional[OgStr] + auslaufartikel: Optional[OgBool] + auslaufdatum: Optional[OgDate] + deeplink: Optional[OgStr] + shop_active: Optional[OgBool] + shop_product_type: Optional[OgStr] + shop_category_id: Optional[OgInt] + shop_long_desc_html: Optional[OgStr] + shop_saas_meta_json: Optional[OgScalar] + shop_saas_package_id: Optional[OgInt] + shop_digital_path: Optional[OgStr] + shop_meta_title: Optional[OgStr] + shop_meta_description: Optional[OgStr] + shop_slug: Optional[OgStr] + shop_payment_methods_csv: Optional[OgStr] shop_free_shipping: OgBool - shop_extra_user_price_cents: OgInt - shop_addon_article_ids: OgScalar + shop_extra_user_price_cents: Optional[OgInt] + shop_addon_article_ids: Optional[OgScalar] shop_included_users: OgInt - omd_tax_code: OgInt + omd_tax_code: Optional[OgInt] @dataclass class WorkOrder: """Rail class `WorkOrder` — classid 0x02020003 (concept 0x0202, app 0x0003).""" CLASSID: ClassVar[int] = 0x02020003 id: OgInt - tenant_id: OgInt + tenant_id: Optional[OgInt] customer_id: OgInt - created_by: OgInt - doc_type: OgStr - status: OgStr - angebot_nr: OgStr - auftrags_nr: OgStr - workorder_nr: OgStr - rechnung_nr: OgStr - gutschrift_nr: OgStr - sammelrechnung_id: OgInt - datum: OgDate - zeit_start: OgStr - zeit_ende: OgStr - anfahrten: OgFloat - mitarbeiter: OgFloat - pause_h: OgFloat - zusatz_h: OgFloat - betreff: OgStr - notizen: OgStr - intern_notizen: OgStr - bezahlt: OgBool - bezahlt_am: OgDate - bezahlt_betrag: OgMoney - mahnstufe: OgInt - letzte_mahnung: OgDate - erfuellung_bis: OgDate - unterschrift: OgStr - signed_at: OgDateTime - signed_ip: OgStr - signed_user_agent: OgStr - zahlungsart: OgStr - anzahlung_prozent: OgFloat - anzahlung_betrag: OgFloat - anzahlung_bezahlt: OgBool - anzahlung_bezahlt_am: OgDate - kleinunternehmer_snapshot: OgBool - zahlungsziel_tage_snapshot: OgInt - gesamt_rabatt_prozent: OgFloat - gesamt_rabatt_betrag: OgFloat - skonto_prozent_snapshot: OgFloat - skonto_tage_snapshot: OgInt - skonto_ausweisen: OgBool - skonto_aufschlag: OgBool - skonto_aufschlag_faktor: OgFloat - created_at: OgDateTime - updated_at: OgDateTime + created_by: Optional[OgInt] + doc_type: Optional[OgStr] + status: Optional[OgStr] + angebot_nr: Optional[OgStr] + auftrags_nr: Optional[OgStr] + workorder_nr: Optional[OgStr] + rechnung_nr: Optional[OgStr] + gutschrift_nr: Optional[OgStr] + sammelrechnung_id: Optional[OgInt] + datum: Optional[OgDate] + zeit_start: Optional[OgStr] + zeit_ende: Optional[OgStr] + anfahrten: Optional[OgFloat] + mitarbeiter: Optional[OgFloat] + pause_h: Optional[OgFloat] + zusatz_h: Optional[OgFloat] + betreff: Optional[OgStr] + notizen: Optional[OgStr] + intern_notizen: Optional[OgStr] + bezahlt: Optional[OgBool] + bezahlt_am: Optional[OgDate] + bezahlt_betrag: Optional[OgMoney] + mahnstufe: Optional[OgInt] + letzte_mahnung: Optional[OgDate] + erfuellung_bis: Optional[OgDate] + unterschrift: Optional[OgStr] + signed_at: Optional[OgDateTime] + signed_ip: Optional[OgStr] + signed_user_agent: Optional[OgStr] + zahlungsart: Optional[OgStr] + anzahlung_prozent: Optional[OgFloat] + anzahlung_betrag: Optional[OgFloat] + anzahlung_bezahlt: Optional[OgBool] + anzahlung_bezahlt_am: Optional[OgDate] + kleinunternehmer_snapshot: Optional[OgBool] + zahlungsziel_tage_snapshot: Optional[OgInt] + gesamt_rabatt_prozent: Optional[OgFloat] + gesamt_rabatt_betrag: Optional[OgFloat] + skonto_prozent_snapshot: Optional[OgFloat] + skonto_tage_snapshot: Optional[OgInt] + skonto_ausweisen: Optional[OgBool] + skonto_aufschlag: Optional[OgBool] + skonto_aufschlag_faktor: Optional[OgFloat] + created_at: Optional[OgDateTime] + updated_at: Optional[OgDateTime] project: ToOne["Project"] positionen: ToMany["Position"] activities: ToMany["Activity"] @@ -355,20 +361,20 @@ class Position: CLASSID: ClassVar[int] = 0x02010003 id: OgInt workorder_id: OgInt - article_id: OgInt - sort_order: OgInt - pos_typ: OgStr - beschreibung: OgStr - menge: OgFloat - einheit: OgStr - einzelpreis: OgMoney - mwst_satz: OgFloat - tax_rate_id: OgInt - versteckt: OgBool - is_optional: OgBool - customer_accepted_at: OgDateTime - rabatt_prozent: OgFloat - einzelpreis_vor_skonto: OgMoney + article_id: Optional[OgInt] + sort_order: Optional[OgInt] + pos_typ: Optional[OgStr] + beschreibung: Optional[OgStr] + menge: Optional[OgFloat] + einheit: Optional[OgStr] + einzelpreis: Optional[OgMoney] + mwst_satz: Optional[OgFloat] + tax_rate_id: Optional[OgInt] + versteckt: Optional[OgBool] + is_optional: Optional[OgBool] + customer_accepted_at: Optional[OgDateTime] + rabatt_prozent: Optional[OgFloat] + einzelpreis_vor_skonto: Optional[OgMoney] @dataclass class Activity: @@ -376,11 +382,11 @@ class Activity: CLASSID: ClassVar[int] = 0x00000000 id: OgInt workorder_id: OgInt - geraet: OgStr - beschreibung: OgStr - logbuch: OgBool - intern: OgBool - created_at: OgDateTime + geraet: Optional[OgStr] + beschreibung: Optional[OgStr] + logbuch: Optional[OgBool] + intern: Optional[OgBool] + created_at: Optional[OgDateTime] acceptance_items: ToOne["AcceptanceItem"] @dataclass @@ -389,26 +395,26 @@ class Picture: CLASSID: ClassVar[int] = 0x00000000 id: OgInt workorder_id: OgInt - dateiname: OgStr - beschreibung: OgStr - logbuch: OgBool - an_kunde: OgBool - created_at: OgDateTime + dateiname: Optional[OgStr] + beschreibung: Optional[OgStr] + logbuch: Optional[OgBool] + an_kunde: Optional[OgBool] + created_at: Optional[OgDateTime] @dataclass class Document: """Rail class `Document` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt + tenant_id: Optional[OgInt] dateiname: OgStr original_name: OgStr - beschreibung: OgStr - mime_type: OgStr - size_bytes: OgInt - an_kunde: OgBool - uploaded_by: OgInt - uploaded_at: OgDateTime + beschreibung: Optional[OgStr] + mime_type: Optional[OgStr] + size_bytes: Optional[OgInt] + an_kunde: Optional[OgBool] + uploaded_by: Optional[OgInt] + uploaded_at: Optional[OgDateTime] customer: ToOne["Customer"] workorder: ToOne["WorkOrder"] @@ -417,20 +423,20 @@ class AcceptanceProtocol: """Rail class `AcceptanceProtocol` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt + tenant_id: Optional[OgInt] sequenz_nr: OgInt - vorgaenger_id: OgInt - aktiv: OgBool - gesamt_abgenommen: OgBool - abnahme_datum: OgDate - abnahme_ort: OgStr - bemerkungen: OgStr - nachbesserungstermin: OgDate - unterschrift_kunde: OgStr - unterschrieben_am: OgDateTime - unterschrieben_von: OgStr - erstellt_am: OgDateTime - erstellt_von_id: OgInt + vorgaenger_id: Optional[OgInt] + aktiv: Optional[OgBool] + gesamt_abgenommen: Optional[OgBool] + abnahme_datum: Optional[OgDate] + abnahme_ort: Optional[OgStr] + bemerkungen: Optional[OgStr] + nachbesserungstermin: Optional[OgDate] + unterschrift_kunde: Optional[OgStr] + unterschrieben_am: Optional[OgDateTime] + unterschrieben_von: Optional[OgStr] + erstellt_am: Optional[OgDateTime] + erstellt_von_id: Optional[OgInt] workorder: ToOne["WorkOrder"] items: ToOne["AcceptanceItem"] defects: ToOne["AcceptanceDefect"] @@ -440,11 +446,11 @@ class AcceptanceItem: """Rail class `AcceptanceItem` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - abgenommen: OgBool - bemerkung: OgStr - bezeichnung: OgStr - status: OgStr - sort_order: OgInt + abgenommen: Optional[OgBool] + bemerkung: Optional[OgStr] + bezeichnung: Optional[OgStr] + status: Optional[OgStr] + sort_order: Optional[OgInt] protocol: ToOne["AcceptanceProtocol"] activity: ToOne["Activity"] @@ -454,12 +460,12 @@ class AcceptanceDefect: CLASSID: ClassVar[int] = 0x00000000 id: OgInt beschreibung: OgStr - erfasst_am: OgDateTime - nachbesserung_bis: OgDate - behoben: OgBool - behoben_am: OgDateTime + erfasst_am: Optional[OgDateTime] + nachbesserung_bis: Optional[OgDate] + behoben: Optional[OgBool] + behoben_am: Optional[OgDateTime] intern_status: OgStr - intern_status_am: OgDateTime + intern_status_am: Optional[OgDateTime] protocol: ToOne["AcceptanceProtocol"] @dataclass @@ -467,11 +473,11 @@ class AcceptanceTemplate: """Rail class `AcceptanceTemplate` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt + tenant_id: Optional[OgInt] name: OgStr - is_default: OgBool - aktiv: OgBool - erstellt_am: OgDateTime + is_default: Optional[OgBool] + aktiv: Optional[OgBool] + erstellt_am: Optional[OgDateTime] items: ToOne["AcceptanceTemplateItem"] @dataclass @@ -480,7 +486,7 @@ class AcceptanceTemplateItem: CLASSID: ClassVar[int] = 0x00000000 id: OgInt bezeichnung: OgStr - sort_order: OgInt + sort_order: Optional[OgInt] template: ToOne["AcceptanceTemplate"] @dataclass @@ -489,9 +495,9 @@ class HistoryEntry: CLASSID: ClassVar[int] = 0x00000000 id: OgInt workorder_id: OgInt - aktion: OgStr - details: OgStr - created_at: OgDateTime + aktion: Optional[OgStr] + details: Optional[OgStr] + created_at: Optional[OgDateTime] user: ToOne["User"] @dataclass @@ -499,19 +505,19 @@ class LogbookEntry: """Rail class `LogbookEntry` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - workorder_id: OgInt + workorder_id: Optional[OgInt] datum: OgDate - abfahrt: OgStr - ankunft: OgStr - rueckfahrt: OgStr - zurueck: OgStr - start_km: OgFloat - ende_km: OgFloat - route: OgStr - zweck: OgStr - fahrzeug: OgStr - privat_anteil: OgFloat - created_at: OgDateTime + abfahrt: Optional[OgStr] + ankunft: Optional[OgStr] + rueckfahrt: Optional[OgStr] + zurueck: Optional[OgStr] + start_km: Optional[OgFloat] + ende_km: Optional[OgFloat] + route: Optional[OgStr] + zweck: Optional[OgStr] + fahrzeug: Optional[OgStr] + privat_anteil: Optional[OgFloat] + created_at: Optional[OgDateTime] user: ToOne["User"] customer: ToOne["Customer"] @@ -520,21 +526,21 @@ class NumberSequence: """Rail class `NumberSequence` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt + tenant_id: Optional[OgInt] name: OgStr - prefix: OgStr - current: OgInt - padding: OgInt + prefix: Optional[OgStr] + current: Optional[OgInt] + padding: Optional[OgInt] @dataclass class Setting: """Rail class `Setting` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt + tenant_id: Optional[OgInt] key: OgStr - value: OgStr - label: OgStr + value: Optional[OgStr] + label: Optional[OgStr] override_master: OgInt @dataclass @@ -544,12 +550,12 @@ class CustomerPortalUser: id: OgInt username: OgStr password_hash: OgStr - aktiv: OgBool - must_change_pw: OgBool - last_login: OgDateTime - created_at: OgDateTime - failed_attempts: OgInt - locked_until: OgDateTime + aktiv: Optional[OgBool] + must_change_pw: Optional[OgBool] + last_login: Optional[OgDateTime] + created_at: Optional[OgDateTime] + failed_attempts: Optional[OgInt] + locked_until: Optional[OgDateTime] customer: ToOne["Customer"] @dataclass @@ -557,19 +563,19 @@ class PasswordEntry: """Rail class `PasswordEntry` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt - created_by: OgInt - gruppe: OgStr + tenant_id: Optional[OgInt] + created_by: Optional[OgInt] + gruppe: Optional[OgStr] titel: OgStr - benutzername: OgStr - passwort_enc: OgStr - url: OgStr - notizen_enc: OgStr - icon: OgStr - aktiv: OgBool - keepass_uid: OgStr - created_at: OgDateTime - updated_at: OgDateTime + benutzername: Optional[OgStr] + passwort_enc: Optional[OgStr] + url: Optional[OgStr] + notizen_enc: Optional[OgStr] + icon: Optional[OgStr] + aktiv: Optional[OgBool] + keepass_uid: Optional[OgStr] + created_at: Optional[OgDateTime] + updated_at: Optional[OgDateTime] customer: ToOne["Customer"] creator: ToOne["User"] @@ -578,19 +584,19 @@ class TimeSheet: """Rail class `TimeSheet` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt - source: OgStr + tenant_id: Optional[OgInt] + source: Optional[OgStr] datum: OgDate - minuten: OgInt - erfasst_von: OgStr - startzeit: OgScalar - endzeit: OgScalar - beschreibung: OgStr - timer_start: OgDateTime - timer_paused_at: OgDateTime - abgerechnet: OgBool - created_at: OgDateTime - updated_at: OgDateTime + minuten: Optional[OgInt] + erfasst_von: Optional[OgStr] + startzeit: Optional[OgScalar] + endzeit: Optional[OgScalar] + beschreibung: Optional[OgStr] + timer_start: Optional[OgDateTime] + timer_paused_at: Optional[OgDateTime] + abgerechnet: Optional[OgBool] + created_at: Optional[OgDateTime] + updated_at: Optional[OgDateTime] customer: ToOne["Customer"] user: ToOne["User"] @@ -599,12 +605,12 @@ class TaxReserve: """Rail class `TaxReserve` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt + tenant_id: Optional[OgInt] jahr: OgInt monat: OgInt - quartal: OgInt - typ: OgStr - erledigt: OgBool + quartal: Optional[OgInt] + typ: Optional[OgStr] + erledigt: Optional[OgBool] @dataclass class TimesheetActivity: @@ -612,7 +618,7 @@ class TimesheetActivity: CLASSID: ClassVar[int] = 0x01030003 id: OgInt beschreibung: OgStr - created_at: OgDateTime + created_at: Optional[OgDateTime] timesheet: ToOne["TimeSheet"] @dataclass @@ -620,30 +626,30 @@ class Reminder: """Rail class `Reminder` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt + tenant_id: Optional[OgInt] titel: OgStr - beschreibung: OgStr + beschreibung: Optional[OgStr] faellig_am: OgDate - prioritaet: OgStr - erledigt: OgBool - erledigt_am: OgDateTime - erstellt_am: OgDateTime - zeit_von: OgStr - zeit_bis: OgStr - termin_typ: OgStr - wiederkehrend: OgBool - intervall: OgStr - intervall_tage: OgStr - intervall_tag: OgInt - intervall_monat: OgInt - intervall_alle: OgInt - token_cancel: OgStr - cancelled_at: OgDateTime - cancelled_ip: OgStr - crm_lead_id: OgInt - crm_contact_id: OgInt - crm_company_id: OgInt - crm_task_id: OgInt + prioritaet: Optional[OgStr] + erledigt: Optional[OgBool] + erledigt_am: Optional[OgDateTime] + erstellt_am: Optional[OgDateTime] + zeit_von: Optional[OgStr] + zeit_bis: Optional[OgStr] + termin_typ: Optional[OgStr] + wiederkehrend: Optional[OgBool] + intervall: Optional[OgStr] + intervall_tage: Optional[OgStr] + intervall_tag: Optional[OgInt] + intervall_monat: Optional[OgInt] + intervall_alle: Optional[OgInt] + token_cancel: Optional[OgStr] + cancelled_at: Optional[OgDateTime] + cancelled_ip: Optional[OgStr] + crm_lead_id: Optional[OgInt] + crm_contact_id: Optional[OgInt] + crm_company_id: Optional[OgInt] + crm_task_id: Optional[OgInt] project: ToOne["Project"] user: ToOne["User"] customer: ToOne["Customer"] @@ -654,20 +660,20 @@ class MaintenanceContract: """Rail class `MaintenanceContract` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt + tenant_id: Optional[OgInt] titel: OgStr - beschreibung: OgStr - intervall: OgStr - preis_netto: OgFloat - mwst_satz: OgFloat + beschreibung: Optional[OgStr] + intervall: Optional[OgStr] + preis_netto: Optional[OgFloat] + mwst_satz: Optional[OgFloat] beginn: OgDate - ende: OgDate - letzte_wartung: OgDate - naechste_wartung: OgDate - auto_rechnung: OgBool - aktiv: OgBool - notizen: OgStr - created_at: OgDateTime + ende: Optional[OgDate] + letzte_wartung: Optional[OgDate] + naechste_wartung: Optional[OgDate] + auto_rechnung: Optional[OgBool] + aktiv: Optional[OgBool] + notizen: Optional[OgStr] + created_at: Optional[OgDateTime] customer: ToMany["Customer"] @dataclass @@ -675,18 +681,18 @@ class RecurringInvoice: """Rail class `RecurringInvoice` — classid 0x02020003 (concept 0x0202, app 0x0003).""" CLASSID: ClassVar[int] = 0x02020003 id: OgInt - tenant_id: OgInt + tenant_id: Optional[OgInt] titel: OgStr - beschreibung: OgStr - intervall: OgStr - preis_netto: OgFloat - mwst_satz: OgFloat + beschreibung: Optional[OgStr] + intervall: Optional[OgStr] + preis_netto: Optional[OgFloat] + mwst_satz: Optional[OgFloat] naechste_ausfuehrung: OgDate - letzte_ausfuehrung: OgDate - auto_versand: OgBool - aktiv: OgBool - notizen: OgStr - created_at: OgDateTime + letzte_ausfuehrung: Optional[OgDate] + auto_versand: Optional[OgBool] + aktiv: Optional[OgBool] + notizen: Optional[OgStr] + created_at: Optional[OgDateTime] customer: ToOne["Customer"] contract: ToOne["MaintenanceContract"] @@ -695,25 +701,25 @@ class Device: """Rail class `Device` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt - kategorie: OgStr - hersteller: OgStr - modell: OgStr - seriennummer: OgStr - hostname: OgStr - ip_adresse: OgStr - mac_adresse: OgStr - standort: OgStr - kaufdatum: OgDate - garantie_bis: OgDate - firmware: OgStr - zugangsdaten: OgStr - notizen: OgStr - letzte_wartung: OgDate - status: OgStr - aktiv: OgBool - created_at: OgDateTime - updated_at: OgDateTime + tenant_id: Optional[OgInt] + kategorie: Optional[OgStr] + hersteller: Optional[OgStr] + modell: Optional[OgStr] + seriennummer: Optional[OgStr] + hostname: Optional[OgStr] + ip_adresse: Optional[OgStr] + mac_adresse: Optional[OgStr] + standort: Optional[OgStr] + kaufdatum: Optional[OgDate] + garantie_bis: Optional[OgDate] + firmware: Optional[OgStr] + zugangsdaten: Optional[OgStr] + notizen: Optional[OgStr] + letzte_wartung: Optional[OgDate] + status: Optional[OgStr] + aktiv: Optional[OgBool] + created_at: Optional[OgDateTime] + updated_at: Optional[OgDateTime] customer: ToMany["Customer"] @dataclass @@ -721,15 +727,15 @@ class KummerkastenEntry: """Rail class `KummerkastenEntry` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - source: OgStr - typ: OgStr + source: Optional[OgStr] + typ: Optional[OgStr] titel: OgStr - beschreibung: OgStr - prioritaet: OgStr - status: OgStr - admin_kommentar: OgStr - erstellt_am: OgDateTime - aktualisiert_am: OgDateTime + beschreibung: Optional[OgStr] + prioritaet: Optional[OgStr] + status: Optional[OgStr] + admin_kommentar: Optional[OgStr] + erstellt_am: Optional[OgDateTime] + aktualisiert_am: Optional[OgDateTime] user: ToOne["User"] tenant: ToOne["Tenant"] customer: ToOne["Customer"] @@ -741,7 +747,7 @@ class PortalViewState: id: OgInt customer_id: OgInt kind: OgStr - last_seen_at: OgDateTime + last_seen_at: Optional[OgDateTime] @dataclass class ReferralLog: @@ -749,17 +755,17 @@ class ReferralLog: CLASSID: ClassVar[int] = 0x00000000 id: OgInt referral_code: OgStr - empfaenger_email: OgStr - empfaenger_name: OgStr - versendet_am: OgDateTime - versendet_von: OgInt - status: OgStr - converted_at: OgDateTime - converted_manually: OgBool - converted_note: OgStr - proposed_by: OgInt - proposed_at: OgDateTime - converted_tenant_id: OgInt + empfaenger_email: Optional[OgStr] + empfaenger_name: Optional[OgStr] + versendet_am: Optional[OgDateTime] + versendet_von: Optional[OgInt] + status: Optional[OgStr] + converted_at: Optional[OgDateTime] + converted_manually: Optional[OgBool] + converted_note: Optional[OgStr] + proposed_by: Optional[OgInt] + proposed_at: Optional[OgDateTime] + converted_tenant_id: Optional[OgInt] customer: ToOne["Customer"] tenant: ToOne["Tenant"] @@ -770,32 +776,32 @@ class ColdLead: id: OgInt tenant_id: OgInt firma: OgStr - ansprechpartner: OgStr - branche: OgStr - strasse: OgStr - plz: OgStr - ort: OgStr - land: OgStr - telefon: OgStr - mobil: OgStr - email: OgStr - webseite: OgStr - quelle: OgStr - briefanrede: OgStr - mitarbeiterzahl: OgStr - notiz_kurz: OgStr - newsletter_sperre: OgBool - newsletter_sperre_grund: OgStr - newsletter_sperre_am: OgDateTime - source_customer_id: OgInt - status: OgStr - wiedervorlage_am: OgDate - notizen: OgStr - converted_customer_id: OgInt - converted_at: OgDateTime - created_at: OgDateTime - created_by: OgInt - updated_at: OgDateTime + ansprechpartner: Optional[OgStr] + branche: Optional[OgStr] + strasse: Optional[OgStr] + plz: Optional[OgStr] + ort: Optional[OgStr] + land: Optional[OgStr] + telefon: Optional[OgStr] + mobil: Optional[OgStr] + email: Optional[OgStr] + webseite: Optional[OgStr] + quelle: Optional[OgStr] + briefanrede: Optional[OgStr] + mitarbeiterzahl: Optional[OgStr] + notiz_kurz: Optional[OgStr] + newsletter_sperre: Optional[OgBool] + newsletter_sperre_grund: Optional[OgStr] + newsletter_sperre_am: Optional[OgDateTime] + source_customer_id: Optional[OgInt] + status: Optional[OgStr] + wiedervorlage_am: Optional[OgDate] + notizen: Optional[OgStr] + converted_customer_id: Optional[OgInt] + converted_at: Optional[OgDateTime] + created_at: Optional[OgDateTime] + created_by: Optional[OgInt] + updated_at: Optional[OgDateTime] activities: ToMany["ColdLeadActivity"] @dataclass @@ -804,14 +810,14 @@ class ColdLeadActivity: CLASSID: ClassVar[int] = 0x00000000 id: OgInt lead_id: OgInt - typ: OgStr - text: OgStr - mail_subject: OgStr - mail_to: OgStr - status_from: OgStr - status_to: OgStr - created_at: OgDateTime - created_by: OgInt + typ: Optional[OgStr] + text: Optional[OgStr] + mail_subject: Optional[OgStr] + mail_to: Optional[OgStr] + status_from: Optional[OgStr] + status_to: Optional[OgStr] + created_at: Optional[OgDateTime] + created_by: Optional[OgInt] @dataclass class ColdCampaign: @@ -820,13 +826,13 @@ class ColdCampaign: id: OgInt tenant_id: OgInt name: OgStr - beschreibung: OgStr - status: OgStr - mail_subject: OgStr - mail_template_html: OgStr - created_at: OgDateTime - created_by: OgInt - updated_at: OgDateTime + beschreibung: Optional[OgStr] + status: Optional[OgStr] + mail_subject: Optional[OgStr] + mail_template_html: Optional[OgStr] + created_at: Optional[OgDateTime] + created_by: Optional[OgInt] + updated_at: Optional[OgDateTime] leads: ToMany["ColdCampaignLead"] @dataclass @@ -835,11 +841,11 @@ class ColdCampaignLead: CLASSID: ClassVar[int] = 0x00000000 id: OgInt campaign_id: OgInt - sent_at: OgDateTime - sent_status: OgStr - sent_error: OgStr - added_at: OgDateTime - added_by: OgInt + sent_at: Optional[OgDateTime] + sent_status: Optional[OgStr] + sent_error: Optional[OgStr] + added_at: Optional[OgDateTime] + added_by: Optional[OgInt] lead: ToOne["ColdLead"] @dataclass @@ -849,21 +855,21 @@ class ServicePackage: id: OgInt tenant_id: OgInt name: OgStr - beschreibung: OgStr - rechnungs_titel: OgStr - rechnungs_text: OgStr - preis_monthly: OgFloat - preis_quarterly: OgFloat - preis_half_yearly: OgFloat - preis_yearly: OgFloat - mwst_satz: OgFloat - free_months_default: OgInt - aktiv: OgBool - sort_order: OgInt - created_at: OgDateTime - with_mail_templates: OgBool - with_demo_data: OgBool - required_server_type: OgStr + beschreibung: Optional[OgStr] + rechnungs_titel: Optional[OgStr] + rechnungs_text: Optional[OgStr] + preis_monthly: Optional[OgFloat] + preis_quarterly: Optional[OgFloat] + preis_half_yearly: Optional[OgFloat] + preis_yearly: Optional[OgFloat] + mwst_satz: Optional[OgFloat] + free_months_default: Optional[OgInt] + aktiv: Optional[OgBool] + sort_order: Optional[OgInt] + created_at: Optional[OgDateTime] + with_mail_templates: Optional[OgBool] + with_demo_data: Optional[OgBool] + required_server_type: Optional[OgStr] @dataclass class RentedServer: @@ -872,44 +878,44 @@ class RentedServer: id: OgInt tenant_id: OgInt name: OgStr - provider: OgStr - hostname: OgStr - ip_address: OgStr - woa_url: OgStr - dns_eintrag: OgStr - ssh_user: OgStr - ssh_port: OgInt - miete_netto: OgFloat - miete_intervall: OgStr - ssh_password_enc: OgStr - root_password_enc: OgStr - notizen_enc: OgStr - master_api_url: OgStr - master_api_token: OgStr + provider: Optional[OgStr] + hostname: Optional[OgStr] + ip_address: Optional[OgStr] + woa_url: Optional[OgStr] + dns_eintrag: Optional[OgStr] + ssh_user: Optional[OgStr] + ssh_port: Optional[OgInt] + miete_netto: Optional[OgFloat] + miete_intervall: Optional[OgStr] + ssh_password_enc: Optional[OgStr] + root_password_enc: Optional[OgStr] + notizen_enc: Optional[OgStr] + master_api_url: Optional[OgStr] + master_api_token: Optional[OgStr] verify_ssl: OgBool - erp_ocr_token: OgStr - last_sync_at: OgDateTime - last_sync_status: OgStr - last_sync_message: OgStr - last_software_version: OgStr - last_health_at: OgDateTime - last_health_payload: OgStr - sa_username: OgStr - sa_password_enc: OgStr - installed_at: OgDateTime - tenant_slug_remote: OgStr - is_master: OgBool - aktiv: OgBool - created_at: OgDateTime - server_type: OgStr - max_tenants: OgInt - tenant_count: OgInt - tenant_count_at: OgDateTime + erp_ocr_token: Optional[OgStr] + last_sync_at: Optional[OgDateTime] + last_sync_status: Optional[OgStr] + last_sync_message: Optional[OgStr] + last_software_version: Optional[OgStr] + last_health_at: Optional[OgDateTime] + last_health_payload: Optional[OgStr] + sa_username: Optional[OgStr] + sa_password_enc: Optional[OgStr] + installed_at: Optional[OgDateTime] + tenant_slug_remote: Optional[OgStr] + is_master: Optional[OgBool] + aktiv: Optional[OgBool] + created_at: Optional[OgDateTime] + server_type: Optional[OgStr] + max_tenants: Optional[OgInt] + tenant_count: Optional[OgInt] + tenant_count_at: Optional[OgDateTime] push_failure_count: OgInt - push_last_failure_at: OgDateTime - push_last_error: OgStr + push_last_failure_at: Optional[OgDateTime] + push_last_error: Optional[OgStr] push_disabled: OgBool - push_disabled_at: OgDateTime + push_disabled_at: Optional[OgDateTime] @dataclass class ServiceContract: @@ -917,27 +923,27 @@ class ServiceContract: CLASSID: ClassVar[int] = 0x00000000 id: OgInt tenant_id: OgInt - intervall: OgStr + intervall: Optional[OgStr] vertrag_start: OgDate rechnung_ab: OgDate naechste_rechnung: OgDate - individualpreis_netto: OgFloat - rabatt_prozent: OgFloat - free_months_remaining: OgInt - credit_eur: OgFloat - bonus_pending_eur: OgFloat - sales_partner_id: OgInt - provision_pct: OgFloat - commission_until: OgDate - auto_versand: OgBool - aktiv: OgBool - gekuendigt_am: OgDate - geloescht_am: OgDateTime - notizen: OgStr - created_at: OgDateTime - last_invoice_at: OgDateTime - last_invoice_workorder_id: OgInt - shop_saas_config_id: OgInt + individualpreis_netto: Optional[OgFloat] + rabatt_prozent: Optional[OgFloat] + free_months_remaining: Optional[OgInt] + credit_eur: Optional[OgFloat] + bonus_pending_eur: Optional[OgFloat] + sales_partner_id: Optional[OgInt] + provision_pct: Optional[OgFloat] + commission_until: Optional[OgDate] + auto_versand: Optional[OgBool] + aktiv: Optional[OgBool] + gekuendigt_am: Optional[OgDate] + geloescht_am: Optional[OgDateTime] + notizen: Optional[OgStr] + created_at: Optional[OgDateTime] + last_invoice_at: Optional[OgDateTime] + last_invoice_workorder_id: Optional[OgInt] + shop_saas_config_id: Optional[OgInt] prorata_pending: OgBool customer: ToOne["Customer"] package: ToOne["ServicePackage"] @@ -953,54 +959,54 @@ class SalesPartner: tier: OgStr provision_pct: OgFloat aktiv: OgBool - firma: OgStr - ansprechpartner: OgStr - email: OgStr - telefon: OgStr + firma: Optional[OgStr] + ansprechpartner: Optional[OgStr] + email: Optional[OgStr] + telefon: Optional[OgStr] is_service_techniker: OgBool - strasse: OgStr - plz: OgStr - ort: OgStr - ustid: OgStr - steuer_status: OgStr - iban: OgStr - bic: OgStr - bank_name: OgStr - commission_months: OgInt - aktiv_ab: OgDate - aktiv_bis: OgDate - notizen: OgStr - vertrag_pdf_path: OgStr - vertrag_versendet_am: OgDateTime - vertrag_versand_method: OgStr - vertrag_signatur_token: OgStr - vertrag_signiertes_pdf_path: OgStr - vertrag_signiert_am: OgDateTime - vertrag_signatur_data: OgStr - vertrag_signatur_ip: OgStr - vertrag_signatur_user_agent: OgStr - created_at: OgDateTime - updated_at: OgDateTime + strasse: Optional[OgStr] + plz: Optional[OgStr] + ort: Optional[OgStr] + ustid: Optional[OgStr] + steuer_status: Optional[OgStr] + iban: Optional[OgStr] + bic: Optional[OgStr] + bank_name: Optional[OgStr] + commission_months: Optional[OgInt] + aktiv_ab: Optional[OgDate] + aktiv_bis: Optional[OgDate] + notizen: Optional[OgStr] + vertrag_pdf_path: Optional[OgStr] + vertrag_versendet_am: Optional[OgDateTime] + vertrag_versand_method: Optional[OgStr] + vertrag_signatur_token: Optional[OgStr] + vertrag_signiertes_pdf_path: Optional[OgStr] + vertrag_signiert_am: Optional[OgDateTime] + vertrag_signatur_data: Optional[OgStr] + vertrag_signatur_ip: Optional[OgStr] + vertrag_signatur_user_agent: Optional[OgStr] + created_at: Optional[OgDateTime] + updated_at: Optional[OgDateTime] @dataclass class PartnerCommission: """Rail class `PartnerCommission` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - payout_id: OgInt + payout_id: Optional[OgInt] tenant_id: OgInt basis_netto: OgFloat provision_pct: OgFloat betrag_netto: OgFloat status: OgStr - pending_at: OgDateTime - earned_at: OgDateTime - paid_at: OgDate - paid_workorder_id: OgInt - cancelled_at: OgDateTime - cancelled_reason: OgStr - notes: OgStr - created_at: OgDateTime + pending_at: Optional[OgDateTime] + earned_at: Optional[OgDateTime] + paid_at: Optional[OgDate] + paid_workorder_id: Optional[OgInt] + cancelled_at: Optional[OgDateTime] + cancelled_reason: Optional[OgStr] + notes: Optional[OgStr] + created_at: Optional[OgDateTime] partner: ToOne["SalesPartner"] contract: ToOne["ServiceContract"] source_workorder: ToOne["WorkOrder"] @@ -1010,21 +1016,21 @@ class PartnerPayout: """Rail class `PartnerPayout` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - payout_nr: OgStr + payout_nr: Optional[OgStr] tenant_id: OgInt paid_at: OgDate summe_netto: OgFloat - summe_ust: OgFloat - summe_brutto: OgFloat - ust_satz: OgFloat - steuer_status_snapshot: OgStr - note: OgStr - pdf_path: OgStr - pdf_generated_at: OgDateTime - mail_sent_at: OgDateTime - mail_sent_to: OgStr - created_by: OgInt - created_at: OgDateTime + summe_ust: Optional[OgFloat] + summe_brutto: Optional[OgFloat] + ust_satz: Optional[OgFloat] + steuer_status_snapshot: Optional[OgStr] + note: Optional[OgStr] + pdf_path: Optional[OgStr] + pdf_generated_at: Optional[OgDateTime] + mail_sent_at: Optional[OgDateTime] + mail_sent_to: Optional[OgStr] + created_by: Optional[OgInt] + created_at: Optional[OgDateTime] partner: ToOne["SalesPartner"] commissions: ToMany["PartnerCommission"] @@ -1034,37 +1040,37 @@ class ContractSetup: CLASSID: ClassVar[int] = 0x00000000 id: OgInt setup_phase: OgStr - partner_decision: OgStr - progress_pct: OgInt - subdomain: OgStr - server_ip: OgStr - dns_recipient: OgStr - dns_bcc: OgStr - dns_mail_sent_at: OgDateTime - dns_confirmed_at: OgDateTime - dns_confirmed_by: OgInt - server_ready_at: OgDateTime - server_ready_by: OgInt - server_notes: OgStr - ssh_host: OgStr - ssh_port: OgInt - ssh_user: OgStr - package_uploaded_at: OgDateTime - package_path_remote: OgStr - package_filename: OgStr - target_tenant_id: OgInt - target_tenant_name: OgStr - target_tenant_slug: OgStr - target_branche: OgStr - welcome_recipient: OgStr - welcome_sent_at: OgDateTime - admin_username: OgStr - onboarding_recipient: OgStr - onboarding_bcc: OgStr - onboarding_sent_at: OgDateTime - onboarding_csv_attached: OgBool - created_at: OgDateTime - updated_at: OgDateTime + partner_decision: Optional[OgStr] + progress_pct: Optional[OgInt] + subdomain: Optional[OgStr] + server_ip: Optional[OgStr] + dns_recipient: Optional[OgStr] + dns_bcc: Optional[OgStr] + dns_mail_sent_at: Optional[OgDateTime] + dns_confirmed_at: Optional[OgDateTime] + dns_confirmed_by: Optional[OgInt] + server_ready_at: Optional[OgDateTime] + server_ready_by: Optional[OgInt] + server_notes: Optional[OgStr] + ssh_host: Optional[OgStr] + ssh_port: Optional[OgInt] + ssh_user: Optional[OgStr] + package_uploaded_at: Optional[OgDateTime] + package_path_remote: Optional[OgStr] + package_filename: Optional[OgStr] + target_tenant_id: Optional[OgInt] + target_tenant_name: Optional[OgStr] + target_tenant_slug: Optional[OgStr] + target_branche: Optional[OgStr] + welcome_recipient: Optional[OgStr] + welcome_sent_at: Optional[OgDateTime] + admin_username: Optional[OgStr] + onboarding_recipient: Optional[OgStr] + onboarding_bcc: Optional[OgStr] + onboarding_sent_at: Optional[OgDateTime] + onboarding_csv_attached: Optional[OgBool] + created_at: Optional[OgDateTime] + updated_at: Optional[OgDateTime] contract: ToMany["ServiceContract"] @dataclass @@ -1074,11 +1080,11 @@ class ContractSetupHistory: id: OgInt contract_id: OgInt action: OgStr - phase_from: OgStr - phase_to: OgStr - user_id: OgInt - notes: OgStr - created_at: OgDateTime + phase_from: Optional[OgStr] + phase_to: Optional[OgStr] + user_id: Optional[OgInt] + notes: Optional[OgStr] + created_at: Optional[OgDateTime] @dataclass class AppVersion: @@ -1086,20 +1092,20 @@ class AppVersion: CLASSID: ClassVar[int] = 0x00000000 id: OgInt version: OgStr - notes: OgStr - set_by: OgInt - set_at: OgDateTime - is_current: OgBool - change_pdf_path: OgStr - change_pdf_sha256: OgStr - change_pdf_size: OgInt - audit_detail_pdf_path: OgStr - audit_detail_pdf_sha256: OgStr - audit_detail_pdf_size: OgInt - audit_layperson_pdf_path: OgStr - audit_layperson_pdf_sha256: OgStr - audit_layperson_pdf_size: OgInt - audit_run_id: OgInt + notes: Optional[OgStr] + set_by: Optional[OgInt] + set_at: Optional[OgDateTime] + is_current: Optional[OgBool] + change_pdf_path: Optional[OgStr] + change_pdf_sha256: Optional[OgStr] + change_pdf_size: Optional[OgInt] + audit_detail_pdf_path: Optional[OgStr] + audit_detail_pdf_sha256: Optional[OgStr] + audit_detail_pdf_size: Optional[OgInt] + audit_layperson_pdf_path: Optional[OgStr] + audit_layperson_pdf_sha256: Optional[OgStr] + audit_layperson_pdf_size: Optional[OgInt] + audit_run_id: Optional[OgInt] @dataclass class IpBlacklist: @@ -1107,18 +1113,18 @@ class IpBlacklist: CLASSID: ClassVar[int] = 0x00000000 id: OgInt ip: OgStr - grund: OgStr - aktiv: OgBool - auto: OgBool - expires_at: OgDateTime - created_at: OgDateTime - created_by: OgInt - last_hit_at: OgDateTime - hit_count: OgInt - safe_marked_by: OgInt - safe_marked_at: OgDateTime - safe_reason: OgStr - origin_server: OgStr + grund: Optional[OgStr] + aktiv: Optional[OgBool] + auto: Optional[OgBool] + expires_at: Optional[OgDateTime] + created_at: Optional[OgDateTime] + created_by: Optional[OgInt] + last_hit_at: Optional[OgDateTime] + hit_count: Optional[OgInt] + safe_marked_by: Optional[OgInt] + safe_marked_at: Optional[OgDateTime] + safe_reason: Optional[OgStr] + origin_server: Optional[OgStr] creator: ToOne["User"] safe_marker: ToOne["User"] @@ -1127,12 +1133,12 @@ class LoginAudit: """Rail class `LoginAudit` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - username: OgStr - ip: OgStr - user_agent: OgStr - success: OgBool - reason: OgStr - created_at: OgDateTime + username: Optional[OgStr] + ip: Optional[OgStr] + user_agent: Optional[OgStr] + success: Optional[OgBool] + reason: Optional[OgStr] + created_at: Optional[OgDateTime] @dataclass class ScopeAuditBlock: @@ -1140,49 +1146,49 @@ class ScopeAuditBlock: CLASSID: ClassVar[int] = 0x00000000 id: OgInt created_at: OgDateTime - actor_user_id: OgInt - actor_username: OgStr - actor_tenant_id: OgInt - actor_is_admin: OgBool - actor_is_superadmin: OgBool - target_model: OgStr - target_id: OgInt - target_tenant_id: OgInt - route: OgStr - method: OgStr - reason: OgStr - ip: OgStr - user_agent: OgStr + actor_user_id: Optional[OgInt] + actor_username: Optional[OgStr] + actor_tenant_id: Optional[OgInt] + actor_is_admin: Optional[OgBool] + actor_is_superadmin: Optional[OgBool] + target_model: Optional[OgStr] + target_id: Optional[OgInt] + target_tenant_id: Optional[OgInt] + route: Optional[OgStr] + method: Optional[OgStr] + reason: Optional[OgStr] + ip: Optional[OgStr] + user_agent: Optional[OgStr] @dataclass class SecurityAudit: """Rail class `SecurityAudit` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - app_version: OgStr - status: OgStr - report_mode: OgStr - audited_by: OgInt - audited_by_name: OgStr - audited_at: OgDateTime - approved_by: OgInt - approved_by_name: OgStr - approved_at: OgDateTime - results_json: OgStr - overall_status: OgStr - pass_count: OgInt - fail_count: OgInt - warn_count: OgInt - skip_count: OgInt - auditor_notes: OgStr - version_history_json: OgStr - signature_hash: OgStr - signed_at: OgDateTime - pdf_path: OgStr - pdf_sha256: OgStr - pdf_size: OgInt - sa_notified_at: OgDateTime - created_at: OgDateTime + app_version: Optional[OgStr] + status: Optional[OgStr] + report_mode: Optional[OgStr] + audited_by: Optional[OgInt] + audited_by_name: Optional[OgStr] + audited_at: Optional[OgDateTime] + approved_by: Optional[OgInt] + approved_by_name: Optional[OgStr] + approved_at: Optional[OgDateTime] + results_json: Optional[OgStr] + overall_status: Optional[OgStr] + pass_count: Optional[OgInt] + fail_count: Optional[OgInt] + warn_count: Optional[OgInt] + skip_count: Optional[OgInt] + auditor_notes: Optional[OgStr] + version_history_json: Optional[OgStr] + signature_hash: Optional[OgStr] + signed_at: Optional[OgDateTime] + pdf_path: Optional[OgStr] + pdf_sha256: Optional[OgStr] + pdf_size: Optional[OgInt] + sa_notified_at: Optional[OgDateTime] + created_at: Optional[OgDateTime] auditor: ToOne["User"] approver: ToOne["User"] @@ -1193,14 +1199,14 @@ class SyncHistory: id: OgInt rented_server_id: OgInt started_at: OgDateTime - finished_at: OgDateTime - action: OgStr - status: OgStr - files_changed: OgInt - backup_path: OgStr - error_message: OgStr - log_excerpt: OgStr - triggered_by: OgInt + finished_at: Optional[OgDateTime] + action: Optional[OgStr] + status: Optional[OgStr] + files_changed: Optional[OgInt] + backup_path: Optional[OgStr] + error_message: Optional[OgStr] + log_excerpt: Optional[OgStr] + triggered_by: Optional[OgInt] server: ToOne["RentedServer"] @dataclass @@ -1208,15 +1214,15 @@ class UpdateJob: """Rail class `UpdateJob` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - status: OgStr + status: Optional[OgStr] started_at: OgDateTime - finished_at: OgDateTime + finished_at: Optional[OgDateTime] last_heartbeat: OgDateTime - current_step: OgStr - progress_percent: OgInt - log_excerpt: OgStr - error_message: OgStr - triggered_by: OgInt + current_step: Optional[OgStr] + progress_percent: Optional[OgInt] + log_excerpt: Optional[OgStr] + error_message: Optional[OgStr] + triggered_by: Optional[OgInt] cancel_requested: OgBool server: ToOne["RentedServer"] @@ -1226,42 +1232,42 @@ class UpdateSnapshot: CLASSID: ClassVar[int] = 0x00000000 id: OgInt snapshot_file: OgStr - snapshot_size: OgInt - version_before: OgStr - version_after: OgStr - status: OgStr - log_excerpt: OgStr + snapshot_size: Optional[OgInt] + version_before: Optional[OgStr] + version_after: Optional[OgStr] + status: Optional[OgStr] + log_excerpt: Optional[OgStr] created_at: OgDateTime - restored_at: OgDateTime - rollback_note: OgStr + restored_at: Optional[OgDateTime] + rollback_note: Optional[OgStr] @dataclass class TerminVorschlag: """Rail class `TerminVorschlag` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt - project_id: OgInt - defect_id: OgInt + tenant_id: Optional[OgInt] + project_id: Optional[OgInt] + defect_id: Optional[OgInt] titel: OgStr - beschreibung: OgStr - termin_typ: OgStr - vorschlaege_json: OgStr - status: OgStr - accepted_index: OgInt - accepted_reminder_id: OgInt - accepted_at: OgDateTime - accepted_ip: OgStr - token_slot1: OgStr - token_slot2: OgStr - token_slot3: OgStr + beschreibung: Optional[OgStr] + termin_typ: Optional[OgStr] + vorschlaege_json: Optional[OgStr] + status: Optional[OgStr] + accepted_index: Optional[OgInt] + accepted_reminder_id: Optional[OgInt] + accepted_at: Optional[OgDateTime] + accepted_ip: Optional[OgStr] + token_slot1: Optional[OgStr] + token_slot2: Optional[OgStr] + token_slot3: Optional[OgStr] token_decline: OgStr - token_cancel: OgStr - cancelled_at: OgDateTime - cancelled_ip: OgStr + token_cancel: Optional[OgStr] + cancelled_at: Optional[OgDateTime] + cancelled_ip: Optional[OgStr] expires_at: OgDateTime - erstellt_am: OgDateTime - updated_at: OgDateTime + erstellt_am: Optional[OgDateTime] + updated_at: Optional[OgDateTime] customer: ToOne["Customer"] user: ToOne["User"] workorder: ToOne["WorkOrder"] @@ -1274,12 +1280,12 @@ class TerminVorschlagBlocker: id: OgInt slot_index: OgInt datum: OgDate - zeit_von: OgStr - zeit_bis: OgStr + zeit_von: Optional[OgStr] + zeit_bis: Optional[OgStr] caldav_uid: OgStr - pushed_at: OgDateTime - deleted_at: OgDateTime - erstellt_am: OgDateTime + pushed_at: Optional[OgDateTime] + deleted_at: Optional[OgDateTime] + erstellt_am: Optional[OgDateTime] tv: ToOne["TerminVorschlag"] @dataclass @@ -1288,14 +1294,14 @@ class HandbookFeature: CLASSID: ClassVar[int] = 0x00000000 id: OgInt titel: OgStr - beschreibung: OgStr - kategorie: OgStr - version: OgStr - reihenfolge: OgInt - aktiv: OgBool - datum: OgDate - created_at: OgDateTime - updated_at: OgDateTime + beschreibung: Optional[OgStr] + kategorie: Optional[OgStr] + version: Optional[OgStr] + reihenfolge: Optional[OgInt] + aktiv: Optional[OgBool] + datum: Optional[OgDate] + created_at: Optional[OgDateTime] + updated_at: Optional[OgDateTime] @dataclass class BrancheTemplate: @@ -1303,35 +1309,35 @@ class BrancheTemplate: CLASSID: ClassVar[int] = 0x00000000 id: OgInt branche: OgStr - label: OgStr - html_content: OgStr - aktiv: OgBool - erstellt_am: OgDateTime - aktualisiert_am: OgDateTime + label: Optional[OgStr] + html_content: Optional[OgStr] + aktiv: Optional[OgBool] + erstellt_am: Optional[OgDateTime] + aktualisiert_am: Optional[OgDateTime] @dataclass class ServiceContractItem: """Rail class `ServiceContractItem` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - sort_order: OgInt - pos_typ: OgStr - titel: OgStr - beschreibung: OgStr - intervall: OgStr - preis_netto: OgFloat - menge: OgFloat - mwst_satz: OgFloat - rabatt_typ: OgStr - rabatt_wert: OgFloat - rabatt_bis: OgDate - rabatt_grund: OgStr - aktiv_ab: OgDate - aktiv_bis: OgDate - abgerechnet_bis: OgDate - sofort_rechnung: OgBool - aktiv: OgBool - created_at: OgDateTime + sort_order: Optional[OgInt] + pos_typ: Optional[OgStr] + titel: Optional[OgStr] + beschreibung: Optional[OgStr] + intervall: Optional[OgStr] + preis_netto: Optional[OgFloat] + menge: Optional[OgFloat] + mwst_satz: Optional[OgFloat] + rabatt_typ: Optional[OgStr] + rabatt_wert: Optional[OgFloat] + rabatt_bis: Optional[OgDate] + rabatt_grund: Optional[OgStr] + aktiv_ab: Optional[OgDate] + aktiv_bis: Optional[OgDate] + abgerechnet_bis: Optional[OgDate] + sofort_rechnung: Optional[OgBool] + aktiv: Optional[OgBool] + created_at: Optional[OgDateTime] contract: ToOne["ServiceContract"] package: ToOne["ServicePackage"] @@ -1341,17 +1347,17 @@ class ContractBonus: CLASSID: ClassVar[int] = 0x00000000 id: OgInt bonus_typ: OgStr - wert: OgFloat - grund: OgStr - aktiv_ab: OgDate - aktiv_bis: OgDate - verbraucht: OgBool - verbraucht_am: OgDateTime - verbraucht_in_workorder_id: OgInt - promo_code_id: OgInt - aktiv: OgBool - created_at: OgDateTime - created_by: OgInt + wert: Optional[OgFloat] + grund: Optional[OgStr] + aktiv_ab: Optional[OgDate] + aktiv_bis: Optional[OgDate] + verbraucht: Optional[OgBool] + verbraucht_am: Optional[OgDateTime] + verbraucht_in_workorder_id: Optional[OgInt] + promo_code_id: Optional[OgInt] + aktiv: Optional[OgBool] + created_at: Optional[OgDateTime] + created_by: Optional[OgInt] contract: ToMany["ServiceContract"] @dataclass @@ -1361,20 +1367,20 @@ class PromoCode: id: OgInt tenant_id: OgInt code: OgStr - titel: OgStr - beschreibung: OgStr - code_typ: OgStr - wert: OgFloat - gueltig_ab: OgDate - gueltig_bis: OgDate - max_einloesungen: OgInt - aktuelle_einloesungen: OgInt - nur_neukunden: OgBool - min_vertragswert_netto: OgFloat - bonus_aktiv_monate: OgInt - aktiv: OgBool - created_at: OgDateTime - created_by: OgInt + titel: Optional[OgStr] + beschreibung: Optional[OgStr] + code_typ: Optional[OgStr] + wert: Optional[OgFloat] + gueltig_ab: Optional[OgDate] + gueltig_bis: Optional[OgDate] + max_einloesungen: Optional[OgInt] + aktuelle_einloesungen: Optional[OgInt] + nur_neukunden: Optional[OgBool] + min_vertragswert_netto: Optional[OgFloat] + bonus_aktiv_monate: Optional[OgInt] + aktiv: Optional[OgBool] + created_at: Optional[OgDateTime] + created_by: Optional[OgInt] @dataclass class OnboardingTemplate: @@ -1382,13 +1388,13 @@ class OnboardingTemplate: CLASSID: ClassVar[int] = 0x00000000 id: OgInt kind: OgStr - label: OgStr - subject: OgStr - html_content: OgStr - plain_body: OgStr - aktiv: OgBool - erstellt_am: OgDateTime - aktualisiert_am: OgDateTime + label: Optional[OgStr] + subject: Optional[OgStr] + html_content: Optional[OgStr] + plain_body: Optional[OgStr] + aktiv: Optional[OgBool] + erstellt_am: Optional[OgDateTime] + aktualisiert_am: Optional[OgDateTime] @dataclass class PartnerContractTemplate: @@ -1396,11 +1402,11 @@ class PartnerContractTemplate: CLASSID: ClassVar[int] = 0x00000000 id: OgInt tenant_id: OgInt - label: OgStr - html_content: OgStr - aktiv: OgBool - erstellt_am: OgDateTime - aktualisiert_am: OgDateTime + label: Optional[OgStr] + html_content: Optional[OgStr] + aktiv: Optional[OgBool] + erstellt_am: Optional[OgDateTime] + aktualisiert_am: Optional[OgDateTime] @dataclass class PortalAutoLoginToken: @@ -1409,16 +1415,16 @@ class PortalAutoLoginToken: id: OgInt tenant_id: OgInt customer_portal_user_id: OgInt - workorder_id: OgInt + workorder_id: Optional[OgInt] token: OgStr created_at: OgDateTime expires_at: OgDateTime - last_used_at: OgDateTime - revoked_at: OgDateTime + last_used_at: Optional[OgDateTime] + revoked_at: Optional[OgDateTime] use_count: OgInt - created_by: OgStr - scope: OgStr - permanent: OgBool + created_by: Optional[OgStr] + scope: Optional[OgStr] + permanent: Optional[OgBool] portal_user: ToOne["CustomerPortalUser"] @dataclass @@ -1430,9 +1436,9 @@ class MailTemplate: kind: OgStr subject: OgStr html_body: OgStr - plain_body: OgStr + plain_body: Optional[OgStr] updated_at: OgDateTime - updated_by_user_id: OgInt + updated_by_user_id: Optional[OgInt] @dataclass class TresorPwResetToken: @@ -1443,8 +1449,8 @@ class TresorPwResetToken: token: OgStr created_at: OgDateTime expires_at: OgDateTime - used_at: OgDateTime - created_by: OgStr + used_at: Optional[OgDateTime] + created_by: Optional[OgStr] @dataclass class LegacyRouteKey: @@ -1455,7 +1461,7 @@ class LegacyRouteKey: key_data: OgStr retired_at: OgDateTime expires_at: OgDateTime - note: OgStr + note: Optional[OgStr] @dataclass class RevokedToken: @@ -1465,8 +1471,8 @@ class RevokedToken: tenant_id: OgInt token_hash: OgStr revoked_at: OgDateTime - revoked_by_uid: OgInt - reason: OgStr + revoked_by_uid: Optional[OgInt] + reason: Optional[OgStr] @dataclass class GeoblockSetting: @@ -1475,10 +1481,10 @@ class GeoblockSetting: id: OgInt country_code: OgStr country_name: OgStr - continent: OgStr + continent: Optional[OgStr] blocked: OgBool - updated_at: OgDateTime - updated_by: OgInt + updated_at: Optional[OgDateTime] + updated_by: Optional[OgInt] @dataclass class GeoblockAllowIP: @@ -1486,9 +1492,9 @@ class GeoblockAllowIP: CLASSID: ClassVar[int] = 0x00000000 id: OgInt cidr: OgStr - note: OgStr - created_at: OgDateTime - created_by: OgInt + note: Optional[OgStr] + created_at: Optional[OgDateTime] + created_by: Optional[OgInt] @dataclass class GeoblockStat: @@ -1509,11 +1515,11 @@ class ClaudeLesson: severity: OgStr title: OgStr body: OgStr - tags: OgStr - related_patches: OgStr - is_active: OgBool - created_at: OgDateTime - created_by: OgStr + tags: Optional[OgStr] + related_patches: Optional[OgStr] + is_active: Optional[OgBool] + created_at: Optional[OgDateTime] + created_by: Optional[OgStr] @dataclass class ClaudeStaticSection: @@ -1524,10 +1530,10 @@ class ClaudeStaticSection: section_key: OgStr title: OgStr content: OgStr - has_timestamp_placeholder: OgBool - is_active: OgBool - created_at: OgDateTime - updated_at: OgDateTime + has_timestamp_placeholder: Optional[OgBool] + is_active: Optional[OgBool] + created_at: Optional[OgDateTime] + updated_at: Optional[OgDateTime] @dataclass class TaxRate: @@ -1540,19 +1546,19 @@ class TaxRate: aktiv: OgBool is_default: OgBool sort_order: OgInt - created_at: OgDateTime + created_at: Optional[OgDateTime] @dataclass class ShiftTicket: """Rail class `ShiftTicket` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt + tenant_id: Optional[OgInt] token: OgStr valid_until: OgDateTime - created_at: OgDateTime - created_by_id: OgInt - used_at: OgDateTime + created_at: Optional[OgDateTime] + created_by_id: Optional[OgInt] + used_at: Optional[OgDateTime] customer: ToOne["Customer"] project: ToOne["Project"] assigned_user: ToOne["User"] @@ -1564,41 +1570,41 @@ class ErpLegalProfile: """Rail class `ErpLegalProfile` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt - rechtsform: OgStr - festschreib_periode: OgStr - kleinunternehmer: OgBool - besteuerungsart: OgStr - buchen_belegpflicht: OgBool - lager_fibu_methode: OgStr - lager_wareneinsatz_konto: OgStr - bank_sachkonto_auto_pi: OgBool - customer_bank_autosync: OgStr - handelsregister: OgStr - steuernummer: OgStr - ust_id: OgStr - default_currency: OgStr - currency_custom_code: OgStr - currency_custom_symbol: OgStr - datev_beraternr: OgStr - datev_mandantnr: OgStr - datev_wj_beginn: OgInt - erp_startdatum: OgDate - legacy_source: OgStr - legacy_object_id: OgStr - created_at: OgDateTime - updated_at: OgDateTime - imap_server: OgStr - imap_port: OgInt - imap_user: OgStr - imap_password: OgStr - imap_folder: OgStr - imap_ssl: OgBool - imap_aktiv: OgBool - imap_forward_to: OgStr - imap_interval: OgInt - gewerbesteuer_hebesatz: OgInt - erechnung_format: OgStr + tenant_id: Optional[OgInt] + rechtsform: Optional[OgStr] + festschreib_periode: Optional[OgStr] + kleinunternehmer: Optional[OgBool] + besteuerungsart: Optional[OgStr] + buchen_belegpflicht: Optional[OgBool] + lager_fibu_methode: Optional[OgStr] + lager_wareneinsatz_konto: Optional[OgStr] + bank_sachkonto_auto_pi: Optional[OgBool] + customer_bank_autosync: Optional[OgStr] + handelsregister: Optional[OgStr] + steuernummer: Optional[OgStr] + ust_id: Optional[OgStr] + default_currency: Optional[OgStr] + currency_custom_code: Optional[OgStr] + currency_custom_symbol: Optional[OgStr] + datev_beraternr: Optional[OgStr] + datev_mandantnr: Optional[OgStr] + datev_wj_beginn: Optional[OgInt] + erp_startdatum: Optional[OgDate] + legacy_source: Optional[OgStr] + legacy_object_id: Optional[OgStr] + created_at: Optional[OgDateTime] + updated_at: Optional[OgDateTime] + imap_server: Optional[OgStr] + imap_port: Optional[OgInt] + imap_user: Optional[OgStr] + imap_password: Optional[OgStr] + imap_folder: Optional[OgStr] + imap_ssl: Optional[OgBool] + imap_aktiv: Optional[OgBool] + imap_forward_to: Optional[OgStr] + imap_interval: Optional[OgInt] + gewerbesteuer_hebesatz: Optional[OgInt] + erechnung_format: Optional[OgStr] @dataclass class ErpSupplierIban: @@ -1607,209 +1613,209 @@ class ErpSupplierIban: id: OgInt supplier_id: OgInt iban: OgStr - bic: OgStr - notiz: OgStr - created_at: OgDateTime + bic: Optional[OgStr] + notiz: Optional[OgStr] + created_at: Optional[OgDateTime] @dataclass class ErpFintsInstitute: """Rail class `ErpFintsInstitute` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 blz: OgStr - bic: OgStr - institut: OgStr - ort: OgStr - rz: OgStr - organisation: OgStr - hbci_dns: OgStr - hbci_version: OgStr - pintan_url: OgStr - fints_version: OgStr - updated_at_src: OgDate - imported_at: OgDateTime + bic: Optional[OgStr] + institut: Optional[OgStr] + ort: Optional[OgStr] + rz: Optional[OgStr] + organisation: Optional[OgStr] + hbci_dns: Optional[OgStr] + hbci_version: Optional[OgStr] + pintan_url: Optional[OgStr] + fints_version: Optional[OgStr] + updated_at_src: Optional[OgDate] + imported_at: Optional[OgDateTime] @dataclass class ErpExchangeRate: """Rail class `ErpExchangeRate` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt - from_currency: OgStr - to_currency: OgStr - rate: OgMoney - valid_from: OgDate - valid_until: OgDate - source: OgStr - created_at: OgDateTime + tenant_id: Optional[OgInt] + from_currency: Optional[OgStr] + to_currency: Optional[OgStr] + rate: Optional[OgMoney] + valid_from: Optional[OgDate] + valid_until: Optional[OgDate] + source: Optional[OgStr] + created_at: Optional[OgDateTime] @dataclass class ErpEstTarifParams: """Rail class `ErpEstTarifParams` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt + tenant_id: Optional[OgInt] jahr: OgInt - grundfreibetrag: OgInt - zone2_bis: OgInt - zone3_bis: OgInt - zone4_bis: OgInt - z2a: OgStr - z2b: OgStr - z3a: OgStr - z3b: OgStr - z3c: OgStr - z4_abzug: OgStr - z5_abzug: OgStr - soli_freigrenze: OgInt - created_at: OgDateTime - updated_at: OgDateTime + grundfreibetrag: Optional[OgInt] + zone2_bis: Optional[OgInt] + zone3_bis: Optional[OgInt] + zone4_bis: Optional[OgInt] + z2a: Optional[OgStr] + z2b: Optional[OgStr] + z3a: Optional[OgStr] + z3b: Optional[OgStr] + z3c: Optional[OgStr] + z4_abzug: Optional[OgStr] + z5_abzug: Optional[OgStr] + soli_freigrenze: Optional[OgInt] + created_at: Optional[OgDateTime] + updated_at: Optional[OgDateTime] @dataclass class ErpKuGrenzen: """Rail class `ErpKuGrenzen` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt + tenant_id: Optional[OgInt] jahr: OgInt - grenze_vorjahr: OgInt - grenze_laufend: OgInt - created_at: OgDateTime - updated_at: OgDateTime + grenze_vorjahr: Optional[OgInt] + grenze_laufend: Optional[OgInt] + created_at: Optional[OgDateTime] + updated_at: Optional[OgDateTime] @dataclass class ErpLedgerLock: """Rail class `ErpLedgerLock` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt + tenant_id: Optional[OgInt] buchungsperiode: OgStr - status: OgStr - locked_at: OgDateTime - locked_by_user_id: OgInt - hash_snapshot: OgStr - created_at: OgDateTime + status: Optional[OgStr] + locked_at: Optional[OgDateTime] + locked_by_user_id: Optional[OgInt] + hash_snapshot: Optional[OgStr] + created_at: Optional[OgDateTime] @dataclass class ErpAuditTrail: """Rail class `ErpAuditTrail` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt + tenant_id: Optional[OgInt] seq_no: OgInt entity: OgStr - entity_id: OgInt + entity_id: Optional[OgInt] aktion: OgStr - user_id: OgInt - ip: OgStr - user_agent: OgStr - freitext: OgStr - before_hash: OgStr - after_hash: OgStr - created_at: OgDateTime + user_id: Optional[OgInt] + ip: Optional[OgStr] + user_agent: Optional[OgStr] + freitext: Optional[OgStr] + before_hash: Optional[OgStr] + after_hash: Optional[OgStr] + created_at: Optional[OgDateTime] @dataclass class ErpChartOfAccounts: """Rail class `ErpChartOfAccounts` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt + tenant_id: Optional[OgInt] rahmen: OgStr - aktiv: OgBool - gesperrt_ab: OgDateTime - created_at: OgDateTime + aktiv: Optional[OgBool] + gesperrt_ab: Optional[OgDateTime] + created_at: Optional[OgDateTime] @dataclass class ErpAccount: """Rail class `ErpAccount` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt + tenant_id: Optional[OgInt] rahmen: OgStr kontonummer: OgStr bezeichnung: OgStr - kontoart: OgStr - kontenklasse: OgInt - steuer_relevant: OgBool - ust_kennzeichen: OgStr - automatik_konto: OgBool - gesperrt: OgBool - sort_order: OgInt - currency: OgStr - legacy_source: OgStr - legacy_object_id: OgStr - created_at: OgDateTime + kontoart: Optional[OgStr] + kontenklasse: Optional[OgInt] + steuer_relevant: Optional[OgBool] + ust_kennzeichen: Optional[OgStr] + automatik_konto: Optional[OgBool] + gesperrt: Optional[OgBool] + sort_order: Optional[OgInt] + currency: Optional[OgStr] + legacy_source: Optional[OgStr] + legacy_object_id: Optional[OgStr] + created_at: Optional[OgDateTime] @dataclass class ErpCostCenter: """Rail class `ErpCostCenter` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt + tenant_id: Optional[OgInt] nummer: OgStr - bezeichnung: OgStr - aktiv: OgBool - parent_id: OgInt - created_at: OgDateTime + bezeichnung: Optional[OgStr] + aktiv: Optional[OgBool] + parent_id: Optional[OgInt] + created_at: Optional[OgDateTime] @dataclass class ErpFiscalYear: """Rail class `ErpFiscalYear` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt + tenant_id: Optional[OgInt] jahr: OgInt beginn: OgDate ende: OgDate - status: OgStr - created_at: OgDateTime + status: Optional[OgStr] + created_at: Optional[OgDateTime] @dataclass class ErpPeriod: """Rail class `ErpPeriod` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt - fiscal_year_id: OgInt + tenant_id: Optional[OgInt] + fiscal_year_id: Optional[OgInt] periode_key: OgStr - bezeichnung: OgStr - status: OgStr - created_at: OgDateTime + bezeichnung: Optional[OgStr] + status: Optional[OgStr] + created_at: Optional[OgDateTime] @dataclass class ErpTaxAccountMap: """Rail class `ErpTaxAccountMap` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt + tenant_id: Optional[OgInt] rahmen: OgStr ust_kennzeichen: OgStr - beschreibung: OgStr - ust_konto: OgStr - vst_konto: OgStr - steuer_konto: OgStr - prozent: OgMoney - gueltig_ab: OgDate - gueltig_bis: OgDate - created_at: OgDateTime + beschreibung: Optional[OgStr] + ust_konto: Optional[OgStr] + vst_konto: Optional[OgStr] + steuer_konto: Optional[OgStr] + prozent: Optional[OgMoney] + gueltig_ab: Optional[OgDate] + gueltig_bis: Optional[OgDate] + created_at: Optional[OgDateTime] @dataclass class ErpJournalEntry: """Rail class `ErpJournalEntry` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt + tenant_id: Optional[OgInt] belegnummer: OgStr belegdatum: OgDate buchungsdatum: OgDate - buchungstext: OgStr - erfasst_von_user_id: OgInt - festgeschrieben: OgBool - festgeschrieben_am: OgDateTime - storno_of_id: OgInt - herkunft: OgStr - herkunft_ref_id: OgInt - currency: OgStr - created_at: OgDateTime + buchungstext: Optional[OgStr] + erfasst_von_user_id: Optional[OgInt] + festgeschrieben: Optional[OgBool] + festgeschrieben_am: Optional[OgDateTime] + storno_of_id: Optional[OgInt] + herkunft: Optional[OgStr] + herkunft_ref_id: Optional[OgInt] + currency: Optional[OgStr] + created_at: Optional[OgDateTime] lines: ToMany["ErpJournalLine"] @dataclass @@ -1819,85 +1825,85 @@ class ErpJournalLine: id: OgInt entry_id: OgInt konto: OgStr - gegenkonto: OgStr - soll_betrag: OgMoney - haben_betrag: OgMoney - steuer_betrag: OgMoney - ust_kennzeichen: OgStr - kostenstelle_id: OgInt - zeilentext: OgStr + gegenkonto: Optional[OgStr] + soll_betrag: Optional[OgMoney] + haben_betrag: Optional[OgMoney] + steuer_betrag: Optional[OgMoney] + ust_kennzeichen: Optional[OgStr] + kostenstelle_id: Optional[OgInt] + zeilentext: Optional[OgStr] @dataclass class ErpDebitorAccount: """Rail class `ErpDebitorAccount` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt + tenant_id: Optional[OgInt] customer_id: OgInt kontonummer: OgStr - created_at: OgDateTime + created_at: Optional[OgDateTime] @dataclass class ErpSupplier: """Rail class `ErpSupplier` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt + tenant_id: Optional[OgInt] name: OgStr - anschrift: OgStr - ust_id: OgStr - steuernummer: OgStr - iban: OgStr - bic: OgStr - zahlungsziel: OgInt - skonto_tage: OgInt - skonto_prozent: OgFloat - kreditorenkonto: OgStr - aufwandskonto_default: OgStr - ansprechpartner: OgStr - ap_position: OgStr - telefon: OgStr - email: OgStr - website: OgStr - strasse: OgStr - plz: OgStr - ort: OgStr - land: OgStr - unsere_kundennr: OgStr - notizen: OgStr - lieferzeit_tage: OgInt - mindestbestellwert: OgMoney - aktiv: OgBool - created_at: OgDateTime + anschrift: Optional[OgStr] + ust_id: Optional[OgStr] + steuernummer: Optional[OgStr] + iban: Optional[OgStr] + bic: Optional[OgStr] + zahlungsziel: Optional[OgInt] + skonto_tage: Optional[OgInt] + skonto_prozent: Optional[OgFloat] + kreditorenkonto: Optional[OgStr] + aufwandskonto_default: Optional[OgStr] + ansprechpartner: Optional[OgStr] + ap_position: Optional[OgStr] + telefon: Optional[OgStr] + email: Optional[OgStr] + website: Optional[OgStr] + strasse: Optional[OgStr] + plz: Optional[OgStr] + ort: Optional[OgStr] + land: Optional[OgStr] + unsere_kundennr: Optional[OgStr] + notizen: Optional[OgStr] + lieferzeit_tage: Optional[OgInt] + mindestbestellwert: Optional[OgMoney] + aktiv: Optional[OgBool] + created_at: Optional[OgDateTime] @dataclass class ErpPurchaseInvoice: """Rail class `ErpPurchaseInvoice` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt + tenant_id: Optional[OgInt] rechnungsnr: OgStr belegdatum: OgDate - eingangsdatum: OgDate - faellig_am: OgDate - netto: OgMoney - steuer: OgMoney - brutto: OgMoney - currency: OgStr - aufwandskonto: OgStr - buchungstext: OgStr - split_buchung: OgStr - geprueft: OgBool - geprueft_von: OgInt - geprueft_am: OgDateTime - dokument_pfad: OgStr - status: OgStr - bezahlt: OgBool - bezahlt_am: OgDate - journal_entry_id: OgInt - created_at: OgDateTime - skontofrist: OgInt - skontosatz: OgMoney + eingangsdatum: Optional[OgDate] + faellig_am: Optional[OgDate] + netto: Optional[OgMoney] + steuer: Optional[OgMoney] + brutto: Optional[OgMoney] + currency: Optional[OgStr] + aufwandskonto: Optional[OgStr] + buchungstext: Optional[OgStr] + split_buchung: Optional[OgStr] + geprueft: Optional[OgBool] + geprueft_von: Optional[OgInt] + geprueft_am: Optional[OgDateTime] + dokument_pfad: Optional[OgStr] + status: Optional[OgStr] + bezahlt: Optional[OgBool] + bezahlt_am: Optional[OgDate] + journal_entry_id: Optional[OgInt] + created_at: Optional[OgDateTime] + skontofrist: Optional[OgInt] + skontosatz: Optional[OgMoney] supplier: ToOne["ErpSupplier"] @dataclass @@ -1905,16 +1911,16 @@ class ErpSupplierArticle: """Rail class `ErpSupplierArticle` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt - lieferanten_artikelnr: OgStr - ek_preis: OgMoney - currency: OgStr - mindestmenge: OgInt - staffelpreise: OgStr - lieferzeit_tage: OgInt - ist_hauptlieferant: OgBool - aktiv: OgBool - created_at: OgDateTime + tenant_id: Optional[OgInt] + lieferanten_artikelnr: Optional[OgStr] + ek_preis: Optional[OgMoney] + currency: Optional[OgStr] + mindestmenge: Optional[OgInt] + staffelpreise: Optional[OgStr] + lieferzeit_tage: Optional[OgInt] + ist_hauptlieferant: Optional[OgBool] + aktiv: Optional[OgBool] + created_at: Optional[OgDateTime] supplier: ToOne["ErpSupplier"] article: ToOne["Article"] @@ -1923,20 +1929,20 @@ class ErpPurchaseOrder: """Rail class `ErpPurchaseOrder` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt + tenant_id: Optional[OgInt] bestellnummer: OgStr - status: OgStr - bestelldatum: OgDate - liefertermin: OgDate - netto_summe: OgMoney - steuer_summe: OgMoney - brutto_summe: OgMoney - currency: OgStr - notizen: OgStr - purchase_invoice_id: OgInt - erstellt_von: OgInt - created_at: OgDateTime - updated_at: OgDateTime + status: Optional[OgStr] + bestelldatum: Optional[OgDate] + liefertermin: Optional[OgDate] + netto_summe: Optional[OgMoney] + steuer_summe: Optional[OgMoney] + brutto_summe: Optional[OgMoney] + currency: Optional[OgStr] + notizen: Optional[OgStr] + purchase_invoice_id: Optional[OgInt] + erstellt_von: Optional[OgInt] + created_at: Optional[OgDateTime] + updated_at: Optional[OgDateTime] supplier: ToOne["ErpSupplier"] lines: ToMany["ErpPurchaseOrderLine"] @@ -1946,14 +1952,14 @@ class ErpPurchaseOrderLine: CLASSID: ClassVar[int] = 0x00000000 id: OgInt purchase_order_id: OgInt - artikelnr: OgStr - bezeichnung: OgStr + artikelnr: Optional[OgStr] + bezeichnung: Optional[OgStr] menge: OgFloat - ek_preis: OgMoney - steuersatz: OgMoney - currency: OgStr - geliefert_menge: OgFloat - notiz: OgStr + ek_preis: Optional[OgMoney] + steuersatz: Optional[OgMoney] + currency: Optional[OgStr] + geliefert_menge: Optional[OgFloat] + notiz: Optional[OgStr] article: ToOne["Article"] @dataclass @@ -1961,20 +1967,20 @@ class ErpMaterialBedarf: """Rail class `ErpMaterialBedarf` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt - menge_position: OgFloat - menge_bestand: OgFloat - menge_bedarf: OgFloat - menge_bestellt: OgFloat - menge_geliefert: OgFloat - einheit: OgStr - best_ek: OgMoney - status: OgStr - workorder_nr: OgStr - artikel_bez: OgStr - created_at: OgDateTime - created_by: OgInt - notiz: OgStr + tenant_id: Optional[OgInt] + menge_position: Optional[OgFloat] + menge_bestand: Optional[OgFloat] + menge_bedarf: Optional[OgFloat] + menge_bestellt: Optional[OgFloat] + menge_geliefert: Optional[OgFloat] + einheit: Optional[OgStr] + best_ek: Optional[OgMoney] + status: Optional[OgStr] + workorder_nr: Optional[OgStr] + artikel_bez: Optional[OgStr] + created_at: Optional[OgDateTime] + created_by: Optional[OgInt] + notiz: Optional[OgStr] workorder: ToOne["WorkOrder"] article: ToOne["Article"] best_supplier: ToOne["ErpSupplier"] @@ -1986,31 +1992,31 @@ class ErpCashRegister: """Rail class `ErpCashRegister` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt - name: OgStr - sachkonto: OgStr - currency: OgStr - aktiv: OgBool - anfangssaldo: OgMoney - created_at: OgDateTime + tenant_id: Optional[OgInt] + name: Optional[OgStr] + sachkonto: Optional[OgStr] + currency: Optional[OgStr] + aktiv: Optional[OgBool] + anfangssaldo: Optional[OgMoney] + created_at: Optional[OgDateTime] @dataclass class ErpCashEntry: """Rail class `ErpCashEntry` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt + tenant_id: Optional[OgInt] lfd_nr: OgInt datum: OgDate vorgang: OgStr betrag: OgMoney - gegenkonto: OgStr - zweck: OgStr - erfasst_von_user_id: OgInt - festgeschrieben: OgBool - storno_of_id: OgInt - journal_entry_id: OgInt - created_at: OgDateTime + gegenkonto: Optional[OgStr] + zweck: Optional[OgStr] + erfasst_von_user_id: Optional[OgInt] + festgeschrieben: Optional[OgBool] + storno_of_id: Optional[OgInt] + journal_entry_id: Optional[OgInt] + created_at: Optional[OgDateTime] register: ToOne["ErpCashRegister"] @dataclass @@ -2018,44 +2024,44 @@ class ErpDocument: """Rail class `ErpDocument` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt + tenant_id: Optional[OgInt] original_name: OgStr stored_name: OgStr - mime_type: OgStr - size_bytes: OgInt - integrity_hash: OgStr - belegtyp: OgStr - beleg_status: OgStr - steuerberater_relevant: OgBool - beschreibung: OgStr - customer_id: OgInt - purchase_invoice_id: OgInt - asset_id: OgInt - journal_entry_id: OgInt - legacy_source: OgStr - legacy_object_id: OgStr - legacy_tree_id: OgStr - legacy_filename: OgStr - legacy_imported_at: OgDateTime - uploaded_by: OgInt - uploaded_at: OgDateTime - kategorie_id: OgInt - periode: OgStr - tags: OgStr - beleg_datum: OgDate - lieferant_id: OgInt - workorder_id: OgInt - bank_tx_id: OgInt - ocr_status: OgStr + mime_type: Optional[OgStr] + size_bytes: Optional[OgInt] + integrity_hash: Optional[OgStr] + belegtyp: Optional[OgStr] + beleg_status: Optional[OgStr] + steuerberater_relevant: Optional[OgBool] + beschreibung: Optional[OgStr] + customer_id: Optional[OgInt] + purchase_invoice_id: Optional[OgInt] + asset_id: Optional[OgInt] + journal_entry_id: Optional[OgInt] + legacy_source: Optional[OgStr] + legacy_object_id: Optional[OgStr] + legacy_tree_id: Optional[OgStr] + legacy_filename: Optional[OgStr] + legacy_imported_at: Optional[OgDateTime] + uploaded_by: Optional[OgInt] + uploaded_at: Optional[OgDateTime] + kategorie_id: Optional[OgInt] + periode: Optional[OgStr] + tags: Optional[OgStr] + beleg_datum: Optional[OgDate] + lieferant_id: Optional[OgInt] + workorder_id: Optional[OgInt] + bank_tx_id: Optional[OgInt] + ocr_status: Optional[OgStr] @dataclass class ErpDocumentFulltext: """Rail class `ErpDocumentFulltext` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - quelle: OgStr - text: OgStr - indexed_at: OgDateTime + quelle: Optional[OgStr] + text: Optional[OgStr] + indexed_at: Optional[OgDateTime] document: ToOne["ErpDocument"] @dataclass @@ -2063,14 +2069,14 @@ class ErpDmsKategorie: """Rail class `ErpDmsKategorie` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt - parent_id: OgInt + tenant_id: Optional[OgInt] + parent_id: Optional[OgInt] name: OgStr - beschreibung: OgStr - icon: OgStr - sort_order: OgInt - aktiv: OgBool - created_at: OgDateTime + beschreibung: Optional[OgStr] + icon: Optional[OgStr] + sort_order: Optional[OgInt] + aktiv: Optional[OgBool] + created_at: Optional[OgDateTime] children: ToMany["ErpDmsKategorie"] @dataclass @@ -2078,35 +2084,35 @@ class ErpDmsAudit: """Rail class `ErpDmsAudit` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt + tenant_id: Optional[OgInt] document_id: OgInt - user_id: OgInt + user_id: Optional[OgInt] aktion: OgStr - von_status: OgStr - zu_status: OgStr - kommentar: OgStr - created_at: OgDateTime + von_status: Optional[OgStr] + zu_status: Optional[OgStr] + kommentar: Optional[OgStr] + created_at: Optional[OgDateTime] @dataclass class SambaShare: """Rail class `SambaShare` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt + tenant_id: Optional[OgInt] name: OgStr path: OgStr - comment: OgStr - browseable: OgBool - aktiv: OgBool - created_at: OgDateTime + comment: Optional[OgStr] + browseable: Optional[OgBool] + aktiv: Optional[OgBool] + created_at: Optional[OgDateTime] @dataclass class SambaShareAcl: """Rail class `SambaShareAcl` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - can_read: OgBool - can_write: OgBool + can_read: Optional[OgBool] + can_write: Optional[OgBool] share: ToOne["SambaShare"] user: ToOne["User"] @@ -2115,56 +2121,56 @@ class ScanRequest: """Rail class `ScanRequest` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt + tenant_id: Optional[OgInt] user_id: OgInt ziel_typ: OgStr - ziel_id: OgInt - status: OgStr - document_id: OgInt - created_at: OgDateTime - completed_at: OgDateTime - expires_at: OgDateTime - hinweis: OgStr + ziel_id: Optional[OgInt] + status: Optional[OgStr] + document_id: Optional[OgInt] + created_at: Optional[OgDateTime] + completed_at: Optional[OgDateTime] + expires_at: Optional[OgDateTime] + hinweis: Optional[OgStr] @dataclass class EinsatzSession: """Rail class `EinsatzSession` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt + tenant_id: Optional[OgInt] pairing_code: OgStr - qr_token: OgStr + qr_token: Optional[OgStr] code_expires_at: OgDateTime - session_token: OgStr - user_id: OgInt - customer_id: OgInt - reminder_id: OgInt - workorder_id: OgInt - scope: OgStr - status: OgStr - paired_at: OgDateTime - expires_at: OgDateTime - last_activity: OgDateTime - ended_at: OgDateTime - ended_by: OgStr - browser_fp: OgStr - ip_address: OgStr - user_agent: OgStr - created_at: OgDateTime + session_token: Optional[OgStr] + user_id: Optional[OgInt] + customer_id: Optional[OgInt] + reminder_id: Optional[OgInt] + workorder_id: Optional[OgInt] + scope: Optional[OgStr] + status: Optional[OgStr] + paired_at: Optional[OgDateTime] + expires_at: Optional[OgDateTime] + last_activity: Optional[OgDateTime] + ended_at: Optional[OgDateTime] + ended_by: Optional[OgStr] + browser_fp: Optional[OgStr] + ip_address: Optional[OgStr] + user_agent: Optional[OgStr] + created_at: Optional[OgDateTime] @dataclass class ErpSerialCharge: """Rail class `ErpSerialCharge` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt - typ: OgStr + tenant_id: Optional[OgInt] + typ: Optional[OgStr] nummer: OgStr - mhd: OgDate - status: OgStr - workorder_id: OgInt - movement_id: OgInt - created_at: OgDateTime + mhd: Optional[OgDate] + status: Optional[OgStr] + workorder_id: Optional[OgInt] + movement_id: Optional[OgInt] + created_at: Optional[OgDateTime] article: ToOne["Article"] @dataclass @@ -2172,35 +2178,35 @@ class ErpEinvoiceImport: """Rail class `ErpEinvoiceImport` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt - status: OgStr - filename: OgStr - dateityp: OgStr - raw_xml: OgStr - profil: OgStr - format_typ: OgStr - lieferant_name: OgStr - lieferant_ustid: OgStr - lieferant_iban: OgStr - rechnungsnr: OgStr - rechnungsdatum: OgDate - faelligkeit: OgDate - waehrung: OgStr - netto_betrag: OgMoney - steuer_betrag: OgMoney - brutto_betrag: OgMoney - steuer_prozent: OgMoney - verwendungszweck: OgStr - positionen_json: OgStr - purchase_invoice_id: OgInt - document_id: OgInt - fehler: OgStr - warnungen: OgStr - hash_dedup: OgStr - created_at: OgDateTime - created_by: OgInt - confirmed_at: OgDateTime - confirmed_by: OgInt + tenant_id: Optional[OgInt] + status: Optional[OgStr] + filename: Optional[OgStr] + dateityp: Optional[OgStr] + raw_xml: Optional[OgStr] + profil: Optional[OgStr] + format_typ: Optional[OgStr] + lieferant_name: Optional[OgStr] + lieferant_ustid: Optional[OgStr] + lieferant_iban: Optional[OgStr] + rechnungsnr: Optional[OgStr] + rechnungsdatum: Optional[OgDate] + faelligkeit: Optional[OgDate] + waehrung: Optional[OgStr] + netto_betrag: Optional[OgMoney] + steuer_betrag: Optional[OgMoney] + brutto_betrag: Optional[OgMoney] + steuer_prozent: Optional[OgMoney] + verwendungszweck: Optional[OgStr] + positionen_json: Optional[OgStr] + purchase_invoice_id: Optional[OgInt] + document_id: Optional[OgInt] + fehler: Optional[OgStr] + warnungen: Optional[OgStr] + hash_dedup: Optional[OgStr] + created_at: Optional[OgDateTime] + created_by: Optional[OgInt] + confirmed_at: Optional[OgDateTime] + confirmed_by: Optional[OgInt] supplier: ToOne["ErpSupplier"] @dataclass @@ -2208,49 +2214,49 @@ class ErpSupplierCsvMapping: """Rail class `ErpSupplierCsvMapping` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt - name: OgStr - trennzeichen: OgStr - encoding: OgStr - header_zeile: OgInt - skip_zeilen: OgInt - col_ean: OgStr - col_artikelnr: OgStr - col_bezeichnung: OgStr - col_ek_preis: OgStr - col_mindestmenge: OgStr - col_einheit: OgStr - col_hersteller: OgStr - col_hersteller_anr: OgStr - col_bild_url: OgStr - col_kategorie: OgStr - col_unterkategorie: OgStr - fuzzy_schwelle: OgInt - auto_anlegen: OgBool - auto_kategorie: OgStr - auto_update_url: OgStr - auto_update_aktiv: OgBool - auto_update_auth_type: OgStr - auto_update_auth_user: OgStr - auto_update_auth_pass: OgStr - auto_update_auth_header: OgStr - auto_update_omd_token_url: OgStr - auto_update_omd_client_id: OgStr - auto_update_omd_client_secret: OgStr - auto_update_omd_cred_location: OgStr - auto_update_omd_customer_id: OgStr - auto_update_intervall: OgInt - auto_update_typ: OgStr - auto_update_uhrzeit: OgStr - auto_update_wochentage: OgStr - auto_update_next_run: OgDateTime - auto_inaktiv_setzen: OgBool - auto_update_last_status: OgStr - auto_update_last_error: OgStr - auto_update_last_run: OgDateTime - last_import_at: OgDateTime - last_import_count: OgInt - created_at: OgDateTime + tenant_id: Optional[OgInt] + name: Optional[OgStr] + trennzeichen: Optional[OgStr] + encoding: Optional[OgStr] + header_zeile: Optional[OgInt] + skip_zeilen: Optional[OgInt] + col_ean: Optional[OgStr] + col_artikelnr: Optional[OgStr] + col_bezeichnung: Optional[OgStr] + col_ek_preis: Optional[OgStr] + col_mindestmenge: Optional[OgStr] + col_einheit: Optional[OgStr] + col_hersteller: Optional[OgStr] + col_hersteller_anr: Optional[OgStr] + col_bild_url: Optional[OgStr] + col_kategorie: Optional[OgStr] + col_unterkategorie: Optional[OgStr] + fuzzy_schwelle: Optional[OgInt] + auto_anlegen: Optional[OgBool] + auto_kategorie: Optional[OgStr] + auto_update_url: Optional[OgStr] + auto_update_aktiv: Optional[OgBool] + auto_update_auth_type: Optional[OgStr] + auto_update_auth_user: Optional[OgStr] + auto_update_auth_pass: Optional[OgStr] + auto_update_auth_header: Optional[OgStr] + auto_update_omd_token_url: Optional[OgStr] + auto_update_omd_client_id: Optional[OgStr] + auto_update_omd_client_secret: Optional[OgStr] + auto_update_omd_cred_location: Optional[OgStr] + auto_update_omd_customer_id: Optional[OgStr] + auto_update_intervall: Optional[OgInt] + auto_update_typ: Optional[OgStr] + auto_update_uhrzeit: Optional[OgStr] + auto_update_wochentage: Optional[OgStr] + auto_update_next_run: Optional[OgDateTime] + auto_inaktiv_setzen: Optional[OgBool] + auto_update_last_status: Optional[OgStr] + auto_update_last_error: Optional[OgStr] + auto_update_last_run: Optional[OgDateTime] + last_import_at: Optional[OgDateTime] + last_import_count: Optional[OgInt] + created_at: Optional[OgDateTime] supplier: ToOne["ErpSupplier"] @dataclass @@ -2258,21 +2264,21 @@ class ErpStockMovement: """Rail class `ErpStockMovement` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt + tenant_id: Optional[OgInt] datum: OgDate - bewegungsart: OgStr + bewegungsart: Optional[OgStr] menge: OgFloat - ek_preis: OgFloat - currency: OgStr - herkunft: OgStr - herkunft_ref_id: OgInt - notiz: OgStr - erfasst_von: OgInt - erfasst_am: OgDateTime - mitarbeiter_id: OgInt - supplier_id: OgInt - gutschrift_nr: OgStr - schadensgrund: OgStr + ek_preis: Optional[OgFloat] + currency: Optional[OgStr] + herkunft: Optional[OgStr] + herkunft_ref_id: Optional[OgInt] + notiz: Optional[OgStr] + erfasst_von: Optional[OgInt] + erfasst_am: Optional[OgDateTime] + mitarbeiter_id: Optional[OgInt] + supplier_id: Optional[OgInt] + gutschrift_nr: Optional[OgStr] + schadensgrund: Optional[OgStr] article: ToOne["Article"] @dataclass @@ -2280,136 +2286,136 @@ class ErpAsset: """Rail class `ErpAsset` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt - inventarnr: OgStr + tenant_id: Optional[OgInt] + inventarnr: Optional[OgStr] bezeichnung: OgStr - anlagenklasse: OgStr + anlagenklasse: Optional[OgStr] anschaffungsdatum: OgDate anschaffungskosten: OgMoney - currency: OgStr - nutzungsdauer_jahre: OgInt - afa_methode: OgStr - anlagenkonto: OgStr - afa_konto: OgStr - kum_afa: OgMoney - letztes_afa_jahr: OgInt - aktiv: OgBool - abgang_datum: OgDate - abgang_art: OgStr - device_id: OgInt - kostenstelle_id: OgInt - created_at: OgDateTime + currency: Optional[OgStr] + nutzungsdauer_jahre: Optional[OgInt] + afa_methode: Optional[OgStr] + anlagenkonto: Optional[OgStr] + afa_konto: Optional[OgStr] + kum_afa: Optional[OgMoney] + letztes_afa_jahr: Optional[OgInt] + aktiv: Optional[OgBool] + abgang_datum: Optional[OgDate] + abgang_art: Optional[OgStr] + device_id: Optional[OgInt] + kostenstelle_id: Optional[OgInt] + created_at: Optional[OgDateTime] @dataclass class ErpBankAccount: """Rail class `ErpBankAccount` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt - name: OgStr - iban: OgStr - bic: OgStr - sachkonto: OgStr - aktiv: OgBool - kontotyp: OgStr - ist_hauptkonto: OgBool - anfangssaldo: OgMoney - fints_aktiv: OgBool - fints_blz: OgStr - fints_endpoint_url: OgStr - fints_login: OgStr - fints_pin_encrypted: OgStr - fints_tan_methode: OgStr - fints_letzter_abruf: OgDateTime - fints_status: OgStr - fints_letzter_fehler: OgStr - fints_system_id: OgStr - created_at: OgDateTime + tenant_id: Optional[OgInt] + name: Optional[OgStr] + iban: Optional[OgStr] + bic: Optional[OgStr] + sachkonto: Optional[OgStr] + aktiv: Optional[OgBool] + kontotyp: Optional[OgStr] + ist_hauptkonto: Optional[OgBool] + anfangssaldo: Optional[OgMoney] + fints_aktiv: Optional[OgBool] + fints_blz: Optional[OgStr] + fints_endpoint_url: Optional[OgStr] + fints_login: Optional[OgStr] + fints_pin_encrypted: Optional[OgStr] + fints_tan_methode: Optional[OgStr] + fints_letzter_abruf: Optional[OgDateTime] + fints_status: Optional[OgStr] + fints_letzter_fehler: Optional[OgStr] + fints_system_id: Optional[OgStr] + created_at: Optional[OgDateTime] @dataclass class ErpBankTransaction: """Rail class `ErpBankTransaction` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt - bank_account_id: OgInt - buchungsdatum: OgDate + tenant_id: Optional[OgInt] + bank_account_id: Optional[OgInt] + buchungsdatum: Optional[OgDate] betrag: OgMoney - verwendungszweck: OgStr - gegenname: OgStr - gegen_iban: OgStr - status: OgStr - match_wo_id: OgInt - match_pi_id: OgInt - match_type: OgStr - match_transfer_tx_id: OgInt - match_sachkonto: OgStr - match_steuersatz: OgInt - import_hash: OgStr - created_at: OgDateTime - document_id: OgInt - match_darlehen_id: OgInt - match_split_data: OgStr - match_abschlag_id: OgInt - match_konto_override: OgStr - hinweis_text: OgStr + verwendungszweck: Optional[OgStr] + gegenname: Optional[OgStr] + gegen_iban: Optional[OgStr] + status: Optional[OgStr] + match_wo_id: Optional[OgInt] + match_pi_id: Optional[OgInt] + match_type: Optional[OgStr] + match_transfer_tx_id: Optional[OgInt] + match_sachkonto: Optional[OgStr] + match_steuersatz: Optional[OgInt] + import_hash: Optional[OgStr] + created_at: Optional[OgDateTime] + document_id: Optional[OgInt] + match_darlehen_id: Optional[OgInt] + match_split_data: Optional[OgStr] + match_abschlag_id: Optional[OgInt] + match_konto_override: Optional[OgStr] + hinweis_text: Optional[OgStr] @dataclass class ErpDarlehen: """Rail class `ErpDarlehen` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt - darlehensnr: OgStr - bezeichnung: OgStr - glaeubiger: OgStr - glaeubiger_iban: OgStr - darlehensbetrag_brutto: OgMoney - restschuld: OgMoney - zinssatz: OgMoney - startdatum: OgDate - enddatum_geplant: OgDate - monatsrate_soll: OgMoney - konto_darlehen: OgStr - konto_zinsen: OgStr - aktiv: OgBool - notiz: OgStr - created_at: OgDateTime + tenant_id: Optional[OgInt] + darlehensnr: Optional[OgStr] + bezeichnung: Optional[OgStr] + glaeubiger: Optional[OgStr] + glaeubiger_iban: Optional[OgStr] + darlehensbetrag_brutto: Optional[OgMoney] + restschuld: Optional[OgMoney] + zinssatz: Optional[OgMoney] + startdatum: Optional[OgDate] + enddatum_geplant: Optional[OgDate] + monatsrate_soll: Optional[OgMoney] + konto_darlehen: Optional[OgStr] + konto_zinsen: Optional[OgStr] + aktiv: Optional[OgBool] + notiz: Optional[OgStr] + created_at: Optional[OgDateTime] @dataclass class ErpAbschlagskonto: """Rail class `ErpAbschlagskonto` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt - bezeichnung: OgStr - anbieter: OgStr - anbieter_iban: OgStr - vertragsnummer: OgStr - art: OgStr - monatlicher_abschlag: OgMoney - konto_aufwand: OgStr - ust_satz: OgInt - konto_aufwand_2: OgStr - anteil_aufwand_2_proz: OgInt - aktiv: OgBool - notiz: OgStr - created_at: OgDateTime + tenant_id: Optional[OgInt] + bezeichnung: Optional[OgStr] + anbieter: Optional[OgStr] + anbieter_iban: Optional[OgStr] + vertragsnummer: Optional[OgStr] + art: Optional[OgStr] + monatlicher_abschlag: Optional[OgMoney] + konto_aufwand: Optional[OgStr] + ust_satz: Optional[OgInt] + konto_aufwand_2: Optional[OgStr] + anteil_aufwand_2_proz: Optional[OgInt] + aktiv: Optional[OgBool] + notiz: Optional[OgStr] + created_at: Optional[OgDateTime] @dataclass class ErpPaymentRun: """Rail class `ErpPaymentRun` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt + tenant_id: Optional[OgInt] ausfuehrungsdatum: OgDate - status: OgStr - summe: OgMoney - anzahl: OgInt - currency: OgStr - xml_exported: OgBool - created_at: OgDateTime - created_by_id: OgInt + status: Optional[OgStr] + summe: Optional[OgMoney] + anzahl: Optional[OgInt] + currency: Optional[OgStr] + xml_exported: Optional[OgBool] + created_at: Optional[OgDateTime] + created_by_id: Optional[OgInt] bank_account: ToOne["ErpBankAccount"] items: ToMany["ErpPaymentRunItem"] @@ -2419,48 +2425,48 @@ class ErpPaymentRunItem: CLASSID: ClassVar[int] = 0x00000000 id: OgInt run_id: OgInt - purchase_invoice_id: OgInt + purchase_invoice_id: Optional[OgInt] empfaenger: OgStr iban: OgStr - bic: OgStr + bic: Optional[OgStr] betrag: OgMoney - currency: OgStr - verwendungszweck: OgStr + currency: Optional[OgStr] + verwendungszweck: Optional[OgStr] @dataclass class ErpVatReturn: """Rail class `ErpVatReturn` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt + tenant_id: Optional[OgInt] jahr: OgInt monat: OgInt - kz81: OgMoney - kz86: OgMoney - kz66: OgMoney - ust_19: OgMoney - ust_7: OgMoney - zahllast: OgMoney - status: OgStr - berechnet_am: OgDateTime - created_at: OgDateTime + kz81: Optional[OgMoney] + kz86: Optional[OgMoney] + kz66: Optional[OgMoney] + ust_19: Optional[OgMoney] + ust_7: Optional[OgMoney] + zahllast: Optional[OgMoney] + status: Optional[OgStr] + berechnet_am: Optional[OgDateTime] + created_at: Optional[OgDateTime] @dataclass class ErpPreisSchema: """Rail class `ErpPreisSchema` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt + tenant_id: Optional[OgInt] name: OgStr - beschreibung: OgStr - fix_aufschlag: OgMoney - rundung_typ: OgStr - mindestmarge_pct: OgMoney - auto_neuberechnung: OgBool - ist_default: OgBool - aktiv: OgBool - created_at: OgDateTime - updated_at: OgDateTime + beschreibung: Optional[OgStr] + fix_aufschlag: Optional[OgMoney] + rundung_typ: Optional[OgStr] + mindestmarge_pct: Optional[OgMoney] + auto_neuberechnung: Optional[OgBool] + ist_default: Optional[OgBool] + aktiv: Optional[OgBool] + created_at: Optional[OgDateTime] + updated_at: Optional[OgDateTime] spannen: ToMany["ErpPreisSpanne"] @dataclass @@ -2468,26 +2474,26 @@ class ErpPreisSpanne: """Rail class `ErpPreisSpanne` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt + tenant_id: Optional[OgInt] schema_id: OgInt preis_von: OgMoney - preis_bis: OgMoney + preis_bis: Optional[OgMoney] aufschlag_pct: OgMoney - sort_order: OgInt + sort_order: Optional[OgInt] @dataclass class ErpPreisGruppe: """Rail class `ErpPreisGruppe` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt + tenant_id: Optional[OgInt] name: OgStr - rabatt_pct: OgMoney - schema_id: OgInt - ist_default: OgBool - sort_order: OgInt - aktiv: OgBool - created_at: OgDateTime + rabatt_pct: Optional[OgMoney] + schema_id: Optional[OgInt] + ist_default: Optional[OgBool] + sort_order: Optional[OgInt] + aktiv: Optional[OgBool] + created_at: Optional[OgDateTime] kunden: ToMany["Customer"] @dataclass @@ -2495,41 +2501,41 @@ class ErpLohnPeriode: """Rail class `ErpLohnPeriode` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt + tenant_id: Optional[OgInt] jahr: OgInt monat: OgInt - brutto_loehne: OgMoney - ag_sv_anteile: OgMoney - an_sv_anteile: OgMoney - lohnsteuer_gesamt: OgMoney - currency: OgStr - notiz: OgStr - gebucht_am: OgDateTime - gebucht_von: OgInt - erfasst_am: OgDateTime - erfasst_von: OgInt + brutto_loehne: Optional[OgMoney] + ag_sv_anteile: Optional[OgMoney] + an_sv_anteile: Optional[OgMoney] + lohnsteuer_gesamt: Optional[OgMoney] + currency: Optional[OgStr] + notiz: Optional[OgStr] + gebucht_am: Optional[OgDateTime] + gebucht_von: Optional[OgInt] + erfasst_am: Optional[OgDateTime] + erfasst_von: Optional[OgInt] @dataclass class DashboardDismissal: """Rail class `DashboardDismissal` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt + tenant_id: Optional[OgInt] user_id: OgInt item_key: OgStr - created_at: OgDateTime + created_at: Optional[OgDateTime] @dataclass class CustomerRemoteDevice: """Rail class `CustomerRemoteDevice` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt + tenant_id: Optional[OgInt] provider: OgStr - label: OgStr + label: Optional[OgStr] device_id: OgStr - notizen: OgStr - created_at: OgDateTime + notizen: Optional[OgStr] + created_at: Optional[OgDateTime] customer: ToOne["Customer"] @dataclass @@ -2539,24 +2545,24 @@ class CrmCompany: id: OgInt tenant_id: OgInt name: OgStr - rechtsform: OgStr - branche: OgStr - website: OgStr - telefon: OgStr - email: OgStr - strasse: OgStr - plz: OgStr - ort: OgStr - land: OgStr - ust_id: OgStr - customer_id: OgInt - inhaber_user_id: OgInt - notizen: OgStr - tags: OgStr - aktiv: OgBool - geloescht_am: OgDateTime - erstellt_am: OgDateTime - geaendert_am: OgDateTime + rechtsform: Optional[OgStr] + branche: Optional[OgStr] + website: Optional[OgStr] + telefon: Optional[OgStr] + email: Optional[OgStr] + strasse: Optional[OgStr] + plz: Optional[OgStr] + ort: Optional[OgStr] + land: Optional[OgStr] + ust_id: Optional[OgStr] + customer_id: Optional[OgInt] + inhaber_user_id: Optional[OgInt] + notizen: Optional[OgStr] + tags: Optional[OgStr] + aktiv: Optional[OgBool] + geloescht_am: Optional[OgDateTime] + erstellt_am: Optional[OgDateTime] + geaendert_am: Optional[OgDateTime] @dataclass class CrmContact: @@ -2564,28 +2570,28 @@ class CrmContact: CLASSID: ClassVar[int] = 0x00000000 id: OgInt tenant_id: OgInt - company_id: OgInt - customer_id: OgInt - anrede: OgStr - vorname: OgStr + company_id: Optional[OgInt] + customer_id: Optional[OgInt] + anrede: Optional[OgStr] + vorname: Optional[OgStr] nachname: OgStr - position: OgStr - abteilung: OgStr - email: OgStr - telefon: OgStr - mobil: OgStr - ist_hauptkontakt: OgBool - quelle: OgStr - notizen: OgStr - tags: OgStr - aktiv: OgBool - geloescht_am: OgDateTime - erstellt_am: OgDateTime - geaendert_am: OgDateTime - carddav_uid: OgStr - carddav_href: OgStr - carddav_etag: OgStr - carddav_synced_at: OgDateTime + position: Optional[OgStr] + abteilung: Optional[OgStr] + email: Optional[OgStr] + telefon: Optional[OgStr] + mobil: Optional[OgStr] + ist_hauptkontakt: Optional[OgBool] + quelle: Optional[OgStr] + notizen: Optional[OgStr] + tags: Optional[OgStr] + aktiv: Optional[OgBool] + geloescht_am: Optional[OgDateTime] + erstellt_am: Optional[OgDateTime] + geaendert_am: Optional[OgDateTime] + carddav_uid: Optional[OgStr] + carddav_href: Optional[OgStr] + carddav_etag: Optional[OgStr] + carddav_synced_at: Optional[OgDateTime] @dataclass class CrmPipeline: @@ -2594,11 +2600,11 @@ class CrmPipeline: id: OgInt tenant_id: OgInt name: OgStr - ist_default: OgBool - sort_order: OgInt - aktiv: OgBool - geloescht_am: OgDateTime - erstellt_am: OgDateTime + ist_default: Optional[OgBool] + sort_order: Optional[OgInt] + aktiv: Optional[OgBool] + geloescht_am: Optional[OgDateTime] + erstellt_am: Optional[OgDateTime] @dataclass class CrmStage: @@ -2608,13 +2614,13 @@ class CrmStage: tenant_id: OgInt pipeline_id: OgInt name: OgStr - sort_order: OgInt - wahrscheinlichkeit: OgInt - ist_gewonnen: OgBool - ist_verloren: OgBool - farbe: OgStr - aktiv: OgBool - geloescht_am: OgDateTime + sort_order: Optional[OgInt] + wahrscheinlichkeit: Optional[OgInt] + ist_gewonnen: Optional[OgBool] + ist_verloren: Optional[OgBool] + farbe: Optional[OgStr] + aktiv: Optional[OgBool] + geloescht_am: Optional[OgDateTime] @dataclass class CrmLead: @@ -2625,25 +2631,25 @@ class CrmLead: titel: OgStr pipeline_id: OgInt stage_id: OgInt - contact_id: OgInt - company_id: OgInt - customer_id: OgInt - wert_netto: OgMoney - waehrung: OgStr - wahrscheinlichkeit: OgInt - quelle: OgStr - zustaendig_user_id: OgInt - erwartetes_datum: OgDate - status: OgStr - verloren_grund: OgStr - source_cold_lead_id: OgInt - notizen: OgStr - tags: OgStr - category_id: OgInt - aktiv: OgBool - geloescht_am: OgDateTime - erstellt_am: OgDateTime - geaendert_am: OgDateTime + contact_id: Optional[OgInt] + company_id: Optional[OgInt] + customer_id: Optional[OgInt] + wert_netto: Optional[OgMoney] + waehrung: Optional[OgStr] + wahrscheinlichkeit: Optional[OgInt] + quelle: Optional[OgStr] + zustaendig_user_id: Optional[OgInt] + erwartetes_datum: Optional[OgDate] + status: Optional[OgStr] + verloren_grund: Optional[OgStr] + source_cold_lead_id: Optional[OgInt] + notizen: Optional[OgStr] + tags: Optional[OgStr] + category_id: Optional[OgInt] + aktiv: Optional[OgBool] + geloescht_am: Optional[OgDateTime] + erstellt_am: Optional[OgDateTime] + geaendert_am: Optional[OgDateTime] @dataclass class CrmCategory: @@ -2652,11 +2658,11 @@ class CrmCategory: id: OgInt tenant_id: OgInt name: OgStr - farbe: OgStr - sort_order: OgInt - aktiv: OgBool - geloescht_am: OgDateTime - erstellt_am: OgDateTime + farbe: Optional[OgStr] + sort_order: Optional[OgInt] + aktiv: Optional[OgBool] + geloescht_am: Optional[OgDateTime] + erstellt_am: Optional[OgDateTime] @dataclass class CrmActivity: @@ -2667,15 +2673,15 @@ class CrmActivity: entity_type: OgStr entity_id: OgInt typ: OgStr - betreff: OgStr - inhalt: OgStr - richtung: OgStr - ref_table: OgStr - ref_id: OgInt - user_id: OgInt - zeitpunkt: OgDateTime - geloescht_am: OgDateTime - erstellt_am: OgDateTime + betreff: Optional[OgStr] + inhalt: Optional[OgStr] + richtung: Optional[OgStr] + ref_table: Optional[OgStr] + ref_id: Optional[OgInt] + user_id: Optional[OgInt] + zeitpunkt: Optional[OgDateTime] + geloescht_am: Optional[OgDateTime] + erstellt_am: Optional[OgDateTime] @dataclass class CrmTask: @@ -2684,18 +2690,18 @@ class CrmTask: id: OgInt tenant_id: OgInt titel: OgStr - beschreibung: OgStr - entity_type: OgStr - entity_id: OgInt - faellig_am: OgDateTime - erinnerung_am: OgDateTime - prioritaet: OgStr - status: OgStr - zustaendig_user_id: OgInt - erledigt_am: OgDateTime - geloescht_am: OgDateTime - erstellt_am: OgDateTime - geaendert_am: OgDateTime + beschreibung: Optional[OgStr] + entity_type: Optional[OgStr] + entity_id: Optional[OgInt] + faellig_am: Optional[OgDateTime] + erinnerung_am: Optional[OgDateTime] + prioritaet: Optional[OgStr] + status: Optional[OgStr] + zustaendig_user_id: Optional[OgInt] + erledigt_am: Optional[OgDateTime] + geloescht_am: Optional[OgDateTime] + erstellt_am: Optional[OgDateTime] + geaendert_am: Optional[OgDateTime] @dataclass class CrmDocument: @@ -2704,22 +2710,22 @@ class CrmDocument: id: OgInt tenant_id: OgInt titel: OgStr - vorlage_id: OgInt - entity_type: OgStr - entity_id: OgInt - customer_id: OgInt - storage_key: OgStr - mime_type: OgStr - format: OgStr - version: OgInt - erp_document_id: OgInt - status: OgStr - erstellt_von: OgInt - folder_id: OgInt - aktiv: OgBool - geloescht_am: OgDateTime - erstellt_am: OgDateTime - geaendert_am: OgDateTime + vorlage_id: Optional[OgInt] + entity_type: Optional[OgStr] + entity_id: Optional[OgInt] + customer_id: Optional[OgInt] + storage_key: Optional[OgStr] + mime_type: Optional[OgStr] + format: Optional[OgStr] + version: Optional[OgInt] + erp_document_id: Optional[OgInt] + status: Optional[OgStr] + erstellt_von: Optional[OgInt] + folder_id: Optional[OgInt] + aktiv: Optional[OgBool] + geloescht_am: Optional[OgDateTime] + erstellt_am: Optional[OgDateTime] + geaendert_am: Optional[OgDateTime] @dataclass class CrmDocumentFolder: @@ -2729,26 +2735,26 @@ class CrmDocumentFolder: tenant_id: OgInt entity_type: OgStr entity_id: OgInt - parent_id: OgInt + parent_id: Optional[OgInt] name: OgStr - sort_order: OgInt - geloescht_am: OgDateTime - erstellt_am: OgDateTime + sort_order: Optional[OgInt] + geloescht_am: Optional[OgDateTime] + erstellt_am: Optional[OgDateTime] @dataclass class CrmDocTemplate: """Rail class `CrmDocTemplate` — classid 0x00000000 (concept 0x0000, app 0x0000).""" CLASSID: ClassVar[int] = 0x00000000 id: OgInt - tenant_id: OgInt + tenant_id: Optional[OgInt] name: OgStr - format: OgStr - storage_key: OgStr - beschreibung: OgStr - kategorie: OgStr - aktiv: OgBool - geloescht_am: OgDateTime - erstellt_am: OgDateTime + format: Optional[OgStr] + storage_key: Optional[OgStr] + beschreibung: Optional[OgStr] + kategorie: Optional[OgStr] + aktiv: Optional[OgBool] + geloescht_am: Optional[OgDateTime] + erstellt_am: Optional[OgDateTime] @dataclass class CrmDocumentLink: @@ -2759,11 +2765,11 @@ class CrmDocumentLink: erp_document_id: OgInt entity_type: OgStr entity_id: OgInt - zugeordnet_von_user_id: OgInt - zugeordnet_am: OgDateTime - quelle: OgStr - geloescht_am: OgDateTime - erstellt_am: OgDateTime + zugeordnet_von_user_id: Optional[OgInt] + zugeordnet_am: Optional[OgDateTime] + quelle: Optional[OgStr] + geloescht_am: Optional[OgDateTime] + erstellt_am: Optional[OgDateTime] @dataclass class RentedServerEvent: @@ -2772,8 +2778,266 @@ class RentedServerEvent: id: OgInt ts: OgDateTime kind: OgStr - level: OgStr - message: OgStr - payload: OgStr + level: Optional[OgStr] + message: Optional[OgStr] + payload: Optional[OgStr] server: ToOne["RentedServer"] +@dataclass +class ShopProduct: + """Rail class `ShopProduct` — classid 0x00000000 (concept 0x0000, app 0x0000).""" + CLASSID: ClassVar[int] = 0x00000000 + id: OgInt + tenant_id: Optional[OgInt] + sku: Optional[OgStr] + name: OgStr + slug: OgStr + short_desc: Optional[OgStr] + long_desc: Optional[OgStr] + product_type: OgStr + price_cents: OgInt + currency: OgStr + vat_rate: OgMoney + stock_qty: OgInt + weight_grams: Optional[OgInt] + saas_meta_json: Optional[OgScalar] + digital_path: Optional[OgStr] + image_path: Optional[OgStr] + gallery_json: Optional[OgScalar] + status: OgStr + sort: OgInt + meta_title: Optional[OgStr] + meta_description: Optional[OgStr] + created_at: Optional[OgDateTime] + updated_at: Optional[OgDateTime] + +@dataclass +class ShopCategory: + """Rail class `ShopCategory` — classid 0x00000000 (concept 0x0000, app 0x0000).""" + CLASSID: ClassVar[int] = 0x00000000 + id: OgInt + tenant_id: Optional[OgInt] + name: OgStr + slug: OgStr + description: Optional[OgStr] + parent_id: Optional[OgInt] + sort: OgInt + image_path: Optional[OgStr] + status: OgStr + created_at: Optional[OgDateTime] + children: ToMany["ShopCategory"] + +@dataclass +class ShopOrder: + """Rail class `ShopOrder` — classid 0x00000000 (concept 0x0000, app 0x0000).""" + CLASSID: ClassVar[int] = 0x00000000 + id: OgInt + tenant_id: Optional[OgInt] + order_number: OgStr + confirmation_token: OgStr + customer_email: OgStr + customer_name: OgStr + customer_phone: Optional[OgStr] + woa_customer_id: Optional[OgInt] + billing_address_json: OgScalar + shipping_address_json: Optional[OgScalar] + has_shipping: OgBool + subtotal_cents: OgInt + vat_cents: OgInt + shipping_cost_cents: OgInt + total_cents: OgInt + currency: OgStr + payment_provider: OgStr + payment_status: OgStr + payment_provider_ref: Optional[OgStr] + payment_meta_json: Optional[OgScalar] + order_status: OgStr + notes_customer: Optional[OgStr] + notes_internal: Optional[OgStr] + created_at: Optional[OgDateTime] + updated_at: Optional[OgDateTime] + paid_at: Optional[OgDateTime] + shipped_at: Optional[OgDateTime] + items: ToMany["ShopOrderItem"] + +@dataclass +class ShopOrderItem: + """Rail class `ShopOrderItem` — classid 0x00000000 (concept 0x0000, app 0x0000).""" + CLASSID: ClassVar[int] = 0x00000000 + id: OgInt + order_id: OgInt + sku: Optional[OgStr] + name: OgStr + product_type: OgStr + qty: OgInt + unit_price_cents: OgInt + vat_rate: OgMoney + line_total_cents: OgInt + meta_json: Optional[OgScalar] + product: ToOne["ShopProduct"] + +@dataclass +class ShopCartSession: + """Rail class `ShopCartSession` — classid 0x00000000 (concept 0x0000, app 0x0000).""" + CLASSID: ClassVar[int] = 0x00000000 + id: OgInt + cart_key: OgStr + tenant_id: Optional[OgInt] + items_json: Optional[OgScalar] + customer_email: Optional[OgStr] + expires_at: OgDateTime + created_at: Optional[OgDateTime] + updated_at: Optional[OgDateTime] + +@dataclass +class ShopPage: + """Rail class `ShopPage` — classid 0x00000000 (concept 0x0000, app 0x0000).""" + CLASSID: ClassVar[int] = 0x00000000 + id: OgInt + tenant_id: Optional[OgInt] + slug: OgStr + title: OgStr + content_html: Optional[OgStr] + meta_title: Optional[OgStr] + meta_description: Optional[OgStr] + status: OgStr + sort: OgInt + is_system: OgBool + show_in_footer: OgBool + show_in_header: OgBool + created_at: Optional[OgDateTime] + updated_at: Optional[OgDateTime] + +@dataclass +class ShopShippingMethod: + """Rail class `ShopShippingMethod` — classid 0x00000000 (concept 0x0000, app 0x0000).""" + CLASSID: ClassVar[int] = 0x00000000 + id: OgInt + tenant_id: Optional[OgInt] + code: OgStr + name: OgStr + description: Optional[OgStr] + icon: Optional[OgStr] + price_cents: OgInt + free_threshold_cents: Optional[OgInt] + weight_max_grams: Optional[OgInt] + delivery_time_min_days: Optional[OgInt] + delivery_time_max_days: Optional[OgInt] + countries_allowed: Optional[OgStr] + active: OgBool + sort: OgInt + created_at: Optional[OgDateTime] + +@dataclass +class ShopPaymentMethod: + """Rail class `ShopPaymentMethod` — classid 0x00000000 (concept 0x0000, app 0x0000).""" + CLASSID: ClassVar[int] = 0x00000000 + id: OgInt + tenant_id: Optional[OgInt] + code: OgStr + label: OgStr + description: Optional[OgStr] + icon: Optional[OgStr] + fee_cents: OgInt + fee_percent: OgMoney + provider: OgStr + min_total_cents: Optional[OgInt] + max_total_cents: Optional[OgInt] + requires_address: OgBool + active: OgBool + is_default: OgBool + sort: OgInt + created_at: Optional[OgDateTime] + +@dataclass +class ShopCustomer: + """Rail class `ShopCustomer` — classid 0x00000000 (concept 0x0000, app 0x0000).""" + CLASSID: ClassVar[int] = 0x00000000 + id: OgInt + tenant_id: Optional[OgInt] + email: OgStr + password_hash: OgStr + first_name: Optional[OgStr] + last_name: Optional[OgStr] + company: Optional[OgStr] + phone: Optional[OgStr] + vat_id: Optional[OgStr] + billing_address_json: Optional[OgScalar] + shipping_address_json: Optional[OgScalar] + is_b2b: OgBool + status: OgStr + email_verified: OgBool + verify_token: Optional[OgStr] + verify_token_expires: Optional[OgDateTime] + reset_token: Optional[OgStr] + reset_token_expires: Optional[OgDateTime] + failed_attempts: OgInt + locked_until: Optional[OgDateTime] + last_login_at: Optional[OgDateTime] + last_login_ip: Optional[OgStr] + accepts_marketing: OgBool + created_at: Optional[OgDateTime] + updated_at: Optional[OgDateTime] + +@dataclass +class ShopPaymentConfig: + """Rail class `ShopPaymentConfig` — classid 0x00000000 (concept 0x0000, app 0x0000).""" + CLASSID: ClassVar[int] = 0x00000000 + id: OgInt + tenant_id: Optional[OgInt] + provider: OgStr + mode: OgStr + public_key: Optional[OgStr] + secret_key: Optional[OgStr] + webhook_secret: Optional[OgStr] + extra_json: Optional[OgScalar] + active: OgBool + created_at: Optional[OgDateTime] + updated_at: Optional[OgDateTime] + +@dataclass +class ShopSaasConfig: + """Rail class `ShopSaasConfig` — classid 0x00000000 (concept 0x0000, app 0x0000).""" + CLASSID: ClassVar[int] = 0x00000000 + id: OgInt + tenant_id: Optional[OgInt] + order_item_id: Optional[OgInt] + article_id: OgInt + dns_mode: OgStr + dns_notes: Optional[OgStr] + extra_users: OgInt + extra_users_price_cents_each: OgInt + addon_article_ids: Optional[OgScalar] + wants_training: OgBool + wants_test_data: OgBool + seed_articles: OgBool + branche: Optional[OgStr] + desired_slug: Optional[OgStr] + admin_username: Optional[OgStr] + notes_for_admin: Optional[OgStr] + setup_service: OgBool + setup_data_json: Optional[OgScalar] + chosen_billing_period: Optional[OgStr] + payment_mode: OgStr + payment_iban: Optional[OgStr] + payment_bic: Optional[OgStr] + payment_holder: Optional[OgStr] + created_at: Optional[OgDateTime] + updated_at: Optional[OgDateTime] + order: ToOne["ShopOrder"] + +@dataclass +class ShopProductReview: + """Rail class `ShopProductReview` — classid 0x00000000 (concept 0x0000, app 0x0000).""" + CLASSID: ClassVar[int] = 0x00000000 + id: OgInt + tenant_id: Optional[OgInt] + article_id: OgInt + rating: OgInt + title: Optional[OgStr] + comment: Optional[OgStr] + status: OgStr + created_at: Optional[OgDateTime] + updated_at: Optional[OgDateTime] + customer: ToOne["ShopCustomer"] + diff --git a/.claude/harvest/woa-rs/ogar_runtime.py b/.claude/harvest/woa-rs/ogar_runtime.py new file mode 100644 index 0000000..9131181 --- /dev/null +++ b/.claude/harvest/woa-rs/ogar_runtime.py @@ -0,0 +1,34 @@ +"""ogar_runtime — reference wrapper-contract for emit_python output. + +The Python mirror of the Rust consumer's wrapper types (OgScalar / ToOne / +ToMany, lance-graph-contract shape). A real consumer supplies its own +ogar_runtime on sys.path; this is the shipped reference so emitted modules +py_compile + import with no setup. +Dataclass annotations are not runtime-enforced, so these permissive aliases +suffice. +""" +from __future__ import annotations +from typing import Generic, TypeVar + +_T = TypeVar("_T") + + +class ToOne(Generic[_T]): + """To-one relation wrapper (forward-ref comodel).""" + + +class ToMany(Generic[_T]): + """To-many relation wrapper (forward-ref comodel).""" + + +OgScalar = object +OgStr = str +OgInt = int +OgFloat = float +OgMoney = object +OgBool = bool +OgDate = object +OgDateTime = object +OgBytes = bytes +OgSelection = object +OgJson = object diff --git a/.claude/harvest/woa-rs/woa_facet.sql b/.claude/harvest/woa-rs/woa_facet.sql index 303e217..92d6bf3 100644 --- a/.claude/harvest/woa-rs/woa_facet.sql +++ b/.claude/harvest/woa-rs/woa_facet.sql @@ -1,4 +1,4 @@ --- WoA sink-in substrate v1 — PostgreSQL DDL, generated, do not hand-edit. +-- WoA sink-in substrate v2 — PostgreSQL DDL, generated, do not hand-edit. -- facet table: classid(4) + 12-byte payload (V3 sink-in System-of-Record) CREATE TABLE facet ( @@ -2410,3 +2410,232 @@ CREATE TABLE RentedServerEvent ( payload TEXT ); +CREATE TABLE ShopProduct ( + id INTEGER NOT NULL, + tenant_id INTEGER, + sku TEXT, + name TEXT NOT NULL, + slug TEXT NOT NULL, + short_desc TEXT, + long_desc TEXT, + product_type TEXT NOT NULL, + price_cents INTEGER NOT NULL, + currency TEXT NOT NULL, + vat_rate NUMERIC NOT NULL, + stock_qty INTEGER NOT NULL, + weight_grams INTEGER, + saas_meta_json TEXT, + digital_path TEXT, + image_path TEXT, + gallery_json TEXT, + status TEXT NOT NULL, + sort INTEGER NOT NULL, + meta_title TEXT, + meta_description TEXT, + created_at TIMESTAMP, + updated_at TIMESTAMP +); + +CREATE TABLE ShopCategory ( + id INTEGER NOT NULL, + tenant_id INTEGER, + name TEXT NOT NULL, + slug TEXT NOT NULL, + description TEXT, + parent_id INTEGER, + sort INTEGER NOT NULL, + image_path TEXT, + status TEXT NOT NULL, + created_at TIMESTAMP +); + +CREATE TABLE ShopOrder ( + id INTEGER NOT NULL, + tenant_id INTEGER, + order_number TEXT NOT NULL, + confirmation_token TEXT NOT NULL, + customer_email TEXT NOT NULL, + customer_name TEXT NOT NULL, + customer_phone TEXT, + woa_customer_id INTEGER, + billing_address_json TEXT NOT NULL, + shipping_address_json TEXT, + has_shipping BOOLEAN NOT NULL, + subtotal_cents INTEGER NOT NULL, + vat_cents INTEGER NOT NULL, + shipping_cost_cents INTEGER NOT NULL, + total_cents INTEGER NOT NULL, + currency TEXT NOT NULL, + payment_provider TEXT NOT NULL, + payment_status TEXT NOT NULL, + payment_provider_ref TEXT, + payment_meta_json TEXT, + order_status TEXT NOT NULL, + notes_customer TEXT, + notes_internal TEXT, + created_at TIMESTAMP, + updated_at TIMESTAMP, + paid_at TIMESTAMP, + shipped_at TIMESTAMP +); + +CREATE TABLE ShopOrderItem ( + id INTEGER NOT NULL, + order_id INTEGER NOT NULL, + sku TEXT, + name TEXT NOT NULL, + product_type TEXT NOT NULL, + qty INTEGER NOT NULL, + unit_price_cents INTEGER NOT NULL, + vat_rate NUMERIC NOT NULL, + line_total_cents INTEGER NOT NULL, + meta_json TEXT +); + +CREATE TABLE ShopCartSession ( + id INTEGER NOT NULL, + cart_key TEXT NOT NULL, + tenant_id INTEGER, + items_json TEXT, + customer_email TEXT, + expires_at TIMESTAMP NOT NULL, + created_at TIMESTAMP, + updated_at TIMESTAMP +); + +CREATE TABLE ShopPage ( + id INTEGER NOT NULL, + tenant_id INTEGER, + slug TEXT NOT NULL, + title TEXT NOT NULL, + content_html TEXT, + meta_title TEXT, + meta_description TEXT, + status TEXT NOT NULL, + sort INTEGER NOT NULL, + is_system BOOLEAN NOT NULL, + show_in_footer BOOLEAN NOT NULL, + show_in_header BOOLEAN NOT NULL, + created_at TIMESTAMP, + updated_at TIMESTAMP +); + +CREATE TABLE ShopShippingMethod ( + id INTEGER NOT NULL, + tenant_id INTEGER, + code TEXT NOT NULL, + name TEXT NOT NULL, + description TEXT, + icon TEXT, + price_cents INTEGER NOT NULL, + free_threshold_cents INTEGER, + weight_max_grams INTEGER, + delivery_time_min_days INTEGER, + delivery_time_max_days INTEGER, + countries_allowed TEXT, + active BOOLEAN NOT NULL, + sort INTEGER NOT NULL, + created_at TIMESTAMP +); + +CREATE TABLE ShopPaymentMethod ( + id INTEGER NOT NULL, + tenant_id INTEGER, + code TEXT NOT NULL, + label TEXT NOT NULL, + description TEXT, + icon TEXT, + fee_cents INTEGER NOT NULL, + fee_percent NUMERIC NOT NULL, + provider TEXT NOT NULL, + min_total_cents INTEGER, + max_total_cents INTEGER, + requires_address BOOLEAN NOT NULL, + active BOOLEAN NOT NULL, + is_default BOOLEAN NOT NULL, + sort INTEGER NOT NULL, + created_at TIMESTAMP +); + +CREATE TABLE ShopCustomer ( + id INTEGER NOT NULL, + tenant_id INTEGER, + email TEXT NOT NULL, + password_hash TEXT NOT NULL, + first_name TEXT, + last_name TEXT, + company TEXT, + phone TEXT, + vat_id TEXT, + billing_address_json TEXT, + shipping_address_json TEXT, + is_b2b BOOLEAN NOT NULL, + status TEXT NOT NULL, + email_verified BOOLEAN NOT NULL, + verify_token TEXT, + verify_token_expires TIMESTAMP, + reset_token TEXT, + reset_token_expires TIMESTAMP, + failed_attempts INTEGER NOT NULL, + locked_until TIMESTAMP, + last_login_at TIMESTAMP, + last_login_ip TEXT, + accepts_marketing BOOLEAN NOT NULL, + created_at TIMESTAMP, + updated_at TIMESTAMP +); + +CREATE TABLE ShopPaymentConfig ( + id INTEGER NOT NULL, + tenant_id INTEGER, + provider TEXT NOT NULL, + mode TEXT NOT NULL, + public_key TEXT, + secret_key TEXT, + webhook_secret TEXT, + extra_json TEXT, + active BOOLEAN NOT NULL, + created_at TIMESTAMP, + updated_at TIMESTAMP +); + +CREATE TABLE ShopSaasConfig ( + id INTEGER NOT NULL, + tenant_id INTEGER, + order_item_id INTEGER, + article_id INTEGER NOT NULL, + dns_mode TEXT NOT NULL, + dns_notes TEXT, + extra_users INTEGER NOT NULL, + extra_users_price_cents_each INTEGER NOT NULL, + addon_article_ids TEXT, + wants_training BOOLEAN NOT NULL, + wants_test_data BOOLEAN NOT NULL, + seed_articles BOOLEAN NOT NULL, + branche TEXT, + desired_slug TEXT, + admin_username TEXT, + notes_for_admin TEXT, + setup_service BOOLEAN NOT NULL, + setup_data_json TEXT, + chosen_billing_period TEXT, + payment_mode TEXT NOT NULL, + payment_iban TEXT, + payment_bic TEXT, + payment_holder TEXT, + created_at TIMESTAMP, + updated_at TIMESTAMP +); + +CREATE TABLE ShopProductReview ( + id INTEGER NOT NULL, + tenant_id INTEGER, + article_id INTEGER NOT NULL, + rating INTEGER NOT NULL, + title TEXT, + comment TEXT, + status TEXT NOT NULL, + created_at TIMESTAMP, + updated_at TIMESTAMP +); + diff --git a/.claude/harvest/woa-rs/woa_graph.spo b/.claude/harvest/woa-rs/woa_graph.spo index aba7f66..ccf5c1d 100644 --- a/.claude/harvest/woa-rs/woa_graph.spo +++ b/.claude/harvest/woa-rs/woa_graph.spo @@ -108,3 +108,8 @@ ErpPreisSchema[0x00000000] --HasMany:spannen--> ErpPreisSpanne[0x00000000] ErpPreisGruppe[0x00000000] --HasMany:kunden--> Customer[0x02040003] CustomerRemoteDevice[0x00000000] --BelongsTo:customer--> Customer[0x02040003] RentedServerEvent[0x00000000] --BelongsTo:server--> RentedServer[0x00000000] +ShopCategory[0x00000000] --HasMany:children--> ShopCategory[0x00000000] +ShopOrder[0x00000000] --HasMany:items--> ShopOrderItem[0x00000000] +ShopOrderItem[0x00000000] --BelongsTo:product--> ShopProduct[0x00000000] +ShopSaasConfig[0x00000000] --BelongsTo:order--> ShopOrder[0x00000000] +ShopProductReview[0x00000000] --BelongsTo:customer--> ShopCustomer[0x00000000] diff --git a/crates/ogar-from-ruff/python/ogar_runtime.py b/crates/ogar-from-ruff/python/ogar_runtime.py new file mode 100644 index 0000000..9131181 --- /dev/null +++ b/crates/ogar-from-ruff/python/ogar_runtime.py @@ -0,0 +1,34 @@ +"""ogar_runtime — reference wrapper-contract for emit_python output. + +The Python mirror of the Rust consumer's wrapper types (OgScalar / ToOne / +ToMany, lance-graph-contract shape). A real consumer supplies its own +ogar_runtime on sys.path; this is the shipped reference so emitted modules +py_compile + import with no setup. +Dataclass annotations are not runtime-enforced, so these permissive aliases +suffice. +""" +from __future__ import annotations +from typing import Generic, TypeVar + +_T = TypeVar("_T") + + +class ToOne(Generic[_T]): + """To-one relation wrapper (forward-ref comodel).""" + + +class ToMany(Generic[_T]): + """To-many relation wrapper (forward-ref comodel).""" + + +OgScalar = object +OgStr = str +OgInt = int +OgFloat = float +OgMoney = object +OgBool = bool +OgDate = object +OgDateTime = object +OgBytes = bytes +OgSelection = object +OgJson = object diff --git a/crates/ogar-from-ruff/src/emit.rs b/crates/ogar-from-ruff/src/emit.rs index ecb4cfe..f909514 100644 --- a/crates/ogar-from-ruff/src/emit.rs +++ b/crates/ogar-from-ruff/src/emit.rs @@ -46,6 +46,22 @@ //! Odoo field named `type` / `ref` becomes `r#type` / `r#ref` in Rust and //! `@type` / `@ref` in C#. Python source field names cannot be keywords (the //! Odoo source would not parse), so Python emit needs no escaping. +//! +//! **DESIGN DECISION — the Python wrapper-contract pivot (Batch-1 Item 1).** +//! [`emit_python`] mirrors [`emit_rust`]'s `Option<...>` rule exactly: +//! `required == Some(false)` wraps the attribute in `Optional[...]`, any other +//! value (`Some(true)`, `None`) stays bare. The emitted module does **not** +//! inline `OgStr`/`ToOne`/etc — those are pulled from a single +//! `from ogar_runtime import (...)` block, emitted once per module by +//! [`emit_python_prelude`] (not per class — the harvest driver concatenates +//! many classes into one module). A shipped reference +//! `crates/ogar-from-ruff/python/ogar_runtime.py` supplies the wrapper types +//! so the emitted module `py_compile`s and imports standalone. This is option +//! (a) of three considered: (b) inlining the aliases into every emitted class +//! was rejected — it re-mints a parallel schema truth per file, violating +//! "pull, never re-mint"; (c) a `try/except import *` shim was rejected — it +//! masks import errors and pollutes the namespace. The "cost of an import" +//! (above) is made literal as exactly one import block per module. use ogar_vocab::app::{app_of, concept_of}; use ogar_vocab::{Association, AssociationKind}; @@ -402,6 +418,23 @@ pub fn emit_csharp(cc: &CompiledClass) -> String { out } +/// Emit the module-level wrapper-contract import block for [`emit_python`] +/// output — once per module (the harvest driver concatenates many classes; +/// this is NOT prepended inside [`emit_python`] itself, callers compose it). +/// Pulls the wrapper types from `ogar_runtime` (see the module-doc DESIGN +/// DECISION above) rather than inlining them, mirroring "pull, never re-mint". +#[must_use] +pub fn emit_python_prelude() -> &'static str { + "from __future__ import annotations\n\ + from dataclasses import dataclass\n\ + from typing import ClassVar, Optional\n\ + from ogar_runtime import (\n \ + OgScalar, OgStr, OgInt, OgFloat, OgMoney, OgBool,\n \ + OgDate, OgDateTime, OgBytes, OgSelection, OgJson,\n \ + ToOne, ToMany,\n\ + )\n" +} + /// Emit a [`CompiledClass`] as **Python** source: a `@dataclass` whose /// annotations use the Python SDK's wrapper-contract types (`OgScalar` / /// `ToOne[T]` / `ToMany[T]`), with the rail `classid` as a `ClassVar[int]`. @@ -410,6 +443,11 @@ pub fn emit_csharp(cc: &CompiledClass) -> String { /// trailing comments (the 15 % adapter). This is the codegen mode of the Python /// SDK (substrate doc §1.6); CPython compiles the emitted module to bytecode on /// import — the "cost of an import" made literal. +/// +/// An attribute whose `AttributeOptions::required == Some(false)` emits +/// wrapped in `Optional[...]`, mirroring [`emit_rust`]'s `Option<...>` rule +/// exactly (`Some(true)`/`None` both emit the bare wrapper type — see the +/// module-doc DESIGN DECISION above). #[must_use] pub fn emit_python(cc: &CompiledClass) -> String { let ty = pascal_case(&cc.class.name); @@ -429,10 +467,18 @@ pub fn emit_python(cc: &CompiledClass) -> String { cc.facet.facet_classid(), )); for attr in &cc.class.attributes { + let base_ty = og_scalar_type(attr.type_name.as_deref()); + // `required == Some(false)` is the ONLY nullability signal that + // wraps in `Optional[...]` — mirrors `emit_rust`'s `Option<...>` + // rule exactly (GAP-4 mirror, Batch-1 Item 1). + let field_ty = if attr.options.required == Some(false) { + format!("Optional[{base_ty}]") + } else { + base_ty.to_string() + }; out.push_str(&format!( - " {}: {}\n", + " {}: {field_ty}\n", escape_ident(&attr.name, Lang::Python), - og_scalar_type(attr.type_name.as_deref()), )); } for assoc in &cc.class.associations { @@ -864,6 +910,51 @@ mod tests { ); } + #[test] + fn emit_python_wraps_nullable_in_optional_bare_otherwise() { + use crate::mint::compile_graph_ruby; + use ogar_vocab::ports::OpenProjectPort; + + // required == Some(false): the Rails schema-stratum fixture's + // `description` is `not_null: None` -> `required: Some(false)` -> + // Optional[...] (GAP-4 mirror, Batch-1 Item 1). + // required == Some(true): the same fixture's `id`/`subject` are + // `not_null: Some(true)` -> bare. + let cc = &compile_graph_ruby::(&work_package_schema_graph())[0]; + let py = emit_python(cc); + assert!(py.contains(" id: OgInt"), "got:\n{py}"); + assert!(py.contains(" subject: OgStr"), "got:\n{py}"); + assert!( + py.contains(" description: Optional[OgStr]"), + "got:\n{py}" + ); + + // required == None: `project_odoo_fields` never sets `required` + // (lib.rs's `lift_model_python_never_sets_required_zero_drift`), so + // the Odoo-lifted fixture's `name`/`narration` stay bare too — the + // Optional[...] change must be additive, not a regression on Odoo. + let odoo_cc = &compile_graph_python::(&account_move_graph())[0]; + let odoo_py = emit_python(odoo_cc); + assert!(odoo_py.contains(" name: OgStr"), "got:\n{odoo_py}"); + assert!( + odoo_py.contains(" narration: OgScalar"), + "got:\n{odoo_py}" + ); + } + + #[test] + fn emit_python_prelude_imports_from_ogar_runtime() { + let prelude = emit_python_prelude(); + assert!( + prelude.contains("from ogar_runtime import ("), + "got:\n{prelude}" + ); + assert!(prelude.contains("ToOne"), "got:\n{prelude}"); + assert!(prelude.contains("ToMany"), "got:\n{prelude}"); + assert!(prelude.contains("Optional"), "got:\n{prelude}"); + assert!(prelude.contains("dataclass"), "got:\n{prelude}"); + } + #[test] fn all_three_emitters_share_the_same_type_vocabulary() { // §1.6: the SDK is a transliteration — same type NAMES, only bracket diff --git a/crates/ogar-from-ruff/src/lib.rs b/crates/ogar-from-ruff/src/lib.rs index d3d4ca5..58bd240 100644 --- a/crates/ogar-from-ruff/src/lib.rs +++ b/crates/ogar-from-ruff/src/lib.rs @@ -37,7 +37,7 @@ //! | `sti.inherits_from` | `parent` | STI parent — matches `Class.parent` slot | //! | `inherits` | `mixins` (appended) | Odoo `_inherit` multi-parent mixin composition — the vocab's `mixins` doc names `_inherit`; the `inheritance` axis excludes mixins. Frontend-agnostic field, populated only by the Odoo frontend | //! | `functions` | `Vec` (DO-arm) | [`lift_actions`] — one `ActionDef` per method; standalone, not on `Class` | -//! | `fields` | `attributes` / `associations` / `computed_fields` | the D-AR-3.5 physical schema stratum (migration-DSL columns for Rails via [`project_rails_fields`]; `fields.X(...)` declarations for Odoo via [`project_odoo_fields`]); `not_null` wires `AttributeOptions::required` (Rails only); a Rails `_id` FK column is skipped when the model also declares an association named `` (double-strata dedup) | +//! | `fields` | `attributes` / `associations` / `computed_fields` | the D-AR-3.5 physical schema stratum (migration-DSL columns for Rails, and `db.Column(...)` for Flask-SQLAlchemy, both via [`project_total_schema_fields`]; `fields.X(...)` declarations for Odoo via [`project_odoo_fields`]); `not_null` wires `AttributeOptions::required` (Rails/SQLAlchemy only); a `_id` FK column is skipped when the model also declares an association named `` (double-strata dedup) | //! //! Fields NOT lifted today (no equivalent on the ruff side OR no clean //! semantic mapping): @@ -204,13 +204,14 @@ fn lift_model_with_language(model: &Model, language: Language) -> Class { // columns via `ruff_ruby_spo::extract_app_with_schema`; Odoo: the same // vector doubles as the declared schema since Odoo has no separate // AR-DSL). So each language gets its own field-projection pass — - // `project_odoo_fields` for Python (Codex P1, PR #131), and - // `project_rails_fields` for Ruby (falsifier #1 gap-close, - // D-PARITY-PROBE-WP-1) — so neither producer silently drops the schema + // `project_odoo_fields` for Python/Odoo (Codex P1, PR #131), and + // `project_total_schema_fields` for Ruby (falsifier #1 gap-close, + // D-PARITY-PROBE-WP-1; also shared by the SQLAlchemy producer, see + // `sqlalchemy.rs`) — so neither producer silently drops the schema // stratum lifted by its frontend. match language { Language::Python => project_odoo_fields(&mut class, model), - Language::Ruby => project_rails_fields(&mut class, model), + Language::Ruby => project_total_schema_fields(&mut class, model), _ => {} } class @@ -258,17 +259,24 @@ fn project_odoo_fields(class: &mut Class, model: &Model) { } } -/// Project a Rails model's D-AR-3.5 schema stratum — the PHYSICAL DB columns -/// in `Model::fields`, populated by `ruff_ruby_spo::extract_app_with_schema` -/// from the `db/migrate/tables/*.rb` migration DSL — onto the schema-carrying -/// [`Class`] columns. Ruby-only counterpart of [`project_odoo_fields`]: the -/// AR-DSL vectors (`attributes` / `associations`, from `attribute :x, :type` -/// / `belongs_to :y`) carry the *declared* schema and are lifted separately, -/// earlier in [`lift_model_with_language`]; `fields` carries the *physical* -/// schema (what the migration actually created). Both strata are real and -/// both must reach the [`Class`], or the Rails lift silently drops the -/// physical column set the way the pre-fix Python-only gate did (falsifier -/// #1 / D-PARITY-PROBE-WP-1). +/// Project a total-nullability model's D-AR-3.5 schema stratum — the +/// PHYSICAL DB columns in `Model::fields` — onto the schema-carrying +/// [`Class`] columns. Shared by two producers: +/// - **Rails** (Ruby): populated by `ruff_ruby_spo::extract_app_with_schema` +/// from the `db/migrate/tables/*.rb` migration DSL. Counterpart of +/// [`project_odoo_fields`]: the AR-DSL vectors (`attributes` / +/// `associations`, from `attribute :x, :type` / `belongs_to :y`) carry the +/// *declared* schema and are lifted separately, earlier in +/// [`lift_model_with_language`]; `fields` carries the *physical* schema +/// (what the migration actually created). Both strata are real and both +/// must reach the [`Class`], or the Rails lift silently drops the physical +/// column set the way the pre-fix Python-only gate did (falsifier #1 / +/// D-PARITY-PROBE-WP-1). +/// - **Flask-SQLAlchemy** (Python/WoA): populated via +/// [`crate::sqlalchemy::lift_model_sqlalchemy`]'s `db.Column(...)` +/// declarations, which are likewise total (every column's nullability is a +/// declared fact) — see that module's doc for why it routes `Language::Python` +/// through this helper instead of [`project_odoo_fields`]. /// /// Per-field mapping (mirrors [`project_odoo_fields`]): /// - relational field (`target` set) → [`Association`]; same shape as the @@ -298,7 +306,7 @@ fn project_odoo_fields(class: &mut Class, model: &Model) { /// - compute field (`emitted_by` set — the D-AR-3.5 compute-linkage pass, /// `ruff_ruby_spo::schema::link_computed_fields`) → [`ComputedField`], /// same as the Odoo path. -fn project_rails_fields(class: &mut Class, model: &Model) { +pub(crate) fn project_total_schema_fields(class: &mut Class, model: &Model) { for field in &model.fields { if let Some(comodel) = &field.target { let kind = odoo_relation_kind(field.relation_kind.as_deref(), field.inverse_name.is_some()); @@ -335,8 +343,8 @@ fn project_rails_fields(class: &mut Class, model: &Model) { } } -/// FK-dedup predicate for [`project_rails_fields`]. Two independent shadow -/// rules, checked in order: +/// FK-dedup predicate for [`project_total_schema_fields`]. Two independent +/// shadow rules, checked in order: /// /// 1. **Explicit `foreign_key:`** — `class` carries an [`Association`] whose /// declared `foreign_key` equals `field_name` verbatim (e.g. @@ -352,7 +360,7 @@ fn project_rails_fields(class: &mut Class, model: &Model) { /// `"_id"`-suffixed prefix of its own, and no real corpus association /// declares `foreign_key: "id"` — and is always kept as a scalar /// attribute. -fn is_fk_shadowed_by_association(field_name: &str, class: &Class) -> bool { +pub(crate) fn is_fk_shadowed_by_association(field_name: &str, class: &Class) -> bool { if class .associations .iter() @@ -1155,13 +1163,14 @@ mod tests { /// PR #156 finding (c): a physical column whose name already matches an /// AR-DSL-declared attribute (`attribute :foo, :string`) must not be - /// projected a second time — `project_rails_fields` previously pushed - /// unconditionally, producing two `foo` entries in `class.attributes` - /// (an invalid duplicate struct field in the generated code). + /// projected a second time — `project_total_schema_fields` previously + /// pushed unconditionally, producing two `foo` entries in + /// `class.attributes` (an invalid duplicate struct field in the + /// generated code). /// /// **Red-before-fix:** on unpatched `main`, the count is 2 (the AR-DSL /// lift populates one `foo` attribute earlier in - /// `lift_model_with_language`, and `project_rails_fields` pushes a + /// `lift_model_with_language`, and `project_total_schema_fields` pushes a /// second, unguarded, for the physical `foo` column). #[test] fn lift_model_ruby_physical_column_does_not_duplicate_declared_attribute() { diff --git a/crates/ogar-from-ruff/src/sqlalchemy.rs b/crates/ogar-from-ruff/src/sqlalchemy.rs index 9164bab..a7a7cc1 100644 --- a/crates/ogar-from-ruff/src/sqlalchemy.rs +++ b/crates/ogar-from-ruff/src/sqlalchemy.rs @@ -2,31 +2,23 @@ //! //! `lift_model_with_language` (in `lib.rs`) routes `Language::Python` through //! [`super::project_odoo_fields`], which never wires `not_null -> required` -//! (only the Rails path, `super::project_rails_fields`, does that). WoA's -//! Flask-SQLAlchemy schema is **total** — every column's nullability is a -//! declared fact (`nullable=True/False`), exactly like a Rails migration -//! column — so it needs the Rails-style wiring (FK-dedup + -//! `not_null -> required` + the attribute-dup guard), NOT the Odoo wiring, -//! on a path that still stamps [`Language::Python`] (WoA is Python source). +//! (only the total-schema path does that). WoA's Flask-SQLAlchemy schema is +//! **total** — every column's nullability is a declared fact +//! (`nullable=True/False`), exactly like a Rails migration column — so it +//! needs the total-schema-style wiring (FK-dedup + `not_null -> required` + +//! the attribute-dup guard), NOT the Odoo wiring, on a path that still stamps +//! [`Language::Python`] (WoA is Python source). //! -//! # Why this module does not share code with `project_rails_fields` +//! # Consolidation onto `project_total_schema_fields` (landed) //! -//! SPEC-5 Part B (design note 1) prefers ONE shared -//! `project_total_schema_fields(class, model)` helper called by both -//! `project_rails_fields` and this module's [`project_sqlalchemy_fields`], -//! with `lib.rs` threading an internal `FieldProjection` selector through -//! `lift_model_with_language`. -//! -//! **This session's operator constraint overrides that design note:** a -//! sibling grind agent (G-A) is editing `lib.rs` concurrently on unrelated -//! functions, and the merge contract for this workstream is "new module -//! file(s) + at most one `mod` line in `lib.rs`, otherwise `lib.rs` -//! untouched." So the FK-dedup predicate and the total-nullability wiring -//! are **duplicated** here rather than threaded through `lib.rs`'s private -//! `is_fk_shadowed_by_association` / `project_rails_fields`. This is a named, -//! reported trade-off (see the mission report), not a silent divergence — -//! once G-A's `lib.rs` changes land, a follow-up should consolidate onto the -//! spec's preferred `project_total_schema_fields` shared helper. +//! SPEC-5 Part B (design note 1) named ONE shared +//! `project_total_schema_fields(class, model)` helper, called by both the +//! Rails (Ruby) and this module's SQLAlchemy (Python) producers, as the +//! target design. That consolidation has **landed**: [`project_sqlalchemy_fields`] +//! is now a thin wrapper around [`crate::project_total_schema_fields`], and +//! the FK-dedup predicate (`crate::is_fk_shadowed_by_association`) is no +//! longer duplicated here — both producers route through the single +//! `lib.rs` implementation (Batch-1 Item 2, D-PARITY-PROBE-WOA-1 Nachtrag). //! //! Consequently [`lift_model_sqlalchemy`] / [`lift_model_graph_sqlalchemy`] //! are deliberately narrower than [`super::lift_model`] / @@ -39,7 +31,7 @@ //! empty state — a Flask-SQLAlchemy model has no such DSL to lift, so this //! is the honest empty state for this producer, not a dropped fact. -use ogar_vocab::{Association, AssociationKind, Attribute, Class, ComputedField, Language}; +use ogar_vocab::{Class, Language}; use ruff_spo_triplet::{Model, ModelGraph}; use crate::lift_association; @@ -49,82 +41,13 @@ use crate::lift_association; /// the not-yet-built `ruff_sqlalchemy_spo` frontend's harvest) onto the /// schema-carrying [`Class`] columns. /// -/// Mirrors `ogar-from-ruff::project_rails_fields` (SPEC-5 Part B, design -/// note 1) field-for-field: -/// - a relational field (`target` set) -> [`Association`]; dormant for the -/// WoA fixture (associations arrive via `Model::associations` — see -/// [`lift_model_sqlalchemy`] — not via a relational `Model::fields` -/// entry), kept for shape-parity with the Odoo/Rails paths. -/// - a scalar field named `_id` whose model ALSO declares an -/// association named `` (SQLAlchemy `db.relationship`, already -/// lifted onto `class.associations` before this function runs) -> -/// **skipped** ([`is_fk_shadowed_by_association`]). The physical FK -/// column and the declared `db.relationship` are the SAME relation seen -/// twice; the ORM spelling (`timesheet_id: OgInt`) is dropped in favour -/// of the AR-ish spelling (`timesheet: ToOne`). -/// - any other scalar field -> [`Attribute`], `type_name` from -/// `Field::field_type`, `AttributeOptions::required` wired from -/// `Field::not_null`: `Some(true)` (`nullable=False`) stays `Some(true)`; -/// `None` (`nullable=True` or absent — SQLAlchemy's column default, per -/// SPEC-5 Part A's mapping table) becomes the EXPLICIT `Some(false)` — the -/// schema stratum is total knowledge, so absence of `nullable=False` IS a -/// positive "nullable" fact, not "unknown". -/// - a compute field (`emitted_by` set) -> [`ComputedField`] — dormant for -/// WoA today (no `@property`/compute-linkage pass exists yet), kept for -/// shape-parity. +/// WoA/Flask-SQLAlchemy schema-stratum projection — the Python producer's +/// named entry point onto the shared total-schema logic (FK-dedup + +/// not_null->required + attribute-dup guard). One implementation, two +/// producers: this and `ogar-from-ruff::project_total_schema_fields` +/// (Rails), which this function delegates to entirely. fn project_sqlalchemy_fields(class: &mut Class, model: &Model) { - for field in &model.fields { - if let Some(comodel) = &field.target { - let kind = if field.relation_kind.as_deref() == Some("one2many") || field.inverse_name.is_some() - { - AssociationKind::HasMany - } else { - AssociationKind::BelongsTo - }; - let mut assoc = Association::new(kind, &field.name); - assoc.class_name = Some(comodel.clone()); - assoc.inverse_of = field.inverse_name.clone(); - class.associations.push(assoc); - } else if !is_fk_shadowed_by_association(&field.name, class) - && !class.attributes.iter().any(|a| a.name == field.name) - { - let mut attr = Attribute::new(&field.name); - attr.type_name = field.field_type.clone(); - attr.options.required = Some(field.not_null.unwrap_or(false)); - class.attributes.push(attr); - } - if let Some(compute_method) = &field.emitted_by { - let mut computed = ComputedField::new(&field.name, compute_method); - computed.depends = field.depends_on.clone(); - class.computed_fields.push(computed); - } - } -} - -/// FK-dedup predicate for [`project_sqlalchemy_fields`] — duplicated from -/// `ogar-from-ruff::is_fk_shadowed_by_association` (kept `lib.rs`-private, -/// see the module doc for why this file cannot call it directly). Same -/// contract: `_id` is shadowed when `class` already carries an -/// [`Association`] named ``; the bare `id` primary key never matches -/// (no `_id`-suffixed prefix of its own). -fn is_fk_shadowed_by_association(field_name: &str, class: &Class) -> bool { - // Kept in lockstep with lib.rs::is_fk_shadowed_by_association (SPEC-0(b)): - // an explicit `foreign_key` on the association is authoritative and is - // checked FIRST; the `_id` convention is the fallback. Follow-up: - // consolidate both copies onto a shared project_total_schema_fields helper - // (named trade-off in the module doc) — until then, any semantic change - // to one copy MUST land in the other. - if class - .associations - .iter() - .any(|a| a.foreign_key.as_deref() == Some(field_name)) - { - return true; - } - field_name - .strip_suffix("_id") - .filter(|prefix| !prefix.is_empty()) - .is_some_and(|prefix| class.associations.iter().any(|a| a.name == prefix)) + crate::project_total_schema_fields(class, model); } /// Lift one WoA/SQLAlchemy [`Model`] to an OGAR [`Class`] stamped @@ -171,11 +94,13 @@ pub fn lift_model_graph_sqlalchemy(graph: &ModelGraph) -> Vec { } /// The WoA/SMB slice of `lib.rs`'s private `classify_domain` — duplicated -/// here for the same merge-constraint reason as -/// [`is_fk_shadowed_by_association`]. Only the branch relevant to a -/// SQLAlchemy producer (WoA / SMB, both Flask-SQLAlchemy German-ERP -/// consumers) is reproduced; the OpenProject/Redmine/Odoo branches don't -/// apply to this producer. +/// here as a separate, named follow-up (the FK-dedup lockstep copy this +/// module used to carry, `is_fk_shadowed_by_association`, is now +/// consolidated onto `crate::is_fk_shadowed_by_association`; this one is +/// deliberately left as-is, out of scope for that consolidation). Only the +/// branch relevant to a SQLAlchemy producer (WoA / SMB, both +/// Flask-SQLAlchemy German-ERP consumers) is reproduced; the +/// OpenProject/Redmine/Odoo branches don't apply to this producer. fn classify_woa_domain(namespace: &str) -> Option { let ns = namespace.to_ascii_lowercase(); if ns.contains("woa") || ns.contains("smb") { @@ -272,4 +197,73 @@ mod tests { assert_eq!(classes[0].source_domain.as_deref(), Some("german-erp")); assert_eq!(classes[0].source_curator.as_deref(), Some("woa")); } + + /// PR #156 finding (b), cross-producer: the explicit-`foreign_key` FK-dedup + /// rule must fire identically for the Rails (Ruby) and SQLAlchemy (Python) + /// producers, since Batch-1 Item 2 routes both through the single + /// `crate::project_total_schema_fields` / `crate::is_fk_shadowed_by_association` + /// implementation. `author`'s association name does NOT match the + /// `_id` convention for `user_id` (that would require an association + /// named `user`) — only the explicit `foreign_key: "user_id"` shadows it. + #[test] + fn both_producers_share_explicit_foreign_key_dedup() { + let mut m = Model::new("Post"); + m.associations.push(AssocDecl { + kind: AssocKind::BelongsTo, + name: "author".to_string(), + options: vec![ + ("class_name".to_string(), "User".to_string()), + ("foreign_key".to_string(), "user_id".to_string()), + ], + }); + m.fields.push(Field { + name: "user_id".to_string(), + field_type: Some("integer".to_string()), + not_null: Some(true), + ..Default::default() + }); + m.fields.push(Field { + name: "title".to_string(), + field_type: Some("string".to_string()), + not_null: Some(true), + ..Default::default() + }); + + let mut graph = ModelGraph::new(""); + graph.models.push(m); + + let ruby_classes = crate::lift_model_graph(&graph); + let sqlalchemy_classes = lift_model_graph_sqlalchemy(&graph); + + for class in [&ruby_classes[0], &sqlalchemy_classes[0]] { + assert!( + !class.attributes.iter().any(|a| a.name == "user_id"), + "user_id must be FK-shadowed by author's explicit foreign_key, not the \ + _id convention (association is named `author`, not `user`): {:?}", + class.attributes, + ); + let author = class + .associations + .iter() + .find(|a| a.name == "author") + .expect("author association present"); + assert_eq!(author.kind, AssociationKind::BelongsTo); + let title = class + .attributes + .iter() + .find(|a| a.name == "title") + .expect("title attribute present"); + assert_eq!(title.options.required, Some(true)); + } + + let ruby_names: std::collections::BTreeSet<_> = + ruby_classes[0].attributes.iter().map(|a| a.name.clone()).collect(); + let sqlalchemy_names: std::collections::BTreeSet<_> = + sqlalchemy_classes[0].attributes.iter().map(|a| a.name.clone()).collect(); + assert_eq!( + ruby_names, sqlalchemy_names, + "both producers must project the identical attribute name set, proving shared \ + FK-dedup semantics from one implementation" + ); + } } diff --git a/crates/ogar-from-ruff/tests/py/instantiate_check.py b/crates/ogar-from-ruff/tests/py/instantiate_check.py index ceb4712..8d3bcfe 100644 --- a/crates/ogar-from-ruff/tests/py/instantiate_check.py +++ b/crates/ogar-from-ruff/tests/py/instantiate_check.py @@ -12,6 +12,7 @@ """ import dataclasses import importlib.util +import os import sys @@ -21,9 +22,20 @@ def main() -> int: return 2 module_path = sys.argv[1] + # The emitted module does `from ogar_runtime import (...)` — make its + # directory importable so that resolves (the copied reference + # `ogar_runtime.py` lives next to it; see woa_parity_probe.rs). + sys.path.insert(0, os.path.dirname(os.path.abspath(module_path))) + spec = importlib.util.spec_from_file_location("woa_timesheet_activity", module_path) assert spec is not None and spec.loader is not None, "module spec must load" module = importlib.util.module_from_spec(spec) + # Register in sys.modules BEFORE exec: the prelude's + # `from __future__ import annotations` (PEP 563) makes every dataclass + # annotation a string, and `dataclasses` resolves ClassVar/InitVar by + # looking the module up via `sys.modules[cls.__module__]` — which is + # None (AttributeError) unless the module is registered first. + sys.modules[spec.name] = module spec.loader.exec_module(module) cls = module.TimesheetActivity diff --git a/crates/ogar-from-ruff/tests/woa_parity_probe.rs b/crates/ogar-from-ruff/tests/woa_parity_probe.rs index 4d8fa67..0baab5b 100644 --- a/crates/ogar-from-ruff/tests/woa_parity_probe.rs +++ b/crates/ogar-from-ruff/tests/woa_parity_probe.rs @@ -37,8 +37,8 @@ //! below and called out explicitly in the printed DRIFT list. This is a //! verified, reported deviation, not an invented workaround. +use ogar_from_ruff::emit::{emit_python, emit_python_prelude}; use ogar_from_ruff::mint::compile_graph_sqlalchemy; -use ogar_from_ruff::emit::emit_python; use ogar_vocab::ports::WoaPort; use ruff_spo_triplet::{AssocDecl, AssocKind, Field, Model, ModelGraph}; @@ -104,7 +104,7 @@ const EXPECTED_LINES: &[&str] = &[ EXPECTED_CLASSID_LINE, " id: OgInt", " beschreibung: OgStr", - " created_at: OgDateTime", + " created_at: Optional[OgDateTime]", " timesheet: ToOne[\"TimeSheet\"]", ]; @@ -178,10 +178,11 @@ fn woa_parity_probe_prints_the_d_parity_probe_wp_1_metric() { in ogar-vocab/src/ports.rs (see src/mint.rs doc)", cc.facet.facet_classid() ); - let emit_nullability_drift = "emit_python rendert options.required nicht — \ -nullable created_at wird als pflichtiges @dataclass-Feld emittiert (kein Optional[]); \ -vorbestehende emit.rs-Luecke, Follow-up" - .to_string(); + let emit_nullability_drift = + "emit_python Optional[]-Nullability: GESCHLOSSEN (Batch-1 Item 1) — \ +created_at wird als Optional[OgDateTime] emittiert; options.required==Some(false) → Optional[…], \ +sonst bare (Spiegel emit_rust)" + .to_string(); let drift_list = [classid_drift, emit_nullability_drift]; let metric = format!( @@ -228,49 +229,14 @@ vorbestehende emit.rs-Luecke, Follow-up" // (Operator §6 / E-PYTHON-SUBSTRATE-MIRROR). Prove it is real, importable // Python: py_compile it, then instantiate it with dummy values. -/// Wrap the raw `emit_python` output with the imports/type-alias stand-ins -/// it references (`dataclass`/`ClassVar`, and `OgInt`/`OgStr`/`OgDateTime`/ -/// `ToOne` as lightweight `object`-alias stand-ins for the real consumer -/// wrapper-contract — SPEC-5 Part D step 1) so the module imports cleanly. -fn wrap_module(body: &str) -> String { - format!( - "from dataclasses import dataclass\n\ - from typing import ClassVar, Generic, TypeVar\n\ - \n\ - _T = TypeVar(\"_T\")\n\ - \n\ - \n\ - class ToOne(Generic[_T]):\n \ - \"\"\"Stand-in wrapper-contract type (real consumer supplies this).\"\"\"\n\ - \n\ - \n\ - class ToMany(Generic[_T]):\n \ - \"\"\"Stand-in wrapper-contract type (real consumer supplies this).\"\"\"\n\ - \n\ - \n\ - OgScalar = object\n\ - OgInt = int\n\ - OgStr = str\n\ - OgFloat = float\n\ - OgBool = bool\n\ - OgMoney = object\n\ - OgDate = object\n\ - OgDateTime = object\n\ - OgBytes = bytes\n\ - OgSelection = object\n\ - OgJson = object\n\ - \n\ - \n\ - {body}" - ) -} - #[test] fn woa_dataclass_py_compiles_and_instantiates() { let graph = timesheet_activity_graph(); let compiled = compile_graph_sqlalchemy::(&graph); - let py_body = emit_python(&compiled[0]); - let module_src = wrap_module(&py_body); + // The emitted module pulls its wrapper-contract types via a single + // `from ogar_runtime import (...)` block (design option (a) — see + // `emit.rs`'s module doc); it is NOT inlined into every emitted file. + let module_src = format!("{}\n{}", emit_python_prelude(), emit_python(&compiled[0])); let tmp_dir = std::env::var("CARGO_TARGET_TMPDIR") .unwrap_or_else(|_| std::env::temp_dir().to_string_lossy().to_string()); @@ -278,6 +244,15 @@ fn woa_dataclass_py_compiles_and_instantiates() { let py_path = std::path::Path::new(&tmp_dir).join("woa_timesheet_activity.py"); std::fs::write(&py_path, &module_src).expect("write emitted module"); + // Copy the shipped reference `ogar_runtime.py` next to the emitted + // module (same dir on `sys.path`) so `from ogar_runtime import …` + // resolves — the consumer pulls the contract, it is not re-minted. + std::fs::copy( + std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("python/ogar_runtime.py"), + std::path::Path::new(&tmp_dir).join("ogar_runtime.py"), + ) + .expect("copy ogar_runtime.py next to the emitted module"); + // Gate 1: valid Python (py_compile, exit 0). let compile_status = std::process::Command::new("python3") .args(["-m", "py_compile", py_path.to_str().unwrap()]) diff --git a/docs/DISCOVERY-MAP.md b/docs/DISCOVERY-MAP.md index 5a5a36f..ee6dd8b 100644 --- a/docs/DISCOVERY-MAP.md +++ b/docs/DISCOVERY-MAP.md @@ -1036,3 +1036,26 @@ isolation. The map's job is to keep them visible. Mongo (only on real blob need). Precedent: ogar-adapter-clickhouse-ddl. Cross-ref D-PARITY-PROBE-WOA-1, E-PYTHON-SUBSTRATE-MIRROR. + +- **D-PARITY-PROBE-WOA-1 — Nachtrag/Regrade 2026-07-06 (Batch-1 Item 1):** + the `emit_python` Optional-nullability gap listed as remaining DRIFT in the + original entry is **CLOSED**: `emit_python` now wraps `required==Some(false)` + attributes in `Optional[…]` (mirror of `emit_rust`'s `Option<…>`) and emits + its wrapper-contract imports via `emit_python_prelude()` → `from ogar_runtime + import …` (design option (a); reference + `crates/ogar-from-ruff/python/ogar_runtime.py` shipped). The `wrap_module` + probe crutch is deleted; the emitted `@dataclass` module py_compiles AND + imports standalone. `created_at` now emits `Optional[OgDateTime]`. Remaining + drift on this fixture = the `WoaPort` classid-alias convergence pin + (`0x0103_0003`) only. + +- **D-PARITY-PROBE-WOA-1 — Nachtrag 2026-07-06 (Batch-1 Item 2):** the lockstep + FK-dedup copy in `ogar-from-ruff/src/sqlalchemy.rs` (its own + `is_fk_shadowed_by_association` + `project_sqlalchemy_fields` body, flagged + "any change MUST land in the other") is **consolidated**: both the Rails + (Ruby) and SQLAlchemy (Python) producers now route through the single + `pub(crate) project_total_schema_fields` / `is_fk_shadowed_by_association` in + `lib.rs`. New test `both_producers_share_explicit_foreign_key_dedup` proves + identical dedup (explicit `foreign_key`, PR #156 finding (b)) across both + paths from one implementation. Behavior-preserving; `classify_woa_domain` + lockstep left as a separate, named follow-up.