Créer des envois par programme

32

Je suis tombé sur différentes façons de créer un envoi par programmation. Elles sont

     //Type 1
     $converter=Mage::getModel('sales/convert_order');
     $shipment=$converter->toShipment($order);
     // snip

     //Type 2
     $shipment = Mage::getModel('sales/service_order', $order)
                                ->prepareShipment($this->_getItemQtys($order));
     // snip

     //Type 3
     $shipment = Mage::getModel('sales/service_order', $order)->prepareShipment($itemQty);
     $shipment = new Mage_Sales_Model_Order_Shipment_Api();
     $shipmentId = $shipment->create($orderId);
     // snip

Quelle est la différence entre ces méthodes? Parmi les trois méthodes, il convient de créer des envois et d’ajouter des numéros de suivi.

blakcaps
la source
Avez-vous besoin de plus de détails dans ma réponse pour justifier une récompense d'acceptation et de récompense? Je suis ouvert à la critique ou à la clarification si vous le désirez.
philwinkle

Réponses:

47

Je vais tenter le coup. Prenons-les un à la fois:

Méthode 1

$converter=Mage::getModel('sales/convert_order');
$shipment=$converter->toShipment($order);

$converterci-dessus est chargé à partir de la classe Mage_Sales_Model_Convert_Order, qui utilise un assistant principal appelé copyFieldsetpour copier les détails de la commande dans un objet d'expédition. $ order doit être de type array ou Varien_Object.

Cette méthode est en réalité au cœur de la méthode 3, comme elle l’utilise Mage::getModel('sales/convert_order')dans son appel de constructeur.

Différenciateur clé de cette méthode - il peut prendre un tableau ou un objet $orderet générer un $shipmentobjet de base . Il s'agit d'une méthode de niveau inférieur utilisée exclusivement par les méthodes que vous avez décrites dans Méthode 2, Méthode 3.

Méthode 2

 $shipment = Mage::getModel('sales/service_order', $order)
                            ->prepareShipment($this->_getItemQtys($order));

Cela semble être le moyen le plus populaire dans le noyau de Magento de générer un envoi tel qu'il est utilisé à la fois dans les contrôleurs d'envoi et de facturation. $orderest utilisé comme argument de constructeur pour l'instanciation de Mage_Sales_Model_Service_Order, en le définissant comme une propriété protégée sur l'objet.

Vous appelez prepareShipmentet passez une quantité. Comme cette méthode utilise la classe de convertisseur de la Méthode 1, vous n'avez pas besoin de spécifier plus de détails, tels que les éléments de commande, les détails de quantité d'envoi d'articles dans l' prepareShipmentargument, appelé ici avec $this->_getItemQtys. Pour l'utiliser dans votre propre contexte, il vous suffit de transmettre la quantité d'éléments d'un tableau au format suivant:

array(
  'order_item_id'=>$qty,
  'order_item_id'=>$qty,
  'order_item_id'=>$qty
)

Différenciateur clé de cette méthode - il vous restitue un objet $ shipping, mais avec tous les éléments convertis. C'est plug-and-play.

Méthode 3

Je n'ai pas pu trouver la preuve de l'utilisation de cette méthode dans le Core. Cela ressemble à un bidouillage, pour être honnête. Voici la méthode:

$itemQty =  $order->getItemsCollection()->count();
$shipment = Mage::getModel('sales/service_order', $order)->prepareShipment($itemQty);
$shipment = new Mage_Sales_Model_Order_Shipment_Api();
$shipmentId = $shipment->create($orderId);

L'étape 1 est identique à la méthode 2 ci-dessus. Aucune différence. Cependant, vous récupérez un $shipmentobjet, qui est remplacé par une insantiation directe de Mage_Sales_Model_Order_Shipment_Api. Ceci est non standard. La meilleure façon d'obtenir un envoi Api Object serait d'appeler Mage::getModel('sales/order_shipment_api').

Ensuite, il utilise cet nouvel objet API d'envoi, écrasé, pour créer un envoi à partir d'une $orderIdvariable qui n'a pas été définie dans votre code. Encore une fois, cela semble être une solution de contournement.

En regardant Mage_Sales_Model_Order_Shipment_Api::create(), cela semble être un guichet unique pour générer un envoi, car les détails les plus élémentaires nécessaires pour créer l'envoi ne sont qu'une commande increment_id.

C'est un hack qui ne devrait pas être utilisé par aucun module ou extension. Cette API est censée être utilisée par les fonctionnalités exposées via les requêtes d'API XML RPC / SOAP et est intentionnellement de base pour éliminer les requêtes d'API à plusieurs étapes.

La méthode 3 finit par atteindre le plus concret, cependant, et via un appel à Mage_Sales_Model_Order, elle appelle prepareShipment, ce qui est une abstraction d’ordre supérieur pour la méthode habituelle 2 ci-dessus:

public function prepareShipment($qtys = array())
{
    $shipment = Mage::getModel('sales/service_order', $this)->prepareShipment($qtys);
    return $shipment;
}

Différenciateur clé ici - si vous avez besoin d'un envoi, que cela ne vous dérange pas et que vous n'ayez qu'un incrément_id - utilisez cette méthode. Également des informations utiles si vous préférez gérer cela via l'API SOAP.

J'espère que ça aide.

