środa, 13 stycznia 2021

WordPress WooCommerce - Wydruk do zamówienia

 Były święta więc masa pracy i nie było czasu pomyśleć, za chwilę Dzień Babci i Dziadka, a w Piernikarni wysyłki. Nasz sklep internetowy działa i można kupić online. Jednak zamówienia są indywidualne, a na produkcji trzeba zachować odpowiednie warunki sanitarne i niema mowy o ekranach dotykowych i sprawdzaniu co chwilę "co to miało być?".

Więc trzeba wydrukować zamówienia i tu mamy nasz problem biznesowy:

 - Chcemy wydrukować ze strony listę zamówień,

a) z opisem klienta bo są tam indywidualne wymagania

b) z listą produktów

c) każdy produkt ma dodatkowe atrybuty (chłopiec/ dziewczynka, pudełko, dedykacja)

d) adres wysyłki i telefon kontaktowy - bywa inny niż adres klienta

e) niech będzie czytelne, ale zajmuje mało miejsca by nie marnować papieru


Uwagi dodatkowe:

- nie można modyfikować kodu pluginów - bo przy aktualizacji zostaną usunięte zmiany.

 

Rozważania koncepcyjne

Koncepcja 1)

Załatwimy to CSS'em. Czyli dodać kod, który w podglądzie zamówień ukryje wszystko oprócz danych które chcemy wydrukować. Oczywiście tylko w wydruku, można to przetestować przy podglądzie wydruku.

@media print {

#adminmenumain, #adminmenuwrap,
#wp__notice-list,
.woocommerce-layout,
.wrap>h1,
.wrap>a,
.wrap>.updated,
.wrap>.notice,
.wpbody-content .wrap>div,
#postbox-container-1
{
display: none;
}

}

 wnioski:

Niestety na tej stronie niema wszystkich danych.


Szukamy dalej...


Koncepcja 2)

Sprawdźmy jak wygląda kod, który tworzy te listy w Wordpress'ie i może to się jakoś wykorzysta. Interesujące nas dane są na dwuch stronach

strona A - Lista zamówień: 

WooCommerce > Zamówienia ( .../edit.php?post_type=shop_order )

Mamy tu teoretycznie pętle, która pobiera wszystkie interesujące nas wpisy i wyświetla je.

strona B - Szczegóły zamówienia

 ( post.php?post=${id}&action=edit )

Mamy tu  dane zamówienia, takie jak: który produkt, atrybuty specjalne, informacje od klienta (imię, dydykacja)

Wniosek wstępny

Tu powinniśmy szukać, pierwsza strona daje nam listę wszystkich produktów, druga informacje, które będą potrzebne.

Zbieranie danych:

Strona A, jest generowana przez class'e :


class WC_Admin_List_Table_Orders extends WC_Admin_List_Table

Sama lista jest w formie <table> więc ciężko będzie nią manipulować. Sam kod css jest bardzo responsywny i w wersji na komórkę wygląda trochę inaczej. Tym zajmiemy się później.

Każda metoda tworzy inną komórkę i są one uruchamiane w pętli otrzymując dabe:

/**
* Define which columns to show on this screen.
*
* @param array $columns Existing columns.
* @return array
*/

