Zum Hauptinhalt springen

Changelog

Release notes of the zSYNC odoo modules. Every module carries its own version; a block below tells which module changed and from which version to which one.

This page is generated from the patchnotes of the modules - please do not edit it directly. New entries are added by the "patchnotes to changelog" workflow on every push to the 19.0 branch.

2026-07-29

zbsync 2.1 → 2.2

  • Horizontale Radiobuttons sind wieder normal lesbar. Das CSS fuer die Pill-/Segmented-Control-Optik (zbsync/static/src/css/pill_container.css) liegt im Backend-Asset-Bundle und galt bisher fuer JEDES Radio-Feld in Odoo, nicht nur fuer die Pills. Mit padding: 0 auf .o_radio_item fiel das padding-left der Bootstrap-Klasse form-check weg - Odoo rendert horizontale Radiobuttons immer als <div class="form-check o_radio_item">, deshalb rutschte der Auswahlkreis mitten ins Label.
  • Zum Nachpruefen: einen Kontakt oeffnen (Verkauf oder Kontakte -> Neu). Die Auswahl "Einzelperson / Unternehmen" muss saubere Abstaende haben, Kreis links, Text rechts daneben - kein Kreis, der im Wort liegt. Das galt genauso fuer alle anderen horizontalen Radio-Felder, z.B. im Rechnungs- oder Lieferassistenten.
  • Zusaetzlich wurde eine zweite ungescopte Regel eingegrenzt, die den unteren Abstand von allen Formular-Labels in Gruppen auf 0 gesetzt hat. Beides gilt jetzt nur noch innerhalb von .o_segmented_control, die Pill-Optik selbst bleibt unveraendert.

zbsync_mapper 2.1 → 2.2

  • Mapper-Vorschau (mapping_json) sprengte bei großen/gestreamten Pipeline-Ausgaben den Speicher. Das Feld wird über einen @api.constrains("output_data") bei JEDEM Batch-Write neu berechnet und serialisierte bisher die KOMPLETTE Batch-Ausgabe per json.dumps. Bei JSONL-Attachment-gestreamten Ausgaben (z.B. SQL-/Web-Streaming-Grabber mit Millionen Zeilen) materialisierte das den gesamten Datensatz in einen einzigen String — der Worker wuchs auf viele GB RAM und wurde in CI per OOM/Worker-Recycle mitten im Batch abgeschossen. Der Batch blieb dann in processing hängen, die Pipeline wurde nie fertig (u.a. Ursache dafür, dass test_bigdata_limits DB-/Web-Szenarien nie grün wurden).
  • Für die Mapper-Vorschau genügt EIN repräsentativer Beispiel-Datensatz (zur Auflösung der Feld-Mappings) — genau wie es für Listen-Ausgaben ohnehin schon gemacht wurde (formatted[0]). Streaming-Ausgaben werden jetzt ebenso behandelt: es wird nur der erste Record lazy aus dem JSONL-Stream gelesen, statt den ganzen Stream zu serialisieren. Der Speicher bleibt damit konstant, unabhängig von der Datenmenge.
  • Tester-Sicht (zSYNC → Pipeline mit Mapper): Bei Pipelines mit sehr großen Quellen (Streaming-Grabber) läuft die Verarbeitung jetzt speicherstabil durch; die Mapper-Vorschau zeigt weiterhin einen Beispiel-Datensatz.

web_diffview 1.1 → 1.2

  • On instances where the old module "web_diffview" was installed, the server logged an error on every start and every update, because the module had been dropped from the code while the database still had it flagged as installed: Some modules are not loaded, some dependencies or manifest may be missing: [web_diffview] Model mixin.diffview is declared but cannot be loaded!
  • Odoo cannot uninstall a module whose code is gone, so the leftover entry could not be cleaned up from the Apps list either.
  • "web_diffview" is shipped again as an empty module (no models, no views, no assets) and is marked for uninstallation in MANIFEST, so the next update removes it by itself - nothing to click.
  • How to test: update an instance that had web_diffview installed. Afterwards the two log lines above are gone, and under Apps (remove the "Apps" filter, search for "web_diffview") the module shows up as not installed. On instances that never had it, nothing changes - the module stays uninstalled.

zbsync 2.0 → 2.1

  • Doppelte Felddefinitionen am Batch entfernt, Log beim Installieren wieder sauber.
  • Im Batch-Modell (zbs.instance.line.batch) waren input_data, output_data, processed_records und done_tries versehentlich zweimal definiert. Die zweite, knappere Definition hat die dokumentierte ueberschrieben und done_tries mit tracking=True versehen - das Modell hat aber kein Chatter, deshalb hat Odoo beim Laden gewarnt. Die Duplikate sind weg, die Hilfetexte an den Feldern bleiben erhalten.
  • Zusaetzlich drei Meldungen aus der View-Pruefung behoben: der Instances-Button im Pipeline-Dashboard hat jetzt role="button", das Zahnrad in der Schritt-Liste der Pipeline-Instanz einen Tooltip ("Open this step"), und die Transformer-Form nutzt hasclass() statt @class.
  • Zum Testen: Instanz frisch installieren bzw. updaten und das Update-Log ansehen - zu zbs.instance.line.batch darf keine Warnung mehr auftauchen.
  • In der Pipeline-Instanz beim Zahnrad in der Schritt-Liste erscheint nun ein Hinweistext, funktional aendert sich nichts.

zbsync_ftp 1.4 → 1.5

  • Modulbeschreibung im Manifest hinterlegt.
  • Ohne eigene Beschreibung hat Odoo die README.md als reStructuredText interpretiert und beim Installieren Formatierungsfehler ins Log geschrieben.
  • Das Manifest enthaelt jetzt eine kurze Beschreibung; die README.md bleibt unveraendert Markdown (inkl. der Docker-Befehle fuer den Test-FTP-Server).
  • Zum Testen: Modul installieren und das Update-Log ansehen - keine Formatierungsmeldungen mehr. In Apps zeigt das Modul jetzt einen kurzen Beschreibungstext.

zbsync_mapper 2.0 → 2.1

  • Warnungen beim Installieren entfernt (Backend-Kategorie, Feldbezeichnungen).
  • Am Feld parent_path der Mapper-Backend-Kategorie stand der Parameter unaccent, den Odoo 19 nicht mehr kennt - er ist entfernt.
  • Ausserdem hatten im Mapping jeweils zwei Felder die gleiche Bezeichnung ("Parent Path" bzw. "Source Field"), was Odoo beim Laden bemaengelt. Die technischen Bezeichnungen sind jetzt eindeutig ("Parent Store Path" bzw.
  • "Source Backend Field"); in den Ansichten steht unveraendert das gleiche Label wie vorher. Die berechneten Felder parent_source_field_id und parent_dest_field_id sind als recursive markiert, weil sie sich ueber die Eltern-Mappings selbst auswerten.
  • Zum Testen: Mapper oeffnen, Quell-/Zielfeld-Auswahl und die Spalte "Parent" pruefen - Beschriftungen und Verhalten sind unveraendert. Beim Suchen/Gruppieren bzw. im Export heissen die technischen Felder jetzt anders.

zbsync_marketplace 1.6 → 1.7

  • Version-Feld im Marketplace-Assistenten eindeutig bezeichnet.
  • Im Assistenten "New Pipeline from Marketplace" hiessen zwei Felder "Version".
  • Das berechnete Feld heisst technisch jetzt "Latest Version"; im Formular steht unter "Latest Version" weiterhin die Beschriftung "Version".
  • Zum Testen: Marketplace-Pipeline anlegen - der Block "Latest Version" sieht aus wie vorher.

zbsync_marketplace_transformer_helper 1.1 → 1.2

  • Fehlende Modellbeschreibung ergaenzt.
  • Der Upload-Assistent (zbs.marketplace.helper.upload) hatte keine _description, worauf Odoo beim Laden hingewiesen hat.
  • Zum Testen: Helper in den Marketplace hochladen - der Assistent verhaelt sich unveraendert.

zbsync_odoo 2.0 → 2.1

  • Eindeutige Feldbezeichnungen und Umstieg von odoo.osv.
  • Am Odoo-Grabber/-Dumper hiessen Model (Text, externe Verbindung) und Model (Auswahlfeld, lokale Verbindung) beide gleich - Odoo hat das beim Laden bemaengelt. Das Textfeld heisst technisch jetzt "Model (Technical Name)", in den Formularen steht weiterhin "Model" an beiden Stellen.
  • Der Import von odoo.osv (in Odoo 19 als veraltet gemeldet) ist raus; die Domain wird jetzt direkt zusammengesetzt, was fuer XML-RPC ohnehin erforderlich ist.
  • Zum Testen: Odoo-Grabber und -Dumper mit lokaler und mit externer Verbindung oeffnen - je nach Verbindung erscheint wie bisher das Auswahlfeld oder das Textfeld, beschriftet mit "Model". Einen Dumper mit ___keys und zusaetzlicher Domain laufen lassen und pruefen, dass die Datensaetze wie vorher gefunden werden.

