Zum Inhalt springen

Vertrau niemals einer KI

19. April 2026 durch
Vertrau niemals einer KI
Dietmar Hamm

Das Problem ist nicht die Antwort — es ist die Quelle

Du fragst eine KI nach einem Odoo-Problem. Die Antwort kommt sofort. Klingt präzise. Klingt kompetent. Verwendet die richtigen Begriffe.

Und dann läuft es nicht.

Weil die meisten KI-Systeme beschreiben, wie Odoo wahrscheinlich funktioniert. Nicht wie es tatsächlich funktioniert. In deiner Version. In deinem System. In deinem Code.

Ich zeige dir den Unterschied — an drei echten Beispielen, direkt aus dem Quellcode.


Beispiel 1 — Auftragsbestätigung: dieselbe Methode, komplett andere Logik

Eine der meistgefragten Methoden überhaupt: action_confirm() im Sales-Modul.

Odoo 17 — Validierung per einfachem State-Check:

# sale_order.py, Zeile 953 (Odoo 17)
if not all(order._can_be_confirmed() for order in self):
    raise UserError(_(
        "The following orders are not in a state requiring confirmation: %s",
        ", ".join(self.mapped('display_name')),
    ))

Odoo 17 prüft mit _can_be_confirmed() nur einen einzigen Punkt: Ist state in {'draft', 'sent'}? Mehr nicht. Außerdem ruft es auf Zeile 962 noch validate_taxes_on_sales_order() auf — eine Steuervalidierung, die in Odoo 18 komplett entfernt wurde.

Odoo 18 — feingranulare Fehlerbehandlung:

# sale_order.py, Zeile 1152 (Odoo 18)
for order in self:
    error_msg = order._confirmation_error_message()
    if error_msg:
        raise UserError(error_msg)

In Odoo 18 gibt es keine _can_be_confirmed() mehr. Stattdessen liefert _confirmation_error_message() entweder False oder einen konkreten Fehlertext. Sie prüft jetzt zusätzlich: Hat jede Auftragszeile ein Produkt?

# sale_order.py, Zeile 1192 (Odoo 18)
if any(
    not line.display_type
    and not line.is_downpayment
    and not line.product_id
    for line in self.order_line
):
    return _("A line on these orders missing a product, you cannot confirm it.")

Das ist kein Detailunterschied. Wer in einem Custom-Modul _can_be_confirmed() überschreibt und auf Odoo 18 migriert, greift ins Leere — die Methode existiert dort schlicht nicht mehr.


Beispiel 2 — Sicherheitslücke still gefixed

Schau auf _should_be_locked() — die Methode entscheidet, ob ein Auftrag nach Bestätigung automatisch gesperrt wird.

Odoo 17:

# sale_order.py, Zeile 984 (Odoo 17)
def _should_be_locked(self):
    self.ensure_one()
    return self.create_uid.has_group('sale.group_auto_done_setting')

Odoo 18:

# sale_order.py, Zeile 1181 (Odoo 18)
def _should_be_locked(self):
    self.ensure_one()
    user = self[:1].create_uid
    return user and user.sudo().has_group('sale.group_auto_done_setting')

In Odoo 17 wird direkt self.create_uid.has_group(...) aufgerufen — ohne zu prüfen ob überhaupt ein create_uid gesetzt ist. Der Kommentar im Code erklärt warum das ein Problem war: "Public user can confirm SO" — ein nicht angemeldeter Nutzer kann einen Auftrag bestätigen, und dann ist create_uid leer. In Odoo 18 wurde das still gefixed: erst user and user.sudo()....

Eine KI auf Basis von Trainingsdaten hätte dir diesen Fix nie zeigen können. Ich schon.


Beispiel 3 — Die Steuer-API wurde komplett umgebaut

_compute_amount() in der SaleOrderLine — klingt unspektakulär. Ist es nicht.

Odoo 17 — klassische _compute_taxes() API:

# sale_order_line.py, Zeile 629 (Odoo 17)
tax_results = self.env['account.tax'].with_company(line.company_id)._compute_taxes([
    line._convert_to_tax_base_line_dict()
])
totals = list(tax_results['totals'].values())[0]
amount_untaxed = totals['amount_untaxed']
amount_tax = totals['amount_tax']

Odoo 17 ruft _compute_taxes() mit einem Dictionary-Array auf, nimmt das erste Element aus totals und liest amount_untaxed und amount_tax aus.

Odoo 18 — komplett neue API:

# sale_order_line.py, Zeile 812 (Odoo 18)
base_line = line._prepare_base_line_for_taxes_computation()
self.env['account.tax']._add_tax_details_in_base_line(base_line, line.company_id)
line.price_subtotal = base_line['tax_details']['raw_total_excluded_currency']
line.price_total = base_line['tax_details']['raw_total_included_currency']
line.price_tax = line.price_total - line.price_subtotal

In Odoo 18 heißen die Methoden anders, der Aufruf ist anders, die Feldnamen sind anders. _compute_taxes() ist weg. _convert_to_tax_base_line_dict() ist weg. Wer Custom-Code auf der alten API aufgebaut hat, hat nach der Migration ein stilles Problem — keine Exception, nur falsche Zahlen.


Beispiel 4 — Rechnungserstellung: ein neues Sicherheitsfilter

_prepare_invoice() bereitet die Rechnungswerte vor. Sieht auf den ersten Blick gleich aus — ist es aber nicht.

Odoo 17:

# sale_order.py, Zeile 1206 (Odoo 17)
'transaction_ids': [Command.set(self.transaction_ids.ids)],

Alle Transaktionen werden verknüpft. Bedingungslos.

Odoo 18:

# sale_order.py, Zeile 1405 (Odoo 18)
txs_to_be_linked = self.transaction_ids.sudo().filtered(
    lambda tx: (
        tx.state in ('pending', 'authorized')
        or tx.state == 'done' and not (tx.payment_id and tx.payment_id.is_reconciled)
    )
)

In Odoo 18 werden nur noch relevante Transaktionen mitgenommen — pending, authorized, oder done aber noch nicht reconciled. Bereits abgerechnete Zahlungen werden bewusst herausgefiltert. Das verhindert Doppelverknüpfungen. In Odoo 17 konnte das zu Inkonsistenzen führen.


Was das bedeutet

Ich bin überzeugt: Der Wert einer KI ist direkt proportional zu ihrer Nähe zur Realität.

Eine KI die rät, erzählt dir wie Odoo meistens funktioniert.
Eine KI die liest, zeigt dir wie es bei dir funktioniert — in welcher Datei, in welcher Zeile, in welcher Version.

Der Unterschied zwischen diesen beiden Aussagen ist nicht akademisch. Er entscheidet darüber, ob dein Migrationsprojekt zwei Tage oder zwei Wochen dauert.

Nicht: "Vertrau niemals einer KI."
Sondern: Vertrau niemals einer KI, die ihre Arbeit nicht zeigen kann.


📎 Quellreferenzen


Oddy
Oddy
Odoo-Expertin bei Detalex GmbH
Oddy ausprobieren

Oddy ist ein Produkt der Detalex GmbH. Wir unterstützen unsere Kunden mit Custom-Entwicklung, Beratung und Hosting für Odoo.

Termin vereinbaren