Propojení DPD a WooCommerce
Pro weby sítě behejsepsem.cz jsem vytvořil WordPress plugin, který propojuje WooCommerce s přepravní službou DPD.
DPD má WSDL API, jehož specifikace je dostupná na https://www.mojedpd.cz/IT4EMWebServices/eshop/ShipmentServiceImpl?wsdl. Pro integraci služeb, které používají WSDL specifikaci, se mi nejvíce osvědčilo vygenerovat si z definice WSDL PHP SDK, které umožnují pohodlně vytvářet XML requesty namísto toho, aby se nějak ručně psaly. Tento způsob umožňuje i výrazně jednodušší údržbu systému, kdy při změně definice API stačí přegenerovat jednotlivé třídy SDK.
Pro generování tříd používám WsdlToPhp/PackageGenerator, který zatím fungoval u všech projektů, u nichž jsem ho použil. Pro vygenerování PHP SDK pro DPD stačí pustit v terminálu:
wsdltophp generate:package \
urlorpath="https://www.mojedpd.cz/IT4EMWebServices/eshop/ShipmentServiceImpl?wsdl" \
--destination="DPDManifestAPI" \
--composer-name="woo-dpd/dpd-manifest-api" \
--force --namespace="WPProgramator\WOO_DPD\DPDManifestApi"
Tento příkaz vygeneruje všechy potřebné třídy pro napojení DPD ShipmentAPI.
Příklad třídy, která zajišťuje samotné napojení, pak vypadá takto:
<?php
namespace WPProgramator\WOO_DPD\DPD;
use WPProgramator\WOO_DPD\DPDManifestApi\StructType\ReferenceVO;
use WPProgramator\WOO_DPD\DPDShipmentApi\ClassMap;
use WPProgramator\WOO_DPD\DPDShipmentApi\ServiceType\Create;
use WPProgramator\WOO_DPD\DPDShipmentApi\StructType\AddFieldShopShipmentVO;
use WPProgramator\WOO_DPD\DPDShipmentApi\StructType\CreateShipment;
use WPProgramator\WOO_DPD\DPDShipmentApi\StructType\ErrorVO;
use WPProgramator\WOO_DPD\DPDShipmentApi\StructType\ParcelResultVO;
use WPProgramator\WOO_DPD\DPDShipmentApi\StructType\ShipmentResultVO;
use WPProgramator\WOO_DPD\DPDShipmentApi\StructType\ShipmentVO;
use WPProgramator\WOO_DPD\DPDShipmentApi\StructType\ParcelVO;
use WPProgramator\WOO_DPD\DPDShipmentApi\StructType\AdditionalServiceVO;
use WPProgramator\WOO_DPD\DPDShipmentApi\StructType\CodVO;
use WPProgramator\WOO_DPD\DPDShipmentApi\StructType\ParcelShopVO;
use WPProgramator\WOO_DPD\DPDShipmentApi\StructType\ParcelShopShipmentVO;
use WPProgramator\WOO_DPD\DPDShipmentApi\StructType\MoreServicesShopShipmentVO;
use WPProgramator\WOO_DPD\DPDShipmentApi\StructType\ServiceShopShipmentVO;
use WsdlToPhp\PackageBase\AbstractSoapClientBase;
use WPProgramator\WOO_DPD\Order;
use function WPProgramator\WOO_DPD\wpp_woo_dpd_container;
class Shipment {
/**
* @var CreateShipment
*/
private $create_shipment;
/**
* @var
*/
private $shipment_vo;
/**
* @var ParcelVO
*/
private $parcel_vo;
/**
* @var ReferenceVO
*/
private $reference_vo;
/**
* @var AdditionalServiceVO
*/
private $additional_service_vo;
/**
* @var CodVO
*/
private $cod_vo;
/**
* @var ParcelShopVO
*/
private $parcel_shop_vo;
/**
* @var ParcelShopShipmentVO
*/
private $parcel_shop_shipment_vo;
/**
* @var DPD
*/
private $dpd;
public function __construct(
DPD $dpd,
CreateShipment $create_shipment,
ShipmentVO $shipment_vo,
ParcelVO $parcel_vo,
ReferenceVO $reference_vo,
AdditionalServiceVO $additional_service_vo,
CodVO $cod_vo,
ParcelShopVO $parcel_shop_vo,
ParcelShopShipmentVO $parcel_shop_shipment_vo
) {
$this->dpd = $dpd;
$this->create_shipment = $create_shipment;
$this->shipment_vo = $shipment_vo;
$this->parcel_vo = $parcel_vo;
$this->reference_vo = $reference_vo;
$this->additional_service_vo = $additional_service_vo;
$this->cod_vo = $cod_vo;
$this->parcel_shop_vo = $parcel_shop_vo;
$this->parcel_shop_shipment_vo = $parcel_shop_shipment_vo;
}
/**
* @param $order_id
*/
public function add_shipments( $order_id ) {
$this->setup_shipment_service();
$shipments = $this->create_shipment->getShipmentList();
if ( is_array( $order_id ) ) {
foreach ( $order_id as $id ) {
$shipments[] = $this->get_shipment( $id );
}
} else {
$shipments[] = $this->get_shipment( $order_id );
}
$this->create_shipment->setShipmentList( $shipments );
$create = $this->get_create_service();
$create->createShipment( $this->create_shipment );
$result = $create->getResult()->getResult()->getResultList();
$data = [];
foreach ( $result as $item ) {
/** @var $res ShipmentResultVO */
/** @var $error ErrorVO */
$error = $item->getError();
if ( $error ) {
$data['errors'][] = new \WP_Error( $error->getCode(), $error->getText() );
} else {
/** @var $item ShipmentResultVO */
$data['shipments'][ $item->getShipmentReference()->getReferenceNumber() ] = [
'shipment_id' => $item->getShipmentReference()->getId(),
'shipment_reference_no' => $item->getShipmentReference()->getReferenceNumber(),
];
foreach ( $item->getParcelResultList() as $parcel_result ) {
/** @var $parcel_result ParcelResultVO */
$data['shipments'][ $item->getShipmentReference()->getReferenceNumber() ]['parcels'][] = [
'parcel_id' => $parcel_result->getParcelId(),
'parcel_reference_no' => $parcel_result->getParcelReferenceNumber(),
];
}
}
}
return $data;
}
/**
* Setup the shipment service
*/
private function setup_shipment_service() {
$this->create_shipment->setWsUserName( $this->dpd->username )
->setWsPassword( $this->dpd->password )
->setWsLang( $this->dpd->lang )
->setApplicationType( $this->dpd->application_type )
->setPriceOption( 'WithoutPrice' );
}
/**
* Get a single shipment
*
* @param $order_id
*
* @return ShipmentVO
*/
private function get_shipment( $order_id ) {
/** @var Order $order */
$order = wpp_woo_dpd_container()->create( 'WPProgramator\WOO_DPD\Order', [ $order_id ] );
$wc_order = $order->get_order();
$street = $wc_order->get_shipping_address_1() ?: $wc_order->get_billing_address_1();
$number = '';
$splitted_street = $this->split_street( $street );
if ( $splitted_street ) {
$street = $splitted_street[1];
$number = $splitted_street[2];
}
$this->shipment_vo = new ShipmentVO();
// Basic package details
$shipment = $this->shipment_vo->setShipmentReferenceNumber( $order->get_reference_no() )
->setPayerId( $this->dpd->payer_id )
->setSenderAddressId( $this->dpd->sender_address_id )
->setReceiverName( $wc_order->get_formatted_shipping_full_name() ?: $wc_order->get_formatted_billing_full_name() )
->setReceiverFirmName( $wc_order->get_billing_company() )
->setReceiverCountryCode( $wc_order->get_shipping_country() ?: $wc_order->get_billing_country() )
->setReceiverZipCode( $wc_order->get_shipping_postcode() ?: $wc_order->get_billing_postcode() )
->setReceiverCity( $wc_order->get_shipping_city() ?: $wc_order->get_billing_city() )
->setReceiverStreet( $street )
->setReceiverHouseNo( $number )
->setReceiverPhoneNo( $wc_order->get_billing_phone() )
->setMainServiceCode( '1' )
->setReceiverEmail( $wc_order->get_billing_email() )
->setMainServiceCode( $this->get_main_service_code( 'private' ) );
$parcels = [];
for ( $i = 0; $i < $order->get_packages_no(); $i ++ ) {
$parcel = new ParcelVO();
$parcels[] = $parcel->setParcelReferenceNumber( $this->dpd->store_prefix . '-' . $wc_order->get_order_number() . '-' . rand() )
->setWeight( "1" );
}
// Reset the additional services
$this->shipment_vo->setParcels( $parcels );
$this->additional_service_vo->setCod( null )
->setParcelShop( null )
->setDocumentReturn( null )
->setHighInsurance( null )
->setExpay( null )
->setIdCheck( null )
->setMoreServices( null )
->setSaturdayDelivery( null )
->setPredictEmail( null )
->setPredictSms( null )
->setTimeFrame( null );
$additional_service_found = false;
$this->shipment_vo->setAdditionalServices( null );
// Support COD payments
if ( $order->has_cod() ) {
$cod = $this->cod_vo->setAmount( $wc_order->get_total() )
->setCurrency( $wc_order->get_currency() )
->setPaymentType( 'Cash' );
$this->additional_service_vo->setCod( $cod );
$additional_service_found = true;
}
// Support Parcel Shop
if ( $order->is_parcel_shop() ) {
$data = $order->get_parcel_shop_data();
$parcel_shop = $this->parcel_shop_shipment_vo->setParcelShopId( $data['id'] )
->setCompanyName( $data['company'] )
->setCity( $data['city'] )
->setStreet( $data['street'] )
->setHouseNo( $data['house_no'] )
->setCountryCode( $data['country_code'] )
->setZipCode( $data['postal_code'] );
$this->additional_service_vo->setParcelShop( $parcel_shop );
$this->shipment_vo->setMainServiceCode( $this->get_main_service_code( 'parcel_shop' ) );
$additional_service_found = true;
}
// Support multiple packages
if ( $order->get_packages_no() > 1 && $order->has_cod() ) {
$field = new AddFieldShopShipmentVO();
$field->setName( 'ComplDel' )
->setValue( '1' );
$service_vo = new ServiceShopShipmentVO();
$service_vo->setCode( '50106' )
->addField( $field );
$more_services_shipment_vo = new MoreServicesShopShipmentVO( [ $field ] );
$this->additional_service_vo->setMoreServices( $more_services_shipment_vo );
}
if ( $additional_service_found ) {
$this->shipment_vo->setAdditionalServices( $this->additional_service_vo );
}
return $shipment;
}
/**
* Get number from street
*
* @param $street
*
* @return bool
*/
private function split_street( $street ) {
if ( preg_match( '/([^\d]+)\s?(.+)/i', $street, $result ) ) {
return $result;
}
return false;
}
/**
* Get the main service code
*
* @param $service
*
* @return string
*/
public function get_main_service_code( $service ) {
switch ( $service ) {
case 'private':
$code = '109';
break;
case 'parcel_shop':
$code = '50101';
break;
default:
$code = '109';
}
return $code;
}
/**
* Setup the service
* @return Create
*/
public function get_create_service() {
$options = array(
AbstractSoapClientBase::WSDL_URL => 'https://www.mojedpd.cz/IT4EMWebServices/eshop/ShipmentServiceImpl?wsdl',
AbstractSoapClientBase::WSDL_CLASSMAP => ClassMap::get(),
);
return new Create( $options );
}
}
WooCommerce plugin pro DPD aktuálně podporuje služby Standard, Private, Parcel Shop, a doplňkové služby Dobírka a Kompletní doručení. Přidání dalších funkcí je díky napojení přes wsdl specifikaci opravdu jednoduché.
Pokud budete mít o propojení WooCommerce s DPD nebo podobné řešení zájem, neváhejte mě kontaktovat.
Václav Greif se programování pro Wordpress věnuje více než 16 let. Za tu dobu nasbíral mnoho zkušeností s tvorbou pluginů pro Wordpress, úpravou šablon a programováním komplexních funkcionalit. Věnuje se programování pro Wordpress a školení programátorů.