zbsync_odoo_mapper 2.0 → 2.1

  • Eindeutige Bezeichnung fuer das gemappte Modell.
  • Das Feld model_external_id hiess wie die beiden Model-Felder aus zbsync_odoo, worauf Odoo beim Laden hingewiesen hat. Technisch heisst es jetzt "Model (Backend)", im Formular steht unveraendert "Model".
  • Zum Testen: Odoo-Grabber/-Dumper mit externer Verbindung oeffnen, Modell aus der Liste der synchronisierten Modelle waehlen - Beschriftung und Verhalten wie bisher.

zbsync_pipeline_widget 2.2 → 2.3

  • Route auf jsonrpc umgestellt.
  • Der Endpoint /zync/change_sequence war noch mit type="json" registriert, was Odoo 19 als veralteten Alias meldet. Er nutzt jetzt type="jsonrpc".
  • Zum Testen: Pipeline-Widget oeffnen und Schritte per Drag & Drop umsortieren.

zbsync_receiver 1.1 → 1.2

  • Leserechte am Authorizer auf interne Benutzer eingeschraenkt.
  • Die Zugriffsregel fuer den Receive-Authorizer war ohne Gruppe hinterlegt, galt also fuer jeden - Odoo weist beim Installieren darauf hin, dass Regeln ohne Gruppe veraltet sind. Das Leserecht haengt jetzt an "Interner Benutzer"; die Vollrechte fuer die zSYNC-Manager bleiben unveraendert. Die oeffentlichen Receiver-Endpoints lesen den Authorizer ohnehin mit erhoehten Rechten, an der Funktion aendert sich nichts.
  • Zum Testen: Receiver-Pipeline mit Authorizer aufrufen (auch die JWT-Anmeldung) - Anmeldung und Verarbeitung laufen wie bisher. Als normaler interner Benutzer ist der Authorizer weiterhin lesbar, als Manager bearbeitbar.

zbsync_specification 1.0 → 1.1

  • Umstieg von odoo.osv auf direkte Domain-Verknuepfung.
  • Der Import von odoo.osv wird in Odoo 19 als veraltet gemeldet. Die Query-Parameter-Domain wird jetzt ohne diesen Import zusammengesetzt, das Ergebnis ist identisch.
  • Zum Testen: REST-Konfiguration mit Query-Parametern aufrufen und pruefen, dass wie bisher genau die passenden Datensaetze geliefert werden.

zbsync_trigger_methods 1.3 → 1.4

  • Tooltip am Hilfe-Symbol im Pipeline-Formular.
  • Die beiden Fragezeichen-Symbole neben den Trigger-Listen hatten keinen Titel, was Odoo bei der View-Pruefung bemaengelt hat. Sie zeigen jetzt "Open the documentation" als Tooltip.
  • Zum Testen: Pipeline oeffnen, Reiter mit Methoden-/Feld-Triggern - beim Ueberfahren des Fragezeichens erscheint ein Hinweistext, der Link fuehrt weiterhin in die Dokumentation.

zbsync_xport 1.5 → 1.6

  • Modulbeschreibung im Manifest hinterlegt.
  • Ohne eigene Beschreibung hat Odoo die README.md als reStructuredText interpretiert und beim Installieren Formatierungsfehler ins Log geschrieben.
  • Das Manifest enthaelt jetzt eine kurze Beschreibung; die README.md bleibt unveraendert Markdown.
  • Zum Testen: Modul installieren und das Update-Log ansehen - keine Formatierungsmeldungen mehr. In Apps zeigt das Modul jetzt einen kurzen Beschreibungstext.

zbsync_xport_ui 1.8 → 1.9

  • Downloading a pipeline failed with a server error when its name contained a "/" (e.g. "Customer/Import"): the name went unfiltered into the export filename, where the slash was treated as a subdirectory.
  • How to test: create a pipeline with a "/" in its name, tick it in the list view and export it via Action -> Download, and also via the XML download on the pipeline form. Both now return a file. In the filename, "/" and similar special characters are replaced by "_"; the pipeline name itself is unchanged.

2026-07-28

