Artikel für x Minuten Reservieren, wenn sie im Warenkorb sind

Thema wurde von BigRib, 14. Oktober 2023 erstellt.

  1. BigRib
    BigRib Erfahrener Benutzer
    Registriert seit:
    26. September 2018
    Beiträge:
    271
    Danke erhalten:
    56
    Danke vergeben:
    32
    Ich möchte ein Modul erstellen, dass bewirkt, dass ein Artikel (bzw. dessen Variante), wenn er in den Warenkorb landet, für diesen Kunden für 10 Minuten reserviert ist. (Bitte keine Diskussion wieso, weshalb warum)

    Ich grübel da schon eine Weile drüber nach, aber ich finde keinen wirklichen Ansatzpunkt.

    Wenn der Artikel in den Warenkorb geschoben wird, müsste dieser in einer weiteren Tabelle eingetragen werden, inkl. dem Verfallsdatum. Für alle anderen Kunden, außer dem, der ihn im Warenkorb hat, müsste dieser Artikel beim Stock_Check / Artikelanzahl abgezogen werden, damit er nicht doppelt im Warenkorb gelegt werden kann.

    @Till (Gambio) hast Du oder einer Deiner Kollegen da eine Idee?

    Gruß Sven
     
  2. Walter Lenk
    Walter Lenk Erfahrener Benutzer
    Registriert seit:
    28. September 2011
    Beiträge:
    623
    Danke erhalten:
    300
    Danke vergeben:
    113
    Hallo Sven,

    Du hast den Ansatz ja schon recht gut erklärt. Genauso würde ich auch ran gehen. Ist klassische Individualprogrammierung würde ich sagen. Da wirst Du bei diversen Dienstleistern anfragen müssen.

    Grüße
    Walter
     
  3. BigRib
    BigRib Erfahrener Benutzer
    Registriert seit:
    26. September 2018
    Beiträge:
    271
    Danke erhalten:
    56
    Danke vergeben:
    32
    Hallo Walter :)

    Ich habe es im Groben schon hinbekommen. Auf der Artikelseite und ab "checkout_shipping" ist es mit einer einfachen Überladung getan. Im Warenkorb muss ich nach aktuellem Kenntnisstand in die Eingeweide, da sich xxxReader.inc.php nicht überladen lassen. Alternativ wäre den StockCheck abzufangen und zu überschreiben, aber da sind wieder ein paar Datenbankabfragen mehr und der Warenkorb ist eh schon ziemlich überladen mit Datenbankabfragen.

    Nene, das bekomme ich selber oder gar nicht hin :)
     
  4. Anonymous
    Anonymous Administrator
    Mitarbeiter
    Registriert seit:
    26. April 2011
    Beiträge:
    1.954
    Danke erhalten:
    1.588
    Danke vergeben:
    339
    @BigRib Wie weit bist du denn schon? Ich würde vor dem Warenkorb prüfen, also beim In den Korb legen ansetzen. Das müsste die CartActionsProcess Klasse sein. Hier kannst du die add_product und update_product Funktion überladen.
    Das müssten die Stellen sein.

    Aber wenn du es genauer wissen willst, müsste man auf deinen bereits geschriebenen Code schauen.
     
  5. BigRib
    BigRib Erfahrener Benutzer
    Registriert seit:
    26. September 2018
    Beiträge:
    271
    Danke erhalten:
    56
    Danke vergeben:
    32
    Aktuell bin ich noch nicht sehr weit. Ich habe mal eine Tabelle in der Datenbank angelegt und es funktioniert so weit, dass in der Artikelansicht die Bestände aktualisiert werden.

    Für Produkte ohne Varianten habe ich folgende Klasse in "products.php" überladen. Diese funktioniert dann auch automatisch im Warenkorb:

    PHP:
    class TndReserveProductsProduct extends TndReserveProductsProduct_parent
    {
        
    /**
         * @inheritDoc
         */
        
    public function getProductQuantity(): float
        
    {
       
            
    $quantity parent::getProductQuantity();

            
    $currentTimestamp time();
            
    $mysqlTimestamp date('Y-m-d H:i:s'$currentTimestamp);


            
    $products_id =  $this->data['products_id'];
            
    $customer_id = !empty($_SESSION['customer_id']) ? (int)$_SESSION['customer_id'] : 0;

                
    $reservedProducts = [];
                
    $db     StaticGXCoreLoader::getDatabaseQueryBuilder();
                
    $reservedProducts $db->distinct()
                  ->
    select('sum(product_quantity) AS product_quantity')
                  ->
    from('tnd_reserved_products')
                  ->
    where('products_id', (int)$products_id)
                  ->
    where('customers_id <>'$customer_id)
                  ->
    where('expiry_datetime >= '$mysqlTimestamp)

                  ->
    get()
                  ->
    result_array();

                
    // Info Box füllen und Anzeigen beeinflussen


                
    $reservedQuantity $reservedProducts[0]['product_quantity'];
                
    $availableQuantity max(0$quantity $reservedQuantity);

                return 
    $availableQuantity;
        }
    }
    Für die Varianten haben ich folgende Überladung, diese funktioniert aber nicht automatisch im Warenkorb:


    PHP:
    class TndReserveProductsProductInfoThemeContentView extends TndReserveProductsProductInfoThemeContentView_parent
    {

      protected function 
    getProductPropertiesCombis($productsId)
      {



        
    $combis parent::getProductPropertiesCombis($productsId);

        if (empty(
    $combis)) {
          return [];
        }

        
    $currentTimestamp time();
        
    $mysqlTimestamp date('Y-m-d H:i:s'$currentTimestamp);

        
    $customer_id = !empty($_SESSION['customer_id']) ? (int)$_SESSION['customer_id'] : 0;

        
    $db StaticGXCoreLoader::getDatabaseQueryBuilder();
        
    $queryResult $db->select('combis_id, sum(product_quantity) AS product_quantity')
          ->
    from('tnd_reserved_products')
          ->
    where('products_id', (int)$productsId)
          ->
    where('customers_id <>'$customer_id)
          ->
    where('expiry_datetime >= '$mysqlTimestamp)
          ->
    group_by('combis_id')
          ->
    get()
          ->
    result_array();

        
    // Erstellen eines Assoziativarrays für die reservierten Mengen.
        
    $reservedQuantities = [];
        foreach (
    $queryResult as $row) {
          
    $reservedQuantities[$row['combis_id']] = (float)$row['product_quantity'];
        }

        
    // Aktualisieren des ursprünglichen Arrays mit den reservierten Mengen.
        
    foreach ($combis as &$item) {
          
    $combiId $item['products_properties_combis_id'];
          if (isset(
    $reservedQuantities[$combiId])) {
            
    $item['combi_quantity'] -= $reservedQuantities[$combiId];
          }
        }

        return 
    $combis;
      }
    }
     
  6. BigRib
    BigRib Erfahrener Benutzer
    Registriert seit:
    26. September 2018
    Beiträge:
    271
    Danke erhalten:
    56
    Danke vergeben:
    32
    In der PropertyReader.inc.php wäre es diese Stelle:


    PHP:
    public function getCombinationsFor(SellingUnitId $idint $limit 2): CombinationCollectionInterface
      
    {
        
    $result = new CombinationCollection();

        [
    $whereAnd$having] = $this->filterProperties($id);
        
    $sql  $this->createQuery($id->productId()->value(), $whereAnd$having$limit);
        
    $data $this->connection->query($sql)->fetchAll();

        
    $currentTimestamp time();
        
    $mysqlTimestamp date('Y-m-d H:i:s'$currentTimestamp);

        
    // Verbindung zur Datenbank herstellen und reservierte Mengen abrufen.

        
    $customer_id = !empty($_SESSION['customer_id']) ? (int)$_SESSION['customer_id'] : 0;

        
    $productId      "products_id = {$id->productId()->value()}";
        
    $customerId      "customers_id <> {$customer_id}";
        
    $expiry_datetime      "expiry_datetime >= '{$mysqlTimestamp}'";

        
    $records $this->connection->createQueryBuilder()
          ->
    select('combis_id, sum(product_quantity) AS product_quantity')
          ->
    from('tnd_reserved_products')
          ->
    where($productId)
          ->
    andWhere($customerId)
          ->
    AndWhere($expiry_datetime)
          ->
    groupBy('combis_id')
          ->
    execute()
          ->
    fetchAll();

        
    // Erstellen eines Assoziativarrays für die reservierten Mengen.
        
    $reservedQuantities = [];
        foreach (
    $records as $row) {
          
    $reservedQuantities[$row['combis_id']] = (float)$row['product_quantity'];
        }

        
    // Aktualisieren des ursprünglichen Arrays mit den reservierten Mengen.
        
    foreach ($data as &$item) {
          if (
    $item['use_properties_combis_quantity'] == 2) {
            
    // Wenn use_properties_combis_quantity gleich 2 ist, ziehe den Bestand von combi_quantity ab.
            
    $item['combi_quantity'] -= $reservedQuantities[$item['products_properties_combis_id']];
          } else {
            
    // Andernfalls ziehe den Bestand von products_quantity ab.
            
    $item['products_quantity'] -= $reservedQuantities[$item['products_properties_combis_id']];
          }
        }

        
    $nonLinearSurcharge = (count($data) > 0) ? $this->hasNonLinearSurcharge($id->productId()->value()) : false;

        foreach (
    $data as $row) {
          
    $result[] = $this->createCombination($row$nonLinearSurcharge);
        }

        return 
    $result;
      }
     
  7. WinHelp GmbH
    WinHelp GmbH Erfahrener Benutzer
    Registriert seit:
    2. April 2019
    Beiträge:
    82
    Danke erhalten:
    27
    Danke vergeben:
    22
    Hallo BigRib,

    wir haben genau dasselbe für eines unserer Module umgesetzt.

    Angenommen, du möchtest Folgendes sicherstellen:

    Die Reservierung wird für Kunden mit Kundenkonto über mehrere Sessions hinweg gespeichert.

    Hier ist ein Beispiel: Die Reservierungsdauer beträgt 60 Minuten. Ein Kunde meldet sich an, legt einen Artikel in den Warenkorb und schließt dann seinen Browser. Während er etwa 30 Minuten später zurückkommt, um seinen Einkauf fortzusetzen, meldet er sich erneut an. Dann sollte die alte Reservierung noch da sein und er sollte dort beginnen können wo er aufgehört hat.

    Ein paar wichtige Punkte, die du beachten solltest:

    Gambio speichert den Warenkorb in der aktuellen Browsersitzung, solange der Kunde nicht eingeloggt ist. Erst nachdem der Kunde sich einloggt oder registriert, wird der Warenkorb in der Datenbank persistiert. Somit können Kunden ohne Kundenkonto keine Reservierungen über mehrere Session hinweg vornehmen, da der Warenkorb nur in der aktuellen Browsersitzung gespeichert wird.

    Solange ein Kunde nicht eingeloggt ist, hat er keine customer_id.

    Wir speichern Reservierungen für nicht eingeloggte Kunden in der Datenbank mithilfe der session_id. Wir haben uns an die LoginContentControl angehängt, um beim Einloggen oder Registrieren im Bestellprozess die Reservierungen von der session_id zur customer_id zu aktualisieren. Auf diese Weise stellen wir sicher, dass die Reservierung weiterhin dem Kunden zugeordnet werden können.

    Um die Sicherheit zu erhöhen, empfehlen wir außerdem, die Checkout-Seiten (insbesondere "check_stock") zu überladen, damit ein direkter Einstieg in den Bestellprozess über eine URL nicht dazu führt, dass die Reservierungsdauer ignoriert wird. Wenn die Überprüfung der Reservierung nur auf Artikelebene erfolgt, kann nach einmaligem Ablegen des Artikels in den Warenkorb immer noch bestellt werden. Der "check_stock" im Checkout ruft keine "check_stock"-Funktionen des Artikels selbst auf.

    Ich hoffe, diese Informationen sind hilfreich! Lass mich wissen, wenn du weitere Fragen dazu hast.
     
  8. BigRib
    BigRib Erfahrener Benutzer
    Registriert seit:
    26. September 2018
    Beiträge:
    271
    Danke erhalten:
    56
    Danke vergeben:
    32
    Hallo Nico,

    vielen Dank für Deinen tollen Beitrag. Ich denke, der Hintergrund unserer Reservierungsoption ist etwas anders als bei Euch und durch andere Umstände ist die Installation nicht ganz so aufwendig.

    Der Hauptgrund für die Reservierung ist, dass wir ein paar Mal im Jahr limitierte Artikel herausbringen, die oft nur als Einzelstück erhältlich sind, also mit vielen Varianten. Dadurch kommt es vor, dass mehrere Kunden gleichzeitig die gleiche Variante kaufen wollen. Teilweise haben wir über 100 Kunden, die diesen Artikel gleichzeitig kaufen wollen und dementsprechend viele, die sich für die gleiche Variante entscheiden. Dann beginnt der Kampf, wer zuerst durch den Bestellprozess kommt. Wir haben Kunden, die mehrmals zurück in den Warenkorb geworfen wurden und dann leer ausgegangen sind, weil jemand anderes schneller auf den Button "kostenpflichtig bestellen" geklickt hat.

    Wenn wir diesen Release haben, dann haben wir zum einen weitere Einschränkungen (Produktlimitierung) und zeitversetzte Releases für Kundengruppen. Das alles hat schon zur Folge, dass der Kunde ein Kundenkonto haben muss und somit eingeloggt sein muss. Deshalb reicht es bei uns aus, dass wir die reservierten Artikelmengen direkt in die Datenbank schreiben.

    Eine Reservierungsdauer von 10 Minuten reicht bei uns aus, da wir nur den Checkout beruhigen wollen. Wenn der Kunde sich zwischendurch ausloggt, weil es ihm doch nicht so wichtig ist, dann dürfen die Artikel nicht länger blockiert werden.

    Bei den Checkout-Seiten baue ich auf jeden Fall etwas ein. Durch den ProduktLimit Mod habe ich da viel gelernt, wie man mit Multibrowser usw. umgeht. Artikel nachträglich in den Warenkorb bekommt *g*.
     
  9. BigRib
    BigRib Erfahrener Benutzer
    Registriert seit:
    26. September 2018
    Beiträge:
    271
    Danke erhalten:
    56
    Danke vergeben:
    32
    Ich werde mir auf alle Fälle mal die Ideen vom @Till (Gambio) anschauen, wenn ich die Kontrolle bereits beim einfügen in den Warenkorb und nach dem Warenkorb habe, dann reicht im Warenkorb die Info, dass sich reservierte Artikel im Warenkorb befinden.
     
  10. WinHelp GmbH
    WinHelp GmbH Erfahrener Benutzer
    Registriert seit:
    2. April 2019
    Beiträge:
    82
    Danke erhalten:
    27
    Danke vergeben:
    22
    Servus Sven,

    deine Situation klingt auf jeden Fall entspannter als bei uns.

    In jedem Fall solltest du überprüfen, ob der CheckStatusController auch eine passende Fehlermeldung an den Artikel zurückgibt. Wenn du eine Variante änderst, wird der CheckStatusController aufgerufen und lädt dynamisch Fehlermeldungen in den Artikel. Ich bin mir nicht sicher, ob dabei die check_stock des Artikels geprüft wird.

    Noch als kleine Idee wenn die Basis steht, nimm eine Überladung der OrderDetailsCartThemeContentView vor. Dadurch kannst du die Warenkorbdaten mit einer artikelbasierten Nachricht ergänzen, die du dann im Template darstellst. Im Standard bietet Gambio nur eine für den gesamten Warenkorb.
    upload_2023-10-18_10-25-30.png

    Zusätzlich haben wir einen kleinen Countdown mit moment.js erstellt, der die verbleibende Reservierungsdauer anzeigt.

    upload_2023-10-18_10-25-17.png
     
  11. BigRib
    BigRib Erfahrener Benutzer
    Registriert seit:
    26. September 2018
    Beiträge:
    271
    Danke erhalten:
    56
    Danke vergeben:
    32
    Schaut sehr gut aus! Ich liege aktuell flach. sobald ich weiter mache, melde ich mich wieder. Vielen Dank für Deinen Input!
     
  12. Anonymous
    Anonymous Erfahrener Benutzer
    Registriert seit:
    18. Januar 2015
    Beiträge:
    867
    Danke erhalten:
    96
    Danke vergeben:
    129
    OT:

    War mal neugierig und hab auf Deine Seite geschaut ... fast das komplette Menü funktioniert bei Dir nicht:


    Code:
    FATAL ERROR(1): "Uncaught  --> Smarty: Unable to load template 'file:/www/htdocs/w014f66b/winhelp.shop/templates/Honeygrid/snippets/product_info/product_box_bottom.html' in 'get_usermod:templates/Honeygrid/snippets/product_info/product_box_bottom.html' <--
      thrown"
    LG: Tammy