Browse code

Add order export job and command

Benjamin Roth authored on13/11/2024 16:03:47
Showing2 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,97 @@
1
+<?php
2
+
3
+declare(strict_types=1);
4
+
5
+/*
6
+ * This file is part of vonRotenberg WMFGO Cevisio Bundle.
7
+ *
8
+ * (c) vonRotenberg
9
+ *
10
+ * @license proprietary
11
+ */
12
+
13
+namespace vonRotenberg\WmfgoCevisioBundle\Command;
14
+
15
+use Contao\CoreBundle\Framework\ContaoFramework;
16
+use Contao\System;
17
+use Symfony\Component\Console\Command\Command;
18
+use Symfony\Component\Console\Input\InputInterface;
19
+use Symfony\Component\Console\Input\InputOption;
20
+use Symfony\Component\Console\Output\OutputInterface;
21
+use Symfony\Component\Console\Style\SymfonyStyle;
22
+use vonRotenberg\WmfgoCevisioBundle\Cron\ShopwareExportOrdersJob;
23
+use vonRotenberg\WmfgoCevisioBundle\Cron\ShopwareImportProductsJob;
24
+
25
+class ShopwareExportOrdersCommand extends Command
26
+{
27
+    protected static $defaultName = 'shopware:export-orders';
28
+
29
+    /**
30
+     * @var ContaoFramework
31
+     */
32
+    private $framework;
33
+
34
+    private $start;
35
+    private $stop;
36
+
37
+    public function __construct(ContaoFramework $framework)
38
+    {
39
+        $this->framework = $framework;
40
+
41
+        parent::__construct();
42
+    }
43
+
44
+    protected function configure(): void
45
+    {
46
+        $this
47
+            ->setName(self::$defaultName)
48
+            ->addOption('start', 'b', InputOption::VALUE_REQUIRED, 'Custom start date')
49
+            ->addOption('stop', 'x', InputOption::VALUE_REQUIRED, 'Custom stop date')
50
+            ->setDescription('Export orders from Shopware instance')
51
+        ;
52
+    }
53
+
54
+    protected function interact(InputInterface $input, OutputInterface $output): void
55
+    {
56
+        if ((null !== $input->getOption('start') && null === $input->getOption('stop'))
57
+            || (null === $input->getOption('start') && null !== $input->getOption('stop')))
58
+        {
59
+            throw new \RuntimeException('You need to provide start and stop date.');
60
+        }
61
+
62
+        if (null !== $input->getOption('start')) {
63
+            try {
64
+                new \DateTime($input->getOption('start'));
65
+                $this->start = $input->getOption('start');
66
+            } catch(\Exception $e)
67
+            {
68
+                throw new \InvalidArgumentException('Start date is invalid. Please provide an ISO date.');
69
+            }
70
+        }
71
+
72
+        if (null !== $input->getOption('stop')) {
73
+            try {
74
+                new \DateTime($input->getOption('stop'));
75
+                $this->stop = $input->getOption('stop');
76
+            } catch(\Exception $e)
77
+            {
78
+                throw new \InvalidArgumentException('Stop date is invalid. Please provide an ISO date.');
79
+            }
80
+        }
81
+    }
82
+
83
+    protected function execute(InputInterface $input, OutputInterface $output): int
84
+    {
85
+        $this->framework->initialize();
86
+
87
+        $io = new SymfonyStyle($input, $output);
88
+        $io->title('Orders export from Shopware');
89
+
90
+        /** @var ShopwareExportOrdersJob $export */
91
+        $export = System::getContainer()->get(ShopwareExportOrdersJob::class);
92
+
93
+        $export->export('cli', $this->start,$this->stop, $io);
94
+
95
+        return 0;
96
+    }
97
+}
0 98
new file mode 100644
... ...
@@ -0,0 +1,228 @@
1
+<?php
2
+
3
+declare(strict_types=1);
4
+
5
+/*
6
+ * This file is part of vonRotenberg WMFGO Cevisio Bundle.
7
+ *
8
+ * (c) vonRotenberg
9
+ *
10
+ * @license proprietary
11
+ */
12
+
13
+namespace vonRotenberg\WmfgoCevisioBundle\Cron;
14
+
15
+use Contao\CoreBundle\Controller\AbstractController;
16
+use Contao\Date;
17
+use Contao\System;
18
+use Doctrine\DBAL\Connection;
19
+use Psr\Log\LoggerInterface;
20
+use Symfony\Component\Console\Helper\ProgressBar;
21
+use Symfony\Component\Console\Output\ConsoleOutput;
22
+use Symfony\Component\Console\Style\SymfonyStyle;
23
+use Symfony\Component\Finder\Finder;
24
+use Symfony\Component\Serializer\Encoder\CsvEncoder;
25
+use Symfony\Component\Serializer\Encoder\XmlEncoder;
26
+use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
27
+use Symfony\Component\Serializer\Serializer;
28
+use Symfony\Component\Serializer\SerializerInterface;
29
+use vonRotenberg\ShopwareApiBundle\API\Shopware;
30
+use vonRotenberg\ShopwareApiBundle\Helper\ShopwareMappings;
31
+use vonRotenberg\WmfgoCevisioBundle\Model\Import\ProductModel;
32
+
33
+class ShopwareExportOrdersJob extends AbstractController
34
+{
35
+    /** @var LoggerInterface */
36
+    private $logger;
37
+
38
+    /** @var SerializerInterface */
39
+    private $serializer;
40
+
41
+    /** @var Shopware */
42
+    private $shopware;
43
+
44
+    /** @var ShopwareMappings */
45
+    private $mappings;
46
+
47
+    private $xmlContext = [];
48
+
49
+    /** @var Finder */
50
+    private $finder;
51
+
52
+    /** @var Connection */
53
+    private $db;
54
+
55
+    /**
56
+     * Remote
57
+     * @var Connection
58
+     */
59
+    private $rdb;
60
+
61
+    public function __construct(Finder $finder, Shopware $shopware, ShopwareMappings $mappings, Connection $db, LoggerInterface $logger)
62
+    {
63
+        $this->finder = $finder;
64
+        $this->db = $db;
65
+        $this->logger = $logger;
66
+        $this->shopware = $shopware;
67
+        $this->mappings = $mappings;
68
+
69
+        $encoder      = [new XmlEncoder()];
70
+        $normalizer   = [new ObjectNormalizer()];
71
+        $this->serializer = new Serializer($normalizer,$encoder);
72
+
73
+        $this->xmlContext = [
74
+            'xml_root_node_name' => 'Order',
75
+            'xml_encoding' => 'UTF-8',
76
+            'xml_format_output' => true,
77
+        ];
78
+    }
79
+
80
+    public function export(string $scope, ?string $start=null, ?string $stop=null, ?SymfonyStyle &$io = null): void
81
+    {
82
+        $intCounter = 0;
83
+        $isCli = false;
84
+        if (strtolower($scope) == 'cli' && $io !== null) {
85
+            $isCli = true;
86
+        }
87
+        $translator = System::getContainer()->get('translator');
88
+        $translator->setLocale('de');
89
+//        date_default_timezone_set('Europe/Berlin');
90
+        $projectDir = System::getContainer()->getParameter('kernel.project_dir');
91
+        $srcDir = $projectDir.'/export/orders';
92
+        if (!file_exists($srcDir)) {
93
+            mkdir($srcDir, 0777,true);
94
+        }
95
+
96
+        $Today = new Date();
97
+
98
+        if (($start || $stop) && ($start === null || $stop === null))
99
+        {
100
+            return;
101
+        }
102
+
103
+        // Set working period
104
+        if ($start && $stop) {
105
+            $StartDate = new Date($start,'Y-m-d');
106
+            $StopDate = new Date($stop,'Y-m-d');
107
+            $intStart = $StartDate->dayBegin;
108
+            $intEnd = $StopDate->dayEnd;
109
+        } else {
110
+            $WorkingDate = new Date($Today->dayBegin);
111
+            $intStart = strtotime(date('Y-m-d',$WorkingDate->dayBegin) . ' -1 day');
112
+            $intEnd = $WorkingDate->dayBegin;
113
+        }
114
+
115
+        if ($isCli) {
116
+            $io->definitionList('Using export timeframe:',['Start'=>Date::parse(Date::getNumericDateFormat(),$intStart)],['End'=>Date::parse(Date::getNumericDateFormat(),$intEnd)]);
117
+        }
118
+
119
+        $arrFilter = [
120
+            [
121
+                'type'=> 'range',
122
+                'field'=> 'createdAt',
123
+                'parameters'=> [
124
+                    'gte'=> date(\DateTime::ATOM,$intStart),
125
+                    'lte'=> date(\DateTime::ATOM,$intEnd),
126
+                ],
127
+            ],
128
+        ];
129
+
130
+        $Response = $this->shopware->findOrdersByFilter($arrFilter);
131
+
132
+        if ($Response->getStatusCode() !== 200 || !json_validate($Response->getContent())) {
133
+            if ($isCli) $io->error('Could not export orders');
134
+            dump($Response->getContent(false));
135
+        }
136
+
137
+        $Content = json_decode($Response->getContent(), true);
138
+
139
+        foreach ($Content['data'] as $order)
140
+        {
141
+            $arrOrderExport = [];
142
+//            dump($order);die;
143
+//            dump($order);
144
+
145
+            $Delivery = count($order['deliveries']) ? array_shift($order['deliveries']) : null;
146
+            $Transaction = count($order['transactions']) ? array_shift($order['transactions']) : null;
147
+            $LineItems = count($order['lineItems']) ? $order['lineItems'] : null;
148
+
149
+            $arrOrderExport = [
150
+                '@xmlns:xsd' => 'http://www.w3.org/2001/XMLSchema',
151
+                '@xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
152
+                'OrderNumber' => $order['orderNumber'],
153
+                'OrderDate'   => date(\DateTime::ATOM,strtotime($order['createdAt'])),
154
+                'TODO' => 'One field missing',
155
+                'PaymentMethod' => isset($Transaction['paymentMethod']['name']) ? $Transaction['paymentMethod']['name'] : null,
156
+                'ShippingKind' => isset($Delivery['shippingMethod']['name']) ? $Delivery['shippingMethod']['name'] : null,
157
+                'BuyerId'     => $order['orderCustomer']['email'],
158
+                'BuyerFirstName'     => $order['billingAddress']['firstName'],
159
+                'BuyerLastName'     => $order['billingAddress']['lastName'],
160
+                'BuyerCompanyName' => $order['billingAddress']['company'],
161
+                'BuyerStreet' => $order['billingAddress']['street'],
162
+                'BuyerPostalCode' => $order['billingAddress']['zipcode'],
163
+                'BuyerCity' => $order['billingAddress']['city'],
164
+                'BuyerCountry' => $order['billingAddress']['country']['name'],
165
+                'BuyerCountryCode' => $order['billingAddress']['country']['iso'],
166
+                'BuyerEmail' => $order['orderCustomer']['email'],
167
+                'BuyerPhone' => $order['billingAddress']['phoneNumber'],
168
+                'BuyerVATID' => $order['billingAddress']['vatId'],
169
+            ];
170
+
171
+            if (count($Delivery['shippingOrderAddress']))
172
+            {
173
+                $arrOrderExport = array_merge($arrOrderExport,[
174
+                    'ShippingName1' => $Delivery['shippingOrderAddress']['firstName']. ' '.$Delivery['shippingOrderAddress']['lastName'],
175
+                    'ShippingStreet' => $Delivery['shippingOrderAddress']['street'],
176
+                    'ShippingPostalCode' => $Delivery['shippingOrderAddress']['zipcode'],
177
+                    'ShippingCity' => $Delivery['shippingOrderAddress']['city'],
178
+                    'ShippingCountry' => $Delivery['shippingOrderAddress']['country']['name'],
179
+                    'ShippingCountryCode' => $Delivery['shippingOrderAddress']['country']['iso'],
180
+                    'ShippingPhone' => $Delivery['shippingOrderAddress']['phoneNumber'],
181
+                ]);
182
+            }
183
+
184
+            $arrOrderExport = array_merge($arrOrderExport,[
185
+                'PriceDeclaration' => ucfirst($order['taxStatus']),
186
+                'TotalAmount' => $order['amountTotal'],
187
+                'ShippingCosts' => $order['shippingTotal'],
188
+                'PaidAmount' => in_array($Transaction['paymentMethod']['shortName'],['cash_payment','pre_payment']) ? 0.00 : $order['amountTotal'],
189
+            ]);
190
+
191
+            if (count($LineItems))
192
+            {
193
+                $arrPositions = [];
194
+                foreach ($LineItems as $item)
195
+                {
196
+                    if ($item['type'] == 'promotion')
197
+                    {
198
+                        $arrPositions['Position'][] = [
199
+                            'PositionNumber' => $item['position'],
200
+                            'ProductNumber' => 'Rabatt-'.$item['payload']['promotionId'],
201
+                            'ProductName' => 'Rabatt als Artikel ('.$item['label']. ' - ' .($item['priceDefinition']['type'] == 'percentage' ? $item['priceDefinition']['percentage']*-1 . '%' : $item['priceDefinition']['price']*-1 . ' EUR') . ')',
202
+                            'Quantity' => $item['quantity'],
203
+                            'Unit' => 'Stk',
204
+                            'UnitPrice' => $item['unitPrice'],
205
+                            'TaxRate' => $item['price']['calculatedTaxes'][0]['taxRate'],
206
+                        ];
207
+                    } else {
208
+                        $arrPositions['Position'][] = [
209
+                            'PositionNumber' => $item['position'],
210
+                            'ProductNumber' => isset($item['product']) ? $item['product']['productNumber'] : null,
211
+                            'ProductName' => $item['label'],
212
+                            'Quantity' => $item['quantity'],
213
+                            'Unit' => 'Stk',
214
+                            'UnitPrice' => $item['unitPrice'],
215
+                            'TaxRate' => $item['price']['calculatedTaxes'][0]['taxRate'],
216
+                        ];
217
+                    }
218
+                }
219
+
220
+                $arrOrderExport = array_merge($arrOrderExport,['Positions' => $arrPositions]);
221
+            }
222
+
223
+            \file_put_contents($projectDir.'/export/orders/order__' . $order['orderNumber'] . '.xml', $this->serializer->encode($arrOrderExport,'xml', $this->xmlContext));
224
+        }
225
+
226
+
227
+    }
228
+}