WooCommerce ESW

Da Webmobili Wiki.

Il progetto prevede la creazione di un mini-sito con commerce integrato con Easy Store Web.

Ambiente di lavoro

Container

Abbiamo optato per un docker container così strutturato
docker-compose.yml

version: '3.9'

services:

  db:
    image: mysql:8.3
    ports:
      - 3306:3306
    environment:
      MYSQL_DATABASE: webmobili
      MYSQL_USER: webmobili
      MYSQL_PASSWORD: w3bm0b1l1
      MYSQL_ROOT_PASSWORD: w3bm0b1l1
    volumes:
      - db:/var/lib/mysql

  wordpress:
    depends_on:
      - db
    build:
      context: ./
    ports:
      - 8000:80
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_USER: webmobili
      WORDPRESS_DB_PASSWORD: w3bm0b1l1
      WORDPRESS_DB_NAME: webmobili
      WORDPRESS_DEBUG: 1
      TZ: "Europe/Rome"
    volumes:
      - wordpress:/var/www/html

volumes:
  db:
  wordpress:

Punta al seguente Dockerfile che si occupa di configurare xdebug per PHP.

FROM wordpress:6.6.2-apache
RUN apt-get update && \
  pecl install xdebug \
  && docker-php-ext-enable xdebug
# Copy xdebug.ini to /usr/local/etc/php/conf.d/
COPY ./*.ini /usr/local/etc/php/conf.d/
# Copy plugin data to to /usr/src/wordpress/wp-content/plugins/
COPY --chown=www-data:www-data ./designbest-esw/ /usr/src/wordpress/wp-content/plugins/designbest-esw/
COPY --chown=www-data:www-data ./designbest-esw-lead/ /usr/src/wordpress/wp-content/plugins/designbest-esw-lead/

Nella root dev'essere presente il file xdebug.ini così configurato:

zend_extension=xdebug.so

[xdebug]
xdebug.mode=develop,debug
xdebug.discover_client_host = true
xdebug.start_with_request=yes

Ecco il devcontainer.json

// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
// https://github.com/microsoft/vscode-dev-containers/tree/v0.238.0/containers/docker-existing-docker-compose
// If you want to run as a non-root user in the container, see .devcontainer/docker-compose.yml.
{
	"name": "Designbest Commerce ESW",

	// Update the 'dockerComposeFile' list if you have more compose files or use different names.
	// The .devcontainer/docker-compose.yml file contains any overrides you need/want to make.
	"dockerComposeFile": [
		"../docker-compose.yml"
	],

	// The 'service' property is the name of the service for the container that VS Code should
	// use. Update this value and .devcontainer/docker-compose.yml to the real service name.
	"service": "wordpress",

	// The optional 'workspaceFolder' property is the path VS Code should open by default when
	// connected. This is typically a file mount in .devcontainer/docker-compose.yml
	"workspaceFolder": "/var/www/html",
	"customizations": {
		"vscode": {
			"extensions": [
				"xdebug.php-pack", "xdebug.php-debug", "bmewburn.vscode-intelephense-client","neilbrayfield.php-docblocker","olback.es6-css-minify"
			]
		}
	},
	
	// Add the IDs of extensions you want installed when the container is created.

	// Use 'forwardPorts' to make a list of ports inside the container available locally.
	"forwardPorts": [8000]
}

Visual Studio

- Cliccare sul pulsante Run & Debug e farsi creare in automatico il file .vscode/launch.json per abilitare xdebug per PHP.
- Creare un file .vscode/settings.json per configurare le estensioni col seguente codice

{
  "es6-css-minify.minifyOnSave": "exists",
  "intelephense.environment.phpVersion": "7.4.3"
}

- Aprire wp-config.php e aggiungere appena sotto a define( 'WP_DEBUG',... la riga

define( 'WP_DEBUG_LOG', true );

Wordpress Admin

Da http://localhost:8000/wp-admin
ho creato credenziali con la mia solita password, utente la mail di lavoro.

  • Aggiornare l'aggiornabile
  • Installare il plugin WooCommerce
  • (opzionale) Installare il tema Astra
    • Aspetto => Opzioni Astra => spalla dx => Installa Plugin Importatore
    • Da questa pagina installare il template (scegliendo Elementor) Custom Printing con tutti i suoi contenuti
  • (opzionale) Installare il plugin Classic Widget
  • Installare il plugin Checkout Field Editor for WooCommerce.
    Entrare nelle impostazioni e aggiungere i campi partita iva e codice fiscale con i nomi billing_cf e billing_piva
  • (developer) Installare il plugin WP Crontrol per vedere la situazione dei cron jobs e poterli eliminare
  • (developer) Installare il plugin WP Hooks Finder (by Muhammad Rehman) per poter vedere tutti gli hook in pagina.

Easy Store - Le specifiche

Ci saranno 3 tipi di utenza

  • Clienti con Easy Store
  • Clienti con Outlet
  • Clienti con Easy Store + Outlet

Il plugin è in grado di distinguere in quale caso ci troviamo in base al file di configurazione
/designbest-esw/config/config.php
Dalla voce

"sorgente-dati" => "designbest", // valori esw o designbest o designbest-esw

Il plugin è basato su 5 interfacce services che cambiano le loro funzioni a seconda del tipo di utenza:

  • IApiConnectService connessione alle API
  • IProductsService sincronizzazione prodotti
  • IOrdersService gestione degli ordini (fattura ecc)
  • IAdminService gestione backoffice WP
  • ICronJobsService gestione di cron-job per sincronizzare


Clienti con Easy Store

File config.php correttamente configurato per questa utenza:

return array(
  "environment" => "test", // "production"
  "sorgente-dati" => "esw", // valori esw o designbest o designbest-esw

  "esw_api-domain" => "https://easyapi6.eswportal.it",
  "esw_negoziocode" => "Attivo 2",
  "esw-x-apikey" => "j9MDojFf4EmbznjJvimErUJ4YWJHWlY4N1VTTWYwM2NRZUZOSmc=",
  "esw-dbconnection" => "291_EMMERRE_TEST",
  "esw-codicemagazzino" => ["MAG_10","MAG_11","MAG_23"], // magazzini accettati dall'import
  "esw-codicevenditore" => "ONL49554",
  "esw-incassi-codice_causale" => "05SLD",
  "esw-servizi-codice" => "SE",
  "esw-mapping-pagamenti" => ["bacs" => "BON","xpay"=> "CC"],
  // Gestione clienti che hanno i campi cf, piva e sdi creati con altri plugin
  "esw-customer-ordermetadata" => [
    "create" => true,  // se true crea i campi customCheckoutFields e li mostra in backoffice (false se lo fa già un plugin)
    "cf" => "_billing_cf",
    "piva" => "_billing_piva",
    "sdi" => "_billing_sdi"
  ]
);

Le API di Easy Store non permettono di caricare articoli sul gestionale, ma solo di leggerli.
Per tenere sincronizzati i prodotti è necessario montare un pooling tramite un cron-job che andrà a leggere i dati del gestionale e li proietterà sul commerce.
Aggiunta funzione per caricare un elenco pre-compilato di categorie in quanto EasyStore non le considera.
Nelle chiamate, tutti i valori booleani vanno passati come stringa utilizzando "true" o "false".

IApiConnectService permette la connessione alle API di Easy Store

https://easyapitest.eswportal.it/developer/index

IProductsService mette a disposizione

  • createBrandTaxonomy() che si occupa di aggiungere la tassonomia del manufacturer che stranamente in WooCommerce non è presente.
Oltre a questo inserisce un elenco convenzionato di categories perché Easy Store non ha i dati sulle categorie/tipologie di prodotto.
  • allProductsSync() Legge tutti i prodotti del negozio specificato in config.php che hanno il flag commerce e li sincronizza con WooCommerce.
Se sono già presenti si limita ad aggiornare solo i campi prezzo e giacenza. Altrimenti inserisce l'intero prodotto.
I dati sincronizzati non hanno dati sufficienti per una pubblicazione (mancano titolo, descrizione e immagini) e saranno importati come bozze.
  • insertDefaultCategories() Inserisce gli ambienti default per definire una lista di partenza.
Il pulsante si trova nell'admin



IOrdersService gestisce l'acquisto sincronizzando tutto col gestionale.

  • customCheckoutFields() Aggiunge codice fiscale e partita I.V.A. ai campi di checkout del carrello.
  • newOrder() spedisce a Easy Store tutte le informazioni sull'acquisto.
Si occupa di registrare l'anagrafica e il prodotto venduto tramite i corrispondenti endpoint post anagrafica e post vendite.
In queste chiamate alcuni parametri sono costanti definite durante la progettazione.
  • codice_venditore = ONL49554
  • tipo_fattura = FT
  • codice_fornitore viene passato se è definito, in caso contrario passa ECOMMERCE
  • codice_magazzino = MAG ora proviene dalla configurazione
  • prezzo_forzato = True utilizzare i valori string "True" o "False" per i booleani



IAdminService gestisce le pagine di backoffice e le loro logiche lato server.

Piazza l'elemento nel menu con l'icona adeguata, mette a disposizione una pagina che riassume le caratteristiche del plugin e permette una sincronizzazione manuale premendo un pulsante.

ICronJobsService Attiva (e predispone la disattivazione) di un cron job che chiama la sincronizzazione dei prodotti ( IProductsService::allProductsSync() ) tutti i giorni a partire dalle 8 per 2 volte al giorno.

Clienti con outlet

File config.php correttamente configurato per questa utenza:

return array(
  "sorgente-dati" => "designbest",
  "environment" => "test", // "production",

  "db-api-domain" => "https://apicore.dbdemo47.com",
  "db-token_endpoint" => "/token",
  "db-token_username" => "designbestrest",
  "db-token_password" => "Z3+3MlGwEbz#",
  "db-shopnetid" => 318182  // Bensa Arredamenti
);

Il database di Designbest ha dei dati più completi rispetto a ESW per un e-commerce.
Allo stesso modo, il meccanismo di sincronizzazione porterà i dati dal database di Designbest a quello del commerce locale.

IApiConnectService permette la connessione alle API di Designbest

https://ws.designbest.com/doc/index

IProductsService mette a disposizione

  • createBrandTaxonomy() che si occupa di aggiungere la tassonomia del manufacturer che stranamente in WooCommerce non è presente.
Non si occupa di aggiungere categories al pool di base perché i prodotti Designbest forniscono la precisa tipologia.
  • allProductsSync() Legge tutti i prodotti del negozio specificato in config.php che hanno il flag e-commerce approved e li sincronizza con WooCommerce.
Se sono già presenti si limita ad aggiornare solo i campi prezzo e giacenza. Altrimenti inserisce l'intero prodotto.
I dati sincronizzati saranno importati come bozze.

IOrdersService gestisce l'acquisto sincronizzando tutto col gestionale.

  • customCheckoutFields() Aggiunge codice fiscale e partita I.V.A. ai campi di checkout del carrello.
  • newOrder() comunica a Designbest la diminuzione della giacenza dell'articolo (non implementato in quanto sovrascritto dalla sincronizzazione programmata).


IAdminService gestisce le pagine di backoffice e le loro logiche lato server.

Piazza l'elemento nel menu con l'icona adeguata, mette a disposizione una pagina che riassume le caratteristiche del plugin e permette una sincronizzazione manuale premendo un pulsante.

ICronJobsService Attiva (e predispone la disattivazione) di un cron job che chiama la sincronizzazione dei prodotti ( IProductsService::allProductsSync() ) tutti i giorni a partire dalle 8 per 2 volte al giorno.

Clienti con Outlet + Easy Store

File config.php correttamente configurato per questa utenza:

return array(
  "sorgente-dati" => "designbest-esw",
  "environment" => "test", // "production"

  // Configurazione esw
  "esw_negoziocode" => "01",  // Arredamenti Casabella su Esw
  "esw-x-apikey" => "hEckyjWBYUycOIRiUZUgJUJ4YWJHWlY4N1VTTWYwM2NRZUZOSmc=",
  "esw-dbconnection" => "173_WMECOMMERCE",
  // Configurazione designbest
  "db-token_endpoint" => "https://ws.designbest.com/token",
  "db-token_username" => "designbestrest",
  "db-token_password" => "Z3+3MlGwEbz#",
  "db-shop_piva" => "00258570274"  // Arredamenti Casabella su Outlet
);

I services dovranno comportarsi in parte come Esw e in parte come Outlet.
Importante che nella configurazione si specifichi la stessa entità con i 2 diversi identificativi (es. Casabella Arredamenti su ESW e lo stesso su Outlet).

Clienti con sviluppi custom non scalabili

Per rispondere alle richieste non scalabili degli utenti sono stati inseriti degli hook nei punti cardine del plugin.

Al momento sono i seguenti:

  • dbesw_eswinsertanagrafica => ($anag, $order) [filter]
  • dbesw_eswinsertvendita => ($vendita, $order) [filter]

Utilizzo

È necessario creare un nuovo file .php, ad esempio NomeClienteCustomHooks.php, che conterrà le personalizzazioni degli hook nella cartella /config/CustomHooks/.

Per utilizzarlo è necessario dichiararlo nel file /config/config.php sotto alla voce 'custom-hooks

<?php
defined( 'ABSPATH' ) || exit;

return array(
  // ... altre conf

  "custom-hooks" => "NomeClienteCustomHooks.php"
);

e il file, che verra eseguito durante l'inizializzazione del plugin, potrà avere i seguenti contenuti:

<?php
// SVILUPPI CUSTOM per DTime
defined( 'ABSPATH' ) || exit;

add_filter("dbesw_eswinsertanagrafica", function($anag, $order) {
  // Manipolazione custom di $anag utilizzando $order per recuperare dati
  return $anag;
}, 10, 2);

add_filter("dbesw_eswinsertvendita", function($vendita, $order) {
  // Manipolazione custom di $vendita utilizzando $order per recuperare dati
  return $vendita;
}, 10, 2);

Attivazione plugin

Qui i passaggi per attivare il plugin sul sito wordpress del cliente.

IMPORTANTE
Nell'ambiente di test, nel file wp-config.php inserire la costante

define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
define( 'WP_DEBUG_DISPLAY', true );
define( 'WP_ENVIRONMENT_TYPE', 'staging' );  // mettere 'local' per sviluppo locale

In produzione rimuovere WP_ENVIRONMENT_TYPE e rimuovere WP_DEBUG e figli.

Innanzitutto bisogna recuperare dal cliente

  • Credenziali per accedere a wp-admin
  • Credenziali per accedere a FTP

Poi bisogna farsi dare da Sintesys

  • api-endpoint
  • negozio_code
  • x-apikey
  • dbconnection
  • codice_magazzino

Una volta che si hanno tutti i dati.

  • Installare (se non presente) il plugin dipendenza WooCommerce
  • Copiare su FTP il folder del plugin nella cartella /wp-content/plugins
  • Attivare il plugin tramite l'apposita sezione della dashboard
  • (opzionale) installare il plugin WP Crontrol per vedere i cron-job sotto il menu Strumenti

⚠ Stranezze tecniche ⚠

Questa sezione raccoglie tutti i tipi di eccezione che abbiamo incontrato durante lo sviluppo.

WooCommerce incompatibilità di configurazione

Fonte:
https://rudrastyh.com/woocommerce/high-performance-order-storage.html

Esiste una modalità di WooCommerce che si chiama HPOS, si basa sull'utilizzo di tabelle proprie ottimizzate invece di quelle del core WP.
Si attiva da WooCommerce => Impostazioni => Avanzate => Funzioni/Features, non si lascia attivare se qualche plugin non è compatibile.

Ci sono diversi casi da gestire

  • Il caso particolare dell'hook shop_order che cambia nome in woocommerce_page_wc-orders.
  • L'impossibilità di usare get_post_meta() quando è attivo.

Nel primo caso è possibile scrivere un codice che è compatibile sia che HPOS sia abilitato che non:

  public function esw_order_json_data() : void {
    add_meta_box(
        'esw-order-json-data',
        'EASY STORE ORDER DATA',
        [$this,'esw_render_meta_box'],       
        wc_get_page_screen_id( 'shop-order' ), // woocommerce_page_wc-orders (oppure shop_order)is the post type of the admin order page
        'normal', // change to 'side' to move box to side column 
        'low' // priority (where on page to put the box)
    );
  }
  public function esw_render_meta_box($post,$metabox) : void {
    //https://rudrastyh.com/woocommerce/high-performance-order-storage.html#screen_id-changes
    //Se opzione di Woocommerce HPOS è attiva la variabile $post è di tipo WC_Order e non può accedere direttamente all'ID
    $order_id = ( $post instanceof \WP_Post ) 
		?  $post->ID
		: $post->get_id();

    $json_data = $this->eswdb_service->getOrder($order_id);
    if($json_data != null){
      echo '<pre>'.$json_data. '</pre>';
    }else{
      echo '<p>Ordine non gestito da Easy Store</p>';
    }
    // Metabox content
    
  }

Clienti che usano questo plugin

Per il momento sono


WooCommerce Pagamenti

  • WooCommerce PayPal Payments
  • WooCommerce Stripe Gateway

PayPal Sandbox

Vedere credenziali usate per commerce http://wiki.wmdemo.it/index.php?title=PayPal_Sandbox