public function define_columns( $columns ) {

 tu jest lista wszystkich komórek (kolumn) w wierszu, pomyślimy może tu się wepniemy.


Strona B, jest bardziej złożona. Kilka class tworzy jej części. Interesują mnie tylko pola z danymi i nie potrzebuje ich edytować:

 DIV  - szczegóły zamówienia



class WC_Meta_Box_Order_Data {


metoda 



public static function output( $post ) {


tworzy html tego div'a

DIV - Produkty


class WC_Meta_Box_Order_Items {

 ale metoda


public static function output( $post ) {

 która ustaliłem, że tworzy listę produktów ładuje kolejny plik...


include __DIR__ . '/views/html-order-items.php';

 i plik ten robi już bardzo dużo - wyświetla, pozwala edytować, ma masę if'ów. 


wnioski:

Dużo do przemyślenia i uwaga nie modyfikujemy orginalnego kodu tylko piszemy pligin, który będzie wpinał się w hooki WooCommerce, albo tworzył własną listę.  Pomyślimy nad nową koncepcją.


 Koncepcja 3)

Wnioski z poprzednich koncepcji

 Przeglądając kod strony A (lista zamówień) trafiłem na metodę:



protected function render_order_number_column() {
/* [...] */
echo '<a href="#" class="order-preview" data-order-id="' . absint( $this->object->get_id() ) . '" title="' . esc_attr( __( 'Preview', 'woocommerce' ) ) . '">' . esc_html( __( 'Preview', 'woocommerce' ) ) . '</a>';
echo '<a href="' . esc_url( admin_url( 'post.php?post=' . absint( $this->object->get_id() ) ) . '&action=edit' ) . '" class="order-view"><strong>#' . esc_attr( $this->object->get_order_number() ) . ' ' . esc_html( $buyer ) . '</strong></a>';

Hmm tworzy dwa guziki, które pobierają ajax'em dane, które mnie interesują. Dodatkowo w formie graficznej, która jest niemal idealna.

 

Możemy stworzyć nową stronę, która zassa te dane i ustawi je CSS'em (grid, albo flex) i prawdopodobnie już po robocie. Powinno być bezpieczne bo WC odpowiada za udostępniony kod.

Uwaga sprawdzić bezpieczeństwo, niechcemy by każdy mógł zobaczyć te dane, awięc mały audyt WC trzeba zrobić, czy weryfikuje uprawnienia przed udostępnieniem tych konkretnych danych.

 

Zbieranie danych

event click pobiera ajaxem z adresu: 

../wp-admin/admin-ajax.php?order_id=**ID**&action=woocommerce_get_order_details&security=**KOD**
 
dane w postaci JSON'a i wstawia je do template js po stronie frontend'u. 

Backend:

 Template js jest tworzony przez tą samą Class'e tutaj...



class WC_Admin_List_Table_Orders extends WC_Admin_List_Table {


public function order_preview_template() {

?>
<script type="text/template" id="tmpl-wc-modal-view-order">

To dość oczywiste, ale popup z template wstawiany jest do DIV#wc-backbone-modal-dialog, który jest niszczony po zamknięciu popup'a.

Uwagi

Widzę też, że zapytanie ma dołączony **KOD** weryfikujący uprawnienia dla zapytania ajax po stronie backendu.

Wnioski

Algorytm mógł by działać tak.

1) Włączamy filtr, który zostawi tylko te, które chcemy wydrukować (albo nie włączymy)

2) Uruchomimy funkcję w pętli jQuery - która przejdzie przez całą tabelę i zainicjuje pobieranie danych

3) dane zostaną wstawione do nowego Box'a 

4) Wystylizowanie css'em (flexbox) i ukrycie w podglądzie wydruku wszystkiego czego nie chcemy drukować (czyli właściwie wszystkiego oprucz naszego box'a).

5) opcjonalnie można usunąć jeszcze boxy z pobranych

6? może drag&drob by zmienić kolejność - opcjonalnie jako gadźet

7) klikamy drukuj i mamy zamieżony efekt ;-)

Zieranie danych - Frontend

Już myślami byłem przy pisaniu kodu, ale jeszcze ważna kwestia jak działa to na frontendzie.

Nasz przyjaciel w IDE to shift+ctrl+f (dzukaj w całym projekcie) znów się sposał...

plik wc_orders.js (wc_orders.min.js)

Dodanie event click... który uruchamia funkcje WCOrdersTable.prototype.onPreview

    $( document )
/** line: 14 **/
.on( 'click', '.order-preview:not(.disabled)', this.onPreview );
};

Szybko też namierzam kod weryfikujący w lini 63 ?


$.ajax({
url: wc_orders_params.ajax_url,
data: {
order_id: $order_id,
action : 'woocommerce_get_order_details',
security: wc_orders_params.preview_nonce /** line: 66 *//
},
type: 'GET',
success: function( response ) {
    $( '.order-preview' ).removeClass( 'disabled' );

    if ( response.success ) {
    $previewButton.data( 'order-data', response.data );

    $( this ).WCBackboneModal({
    template: 'wc-modal-view-order',
    variable : response.data
    });
}
}

WCBackboneModal jest w pliku backbone-modal.js i odpowiada za stworzenie popup'a div#wc-backbone-modal-dialog.

Sprawdziłem jeszcze listę hooks do WC z tej strony : https://premmerce.com/woocommerce-hooks-guide/#reading-progress-title-8

tu się będziemy wpinać:

<?php do_action( 'woocommerce_order_details_after_order_table', $order ); ?>