Plugin Directory

Changeset 3738724


Ignore:
Timestamp:
10/10/2026 09:28:56 PM (3 weeks ago)
Author:
dtpm
Message:

Version 1.1.3

Location:
dtpm-ai-sale-assistant-woocommerce
Files:
39 added
7 edited

Legend:

Unmodified
Added
Removed
  • dtpm-ai-sale-assistant-woocommerce/trunk/admin/content-processing-resume.php

    r3735498 r3738724  
    6464 * ki-berater.php bei Plugin-Aktivierung/-Deaktivierung, ruft aber die
    6565 * Funktionen dieser Datei auf - gleiches Prinzip wie admin/availability-sync.php/
    66  * admin/testmonat-check.php. Braucht ki_berater_fetch_sample_products() aus
    67  * admin/category-analysis-page.php - deshalb erst NACH dieser Datei in
    68  * ki-berater.php eingebunden (siehe dort).
     66 * admin/testmonat-check.php. Braucht ki_berater_fetch_content_products_for_category()
     67 * aus admin/content-page.php (Fund 09.10.2026, siehe
     68 * ki_berater_resume_content_processing_for_subcategory() unten fuer die volle
     69 * Begruendung) - deshalb erst NACH dieser Datei in ki-berater.php eingebunden
     70 * (siehe dort).
    6971 */
    7072
    … …  
    216218
    217219/**
     220 * GET /variation-attributes (admin/variation_attributes.py) - die
     221 * bestaetigten RELEVANTEN Variationsattribut-Namen einer Subkategorie,
     222 * dieselbe Berechnung (bestaetigt UND relevant) wie
     223 * fetchRelevantAttributeNames() in js/content.js fuer den Browser-Pfad
     224 * (Fund 09.10.2026, siehe ki_berater_resume_content_processing_for_subcategory()
     225 * unten). Leeres Array bei jedem Fehler (Netzwerk, Nicht-200, kaputtes
     226 * JSON) - dieselbe konservative "zaehlt wie nicht relevant"-Regel wie in
     227 * ki_berater_expand_variable_product_for_content() (admin/content-page.php):
     228 * erzeugt im Zweifel WENIGER statt mehr Varianten-Zeilen, nie mehr.
     229 */
     230function ki_berater_fetch_relevant_attribute_names_for_resume( $subcategory_id, $tenant_id ) {
     231        $response = wp_remote_get(
     232                trailingslashit( KI_BERATER_API_URL ) . 'variation-attributes?subcategory_id=' . rawurlencode( $subcategory_id )
     233                        . '&tenant_id=' . rawurlencode( $tenant_id ),
     234                array(
     235                        'headers' => ki_berater_auth_headers(),
     236                        'timeout' => 20,
     237                )
     238        );
     239
     240        if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) {
     241                return array();
     242        }
     243
     244        $body = json_decode( wp_remote_retrieve_body( $response ), true );
     245        if ( ! is_array( $body ) ) {
     246                return array();
     247        }
     248
     249        $relevant_attributes = array();
     250        foreach ( $body as $entry ) {
     251                if ( ! empty( $entry['bestaetigt'] ) && ! empty( $entry['relevant'] ) ) {
     252                        $relevant_attributes[] = $entry['attribut'];
     253                }
     254        }
     255        return $relevant_attributes;
     256}
     257
     258/**
    218259 * Ermittelt fuer EINE als "angefangen, aber unvollstaendig" erkannte
    219260 * Subkategorie (processed_count > 0, siehe ki_berater_run_content_processing_resume())
    220  * die aktuelle, vollstaendige WooCommerce-Produktliste (gleiches Muster wie
    221  * ki_berater_sync_availability_for_subcategory() in admin/availability-sync.php -
    222  * ueber vaterprodukt_id dedupliziert, -1 = kein Limit), vergleicht sie gegen
     261 * die aktuelle, vollstaendige WooCommerce-Produktliste, vergleicht sie gegen
    223262 * die bereits gespeicherten SKUs (GET /content/state) und stoesst
    224263 * POST /content/process-subcategory NUR fuer die tatsaechlich noch
    … …  
    228267 * still ab (return), gleiches "kein eigener Aufrufer/Fehlerkanal"-Prinzip
    229268 * wie die uebrigen Cron-Callbacks in diesem Plugin.
     269 *
     270 * BUGFIX (Fund 09.10.2026, Abgleich Shopify-Projekt gegen Kern-API): bisher
     271 * ueber ki_berater_fetch_sample_products() (admin/category-analysis-page.php)
     272 * ermittelt - die liefert IMMER genau EINE Zeile je WooCommerce-Vaterprodukt
     273 * mit dessen eigener (oft leerer) SKU/Preis, OHNE jede Varianten-
     274 * Aufschluesselung. Der Browser-Pfad (ki_berater_fetch_content_products_for_category(),
     275 * admin/content-page.php) gruppiert dagegen korrekt nach den RELEVANTEN
     276 * unterscheidenden Attributen (siehe ki_berater_expand_variable_product_for_content()
     277 * dort). Ein Variationsprodukt mit mehreren relevanten Gruppen wurde dadurch
     278 * im stuendlichen Cron faelschlich nur als EIN Eintrag mit der falschen
     279 * SKU/dem falschen Preis (Vaterprodukt statt Variante) gezaehlt/gemeldet -
     280 * jetzt dieselbe Funktion wie der Browser-Pfad, mit denselben bestaetigten
     281 * relevant_attributes (siehe ki_berater_fetch_relevant_attribute_names_for_resume()
     282 * oben). Dedup jetzt ueber sku statt vaterprodukt_id - echte, unterschiedliche
     283 * Varianten-SKUs desselben Vaterprodukts waeren sonst faelschlich als
     284 * "Duplikat" zusammengefallen (gleiche Begruendung wie
     285 * ki_berater_ajax_fetch_content_products() in admin/content-page.php).
    230286 */
    231287function ki_berater_resume_content_processing_for_subcategory( $subcategory_id, $tenant_id, $processed_count ) {
    … …  
    235291        }
    236292
    237         $categories = array_map(
    238                 function ( $category ) {
    239                         return array( 'id' => $category['external_category_id'] );
    240                 },
    241                 $external_categories
    242         );
    243 
    244         $products_per_category = ki_berater_fetch_sample_products( $categories, -1 );
    245 
    246         $products_by_vaterprodukt_id = array();
    247         foreach ( $products_per_category as $products ) {
     293        $relevant_attributes = ki_berater_fetch_relevant_attribute_names_for_resume( $subcategory_id, $tenant_id );
     294
     295        $products_by_sku = array();
     296        foreach ( $external_categories as $external_category ) {
     297                $products = ki_berater_fetch_content_products_for_category(
     298                        $external_category['external_category_id'],
     299                        $relevant_attributes
     300                );
    248301                foreach ( $products as $product ) {
    249                         $products_by_vaterprodukt_id[ $product['vaterprodukt_id'] ] = $product;
     302                        $products_by_sku[ $product['sku'] ] = $product;
    250303                }
    251304        }
    252305
    253         $total = count( $products_by_vaterprodukt_id );
     306        $total = count( $products_by_sku );
    254307        if ( 0 === $total || $processed_count >= $total ) {
    255308                // Entweder der Katalog ist gerade leer (voruebergehender
    … …  
    269322
    270323        $remaining = array();
    271         foreach ( $products_by_vaterprodukt_id as $product ) {
     324        foreach ( $products_by_sku as $product ) {
    272325                if ( ! isset( $already_processed_lookup[ $product['sku'] ] ) ) {
    273326                        $remaining[] = $product;
  • dtpm-ai-sale-assistant-woocommerce/trunk/admin/js/causal-chain.js

    r3723458 r3738724  
    726726                var picker = document.getElementById( 'ki-berater-causal-chain-subcategory-picker' );
    727727                var hint = document.getElementById( 'ki-berater-causal-chain-no-subcategories-hint' );
     728                // BUGFIX (Fund 10.10.2026, siehe js/tabs.js "ki-berater-tab-activated"):
     729                // dieser Aufruf laeuft jetzt bei JEDEM Wechsel zu diesem Tab erneut,
     730                // nicht mehr nur einmal beim Seitenaufbau - populateSubcategorySelect()
     731                // baut die <select>-Optionen dabei komplett neu auf UND wuerde ohne
     732                // diese Zwischenspeicherung die Auswahl stillschweigend auf "Bitte
     733                // auswaehlen" zuruecksetzen, selbst wenn der Haendler mitten in der
     734                // Bearbeitung GENAU dieser Subkategorie war (selectedSubcategoryId
     735                // selbst bleibt davon unberuehrt, nur die sichtbare <select>-Anzeige
     736                // wuerde sonst davon abweichen).
     737                var select = document.getElementById( 'ki-berater-causal-chain-subcategory-select' );
     738                var previouslySelectedId = select ? select.value : '';
    728739
    729740                hideApiSecretHint();
    … …  
    748759                                        }
    749760                                } else {
     761                                        // BUGFIX (Fund 10.10.2026): dieser Zweig setzte picker/hint
     762                                        // bisher NIE explizit zurueck - unschaedlich, SOLANGE
     763                                        // loadSubcategories() nur einmal pro Seitenaufbau lief (der
     764                                        // server-seitige HTML-Ausgangszustand zeigt den picker
     765                                        // bereits/versteckt den hint bereits, siehe
     766                                        // causal-chain-page.php). Seit dem erneuten Aufruf bei jedem
     767                                        // Tab-Wechsel (siehe js/tabs.js) MUSS dieser Zweig den
     768                                        // "keine Subkategorien"-Zustand eines fruehen Aufrufs
     769                                        // explizit wieder aufheben koennen, sonst bleiben picker/hint
     770                                        // auf dem letzten "leer"-Stand haengen, obwohl die neue
     771                                        // Antwort bereits Subkategorien enthaelt.
     772                                        if ( picker ) {
     773                                                picker.style.display = 'block';
     774                                        }
     775                                        if ( hint ) {
     776                                                hint.style.display = 'none';
     777                                        }
    750778                                        populateSubcategorySelect( subcategories );
     779                                        // Nur wiederherstellen, wenn diese Subkategorie in der
     780                                        // FRISCHEN Liste tatsaechlich noch vorkommt - sonst bleibt
     781                                        // bewusst der Platzhalter stehen (z. B. tatsaechlich
     782                                        // geloescht).
     783                                        if ( previouslySelectedId && subcategoryNameById[ previouslySelectedId ] ) {
     784                                                select.value = previouslySelectedId;
     785                                        }
    751786                                }
    752787
    … …  
    21602195                        loadSubcategories();
    21612196                        loadOpenOverview();
     2197
     2198                        // BUGFIX (Fund 10.10.2026, Dirk, siehe js/tabs.js fuer die volle
     2199                        // Begruendung): ohne Seiten-Reload zeigte dieser Tab nach einem
     2200                        // rein client-seitigen Wechsel weiterhin den Stand von VOR einer
     2201                        // zwischenzeitlich in "Kategorien-Analyse" bestaetigten Analyse
     2202                        // ("Kategorie-Analyse noch nicht gemacht"). loadSubcategories()/
     2203                        // loadOpenOverview() sind beide bereits vollstaendig
     2204                        // idempotent (bauen Auswahl/Uebersicht jedes Mal komplett neu
     2205                        // aus der Server-Antwort auf) - ein erneuter Aufruf bei JEDEM
     2206                        // Wechsel zu diesem Tab ist deshalb gefahrlos.
     2207                        if ( typeof document.addEventListener === 'function' ) {
     2208                                document.addEventListener( 'ki-berater-tab-activated', function ( event ) {
     2209                                        if ( event.detail && event.detail.panelId === 'ki-berater-tab-panel-causal-chain' ) {
     2210                                                loadSubcategories();
     2211                                                loadOpenOverview();
     2212                                        }
     2213                                } );
     2214                        }
    21622215                } );
    21632216        }
  • dtpm-ai-sale-assistant-woocommerce/trunk/admin/js/content.js

    r3735498 r3738724  
    726726                var noSubcategoriesHint = document.getElementById( 'ki-berater-content-no-subcategories-hint' );
    727727                var noCausalChainsHint = document.getElementById( 'ki-berater-content-no-causal-chains-hint' );
     728                // BUGFIX (Fund 10.10.2026, siehe js/tabs.js "ki-berater-tab-activated"):
     729                // dieser Aufruf laeuft jetzt bei JEDEM Wechsel zu diesem Tab erneut -
     730                // populateSubcategorySelect() baut die <select>-Optionen dabei
     731                // komplett neu auf und wuerde ohne diese Zwischenspeicherung die
     732                // Auswahl stillschweigend auf "Bitte auswaehlen" zuruecksetzen, selbst
     733                // wenn der Haendler mitten in der Bearbeitung GENAU dieser
     734                // Subkategorie war.
     735                var select = document.getElementById( 'ki-berater-content-subcategory-select' );
     736                var previouslySelectedId = select ? select.value : '';
    728737
    729738                if ( picker ) {
    … …  
    768777                                        }
    769778                                        populateSubcategorySelect( subcategoriesWithCausalChain );
     779                                        // Nur wiederherstellen, wenn diese Subkategorie in der
     780                                        // FRISCHEN, gefilterten Liste tatsaechlich noch vorkommt.
     781                                        var bleibtGueltig = subcategoriesWithCausalChain.some( function ( subcategory ) {
     782                                                return subcategory.id === previouslySelectedId;
     783                                        } );
     784                                        if ( previouslySelectedId && bleibtGueltig ) {
     785                                                select.value = previouslySelectedId;
     786                                        }
    770787                                } );
    771788                        } )
    … …  
    17581775                        loadSubcategories();
    17591776                        loadOpenOverview();
     1777
     1778                        // BUGFIX (Fund 10.10.2026, Dirk, siehe js/tabs.js fuer die volle
     1779                        // Begruendung): ohne Seiten-Reload zeigte dieser Tab nach einem
     1780                        // rein client-seitigen Wechsel weiterhin den Stand von VOR einer
     1781                        // zwischenzeitlich in "Kausalketten" bestaetigten Kausalkette
     1782                        // ("noch keine Kausalkette bestaetigt"). loadSubcategories()/
     1783                        // loadOpenOverview() sind beide bereits vollstaendig idempotent -
     1784                        // ein erneuter Aufruf bei JEDEM Wechsel zu diesem Tab ist deshalb
     1785                        // gefahrlos.
     1786                        if ( typeof document.addEventListener === 'function' ) {
     1787                                document.addEventListener( 'ki-berater-tab-activated', function ( event ) {
     1788                                        if ( event.detail && event.detail.panelId === 'ki-berater-tab-panel-content' ) {
     1789                                                loadSubcategories();
     1790                                                loadOpenOverview();
     1791                                        }
     1792                                } );
     1793                        }
    17601794                } );
    17611795        }
  • dtpm-ai-sale-assistant-woocommerce/trunk/admin/js/tabs.js

    r3723458 r3738724  
    11/**
    2  * Reines Tab-Umschalten fuer admin/dashboard-page.php - greift NICHT in
    3  * settings.js/category-analysis.js/causal-chain.js/content.js ein, kennt
    4  * nur die generischen .ki-berater-tab-trigger/.ki-berater-tab-panel-
    5  * Elemente und deren data-tab-target/id-Kopplung. Gleicher Code-Stil wie
    6  * die vier Feature-Skripte (IIFE, var, DOMContentLoaded).
     2 * Reines Tab-Umschalten fuer admin/dashboard-page.php - kennt nur die
     3 * generischen .ki-berater-tab-trigger/.ki-berater-tab-panel-Elemente und
     4 * deren data-tab-target/id-Kopplung. Gleicher Code-Stil wie die vier
     5 * Feature-Skripte (IIFE, var, DOMContentLoaded).
    76 *
    87 * Kein URL-Sync (siehe Auftrag - entspricht dem v0-Mockup, dort auch nur
    … …  
    109 * serverseitigen Standard-Tab (Einstellungen, siehe dashboard-page.php)
    1110 * zurueck.
     11 *
     12 * BUGFIX (Fund 10.10.2026, Dirk): category-analysis.js/causal-chain.js/
     13 * content.js laden ihren jeweiligen Anfangszustand (u. a. die
     14 * Subkategorie-Liste, "Noch offen"-Uebersicht) bisher NUR EINMAL beim
     15 * DOMContentLoaded dieser Seite - ein rein client-seitiger Tab-Wechsel
     16 * (GENAU das, was dieses Skript tut) loeste seitdem NIE einen erneuten
     17 * Abruf aus. Wer z. B. in "Kategorien-Analyse" etwas bestaetigt und DANN
     18 * ohne Seiten-Reload zu "Kausalketten" wechselt, sah dort weiterhin den
     19 * Stand von vor der Bestaetigung ("Kategorie-Analyse noch nicht gemacht") -
     20 * derselbe Effekt beim Wechsel von "Kausalketten" zu "Produktabdeckung".
     21 * activateTab() loest deshalb jetzt zusaetzlich ein generisches
     22 * CustomEvent "ki-berater-tab-activated" aus (detail.panelId = die Ziel-
     23 * Panel-ID) - jedes Feature-Skript kann darauf lauschen und seinen
     24 * eigenen Anfangszustand bei Bedarf erneut laden, OHNE dass dieses Skript
     25 * (oder ein anderes Feature-Skript) die Feature-Skripte direkt kennen
     26 * oder aufrufen muss (bleibt bewusst entkoppelt, siehe Modul-Docstrings
     27 * dort - "bewusst dupliziert, nicht importiert"). NUR bei einem
     28 * tatsaechlichen Klick ausgeloest (siehe handleTriggerClick() unten),
     29 * NICHT beim initialen Seitenaufbau - der server-seitig aktive Standard-
     30 * Tab hat seinen Anfangszustand ohnehin schon ueber sein eigenes
     31 * DOMContentLoaded geladen, ein zusaetzlicher Abruf dafuer waere
     32 * redundant.
    1233 */
    1334( function () {
    … …  
    3556                        panel.hidden = panel.id !== targetPanelId;
    3657                } );
     58
     59                document.dispatchEvent(
     60                        new CustomEvent( 'ki-berater-tab-activated', { detail: { panelId: targetPanelId } } )
     61                );
    3762        }
    3863
  • dtpm-ai-sale-assistant-woocommerce/trunk/changelog.txt

    r3735498 r3738724  
    11*** DT-PM AI Sale Assistant for WooCommerce Changelog ***
     2
     32026-10-10 - version 1.1.3
     4Requires Kern-API >= commit 3c31fd7 (unchanged from 1.1.2) - this is a pure frontend/JS fix, no Kern-API dependency.
     5* Fixed - `admin/js/causal-chain.js` and `admin/js/content.js` loaded their subcategory list exactly once, at page load (`DOMContentLoaded`) - a purely client-side tab switch (`admin/js/tabs.js`) never triggered a re-fetch. After confirming a Category Analysis and switching to the Causal Chains tab without reloading the page (or, equally, after confirming a Causal Chain and switching to Product Coverage), the newly confirmed subcategory still showed as "not done yet" until the page was reloaded. `tabs.js` now dispatches a `ki-berater-tab-activated` custom event whenever a tab is actually clicked, which both feature scripts listen for on their own panel to refresh their state - kept the two scripts decoupled (no direct cross-file calls, consistent with the existing "duplicated, not imported" convention). Also fixed a related, previously latent bug this exposed: `causal-chain.js`'s loader never explicitly reset its "no subcategories yet" hint/picker visibility back to normal once shown, relying on a one-time page load always starting from the correct default HTML state - harmless as long as it only ran once, but would have left the hint stuck visible forever on any later successful re-fetch. Also preserves the currently selected subcategory across a re-fetch in both tabs (previously, re-running the loader would have silently reset an in-progress selection back to the placeholder).
     6
     72026-10-10 - version 1.1.2
     8Requires Kern-API >= commit 3c31fd7 (unchanged from 1.1.1) - this fix calls only the already-existing `GET /variation-attributes` endpoint, no new Kern-API dependency.
     9* Fixed - the hourly catch-up cron for incomplete product processing (`admin/content-processing-resume.php`) used a different, simpler product-fetching routine (`ki_berater_fetch_sample_products()`) than the one the browser's "Process remaining" button uses (`ki_berater_fetch_content_products_for_category()`/`ki_berater_expand_variable_product_for_content()`) - for a variable product with at least one merchant-confirmed relevant distinguishing attribute (e.g. color), the cron reported only a single row using the parent product's own (often empty) SKU/price, instead of one row per actual relevant variant combination with that variant's own SKU/price. The cron now fetches the merchant's confirmed relevant attributes itself (`GET /variation-attributes`) and uses the same variant-aware logic as the browser path; deduplication switched from `vaterprodukt_id` to `sku` accordingly, since a variant-expanded product can now report multiple distinct SKUs.
    210
    3112026-10-08 - version 1.1.1
  • dtpm-ai-sale-assistant-woocommerce/trunk/ki-berater.php

    r3735498 r3738724  
    33 * Plugin Name: DT-PM AI Sale Assistant for WooCommerce
    44 * Description: Thin wp-admin interface for the AI product advisor (ARCHITECTURE.md section 2.1). Renders no business logic itself, only calls the Kern-API.
    5  * Version: 1.1.1
     5 * Version: 1.1.3
    66 * Author: KI-Produktberater-Team
    77 * License: GPLv2 or later
    … …  
    6464// BEIDE hochzaehlen (war zuletzt auseinandergelaufen - 0.1.13 im Header,
    6565// 0.1.12 hier - hiermit wieder angeglichen).
    66 define( 'KI_BERATER_VERSION', '1.1.1' );
     66define( 'KI_BERATER_VERSION', '1.1.3' );
    6767
    6868require_once KI_BERATER_PLUGIN_DIR . 'admin/diagnosis-page.php';
  • dtpm-ai-sale-assistant-woocommerce/trunk/readme.txt

    r3735498 r3738724  
    88WC tested up to: 11.1
    99Requires Plugins: woocommerce
    10 Stable tag: 1.1.1
     10Stable tag: 1.1.3
    1111License: GPLv2 or later
    1212License URI: https://www.gnu.org/licenses/gpl-2.0.html
    … …  
    9494== Changelog ==
    9595
     96= 1.1.3 =
     97* Fixed: after confirming a Category Analysis, the Causal Chains tab (and, after confirming a Causal Chain, the Product Coverage tab) still said "not done yet" for it until the page was reloaded - switching tabs without reloading now refreshes that status correctly.
     98
     99= 1.1.2 =
     100* Fixed: the hourly catch-up check for incomplete product processing could report the wrong price/SKU for a product with multiple customer-relevant option combinations (e.g. several colors), using the overall product's own (often blank) price/SKU instead of the matching option's - the manual "Process remaining" button already handled this correctly; the hourly check now uses the same logic.
     101
    96102= 1.1.1 =
    97103* Fixed: an automatic catch-up run for a handful of still-missing products (the hourly background check, or clicking "Process remaining" right after a few products failed to process) could wipe out the rest of an already-processed subcategory's recommendations - the run now only clears out genuinely removed products when it actually has the complete, current product list, not a partial one.
Note: See TracChangeset for help on using the changeset viewer.