### Shipment logic

**Boostrap.php**:

* Inside the `Bootstrap.php` file add the syntax for the shipment and document table creation.
```sql
Example:

$database->query(
'CREATE TABLE IF NOT EXISTS `s_viison_PROVIDER_NAME_shipments` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`orderId` int(11) DEFAULT NULL,
`productId` int(11) unsigned COLLATE utf8_unicode_ci NOT NULL,
`trackingCode` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`customerAddress` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`returnShipment` tinyint(1) NOT NULL,
`weight` double DEFAULT NULL,
`created` datetime NOT NULL,
PRIMARY KEY (`id`),
KEY `GENERATE_KEY` (`productId`),
KEY `GENERATE_KEY` (`orderId`),
CONSTRAINT `GENERATE_FK_KEY1` FOREIGN KEY (`productId`) REFERENCES `s_viison_PRIVIDER_NAME_products` (`id`),
CONSTRAINT `GENERATE_FK_KEY2` FOREIGN KEY (`orderId`) REFERENCES `s_order` (`id`) ON DELETE CASCADE,
)  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci'
);

$database->query(
'CREATE TABLE IF NOT EXISTS `s_viison_PROVIDER_NAME_shipment_documents`  (
`id` int(11) NOT NULL AUTO_INCREMENT,
`labelType` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`pageSize` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`shipmentId` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `GENERATE_KEY` (`shipmentId`),
CONSTRAINT `GENERATE_KEY` FOREIGN KEY (`shipmentId`) REFERENCES `s_viison_PROVIDER_NAME_shipments` (`id`) ON DELETE CASCADE
) DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci'
);
```