philwinkle
la source
1
Un avertissement pour tous ceux qui utilisent Magestore Inventory Management: La méthode 3 ne déclenche pas leurs points d'ancrage, ce qui entraîne des différences entre les envois entre les envois Magento Core et les envois Warehouse. Aussi bonne réponse OP :)
Ricky Odin Matthews
7

L'essentiel ici est que les méthodes 1 et 2 ne fonctionnent pas ...

Je suis d'accord avec @philwinkle cependant, la méthode 3 est hacky. Les fonctions de l'API ne doivent pas vraiment être appelées dans un contexte non-API. Vous ne savez jamais ce que les futures versions pourront apporter à ce type de code.

Alors qu'est-ce que cela laisse? Eh bien, les méthodes 1 et 2 ne sont pas cassées exactement. C'est juste qu'ils ne font qu'une partie du travail. Voici à quoi ils devraient ressembler:

Remarque: par souci de brièveté, les extraits de code suivants ajouteront tous les articles éligibles à l'envoi. Si vous souhaitez simplement expédier une partie d'une commande, vous devrez modifier certaines parties du code. J'espère cependant vous en avoir donné suffisamment pour continuer.

Méthode 1

Si vous regardez le code dans app/code/core/Mage/Sales/Model/Order/Shipment/Api.php(tel qu'utilisé dans la méthode 3), vous verrez qu'en plus, $convertor->toShipment($order)il appelle également $item = $convertor->itemToShipmentItem($orderItem), $item->setQty($qty)et $shipment->addItem($item)pour chaque poste de commande éligible. Eh oui, Magento est vraiment paresseux, il faut le cajoler à travers tous. Unique. Étape. Ensuite, vous devez parcourir quelques étapes supplémentaires pour enregistrer l’envoi dans la base de données.

Donc, la méthode 1 devrait ressembler à ceci:

$convertor = Mage::getModel('sales/convert_order');
$shipment = $convertor->toShipment($order);
foreach ($order->getAllItems() as $orderItem) {
    if ($orderItem->getQtyToShip() && !$orderItem->getIsVirtual()) {
        $item = $convertor->itemToShipmentItem($orderItem);
        $item->setQty($orderItem->getQtyToShip());
        $shipment->addItem($item);
    }
}
$shipment->register();
$order->setIsInProcess(true);
Mage::getModel('core/resource_transaction')
         ->addObject($shipment)
         ->addObject($order))
         ->save();

Méthode 2

Tout d’abord, vous avez un appel $this->_getItemQtys()qui ne fonctionnera évidemment que dans certaines classes (celles qui ont ou héritent d’une fonction _getItemQtys, natch). Cela doit donc changer et, comme pour la méthode 1, vous devez également préciser le processus.

En regardant dans app/code/core/Mage/Adminhtml/controllers/Sales/Order/ShipmentController.phpc'est une situation un peu mieux avec cette approche - il semble que les éléments sont convertis en même temps que l'expédition elle - même. Mais vous ne récupérez toujours qu'un objet transitoire que vous devez enregistrer vous-même dans la base de données, comme suit:

$itemQtys = array();
foreach ($order->getAllItems() as $orderItem) {
    if ($orderItem->getQtyToShip() && !$orderItem->getIsVirtual()) {
        $itemQtys[$orderItem->getId()] = $orderItem->getQtyToShip();
    }
}
$shipment = Mage::getModel('sales/service_order', $order)->prepareShipment($itemQtys);
$shipment->register();
$order->setIsInProcess(true);
Mage::getModel('core/resource_transaction')
         ->addObject($shipment)
         ->addObject($order)
         ->save();

Je vous recommande également d’ajouter un petit contrôle d’erreur, par exemple pour vous assurer que votre envoi contient bien les articles qui vous ont été présentés register().

Quel est le meilleur?

Je dirais que c'est une question d'opinion. Je n'ai fait aucun test de référence, mais je suis assez confiant que la différence de vitesse entre les deux méthodes serait négligeable. En ce qui concerne la taille et la lisibilité du code, il n'y a pas beaucoup entre eux.

J'aime bien la méthode 2 pour ne pas avoir à convertir explicitement tous les articles de la commande, mais vous devez quand même les parcourir pour en extraire les quantités. Pour une belle petite empreinte de code, la méthode 3 serait ma préférée! Mais en tant qu’ingénieur logiciel, je ne peux pas le recommander. Donc, je vais dodue pour la méthode 2.

Doug McLean
la source
1

Les gars Aucun de ce qui précède n'a travaillé sur mon problème. Ce qui suit a fonctionné pour moi. Mettez-le ici au cas où cela pourrait vous aider.

public function _createShipment($orderIncrementId = '100310634'){
    // Load Product ..
    $order = Mage::getModel('sales/order')->loadByIncrementId($orderIncrementId);

    // Create Qty array
    $shipmentItems = array();
    foreach ($order->getAllItems() as $item) {
        $shipmentItems [$item->getId()] = $item->getQtyToShip();
    }

    // Prepear shipment and save ....
    if ($order->getId() && !empty($shipmentItems) && $order->canShip()) {
        $shipment = Mage::getModel('sales/service_order', $order)->prepareShipment($shipmentItems);
        $shipment->save();
    }
}
m82amjad
la source
Pourquoi qty_shipped n'est-il pas renseigné dans la table sales_flat_order_item avec cette méthode?
Applications créatives