zbsync 1.13 → 2.0

  • Heimruf (call_home) sendet weniger personenbezogene Daten (DSGVO).
  • Der tägliche call_home-Ping an das Zebroo-Backend übermittelt jetzt nur noch die zur Zuordnung und Abrechnung nötigen Angaben: die database_uuid, die Firmennamen und die Nutzungszahlen (Pipelines, Mappings).
  • Die bisher zusätzlich gesendeten Adressdaten (Straße, PLZ, Ort je Firma) werden NICHT mehr übertragen. Funktional ändert sich für den Anwender nichts; die Datenübermittlung ist datensparsamer.
  • Error display, popover pagination, domain expressions, and pipeline UX.
  • Error display
  • err_desc_display: new computed HTML field that renders structured error data (message, record, traceback) as formatted HTML in the UI.
  • ExceptionFormatter hardened: extracts args[0] directly, stores record as None, joins traceback lines into a string.
  • Popover pagination
  • Large input/output/error text is paginated in 250-line pages with a 1 000-character-per-line cap; new get_batch_field_page() and get_popover_field() methods on Batch and InstanceLine.
  • New ZbsPagedTextField JS component with batch navigation, inline range editing, and improved fixed positioning (bottom anchor, 10 vh top).
  • Domain expressions
  • _resolve_dynamic_values() evaluates $-prefixed runtime expressions in domain tuples ($record.name, $uid, $data.get('x')).
  • domain field now defaults to "[]" and is no longer required.
  • Pipeline UX
  • blocked_by_step_mode field shows a warning alert and ribbon when another instance is running in step-by-step mode.
  • Tree view decorations for paused and blocked pipeline instances.
  • Disabled workers shown as muted/greyed out in list views.
  • Heartbeat interval slowed from 10 seconds to 1 minute.
  • Misc
  • uid added to default execution objects.
  • propagate_toggle.js/xml: new boolean field widget.
  • Post-init hook backfills UTF-8 encoding xmlid aliases (encoding_utf-8, encoding_utf8 → encoding_utf_8).
  • Removed privilege_id from manager group.
  • Zwei Regressionen aus dem Streaming-/Cursor-Reduction-Umbau behoben, die die Testsuite auf 19.0 rot gemacht haben.
    1. Resume-Cursor + Idempotency-Token griffen im echten Batch-Lauf gar nicht.
  • Der Worker-Loop liest den Batch aus context['zbs_current_batch_id'], aber zbs.instance.line.batch._exec_run hat nur zbs_batch_id gesetzt (anderer Key). Folge: der Cursor processed_input_index wurde nie fortgeschrieben und context['zbs_idempotency_token'] war fuer jeden Record None. _exec_run setzt jetzt zusaetzlich zbs_current_batch_id.
  • Tester-Sicht (Backend, Tech-View eines zbs.instance.line.batch): Nach einem Lauf steht "Processed Input Index" korrekt auf der Anzahl verarbeiteter Records; crasht ein Worker mittendrin und laeuft erneut, ueberspringt er die bereits erledigten Records statt alles zu wiederholen.
    1. Attribut-Zugriff auf ein Streaming-Ergebnis (z.B. data.Name nach einem CSV-Grabber, dessen Output als JSONL-Attachment gestreamt wird) warf AttributeError. ZebrooSet-Dive kollabierte ein Ein-Record-Ergebnis nur fuer echte Listen, nicht fuer den lazy JsonlAttachmentIterator.
  • Dive behandelt einen Ein-Record-Stream jetzt wie eine 1-Element-Liste.
  • Tester-Sicht: CSV-Import (Grabber) liefert die Felder wieder korrekt — Name/Street/City sind ueber das Ergebnis erreichbar, auch bei Streaming-Ausgabe. Betrifft alle Streaming-Grabber (CSV, SQL, Web, XML).
  • Coverage: gruen sind wieder zbsync/tests/test_batch_resume.py (8 Tests, die den Worker real ausfuehren) und zbsync_excel_csv/tests/test_csvexcel.py (CSV-Import).
  • Big-Data-Streaming-Testsuite (tests/suites/test_bigdata_limits) so dimensioniert, dass sie in CI durchlaeuft. Bisher erzeugte die Suite per Default 25 Mio. Zeilen (~18 GB CSV / ~30 GB XML) und lief damit ~2,3 h pro Lauf -> der Robotests-Job lief in sein 140-Minuten-Timeout und wurde als "cancelled" gewertet. Die Suite war deshalb seit ihrer Einfuehrung nie gruen.
  • Der Standard-Umfang liegt jetzt bei 1 Mio. Zeilen. Das streamt weiterhin ueber mehrere Chunks, haelt den Speicher nachweislich konstant und pruefen weiterhin CSV/XML/DB/Web end-to-end, ist aber in wenigen Minuten fertig. Der volle 25-Mio-Zeilen-Stresstest bleibt erhalten und wird explizit angestossen: BIGDATA_ROWS=25000000 odoo robot run test_bigdata_limits bzw. odoo robot run test_bigdata_limits -p BIGDATA_ROWS=25000000 Hintergrund: Die gemeinsame Robotests-Workflow (robotests.yml) kann weder -p noch zusaetzliche Env-Variablen in den Robot-Container reichen, deshalb muss der CI-Umfang im Default der Generatoren liegen, nicht im CI-Aufruf.
  • Tester-Sicht (zSYNC -> Robot-Tests): test_bigdata_limits laeuft in CI jetzt in Minuten auf gruen durch. Wer die echten Grenzen testen will, setzt BIGDATA_ROWS hoch.
  • Fix TypeError in zbs.environment._import() when a JSON variable has a non-list value (e.g. boolean True): use isinstance check instead of values or [] which returns True for truthy booleans.
  • In-Order-Ausfuehrung von Pipeline-Schritten repariert (batch_execution_in_order). Eine Pipeline mit einem so konfigurierten Schritt blieb haengen und warf beim manuellen "Go" einen Fehler.
  • zbs.instance.line.batch.retry_if_batch_execution_in_order hatte zwei Fehler:
  • Sie verlangte ein Argument instance_line_id, das der einzige Aufrufer nie uebergab und der Rumpf nie nutzte -> "missing 1 required positional argument: 'instance_line_id'" beim Retry.
  • Die Wartebedingung verglich das exists(...)-Bool mit self.id (next_batch[0] != self.id) und war damit auch dann wahr, wenn KEINE fruehere Batch offen war -> die erste Batch wartete endlos, die Pipeline kam nie weiter ("haengt im n-ten Step").
  • Beide behoben: das ueberfluessige Argument ist entfernt, die Bedingung prueft jetzt korrekt, ob eine fruehere todo/processing-Batch existiert.
  • Tester-Sicht (zSYNC -> Pipeline mit einem Schritt, bei dem "Batch-Ausfuehrung der Reihe nach" aktiv ist): Die Pipeline laeuft die Steps in Reihenfolge durch, bleibt nicht stehen, und "Go"/Retry wirft keinen Fehler mehr. Batches desselben Steps werden weiterhin strikt in Reihenfolge abgearbeitet (eine Batch wartet, solange eine fruehere noch offen ist).
  • Coverage: neuer Unit-Test zbsync/tests/test_batch_execution_in_order.py (5 Faelle).
  • Fix: Step-Reihenfolge nach Marketplace-Download stimmt wieder (ZO-05230).
  • Beim Upload zum Marketplace werden die automatischen "Start"-/"Stop"-Schritte entfernt; beim Download wurden sie zwar neu angelegt, landeten aber beide am Ende der Pipeline – "Start" stand dadurch an vorletzter Stelle.
  • Tester: Pipeline zum Marketplace hochladen, lokal löschen, Katalog updaten und wieder herunterladen. In der heruntergeladenen Pipeline muss "Start" jetzt ganz oben und "Stop" ganz unten stehen, die echten Schritte unverändert dazwischen.
  • Weitere Regressionen aus dem Streaming-/Cursor-Reduction-Umbau behoben, die Streaming-Pipelines mit groesseren Datenmengen auf 19.0 rot gemacht haben.
    1. Streaming-Ergebnis wurde an Nicht-Streaming-Code als EIN Pseudo-Record durchgereicht bzw. war nicht serialisierbar. ZebrooSet.iterate_data iteriert einen lazy Record-Stream (JsonlAttachmentIterator) jetzt Record fuer Record, statt den ganzen Stream als [stream] zu yielden; und der JSON-Encoder kann einen solchen Stream serialisieren (materialisiert ihn bewusst, z.B. im Fehler-Handler des Workers).
  • Tester-Sicht: Grabber mit gestreamter Ausgabe (CSV/SQL/Web/XML) laufen durch, ohne dass der naechste Step AttributeError bzw.
  • "Object of type JsonlAttachmentIterator is not JSON serializable" wirft.
    1. Serialization-Konflikt beim Verarbeiten grosser/gestreamter Batches. Der Batch-Worker laeuft in einem eigenen DB-Cursor (new_env), der per Odoo- Default REPEATABLE READ ist. Der Resume-Cursor-Update pro Record (processed_input_index) und das Schreiben der Ergebnis-Zeile liefen so gegen den Heartbeat-Thread (der processing_since auf derselben Batch-Zeile aktualisiert) und warfen "could not serialize access due to concurrent update" — systematisch bei grossen Dateien, weil das Attachment-Schreiben laenger als das 30s-Heartbeat-Intervall dauert. Der Worker-Cursor wird jetzt auf READ COMMITTED gesetzt (Konflikt blockiert kurz statt zu erroren); zusaetzlich wird ein solcher transienter Fehler in der Nachbearbeitung als retrybar behandelt statt als permanenter BatchFailed.
  • Tester-Sicht: Grosse CSV-/XML-/DB-/Web-Importe laufen auf "success" durch, statt mit einer einzelnen fehlgeschlagenen Batch haengenzubleiben.
  • Coverage: gruen ist wieder tests/suites/test_bigdata_limits/test_bigdata_limits.robot (CSV/XML/DB/Web) sowie die Streaming-Grabber-Suiten.
  • Subpipeline-Aufruf ("Pipeline Trigger"-Worker) im echten Betrieb repariert — eine Regression aus dem Cursor-Reduction-Umbau, die auf 19.0 einen Runaway ausgeloest hat.
  • Der Worker process() laeuft seit dem Umbau in einem eigenen DB-Cursor (new_env). Der Trigger merkt sich die gestartete Sub-Instanz in batch.started_sub_instance_id, committete diesen Write aber auf dem falschen Cursor: die Sub-Instanz wurde persistiert, das Merkfeld auf der Batch-Zeile hing am aeusseren Cursor und wurde beim anschliessenden WaitHere-Retry zurueckgerollt. Folge: bei JEDEM Heartbeat sah der Trigger wieder ein leeres started_sub_instance_id und startete eine weitere Sub-Instanz -> unendlich viele "pending" Sub-Instanzen, die Hauptpipeline wurde nie fertig. batch/worker werden jetzt an den committenden Cursor gebunden, sodass Write und Commit zusammenpassen.
  • Tester-Sicht (zSYNC -> Pipelines): Eine Pipeline mit einem "Pipeline Trigger"-Schritt, per Cronjob/Button gestartet, erzeugt jetzt GENAU EINE Sub-Instanz (zu sehen unter der Instanz -> Sub-Instanzen). Die Hauptpipeline wartet korrekt auf die Sub-Pipeline und laeuft danach auf "success" durch — in allen Modi (Fire and Forget, Wait Until Done, Return Data). Vorher blieb sie haengen und die Sub-Instanz-Liste lief voll.
  • Coverage: gruen ist wieder tests/suites/test_pipelines/test_call_subpipeline.robot.
  • Heimruf (call_home) härtet die ausgehende Verbindung ab.
  • Der tägliche call_home-Cronjob (zbs.call_base._call_base) sendet die Nutzungs-Telemetrie jetzt mit einem Netzwerk-Timeout (30 s) und fängt Verbindungsfehler ab. Ist der Backend-Server (odoo.zebroo.de) nicht erreichbar, läuft der geplante Job nicht mehr auf Fehler ("rot"), sondern protokolliert nur eine Warnung im Log und endet sauber.
  • Zum Testen: Settings → Technical → Automation → Scheduled Actions → "zync:call_home" manuell ausführen; bei nicht erreichbarem Backend bleibt der Job grün und im Log erscheint "zSYNC call_home failed: ...".
  • Odoo 19 compatibility and robustness fixes.
  • zbs.worker: replaced the removed name_get() override with _compute_display_name (keeps the "deleted" fallback).
  • _roll_after_start: fixed a NameError where the BatchFailed exception variable was referenced after its except block.
  • default-environment migration is now idempotent (never creates a second default, which would break _get_default_env).
  • Reduce memory footprint of the batch run loop:
  • cache _get_data() result in _clearance_granted to skip the second json.loads/ZebrooSet decode when locks are identified
  • flush input_data/output_data from the ORM cache after _log_process_result so prior batches don't linger in memory across a cron pass
  • drop json indent=4 outside test_mode (env context zbs_test_mode); saves ~30% on serialized payload size for production runs
  • cap exception formatter: traceback frames (5 head + 15 tail), zbs_record payload (16 KB), exception str (4 KB) — prevents secondary OOM when err_desc is built on a memory-saturated worker Eliminate extra pool cursors for lock coordination.
  • Replaced 9 pglock(...) advisory-lock sites that each opened a fresh connection via new_env(enabled=True) with three mechanisms that all use self.env.cr — zero extra cursor for locking:
  • Batch identity: atomic UPDATE state='todo'->'processing' state-machine on zbs.instance.line.batch. Watchdog cron (zbsync_reset_stale_batches) resets stale processing rows after zsync.batch.stale.timeout.sec (default 300s).
  • Resource reservation: new model zbs.resource.lock with UNIQUE(resource_key) + ondelete=cascade. INSERT ON CONFLICT DO NOTHING for acquire; cascade-delete for release.
  • Pipeline coordination (_make_batches, _goto_next_line, _make_and_queue_batches, _check_batchdone_continue, _reset_processed): SELECT ... FOR UPDATE row-lock on zbs_instance.
  • Motivation: "too many clients" errors on Odoo.sh and large deployments under parallel pipelines.
  • Cut redundant pool-cursor opens in the batch run loop and on the processed_records computed field.
  • Each batch run used to acquire up to 3 sequential advisory-lock cursors via new_env(enabled=True)pglock("batch_<i>/<b>") (kept), plus pglock("resoure_check_<line>"), pglock(locks) and pglock("exec_run_<batch>") (all redundant with each other for the batch-id scope or coalescable).
  • _exec_run: the inner pglock("exec_run_<batch>") is gone. The outer batch_<i>/<b> lock from heartbeat() plus the batch-id lock inside _clearance_granted() already serialise the same scope.
  • _clearance_granted: the previous "acquire resource_check pglock → identify → release → acquire locks pglock" pattern now lives on a single new_env. Two new helpers on zbs.tools, pg_advisory_acquire(name, wait=False, cr=None) and pg_advisory_release(name, cr=None), let callers coalesce sequential advisory locks onto one extra connection instead of opening one per pglock(...).
  • _compute_counter (zbs.instance.line.batch + zbs.instance.line): new_env() is gone in favour of reusing the main cursor with cursor_change_to_read_committed. The per-line N+1 loop on the instance-line side collapses to a single LEFT JOIN. Computed fields no longer force an extra commit on every UI render.
  • Net effect: ~2-3 fewer cursor acquisitions per batch (still 1 for the outer batch lock + 1 for _clearance_granted's locks + 1 for the worker_env inside _exec_run), and processed_records reads stop hammering the pool entirely.
  • Per-record resume-cursor on batches: a crashed worker now resumes at the next unprocessed input record instead of replaying the whole batch.
  • Tester-Sicht (developer/admin im Backend):
  • Auf zbs.instance.line.batch gibt es das neue Feld "Processed Input Index" (processed_input_index). Es zeigt, bei welchem 0-basierten Input-Record der Worker beim nächsten Run weitermacht. Sichtbar im Tech-View des Batch-Records.
  • Wenn ein Worker bei Record 3 von 5 crasht (kill -9, OOM, RetryableJobError), bleibt processed_input_index=3 stehen. Der Watchdog-Reset setzt den Batch nur von state='processing' auf 'todo' zurück — der Cursor wird NICHT genullt. Im erneuten Lauf überspringt der Worker Records 0–2 und beginnt bei Record 3. Records 0–2 sehen den Worker also nicht zweimal.
  • "Reset & Play" / _debug_play() setzt den Cursor explizit auf 0 (Developer will sauberen Re-Run, kein Resume).
  • heartbeat_ui() (User-Klick auf "wieder versuchen") behält den Cursor (Side-Effects nicht nochmal feuern).
  • Beim _make_batches-Rebuild (Pipeline neu losschicken) sind die alten Batches per cascade-delete weg → neue Batches starten bei 0.
  • Worker-API:
  • Der Worker-Context führt jetzt pro Record einen stabilen Idempotency-Token: context['zbs_idempotency_token'] mit dem Format batch_<batch_id>_rec_<index>. Stable über alle Retries derselben (Batch, Index)-Kombination.
  • In zbs.transformer (python-transformer) ist der Token zusätzlich als idempotency_token in den python-globals verfügbar, kann also direkt im Worker-Snippet gelesen werden (z.B. als X-Idempotency-Key auf einem HTTP-POST oder als Dedup-Spalte beim DB-Insert).
  • context['zbs_record_index'] ist der 0-basierte Index des aktuellen Records im Batch.
  • Semantik:
  • At-least-once: ein Crash exakt zwischen dem Side-Effect des Workers und dem Cursor-Commit kann diesen einen Record erneut feuern. Für nicht-idempotente Side-Effects nutzt der Worker den Token. Workers selbst werden mit diesem PR NICHT angepasst — der Token ist da, Worker können opt-in.
  • Bei Streaming-Workern (Generator-Output) wird der Cursor schon im Outer-Loop pro Input-Record vorangezogen, nicht erst beim Verbrauch des Generators. Das hält "Cursor = Input wurde gelesen + verarbeitet" invariant; downstream-Consumer (dump_data) holt sich die Outputs weiterhin lazy via _chain().
  • Coverage: 15 neue TransactionCase-Tests in zbsync/tests/test_batch_resume.py decken den Cursor durch Crashes, Watchdog-Reset, _debug_play, heartbeat_ui, RetryableJobError, Streaming-Workers und konkurrenten Acquire ab.
  • Code-review follow-ups on the streaming refactor:
  • _clearance_granted: fix partial-acquire lock leak. The loop that pulls multiple advisory locks onto the shared new_env now tracks each successful acquire in a held list and releases only those in finally. A mid-loop contention RetryableJobError no longer leaves earlier locks dangling.
  • stream_attachment_from_iter / stream_attachment_from_path now return (attachment, true_size_bytes). The previous shape returned just the attachment whose file_size field is silently clamped to int4 max (~2 GiB) — callers that pass the value downstream (file / FTP / web grabbers) now propagate the real byte count.
  • pg_advisory_held(name, wait=False, cr=None): new contextmanager flavour of pg_advisory_acquire. Single-lock callers should prefer it so the release can't be forgotten. The plain pg_advisory_acquire now ships with a docstring warning explaining that the lock is session-scoped (no auto-release on commit).
  • iter_records (zbsync_sql) drops the unreachable tuple-row fallback; every supported driver returns dicts at this layer.
  • JsonlAttachmentIterator.__getitem__ docstring spells out the O(N) cost; downstream code that needs full iteration should call _zbs_iterate_records directly.
  • bigfaker / bigxml emit a one-liner to stderr at the start of generation so the multi-minute CSV/XML write doesn't look like a hang.
  • zbs.grabber_dumper gets _open_record_stream(record, requires='str') context manager: when the input record carries _source_attachment_id (PR 1's streaming emit), it opens the attachment file directly from the filestore — no ir.attachment.raw read that loads the whole file into RAM. Falls back to wrapping inline content in BytesIO/StringIO so existing pipelines keep working.
  • Fixes the helper so the attachment record correctly references its streamed file: Odoo's ir.attachment.create strips store_fname, file_size, checksum from create vals — we patch them via SQL immediately after, then invalidate the cache.
  • End-to-end streaming for source-grabber → parser → next worker pipelines.
  • Pipeline output_data no longer materialises records as a single JSON blob in a Text column; instead workers that return a generator stream their records as JSONL into an ir.attachment, and the next stage reads them back line-by-line via a JsonlAttachmentIterator.
  • Changes:
  • zbs.attachment.helper.stream_jsonl_from_iter(): write a generator of records as JSONL through the existing chunked Filestore writer. Each record is encoded with ZebrooSet's Encoder so recordsets, datetimes and Decimals survive round-trips. Returns (attachment, row_count).
  • ir.attachment.file_size is a 32-bit Integer; the helper now clamps the value and stashes the true size in description for files > 2 GiB.
  • JsonlAttachmentIterator (new in zebrooset.py): lazy line-by-line iterator over a JSONL attachment. Implements __iter__, __len__ (cached row_count), __bool__, __getitem__ (integer-only, expensive), and the existing _zbs_iterate_records hook so it slots into ZebrooSet without changing the iteration API. Re-opens the file on every iteration so it can be consumed multiple times.
  • zbs.pipeline.line.dump_data / load_data: new marker ZBS_JSONL:<att_id>:<row_count> for streaming payloads. dump_data detects Python generators, JsonlAttachmentIterators, or ZebrooSets wrapping them, and routes through _dump_jsonl which calls the helper.
  • load_data unpacks the marker into a JsonlAttachmentIterator wrapped in a ZebrooSet — the iterator only opens the file when actually iterated.
  • zbs.worker._process_worker_base: collects per-record worker outputs without losing laziness. If any process_record returned a generator, the whole worker output is chained through a single generator (_chain()) so it flows straight into dump_data and never materialises in RAM.
  • zbs.instance.line._append_data: streaming-guard branch — when batch output sits behind a lazy iterator, the single-batch case passes through untouched instead of doing container += item which would burn the records out.
  • Combined with PR 1+2, a 5M-row / 3.7 GB CSV now streams end-to-end through the pipeline in ~4 minutes under a 1 GB worker heap budget.
  • Fast-path in _dump_jsonl: when the data is already a JsonlAttachmentIterator backed by an existing filestore attachment, re-emit the original marker ZBS_JSONL:<id>:<rows> instead of reading the file and writing a new one. Skips the per-record encode/decode pass on line transitions where no record actually changed (typically the _make_batches step with batchsize=NULL between streaming workers).
  • For a 25M-row / 18 GB CSV the line→line transition used to rewrite a 29 GB JSONL — now it's a no-op. End-to-end run time drops from ~14 min (test timed out) to 10.6 min (test passes).
  • Side-fix: _log_process_result now propagates zbs_current_batch_id into the env that calls dump_data, so streaming JSONL attachments produced during worker output get tied to the current batch (cascade cleanup via keep_instances_for_days) instead of being orphaned in the filestore.
  • New abstract helper zbs.attachment.helper that streams arbitrary byte sources into an ir.attachment without holding the full payload in RAM: chunks are appended to a temp file in the filestore, sha1 is computed on the fly, then the file is atomically moved into the canonical <sha>[:2]/<sha> layout and an ir.attachment record is created with store_fname set directly.
  • Source-grabbers can now emit _source_attachment_id in their output records when input exceeds zsync.streaming_threshold_bytes (default 10 MB) and a batch context is available. The attachment is tied to the current batch via res_model='zbs.instance.line.batch' so it gets cleaned up together with the batch (existing keep_instances_for_days cron).
  • Worker context now carries zbs_current_batch_id so workers can resolve their owning batch via the helper's _current_batch() without changing the worker API signature.

zbsync_ai_code 1.0 → 1.1

  • New module: AI-powered code generation and automation for ZebrooSync.
  • AI providers
  • Configurable AI provider records (OpenAI, Anthropic, Google Gemini) with API key storage and a default selection; unified single- and multi-turn call interface across all providers.
  • Auto-mapping wizard
  • "Map" button on the mapper form analyses source/destination backend schemas and suggests field mappings (with action type and optional eval code).
  • Supports bulk-selecting existing mappings for a targeted re-suggestion.
  • Code generation wizard
  • "Generate Code" button on mapping and Python-transformer records.
  • Accepts a natural-language instruction and returns ready-to-use Python code for eval expressions or full transformer worker bodies.
  • Pipeline creator wizard
  • "Create Pipeline" buttons on the pipeline list and form header.
  • Builds a complete multi-step ETL pipeline (workers, types, field values) from a plain-text task description.
  • Error analysis wizard
  • "Analyse" button on batch execution records.
  • Sends the error message, traceback and failed-record context to the AI and returns a plain-language root-cause explanation with fix suggestions.
  • AI chat assistant
  • Floating chat panel (bottom-right, available across the whole Odoo UI) for ZebrooSync / Odoo / data-integration questions.
  • Dependency change
  • Removed zbsync_mapper_import dependency; its functionality is now part of zbsync_mapper.

zbsync_excel_csv 1.2 → 1.3

  • _read_csv is now a generator: it yields one dict per row instead of materialising the records into a list. The surrounding with on _open_record_stream stays open for the lifetime of the generator because Python keeps the function frame alive until the generator is exhausted, so the file handle is closed deterministically after the consumer (typically dump_data writing JSONL) is done.
  • This unlocks end-to-end streaming with the new JSONL-output pipeline introduced in zbsync — _process_worker_base now propagates the generator straight through to dump_data without ever materialising the records.
  • CSV and Excel parsers now consume attachment-backed source records via zbs.grabber_dumper._open_record_stream: the file is read directly from the filestore line-by-line (csv.reader(open(path)) / openpyxl.load_workbook(stream, read_only=True)) instead of loading the full payload into RAM through _extract_content.
  • Inline content-style records keep working — the new helper falls back to BytesIO/StringIO transparently.
  • Pairs with the streaming source grabber changes in zbsync / zbsync_files / zbsync_ftp. End-to-end memory footprint for medium- sized CSVs (low-tens of MB) drops to the records-list materialisation cost only; the file content no longer doubles in RAM.

zbsync_files 1.3 → 1.4

  • zbs.grabber.file now streams files above the size threshold into an ir.attachment instead of loading them into RAM via read_text/read_bytes.
  • Output records carry _source_attachment_id (the attachment id) plus filename, filepath and file_size; downstream parsers must read the attachment from the filestore (the parser-side streaming lands in a follow-up patch).
  • Small files (< zsync.streaming_threshold_bytes, default 10 MB) keep emitting inline content — backward compatible.

zbsync_ftp 1.3 → 1.4

  • zbs.connection.ftp gets a new download_file_streamed(filename) context manager that streams a remote file via retrbinary / SFTP.get into a local temp file and yields its path — no full RAM materialisation. The legacy download_file() is kept as a thin wrapper for backward compat.
  • zbs.grabber.file.ftp uses this and, when the downloaded file exceeds the streaming threshold, streams the temp file into an ir.attachment via zbs.attachment.helper and emits _source_attachment_id in the output record. Below the threshold the legacy inline content path is unchanged.

zbsync_mapper 1.2 → 2.0

  • Mapper UX preview is rendered via textContent instead of innerHTML so external sample data can no longer be interpreted as HTML (XSS).
  • Merged zbsync_mapper_import and zbsync_mapper_ux into zbsync_mapper.
  • Full backend schema system, live preview, and mapping improvements.
  • Module merge
  • zbsync_mapper_import and zbsync_mapper_ux are retired; all their functionality is now part of zbsync_mapper.
  • Update any module that depended on zbsync_mapper_import or zbsync_mapper_ux to depend on zbsync_mapper instead.
  • Backend schema system
  • New zbs.mapper.backend model: describes input/output data structures as Empty, Backend Model (JSON/YAML/XML), or Odoo Model (introspected).
  • New zbs.mapper.backend.field: hierarchical field tree with lazy loading for Odoo models; auto-added to backend JSON on creation.
  • New zbs.mapper.backend.category: hierarchical category tree for organising backends.
  • Mappers gain input_backend_id / output_backend_id (external schemas) and input_odoo_model_id / output_odoo_model_id (Odoo sources/targets).
  • integration_type field: ext_to_odoo, odoo_to_ext, ext_to_ext, odoo_to_odoo — replaces mapping_direction.
  • field_source_id / field_dest_id M2O pickers on mappings for selecting backend fields with path breadcrumb display.
  • action_generate_from_legacy() migrates raw dot-notation mappings to the structured backend system automatically.
  • Mapping improvements
  • force_name is now a computed recursive field showing the full ancestor path of a mapping.
  • New code field replaces format_value for eval expressions; migration 1.2 converts existing format_value values automatically.
  • New action list_object combines list and object in one step.
  • skip_record_expr: expression to abort processing the entire record.
  • output_stream: routes a mapped value to a named env stream instead of the main record.
  • has_duplicate_dest: computed warning when two mappings write the same destination field.
  • action_sort_mappings() reorders mappings alphabetically by source/dest.
  • List/object mappings now merge into existing values instead of overwriting.
  • Live UX preview (from zbsync_mapper_ux)
  • ux_input_sample, ux_input_path, ux_input_mapped, ux_output_mapped, ux_preview fields for real-time mapping preview in the form.
  • Drag-and-drop resequencing with automatic DFS resequencing of children.
  • Embedded mapping list widget with search bar.
  • Breaking changes
  • mapping_direction field removed — use integration_type instead.
  • format_value field removed — migration converts to code automatically.
  • Mappers using raw field names must run action_generate_from_legacy() to use the new backend field picker system.
  • Modules depending on zbsync_mapper_import or zbsync_mapper_ux must update their dependencies to zbsync_mapper.

:::warning Breaking changes

  • Merged zbsync_mapper_import and zbsync_mapper_ux into zbsync_mapper.
  • Full backend schema system, live preview, and mapping improvements.
  • Module merge
  • zbsync_mapper_import and zbsync_mapper_ux are retired; all their functionality is now part of zbsync_mapper.
  • Update any module that depended on zbsync_mapper_import or zbsync_mapper_ux to depend on zbsync_mapper instead.
  • Backend schema system
  • New zbs.mapper.backend model: describes input/output data structures as Empty, Backend Model (JSON/YAML/XML), or Odoo Model (introspected).
  • New zbs.mapper.backend.field: hierarchical field tree with lazy loading for Odoo models; auto-added to backend JSON on creation.
  • New zbs.mapper.backend.category: hierarchical category tree for organising backends.
  • Mappers gain input_backend_id / output_backend_id (external schemas) and input_odoo_model_id / output_odoo_model_id (Odoo sources/targets).
  • integration_type field: ext_to_odoo, odoo_to_ext, ext_to_ext, odoo_to_odoo — replaces mapping_direction.
  • field_source_id / field_dest_id M2O pickers on mappings for selecting backend fields with path breadcrumb display.
  • action_generate_from_legacy() migrates raw dot-notation mappings to the structured backend system automatically.
  • Mapping improvements
  • force_name is now a computed recursive field showing the full ancestor path of a mapping.
  • New code field replaces format_value for eval expressions; migration 1.2 converts existing format_value values automatically.
  • New action list_object combines list and object in one step.
  • skip_record_expr: expression to abort processing the entire record.
  • output_stream: routes a mapped value to a named env stream instead of the main record.
  • has_duplicate_dest: computed warning when two mappings write the same destination field.
  • action_sort_mappings() reorders mappings alphabetically by source/dest.
  • List/object mappings now merge into existing values instead of overwriting.
  • Live UX preview (from zbsync_mapper_ux)
  • ux_input_sample, ux_input_path, ux_input_mapped, ux_output_mapped, ux_preview fields for real-time mapping preview in the form.
  • Drag-and-drop resequencing with automatic DFS resequencing of children.
  • Embedded mapping list widget with search bar.
  • Breaking changes
  • mapping_direction field removed — use integration_type instead.
  • format_value field removed — migration converts to code automatically.
  • Mappers using raw field names must run action_generate_from_legacy() to use the new backend field picker system.
  • Modules depending on zbsync_mapper_import or zbsync_mapper_ux must update their dependencies to zbsync_mapper.

:::

zbsync_marketplace 1.5 → 1.6

  • Odoo 19 compatibility: the marketplace item version uniqueness constraint moved from _sql_constraints (ignored on Odoo 19) to models.Constraint.
  • Restore the worker "Upload to Marketplace" header button (server-action bindings on the abstract zbs.worker model never show up on the concrete workers); restrict the remaining marketplace server actions to form views so multi-select cannot hit ensure_one(); complete the @api.depends of the vault_url computes; drop the invalid password=True field parameter on the connection token field.
  • Upload API refactor and button migration to server actions.
  • Upload calls now use marketplace_id.connection_id.upload({...}) directly instead of manually serialising to JSON and calling the legacy RPC path.
  • Removed the unused json import from the upload wizard.
  • Upload buttons migrated from inline form headers to ir.actions.server bindings, giving better permission and action-menu control.

zbsync_marketplace_mapper 1.1 → 1.2

  • Upload API refactor, consistent with zbsync_marketplace.
  • Upload calls now use marketplace_id.connection_id.upload({...}) directly instead of manually serialising to JSON and calling the legacy RPC path.
  • Removed the unused json import from the mapper upload wizard.

zbsync_marketplace_transformer_helper 1.0 → 1.1

  • Extensible button bar and UI layout improvements.
  • Extra buttons can now be injected via the zbs_helper_action_buttons registry category; each entry needs a label and a data-method attribute that maps to an Odoo method called on the current record.
  • "Find in Code Collection" label shortened to "Find" to save space.
  • Button bar switched to flexbox (d-flex); secondary buttons stay left, "Save To Code Collection" moves to the right (ms-auto).

zbsync_odoo 1.4 → 2.0

  • Grabber / Dumper improvements and domain builder.
  • Domain builder
  • New editable domain field widget (domain_editable) — build Odoo domains visually or as raw text with support for runtime Python expressions ($record.name, uid, etc.).
  • limit and offset moved from the grabber to individual domain records, so different limits can be set per domain configuration.
  • Domain form shows the interactive builder for local connections and readonly text for external connections.
  • Model selection
  • New model_id Many2one (ir.model) on grabbers and dumpers — provides a dropdown picker for local connections instead of manual name entry.
  • model_id is auto-populated by migration for existing local-connection workers that already have a model name set.
  • New grabber options
  • browse_as_list toggle: returns each record individually to downstream workers instead of as a recordset (visible only in browse format mode).
  • Default output format changed to browse; default connection defaults to the local Odoo instance.
  • New dumper options
  • do_insert / do_update toggles control whether the dumper creates new records, updates existing ones, or both (both default to true).
  • only_new_values field now includes help text explaining it skips unchanged fields to avoid unnecessary write bumps.
  • connection_id now defaults to the local connection on new dumpers.
  • Connection defaults
  • SSL enabled by default on new connections.
  • Default port changed to 443 (HTTPS).
  • External API
  • New external_fields_get() method on connections — lets clients fetch field metadata from remote Odoo instances (intended for the domain builder on external connections).
  • Migrations (2.0)
  • Migrates limit/offset values from grabber records to their domain records. Workers with a non-zero limit but no domain record will log a warning to add one manually.
  • Resets browse_as_list to False on existing grabbers so they keep the legacy recordset behaviour.
  • Auto-populates model_id on existing local-connection workers.
  • BREAKING
  • limit/offset fields were removed from zbs.grabber.odoo (moved to the domain records); external API users and old pipeline exports that reference them must switch to the domain-level fields.
  • browse_as_list changes the grabber output shape when enabled; existing grabbers are kept on the legacy behaviour by the migration. Re-importing an old pipeline export creates the grabber with the new default (browse_as_list=True) — disable the toggle manually if the legacy recordset shape is required.

:::warning Breaking changes

  • Grabber / Dumper improvements and domain builder.
  • Domain builder
  • New editable domain field widget (domain_editable) — build Odoo domains visually or as raw text with support for runtime Python expressions ($record.name, uid, etc.).
  • limit and offset moved from the grabber to individual domain records, so different limits can be set per domain configuration.
  • Domain form shows the interactive builder for local connections and readonly text for external connections.
  • Model selection
  • New model_id Many2one (ir.model) on grabbers and dumpers — provides a dropdown picker for local connections instead of manual name entry.
  • model_id is auto-populated by migration for existing local-connection workers that already have a model name set.
  • New grabber options
  • browse_as_list toggle: returns each record individually to downstream workers instead of as a recordset (visible only in browse format mode).
  • Default output format changed to browse; default connection defaults to the local Odoo instance.
  • New dumper options
  • do_insert / do_update toggles control whether the dumper creates new records, updates existing ones, or both (both default to true).
  • only_new_values field now includes help text explaining it skips unchanged fields to avoid unnecessary write bumps.
  • connection_id now defaults to the local connection on new dumpers.
  • Connection defaults
  • SSL enabled by default on new connections.
  • Default port changed to 443 (HTTPS).
  • External API
  • New external_fields_get() method on connections — lets clients fetch field metadata from remote Odoo instances (intended for the domain builder on external connections).
  • Migrations (2.0)
  • Migrates limit/offset values from grabber records to their domain records. Workers with a non-zero limit but no domain record will log a warning to add one manually.
  • Resets browse_as_list to False on existing grabbers so they keep the legacy recordset behaviour.
  • Auto-populates model_id on existing local-connection workers.
  • BREAKING
  • limit/offset fields were removed from zbs.grabber.odoo (moved to the domain records); external API users and old pipeline exports that reference them must switch to the domain-level fields.
  • browse_as_list changes the grabber output shape when enabled; existing grabbers are kept on the legacy behaviour by the migration. Re-importing an old pipeline export creates the grabber with the new default (browse_as_list=True) — disable the toggle manually if the legacy recordset shape is required.

:::

zbsync_odoo_flows 1.1 → 1.2

  • Odoo 19 compatibility: custom-data representation uses display_name instead of the removed name_get() (was crashing the pipeline-widget preview with AttributeError).

zbsync_odoo_mapper 1.1 → 2.0

  • External Odoo model sync and dropdown model selection.
  • New model_external_id Many2one field on OdooGrabber and OdooDumper for selecting models from the synced model list of an external connection instead of typing the model name manually.
  • "Sync Models" button on the connection form fetches and stores the available models from the remote Odoo instance.
  • zbs.backend.model extended with odoo_connection_id and odoo_model fields to track which models are available on each connection.
  • Post-migrate script auto-populates model_external_id for existing grabbers/dumpers by syncing ir.model from their connections.
  • Breaking change
  • Manual model name text entry is no longer available for external connections; models must be synced and selected from the dropdown.

:::warning Breaking changes

  • External Odoo model sync and dropdown model selection.
  • New model_external_id Many2one field on OdooGrabber and OdooDumper for selecting models from the synced model list of an external connection instead of typing the model name manually.
  • "Sync Models" button on the connection form fetches and stores the available models from the remote Odoo instance.
  • zbs.backend.model extended with odoo_connection_id and odoo_model fields to track which models are available on each connection.
  • Post-migrate script auto-populates model_external_id for existing grabbers/dumpers by syncing ir.model from their connections.
  • Breaking change
  • Manual model name text entry is no longer available for external connections; models must be synced and selected from the dropdown.

:::

zbsync_pipeline_widget 2.1 → 2.2

  • Preview popover support and chart page layout.
  • onShowPreview callback added to the pipeline instance flow visualisation so batch rows can trigger a preview popover.
  • Node height calculation updated to account for the preview button row when batches are present.
  • Chart renderer moved from an inline group to a dedicated "Chart" notebook page, shown before other pages.

zbsync_pipeline_widget_chart_preview 1.0 → 1.1

  • New module: chart preview popover for pipeline batch rows.
  • Patches the pipeline renderer to add a preview popover that shows paginated input/output data from a batch execution. Uses zbs.instance.line.get_popover_texts / get_popover_field for data retrieval, with load-more support and per-field scrolling via the Odoo popover service.

zbsync_sql 1.2 → 1.3

  • SQL-Dumper: mehrere Fehler behoben, durch die Daten nicht oder falsch geschrieben wurden.
    1. MSSQL schrieb gar nicht: die Transaktion wurde nie committed (verschachtelte BEGIN/COMMIT TRANSACTION -> @@TRANCOUNT erreichte nie 0 -> beim Schliessen der Verbindung wurde alles zurueckgerollt). Die Transaktionssteuerung liegt jetzt beim DB-Treiber (commit/rollback), es werden keine manuellen BEGIN/COMMIT/ROLLBACK-Statements mehr abgesetzt.
  • Faellt ein Schreibvorgang mitten im Lauf fehl, wird sauber zurueckgerollt und die urspruengliche Fehlermeldung bleibt sichtbar.
    1. Tabellen mit mehreren Schluesselspalten (zusammengesetzter Key): beim Suchen/Aktualisieren/Loeschen wurde nur die ERSTE Schluesselspalte beruecksichtigt -> es konnten falsche Zeilen aktualisiert/geloescht werden. Die WHERE-Bedingung wird jetzt korrekt ueber alle Schluessel gebildet.
    1. Option "Recreate record": der bestehende Datensatz wurde geloescht, aber danach NICHT neu angelegt (Datenverlust). Jetzt wird nach dem Loeschen korrekt neu eingefuegt.
    1. Der Plain-SQL-Worker (zbs.plain.sql) war in der Standard-Einstellung (nicht "pro Datensatz") nicht lauffaehig und lief mit ungerendertem SQL; ausserdem gab es doppelte Modelldefinitionen. Bereinigt und lauffaehig gemacht.
  • Zum Testen (Menue zSYNC -> Connections/Dumper):
  • MSSQL-Dumper auf eine Zieltabelle laufen lassen und in der Ziel-DB pruefen, dass die Zeilen jetzt ankommen (Postgres verhaelt sich unveraendert).
  • Eine Zieltabelle mit zusammengesetztem Schluessel (2+ Key-Spalten) updaten und pruefen, dass genau die richtige Zeile getroffen wird.
  • "Recreate record" aktivieren, bestehenden Datensatz erneut dumpen und pruefen, dass er danach noch existiert (neu angelegt).
  • Hinweis: Das Feld "Use Transaction" schaltet nun Autocommit (aus = jede Anweisung committet sofort). Die Felder Begin/Commit/Rollback Transaction haben aktuell keine Wirkung mehr.
  • New streaming flag on zbs.grabber.sql: when set, rows are yielded one at a time via the connection's new iter_records generator and the worker output flows straight into the JSONL output_data path (PR 3 in zbsync). The whole result set never sits in RAM, regardless of how many millions of rows the query returns.
  • On Postgres iter_records opens a server-side (named) cursor so the database itself doesn't materialise the result either — true end-to-end streaming. Other engines fall back to client-side fetchmany-loop streaming.
  • Covered by the new Big DB pipeline must stream through to success test case in test_bigdata_limits.robot: a local Postgres table is populated via generate_series and pulled through the pipeline under the same 1 GB worker heap budget the file-based tests use.

zbsync_transformer_helper 1.1 → 1.2

  • Code Helpers group is now shown only when the mapping action is eval.
  • Simplified XML structure and visibility rules.

zbsync_trigger_methods 1.2 → 1.3

  • Add deferred-reference queue tests (test_import_queue) to zbsync_trigger_methods where zbs.pipeline and zbs.trigger.methodhook are both available.
  • Host the deferred-import-queue tests (moved from zbsync_xport, whose at_install run lacked zbs.pipeline / zbs.trigger.methodhook in the registry) and extend them: x2many resolver linking, zync_uuid=None guard, unresolvable use_existing list elements.

zbsync_uibinding 1.1 → 1.2

  • instance_env added to button action eval context.
  • Button actions now receive instance_env (the parsed JSON of pipeline_instance.environment) alongside the existing pipeline_instance variable.

zbsync_web 1.3 → 1.4

  • Content-type defaults, GraphQL removal, and view cleanup.
  • request_content_type now defaults to "application/json".
  • response_content_type now defaults to "json".
  • method now defaults to "get".
  • GraphQL Query removed from request content type choices.
  • Help text and examples in the web worker view condensed and reformatted with proper code tags.
  • Odoo 19 compatibility: zbs.connection.web.auth replaced the removed name_get() override with _compute_display_name.
  • New streaming flag on zbs.web.worker. When set, the response body is fetched with requests.get(..., stream=True) and piped chunk-by- chunk into an ir.attachment via the chunked filestore helper — response.content / response.json() / ElementTree.fromstring never touch the bytes. The output record carries _source_attachment_id (and filename/file_size), the same shape the streaming file-grabber emits, so a downstream CSV / XML parser consumes it through the existing attachment path.
  • Pagination, extract_path and response_content_type branches are bypassed in this mode — streaming is a single GET, the file lands in the filestore, and downstream workers take it from there.
  • Covered by the new Big Web pipeline must stream through to success test case in test_bigdata_limits.robot: nginx is spun up in a sibling container serving big.csv, the pipeline pulls it via HTTP and runs it through the same CSV parser the file-based test uses.

zbsync_web_oauth 1.0 → 1.1

  • Fix cryptic "can only concatenate str (not 'bool') to str" error when an OAuth2 web auth is run without an application type selected.
  • When the "Application Type" (Mobile / Backend / Legacy) was left empty on an OAuth2 connection auth, the code did getattr(self, "_" + self.application_type) with application_type == False, raising a TypeError during the sync run.
  • Tester: open the connection -> Auth tab, set Auth Type = OAuth2 but leave "Application Type" empty. Saving now raises a clear validation error asking you to pick an application type, and running a batch no longer crashes with the TypeError - it tells you exactly what to configure.
  • Also fixes an AttributeError ("object has no attribute 'scopes'") when using Application Type = Mobile: the OAuth2 Mobile flow referenced a non-existent scopes field instead of scope. The Scope field is now read correctly (comma-separated), consistent with the Backend flow.
  • Add an "Allow insecure transport (http)" option to OAuth2 connection auths. oauthlib refuses OAuth2 over plain http and raises "InsecureTransportError: OAuth 2 MUST utilize https". This blocked syncs against local/dev endpoints that are only reachable via http.
  • Tester: open the connection -> Auth tab (Auth Type = OAuth2). There is a new checkbox "Allow insecure transport (http)". Leave it off for production (https). Only tick it when the token/authorization URL is a local/dev http endpoint - the sync then no longer aborts with the InsecureTransportError.

zbsync_xml 1.1 → 1.2

  • New stream2records ttype on zbs.transformer.xml: parses XML via lxml.etree.iterparse and yields one dict per <root_element_name> element. Generator-based, so it pairs with the JSONL-output streaming in zbsync (PR 3) — record subtree is .clear()-ed after each yield, and preceding siblings are dropped so the in-memory document never grows beyond a single record.
  • When the input record carries _source_attachment_id (streaming file-grabber), the XML file is opened directly from the filestore; otherwise the helper wraps inline content in BytesIO. Same dual-path shape as the CSV parser.
  • This lets a flat <root><record>...</record></root> document of effectively any size stream through the pipeline under a tight worker heap budget — covered by the new test case Big XML pipeline must stream through to success in test_bigdata_limits.

zbsync_xport 1.4 → 1.5

  • Fix zync_uuid being overwritten on every create(): the guard checked vals_list (always truthy) instead of val, causing imported records to get a fresh UUID and then be deleted by _import_reconcile_o2m.
  • Odoo 19 compatibility fixes.
  • export/import by_name now uses display_name instead of the removed name_get() (was crashing with AttributeError on Odoo 19).
  • zync_uuid uniqueness migrated from _sql_constraints to models.Constraint so the DB constraint is actually created on Odoo 19.
  • Import robustness fixes from the 5-way review of the marketplace branch: deferred use_existing elements inside x2many lists are queued per element and linked by the resolver instead of silently dropping the whole field; the resolver refuses to match records via an empty zync_uuid; the self-reference skip only applies to the actual o2m inverse field; _import_reconcile_o2m keeps records created by the import itself and records referenced via use_existing, and an empty o2m list now removes all previous sub-records; password/token-like field values are excluded from the import change-log.

zbsync_xport_ui 1.7 → 1.8

  • Fix AttributeError in import wizard when change_log was non-empty: ok() called _format_change_log() but the method is named _format_change_log_html().
  • Pre-import Odoo model reference validation.
  • Before completing a pipeline import, the importer now scans all worker configs for unresolved Odoo model references (xmlid or by_name lookups) and blocks the import with a clear error message listing the missing models. Implemented via _collect_missing_odoo_model_refs() and _walk_odoo_model_refs().
  • Import wizard fixes: ok() no longer crashes with AttributeError when a change-log exists (undefined _format_change_log); error/warning detection is based on the raw log messages instead of the rendered HTML (field diffs containing "ERROR" no longer abort the import); the sequence value from the import JSON is HTML-escaped (XSS); the log field is readonly and the result HTML is wrapped in a scrollable container.
  • Regressionstest für ZO-05230: ein Import ohne "Start"/"Stop"-Schritte (wie er beim Marketplace-Download entsteht) muss "Start" wieder an erste und "Stop" an letzte Position setzen.

2026-06-08

zbsync_xport_ui 1.6 → 1.7

  • Import wizard log field upgraded to fields.Html with rich diff view: collapsible unified diffs (dark theme) for code/text changes, inline old→new for short values, color-coded action badges. log field changed from Text to Html (sanitize=False).

zbsync_xport 1.3 → 1.4

  • load() tracks import changes via _xport_change_log context list: created/updated/deleted workers and scalar field diffs.
  • Helpers: _xport_log_change, _xport_record_label, _xport_scalar_changes.

zbsync_xport_ui 1.5 → 1.6

  • Import wizard now shows a structured change-log after import: which pipeline steps were created/updated/deleted and which fields changed (old → new value). Changes are also posted as a chatter message on the pipeline.

zbsync_xport 1.2 → 1.3

  • load() now deletes one2many sub-records (e.g. pipeline steps) whose zync_uuid is absent from the import JSON, so removing a step from an export file removes it from the existing pipeline on re-import.

zbsync_xport_ui 1.4 → 1.5

  • Added import/export scenario tests for pipelines with Python worker and mapper: reset changes, apply JSON changes, delete step, add new step.

zbsync 1.12 → 1.13

  • zbs.trigger.pipeline_id gets xport_ignore=True so pipeline exports no longer produce a circular use_existing self-reference for trigger.pipeline_id.

zbsync_trigger_methods 1.1 → 1.2

  • Added regression tests for ZO-04788: pipeline import with method_trigger_ids containing a use_existing self-reference for pipeline_id.

zbsync_xport 1.1 → 1.2

  • Fixed KeyError when importing pipelines whose method_trigger_ids contain a use_existing self-reference (trigger.pipeline_id → the pipeline being imported).
  • Introduced DeferredReference exception and _import_resolve_deferred() queue that retries unresolved references after the record is created.

2026-06-04

zbsync 1.11 → 1.12

  • Pipeline grouping by tags is now sorted alphabetically again, version-safe across Odoo 17 (read_group) and 18+/19 (formatted_read_group), honouring an explicit descending order. Fixed pipeline line form fields that were accidentally shown (column_invisible -> invisible), removed a duplicate Zebroo-Sync privilege record, and hardened the recordset merge against a dict/list mismatch crash.

zbsync_marketplace 1.4 → 1.5

  • Fixed the Python-worker marketplace upload that crashed after the connection API migration (now uses connection.upload). The catalog finder renders descriptions from the sanitized field only (XSS fix), the migration folder version was corrected so it actually runs on upgrade, and catalog refresh now whitelists the fields it creates.

zbsync_marketplace_mapper 1.0 → 1.1

  • Fixed the mapper marketplace upload/download round-trip: it now uses the current connection API, the python_code item type, and a content shape that matches the download side, so uploaded mappers can be found and downloaded again.

zbsync_odoo_flows 1.0 → 1.1

  • Odoo 19 compatibility: declare the _inherits reference field (item_id) on of.zync.pipeline with delegate=True so the registry loads.

zbsync_transformer_helper 1.0 → 1.1

  • The helper export route (/zync/transformer_helper/download) is now restricted to Zebroo-Sync managers and validates the requested ids instead of raising a server error on bad input, matching the pipeline export routes.

zbsync_xport_ui 1.3 → 1.4

  • Pipeline export routes (/zync/download and /zync/download_pipeline_xml) are now restricted to Zebroo-Sync managers and validate the requested id instead of raising a server error on bad input.

2026-05-26

zbsync_xml 1.0 → 1.1

  • Add optional XSLT field on the XML worker. When set, the input XML is transformed via the stylesheet before being parsed (xml2dict / lxml2dict).

2026-05-21

zbsync 1.10 → 1.11

  • Configuration: new "Python-Steps" menu listing all zbs.transformer.python records (analog to Connections/Grabbers/Dumpers).
  • zbs.cronstart: optional zbs_environment_id field. When set, the cronjob starts the pipelines with that environment (explicit zbs_environment kwarg still wins). Shown in the form view; hidden-by-default column on the list.

2026-05-19

zbsync_pipeline_widget 2.0 → 2.1

  • test_add_worker_edge: rename worker arg from "Demo-Data" to "Demo Data" (f1a42141e renamed the zbs.const description). Test-only.

zbsync_odoo 1.3 → 1.4

  • Robot tests + dumper.robot.template: switch from "Odoo Button text=..." (OWL chart) to the new "Zync Open Worker From Chart" helper which finds workers on the Svelte chart by title and clicks the hover-revealed edit button. ZO-04608 Chart UX. Test-only.

zbsync_ftp 1.2 → 1.3

  • Robot tests: adapt worker keyword + worker name strings to the ZO-04608 Chart UX rewrite (Svelte chart, worker renames). Test-only.

zbsync_odoo 1.2 → 1.3

  • Robot tests: adapt worker keyword + worker name strings to the ZO-04608 Chart UX rewrite (Svelte chart, worker renames). Test-only.

zbsync_odoo_mapper 1.0 → 1.1

  • Robot tests: adapt worker keyword + worker name strings to the ZO-04608 Chart UX rewrite (Svelte chart, worker renames). Test-only.

zbsync_xport_ui 1.2 → 1.3

  • Replace web_diffview HTML diff with a native Owl pipeline_diff widget.
  • Per-step collapsible field-level diff with smart matching (zync_uuid + sequence/worker fallback), nested-dict recursion, and noise-key skipping.
  • Wizard closes on warn-only imports and posts warnings to the pipeline chatter; errors still keep it open.

zbsync 1.9 → 1.10

  • Bugfixes in zebrooset (Recordset-Wrapper):
  • B1: _Dive int-index on raw dict crashed in Python 3 (dict_keys not subscriptable).
  • B2: _loads now actually uses the _prepare_load hook result (was discarded).
  • B3: Decoder no longer crashes on unknown _type markers; mirrors Encoder try/except.
  • B4: _recursive_dict_merge handles multi-element lists instead of crashing.
  • B5/B7/B8: dead code removed in _WrappedDict._iterate_records, Decoder, and check_setattr_forbidden.
  • B6: _merge return value handled consistently across ZebrooSet/_WrappedDict/_WrappedList.
  • Plus regression tests for B1-B4.

zbsync 1.8 → 1.9

  • Demo data: rename groups_id to group_ids on the res.users record for the admin assignment to zbsync.group_manager. Odoo 19 renamed the Many2many field; without this, admin no longer gets the manager group in demo mode, which broke robotests requiring manager-level access.

zbsync_pipeline_widget 1.0 → 2.0

  • Replaces the old pipeline/instance charts completely with custom Svelte charts.
  • Creates a clear visiual differenciation between pipeline and instance chart
  • Allows adding workers directly in place: with a + button between existing nodes
  • Adds drag-drop reordering of workers
  • Adds deleting & disabling of workers
  • Adds a preview of the worker comment/documentation
  • Adds clear edit & line detail buttons: instead of clicking different chart sections

2026-05-13

zbsync 1.7 → 1.8

  • Pipeline overview:
  • Several Workers renamed
  • Description added to pipelines
  • Tabs restructured, icons added
  • Workers renamed to Steps. Columns tidied up. Line details accessible via icon at the start of the row now. Technical name not visible by default, only by selection.
  • Triggers grouped and renames as Automation
  • Description added to Versioning, and auto-population with version number
  • Details renamed to Advanced.
  • Module XML removed and replaced by a download button in the Versioning tab Pipeline list view:
  • sorting added for pipelines and tags
  • additional columns included
  • existing columns made optional Menus
  • Mapper menu integrated into Configuration
  • Worker Components section added to Configuration (to be further populated later)

2026-05-12

zbsync_marketplace 1.2 → 1.3

  • Fix wiz_upload_python view referencing fields (uniqueid, changelog) that were never added to the zbs.marketplace.upload.python model. Restored previous working view so module installs again.

2026-05-04

zbsync 1.6 → 1.7

  • Fix stuck instances: cron_heartbeat now recovers instances where all batches are done but state is still running

zbsync 1.5 → 1.6

  • Fix stuck instances: cron_heartbeat now recovers instances where all batches are done but state is still running

2026-04-30

zbsync_marketplace 1.1 → 1.2

  • Remove live-fetch from onchange handlers; use local version cache and local content in make_pipeline

2026-04-29

zbsync_xport_ui 1.0 → 1.1

  • Fix trailing comma in _remove_uuids causing json.loads failure on Python 3.13

zbsync_odoo 1.1 → 1.2

  • Fix "Cursor already closed" error in _get_obj: removed ormcache so each batch gets a fresh env[model] bound to its own cursor instead of a stale closed one.

2026-04-27

zbsync_receiver 1.0 → 1.1

  • Receiver: log pipeline run exception via _logger.exception for diagnostics

zbsync 1.4 → 1.5

  • Populate zbs.encoding with all Python-supported character encodings (101 total) Add all Python-supported encodings to encoding list Add uuid to default objects available in expressions Fix continue_immediately: enqueue batches via HTTP immediately instead of waiting for cron pusher, and read continue_immediately flag before advancing to next pipeline line.

zbsync_excel_csv 1.1 → 1.2

  • Add test for CSV import with latin1 encoding Add unit test for importing ISO-8859-1 encoded CSV files

zbsync_files 1.2 → 1.3

  • File dumper now uses _eval_expression for filepath, making datetime, uuid etc. available

zbsync_ftp 1.1 → 1.2

  • FTP dumper uses _eval_expression for filepath, fix pwd() bug in FTP connection, auto-mkdir before upload, add robot test Fix FTP connection pwd call and use eval_expression for filepath in dumper

zbsync_marketplace 1.0 → 1.1

  • Fix missing api import in choose_worker.py

zbsync_sql 1.1 → 1.2

  • Fix pytds compatibility: use dsn instead of host, move as_dict to connect()

zbsync_uibinding 1.0 → 1.1

  • Fix zync UI button in tree/list view to handle multiple selected records.
  • Add batch_mode option: when enabled, a single pipeline is started with all selected records instead of one pipeline per record.

zbsync_webdav 1.1 → 1.2

  • Use eval_expression for local_path in WebDAV grabber

2026-03-31

zbsync_files 1.1 → 1.2

  • Fix destination_path overwrite in after_commit causing cascading _001_002_003 suffixes

zbsync_ftp 1.0 → 1.1

  • Fix off-by-one error in grabber file limit — processed one file too few

zbsync_webdav 1.0 → 1.1

  • Fix off-by-one error in grabber file limit — processed one file too few

2025-05

  • switching from outdated pymssql to python-tds - compatibility with debian packages

2025-04

  • BREAKING: renamed following functions to just "_as_python_object()" in zebrooset from

    • as_json
    • _as_python_type
    • _as_plain_python
    • _get_python_type

    Affected: perhaps local created python transformers / code snippets

2025-03

  • BREAKING: file grabber: just one expression for folders; recursive expression is extra

2024-07

  • BREAKING: button after success turned from json to python
  • BREAKING: fileupload dialog and buttons - delete existing data

2024-06

  • change the code in cronjobs to start with .start() --> .start(data=None, manual=False)
  • BREAKING: xml2dict - result in content

2024-01

  • deleting all mappings: making a json snapshot before
  • BREAKING: mapping: browse objects (local ones or rpc remote objects) of odoo now export many2one fields in direct mappings as int/False instead of lists

2023-10-12

  • BREAKING: Triggers - Web Worker looses data, Triggers are lost!