> The value `GENERATE_KEY` is a index key that doctrine generates. Because we are not using the Schema Generator from Doctrine, to ensure better idempotents and control, the FK key needs to be generated manually via the Shopware Common [foreign key generator](https://github.com/VIISON/ShopwareCommon/blob/cd28dc4630424e4a01dc3c8a44701f502208d73d/Classes/Installation/SQLHelper.php#L542) method.
>
> The value `GENERATE_FK_KEY1` & `GENERATE_FK_KEY2` is a FK key and can be generated by the `ViisonCommon::Util::ensureForeignKeyConstraint` method, 
>
> example: 
>Product: `$sqlHelper->ensureForeignKeyConstraint('s_viison_PROVIDER_NAME_shipments', 'productId', 's_viison_PROVIDER_NAME_products', 'id');`
> Shipment: `$sqlHelper->ensureForeignKeyConstraint('s_viison_PROVIDER_NAME_shipments', 'orderId', 's_order', 'id', 'CASCADE'');`

The initial Tables can be generated via the Schema tool (the schema code needs to be deleted later) or manual.
To do so you will need to execute the following code snippet once and copy the `CREATE` statement to use in the `Bootstrap.php`

```php
use Shopware\Plugins\ViisonCommon\Classes\Installation\Schema\SchemaCreator;
use Doctrine\ORM\Tools\SchemaTool;

...

$entityManager = $this->get('models');
$schemaTool = new SchemaTool($entityManager);
$schemaCreator = new SchemaCreator(
    $entityManager->getConnection()->getSchemaManager(),
    $schemaTool
);

// Create the tables of all custom models
$schemaCreator->createSchemaUnlessTableExists(array(
    $entityManager->getClassMetadata(
        'Shopware\CustomModels\Viison_PROVIDER_NAME_\Shipment\Shipment'
    ),
    $entityManager->getClassMetadata(
        'Shopware\CustomModels\Viison_PROVIDER_NAME_\Shipment\Document'
    )
));
```

* Add the drop table statement inside the `uninstall()` method for the `shipment_documents` and `shipment` (in this order because of the constraints) table to be dropped. Also the Product table should be the last one in the list.

* Inside the `loadPlugin()` method add `registerCustomModels()` method call after the namespace registration:
```php
private function loadPlugin()
    {
        // Check plugin licence
		...

        // Register the main namespace of this plugin
        ...

	    // Register custom models
        $this->registerCustomModels();
    }
```
> **Important**
>
> It is required to run a Migration Script (WORK IN PROGRESS) to migrate the old table data to the new Shipment tables. This is only **required** for the Shipping Providers that are released and by Customers in use before the `##NEXT RELEASE` version.

* Modify the `checkLicense()` method to call the Container directly ([more info](https://github.com/VIISON/ShopwareShippingCommon/issues/42)).

```php
public function checkLicense($throwException = true)
{
		// Check for dev modus
		...

        // Check license manager status
        if (!Shopware()->Container()->has('license')) {
            if ($throwException) {
                throw new \Exception('The license manager has to be installed and active');
            } else {
                return false;
            }
        }

        ...

        // Change this ...
        $l = $this->Application()->License();
        // ... to this
        $l = $this->get('license');
}
```

**Plugin Info**

* Inside the `PluginInfo.php` file override the `getShipmentDocumentModelName()` & `getShipmentModelName()`.

```php
...
    /**
     * @Override from ShippingCommon
     */
    public function getShipmentDocumentModelName()
    {
        return 'Shopware\CustomModels\PROVIDER_NAME\Shipment\Document';
    }

    /**
     * @Override from ShippingCommon
     */
    public function getShipmentModelName()
    {
        return 'Shopware\CustomModels\PROVIDER_NAME\Shipment\Shipment';
    }
...
```
> The following methods are not declared as Abstract inside Shipping Common because of the Backwards compatibility of Shipping Common.

**Controllers:**

> If somewhere we are calling the Communciation class we need to make sure that the Constructor is getting the EntityManager.

* Inside the `Controllers\ViisonPROVIDER_NAMEOrder.php` add the `Communication class` as a use statement. After that create a instance of it inside the constructor and pass it to the parent constructor as the last argument.
```php
use PATH\...\ProviderCommunicationClass;

class Shopware_Controllers_Backend_Viison_PROVIDER_NAME_Order extends ViisonShippingCommonOrder
{
    public function __construct(Enlight_Controller_Request_Request $request, Enlight_Controller_Response_Response $response)
    {
		...
        $communication = new ProviderCommunicationClass(Shopware()->Container()->get('models'));
        parent::__construct(..., $communication);
    }
}
```

**Shipping provider:**

* Inside the `Classes\ShippingProvider.php` add the `Communication class` as a use statement. After that create a instance of it inside the constructor and pass it to the parent constructor as the last argument.
```php
use PATH\...\ProviderCommunicationClass;

class ShippingProvider extends ShippingCommonProvider
{
    /**
     * ShippingProvider constructor.
     */
    public function __construct()
    {
		...
        $communication = new ProviderCommunicationClass(Shopware()->Container()->get('models'));
        parent::__construct(..., $communication);
    }
}
```

**Shipping label generator:**

* The ShippingLabelGenerator is now deprecated, but the delete process can be done after we remove it from ShippingCommon. For now we just needs to set the Communication class to null;
```
use PATH\...\ProviderCommunicationClass;

class ShippingLabelGenerator extends ShippingCommonLabelGenerator
{
    /**
     * ShippingLabelGenerator constructor.
     */
    public function __construct()
    {
        $this->com = null;
        ...
    }
}
```
**Entities:**

* Create the `Models` directory inside the the root directory of the Shipping Provider.
* Add `ViisonProviderName\Shipment` folders to the `Models` directory.
* Add `Shipment.php`, `Document.php` & `Repository.php` files inside the Shipment directory.
* Define the namespaces to follow this schema:
 `namespace Shopware\CustomModels\ViisonProviderName\Shipment;`
* Include the respective file namespaces from Shipping Common (for `Shipment.php` the [Shipment class](Models/ViisonShippingCommon/Shipment/Shipment.php) from ShippingCommon and so on) also add this following namespaces to `Shipment.php` and `Document.php`
```php
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
```
* Add the `@ORM\Table` and `@ORM\Entity` tags on the top of the class declaration s inside `Shipment.php` and `Document.php`

```php
/**
 * Extends Shipment model from ShippingCommon
 *
 * @ORM\Entity(repositoryClass="Shopware\CustomModels\ViisonProviderName\Shipment\Repository")
 * @ORM\Table(name="s_viison_PROVIDERE_NAME_shipments")
 *
 */
class Shipment extends ViisonShippingCommonShipment { ... }

/**
 * Extends Document model from ShippingCommon
 *
 * @ORM\Entity(repositoryClass="Shopware\CustomModels\ViisonProviderName\Shipment\Repository")
 * @ORM\Table(name="s_viison_PROVIDERE_NAME_shipment_documents")
 *
 */
class Document extends ViisonShippingCommonDocument { ... }
```

* Next we need to implement the abstract methods and define the associations for the `Product`, `Shipment` and `Document`
``` php
// Shipment.php
namespace Shopware\CustomModels\ViisonProviderName\Shipment;

use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Shopware\CustomModels\ViisonShippingCommon\Shipment\Shipment as ViisonShippingCommonShipment;

...
class Shipment extends ViisonShippingCommonShipment
{
    /**
     * OWNING SIDE
     *
     * @var int $shipmentId
     *
     * @ORM\OneToMany(targetEntity="Shopware\CustomModels\ViisonProviderName\Shipment\Document", mappedBy="shipment")
     */
    protected $documents;
    
    /**
     * OWNING SIDE
     *
     * @var $order
     * @ORM\ManyToOne(targetEntity="Shopware\CustomModels\ViisonGermanPost\Product\Product")
     * @ORM\JoinColumn(name="productId", referencedColumnName="id")
     */
    protected $product;

    /**
     * Abstract method implementation.
     */
    public function getDocuments()
    {
        return $this->documents;
    }

    /**
     * Abstract method implementation.
     *
     * @param Document[] $documents
     */
    public function setDocuments($documents)
    {
        $this->documents = new ArrayCollection($documents);
    }
    
    /**
     * Abstract method implementation.
     */
    public function getProduct()
    {
        return $this->getProduct();
    }

    /**
     * Abstract method implementation.
     *
     * @param Product $product
     */
    public function setProduct(Product $product)
    {
        $this->product = $product;
    }

    /**
     * Abstract method implementation.
     *
     * @param Document $document
     */
    public function addDocument($document)
    {
        if (!$this->documents->contains($document)) {
            $document->setShipmentInfo($this);
            $this->documents->add($document);
        }
    }

    /**
     * Shipment constructor.
     */
    public function __construct()
    {
        $this->documents = new ArrayCollection();
    }
}


...

// Document.php
namespace Shopware\CustomModels\ViisonProviderName\Shipment;

use Doctrine\ORM\Mapping as ORM;
use Shopware\CustomModels\ViisonProviderName\Shipment\Shipment;
use Shopware\CustomModels\ViisonShippingCommon\Shipment\Document as ViisonShippingCommonShippingDocument;

...

class Document extends ViisonShippingCommonShippingDocument
{
    /**
     * OWNING SIDE
     *
     * @var Shipment $shipment
     *
     * @ORM\ManyToOne(targetEntity="Shopware\CustomModels\ViisonProviderName\Shipment\Shipment",inversedBy="documents")
     * @ORM\JoinColumn(name="shipmentId", referencedColumnName="id", onDelete="CASCADE")
     */
    protected $shipment;

    /**
     * @return Shipment
     */
    public function getShipment()
    {
        return $this->shipment;
    }

    /**
     * @param Shipment $shipment
     */
    public function setShipment(Shipment $shipment)
    {
        $this->shipment = $shipment;
    }

    /**
     * @return string
     */
    public function getFilePrefix()
    {
        return 'provider_name';
    }
}
```
* Extend the [Repository class](Models/ViisonShippingCommon/Shipment/Repository.php) from Shipping Common in `Repository.php`.

> The Abstract methods In `Shipment.php` and `Document.php` inside Shipping Common are a reminder to implement the association properties inside the Shipping Providers, because *abstract entities can't have a referencing outside of it self*.

**Communication class:**

* Extend the Shipping Providers `Communication class` with the [Communication class](Classes/Communication/Communication.php) from Shipping Common:
```php
use Shopware\Plugins\ViisonShippingCommon\Classes\Communication\Communication as ShippingCommonCommunication;
...
class PrivoderCommunication extends ShippingCommonCommunication { ... }
```
* Include the `Shipment model's` from the Shipping Provider:
```php
use Shopware\Plugins\ViisonShippingCommon\Classes\Communication\Communication as ShippingCommonCommunication;
use Doctrine\ORM\EntityManager;
use Shopware\CustomModels\PROVIDER_NAMESPACE\Shipment\Document;
use Shopware\CustomModels\PROVIDER_NAMESPACE\Shipment\Shipment;

...

class PrivoderCommunication extends ShippingCommonCommunication { ... }
```

* Override the Shipping Provider's Constructor and add the following overrides:

``` 
    /**
     * Communication class constructor.
     *
     * @param EntityManager $entityManager
     */
    public function __construct(EntityManager $entityManager)
    {
        /** @Override from ShippingCommon */
        $this->util = Util::instance();
        /** @Override from ShippingCommon */
        $this->shippingUtil = new ShippingUtil();
        /** @Override from ShippingCommon */
        $this->adapterDisplayName = 'ProviderName';
        
        /** @Override from ShippingCommon */
        $this->useReturnCode = ...; // Optional parameter, by default 'true' the const SHOP_RETURN_PRODUCT_CODE needs to be defined

        parent::__construct($entityManager);

    }
```

* Implement all abstract methods from `ShippingCommonCommunication`.
* Inside the `sendRequestAndGetShipmentData(...)` method do the Request to get the documents, after that create the `Shipment model`  with the Request information and the `Document model's`. At the end downlaod the Document's.
```php
use Shopware\Plugins\ViisonShippingCommon\Classes\Communication\Communication as ShippingCommonCommunication;
use Shopware\CustomModels\PROVIDER_NAMESPACE\Shipment\Document;
use Shopware\CustomModels\PROVIDER_NAMESPACE\Shipment\Shipment;
...
class PrivoderCommunication extends ShippingCommonCommunication
{
 ...
     protected function sendRequestAndGetShipmentData
     {
      // Some Adapter logic and send the Request to get the documents
      ...

      /** @var Shipment $newShipment */
      $newShipment = new Shipment();
      $newShipment->fromArray(
           $this->getParameterForShipment(
               $trackingCode,
               $isReturn,
               $shipmentWeight,
               $shippingDetails
           )
      );
      
      // Set product
      if ($product['productId']) {
          $newShipmentInfo->setProduct(
              $this->entityManager->getReference(
                  $this->util->getPluginInfo()->getProductModelName(),
                  $product['productId']
              )
          );
      }
      
      // Set order
      if ($orderId) {
          $newShipmentInfo->setOrder(
              $this->entityManager->getReference(
                  $this->util->getPluginInfo()->getShopwareOrderModel(),
                  $orderId
              )
          );
      }

      /** @var Document $newDocumentOne */
      $newDocumentOne = new Document();
      $newDocumentOne->setShipmentInfo($newShipment);
      ...
      $this->entityManager->persist($newDocumentOne);

	  /** @var Document $newDocumentTwo */
      $newDocumentTwo = new Document();
      $newDocumentTwo->setShipmentInfo($newShipment);
      ...
      $this->entityManager->persist($newDocumentTwo);

	  // Add Documents to Shipment
      $newShipment->addDocument($newDocumentOne);
      $newShipment->addDocument($newDocumentTwo);
      ...
      $this->entityManager->persist($newShipment);

	  $this->entityManager->flush(
	   array(
	    $newShipment,
	    $newDocumentOne,
	    $newDocumentTwo,
	    ...
	   )
	  );

	  // Download the documents
	  $this->util->downloadDocumentToGivenPath($pdfFromDocumentOne,
	  $newDocumentOne->getDocumentPath());
      $this->util->downloadDocumentToGivenPath($pdfFromDocumentTwo,
      $newDocumentTwo->getDocumentPath());
      ...
      return $newShipment;
     }
 ...
}
```

> Tip: We can use $configuration entity for the sender data. For label creation please use the Util method `getConfigurationEntityByShopId`.

**Label Shipment Grid:**

* Go to `View\viison_PROVIDER_NAME_order\view\list` and duplicate the `label.js` file with the name `label_shipment.js`.
* Rename the name of `...list.Label` to `...list.LabelShipment` in the label_shipment.js and also add `Shipment` to the extend property.
*  Override the `url` property from Shipping Common with the `Order Controller name` from the Shipping Provider.
```javascript
/**
 * @Extend
 */
//{namespace name="backend/PROVIDER_NAME/order"}
Ext.define('Shopware.apps.PROVIDER_NAME.view.list.LabelShipment', {

    /**
     * Extend from the parent shipping common class.
     */
    extend: 'Shopware.apps.ViisonShippingCommonOrder.view.list.LabelShipment',

    /**
     * List of short aliases for class names. Most useful for defining xtypes for widgets.
     */
    alias: 'widget.order-viison-PROVIDER_NAME-label-shipment-list',

	...

    /**
     * @Override from ShippingCommon
     */
    url: '{url controller=ViisonPROVIDER_NAMEOrder action=getDocument}',

    /**
     * @Override from ShippingCommon
     */
    createDocumentLinksColumns: function () {
        return this.callParent(arguments).concat(
            {
                header: this.snippets.columns.exportDocumentUrl,
                dataIndex: 'documents',
                flex: 10,
                renderer: function (documents, style, record) {
                    return this.renderDocumentUrl(documents, style, record, 'exportDocument')
                }
            }
        );
    },

});
```
* Shipping Common by default support the rendering of the `label` and `return label` inside the `label_shipment` grid. The rendering of additional documents the Adapter needs to handle by himself:

```javascript
/**
 * @Extend
 */
//{namespace name="backend/PROVIDER_NAME/order"}
Ext.define('Shopware.apps.PROVIDER_NAME.view.list.LabelShipment', {

   ...

	/**
	* @Override from ShippingCommon
	*/
	createDocumentLinksColumns: function () {
	    return this.callParent(arguments).concat(
	          {
	             header: this.snippets.columns.DOCUMENT_TYPEUrl,
	             dataIndex: 'documents',
	             flex: 10,
	             renderer: function (documents, style, record) {
	                    return this.renderDocumentUrl(
	                    documents,
	                    style,
	                    record,
	                    'DOCUMENT_TYPE_NEEDS_TO_BE_ADD_HERE'
	                    )
	                }
	          },

	          ...

	        );
	    },

});
```
> The `renderDocumentUrl` function is also handling the snippet's. By default all snippets should have the name `docuemnt_type_nameUrl` and `no_docuemnt_type_nameUrl` otherwise we can send the snippets as argument's to the function.

* Go to `View\viison_PROVIDER_NAME_order\view\detail\tab.js` and override the `gridView` property
```javascript
...

    /**
     * @Override from ShippingCommon
     */
    gridView: 'Shopware.apps.ViisonPROVIDER_NAMEOrder.view.list.LabelShipment',

...
```
* Do the same override in `View\viison_PROVIDER_NAME_free_form_labels\view\window.js`.
> This overrides are required because of the backwards compatibility inside Shipping Common. By default the Order Flow is using the `...\label.js` grid so we need to explicitly say that we want to use the new shipment grid inside the Shipping Provider.
