PluginProbe
Packeta / 2.0.9
Packeta v2.0.9
2.3.2 2.3.1 trunk 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.3.0 1.3.1 1.3.2 1.4 1.4.1 1.4.2 1.4.3 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 All 56 releases
← All changes | src/Packetery/Module/Order/Repository.php +450 -320 1.42.0.9 View file →
@@ -8,15 +8,22 @@
8 8 declare( strict_types=1 );
9 9
10 10 namespace Packetery\Module\Order;
11 11
12 -use Packetery\Core\Helper;
12 +use DateTimeImmutable;
13 +use Exception;
14 +use Packetery\Core\CoreHelper;
15 +use Packetery\Core\Entity;
13 16 use Packetery\Core\Entity\Order;
14 17 use Packetery\Core\Entity\PickupPoint;
15 -use Packetery\Core\Entity\Size;
16 -use Packetery\Core\Entity\Address;
17 18 use Packetery\Module\Carrier;
18 -use Packetery\Module\ShippingMethod;
19 +use Packetery\Module\Carrier\PacketaPickupPointsConfig;
20 +use Packetery\Module\CustomsDeclaration;
21 +use Packetery\Module\Exception\InvalidCarrierException;
22 +use Packetery\Module\ModuleHelper;
23 +use Packetery\Module\Shipping\ShippingProvider;
24 +use Packetery\Module\WpdbAdapter;
25 +use WC_Order;
19 26 use WP_Post;
20 27
21 28 /**
22 29 * Class Repository.
@@ -25,13 +32,13 @@
25 32 */
26 33 class Repository {
27 34
28 35 /**
29 - * Wpdb.
36 + * WpdbAdapter.
30 37 *
31 - * @var \wpdb
38 + * @var WpdbAdapter
32 39 */
33 - private $wpdb;
40 + private $wpdbAdapter;
34 41
35 42 /**
36 43 * Order factory.
37 44 *
@@ -39,28 +46,71 @@
39 46 */
40 47 private $builder;
41 48
42 49 /**
50 + * CoreHelper.
51 + *
52 + * @var CoreHelper
53 + */
54 + private $coreHelper;
55 +
56 + /**
57 + * Internal pickup points config.
58 + *
59 + * @var PacketaPickupPointsConfig
60 + */
61 + private $pickupPointsConfig;
62 +
63 + /**
64 + * Carrier repository.
65 + *
66 + * @var Carrier\EntityRepository
67 + */
68 + private $carrierRepository;
69 +
70 + /**
71 + * Customs declaration repository.
72 + *
73 + * @var CustomsDeclaration\Repository
74 + */
75 + private $customsDeclarationRepository;
76 +
77 + /**
43 78 * Repository constructor.
44 79 *
45 - * @param \wpdb $wpdb Wpdb.
46 - * @param Builder $orderFactory Order factory.
80 + * @param WpdbAdapter $wpdbAdapter WpdbAdapter.
81 + * @param Builder $orderFactory Order factory.
82 + * @param CoreHelper $coreHelper CoreHelper.
83 + * @param PacketaPickupPointsConfig $pickupPointsConfig Internal pickup points config.
84 + * @param Carrier\EntityRepository $carrierRepository Carrier repository.
85 + * @param CustomsDeclaration\Repository $customsDeclarationRepository Customs declaration repository.
47 86 */
48 - public function __construct( \wpdb $wpdb, Builder $orderFactory ) {
49 - $this->wpdb = $wpdb;
50 - $this->builder = $orderFactory;
87 + public function __construct(
88 + WpdbAdapter $wpdbAdapter,
89 + Builder $orderFactory,
90 + CoreHelper $coreHelper,
91 + PacketaPickupPointsConfig $pickupPointsConfig,
92 + Carrier\EntityRepository $carrierRepository,
93 + CustomsDeclaration\Repository $customsDeclarationRepository
94 + ) {
95 + $this->wpdbAdapter = $wpdbAdapter;
96 + $this->builder = $orderFactory;
97 + $this->coreHelper = $coreHelper;
98 + $this->pickupPointsConfig = $pickupPointsConfig;
99 + $this->carrierRepository = $carrierRepository;
100 + $this->customsDeclarationRepository = $customsDeclarationRepository;
51 101 }
52 102
53 103 /**
54 104 * Applies custom order status filter.
55 105 *
56 - * @param array $clauses Query clauses.
57 - * @param \WP_Query $queryObject WP Query.
58 - * @param array $paramValues Param values.
106 + * @param array<string, string|null> $clauses Query clauses.
107 + * @param \WP_Query|null $queryObject WP Query.
108 + * @param array<string, string|null> $paramValues Param values.
59 109 *
60 110 * @return void
61 111 */
62 - private function applyCustomFilters( array &$clauses, \WP_Query $queryObject, array $paramValues ): void {
112 + private function applyCustomFilters( array &$clauses, ?\WP_Query $queryObject, array $paramValues ): void {
63 113 /**
64 114 * Filters order statuses to exclude in Packeta order list filtering.
65 115 *
66 116 * @since 1.4
@@ -65,72 +115,71 @@
65 115 *
66 116 * @since 1.4
67 117 *
68 118 * @param array $orderStatusesToExclude Order statuses to exclude.
69 - * @param \WP_Query $queryObject WP Query.
119 + * @param \WP_Query|null $queryObject WP Query.
70 120 * @param array $paramValues Param values.
71 121 */
72 - $orderStatusesToExclude = apply_filters( 'packetery_exclude_orders_with_status', [], $queryObject, $paramValues );
73 - if ( ! $orderStatusesToExclude ) {
122 + $orderStatusesToExclude = (array) apply_filters( 'packetery_exclude_orders_with_status', [], $queryObject, $paramValues );
123 + if ( count( $orderStatusesToExclude ) === 0 ) {
74 124 return;
75 125 }
76 126
77 - $sqlStatusesToExclude = implode(
78 - ',',
79 - array_map(
80 - function ( string $orderStatus ): string {
81 - return sprintf( '"%s"', $this->wpdb->_real_escape( $orderStatus ) );
82 - },
83 - $orderStatusesToExclude
84 - )
85 - );
86 -
87 - $clauses['where'] .= sprintf( ' AND `%s`.`post_status` NOT IN (%s)', $this->wpdb->posts, $sqlStatusesToExclude );
127 + if ( ModuleHelper::isHposEnabled() ) {
128 + $clauses['where'] .= sprintf( ' AND `%s`.`status` NOT IN (%s)', $this->wpdbAdapter->wcOrders, $this->wpdbAdapter->prepareInClause( $orderStatusesToExclude ) );
129 + } else {
130 + $clauses['where'] .= sprintf( ' AND `%s`.`post_status` NOT IN (%s)', $this->wpdbAdapter->posts, $this->wpdbAdapter->prepareInClause( $orderStatusesToExclude ) );
131 + }
88 132 }
89 133
90 134 /**
91 - * Extends WP_Query to include custom table.
135 + * Extends SQL query clauses.
92 136 *
93 - * @link https://wordpress.stackexchange.com/questions/50305/how-to-extend-wp-query-to-include-custom-table-in-query
137 + * @param array<string, string> $clauses Clauses.
138 + * @param \WP_Query|null $queryObject Query object.
139 + * @param array<string, string|null> $paramValues Param values.
94 140 *
95 - * @param array $clauses Clauses.
96 - * @param \WP_Query $queryObject WP_Query.
97 - * @param array $paramValues Param values.
98 - *
99 - * @return array
141 + * @return array<string, string>
100 142 */
101 - public function processPostClauses( array $clauses, \WP_Query $queryObject, array $paramValues ): array {
102 - if ( isset( $queryObject->query['post_type'] ) &&
103 - (
104 - 'shop_order' === $queryObject->query['post_type'] ||
105 - ( is_array( $queryObject->query['post_type'] ) && in_array( 'shop_order', $queryObject->query['post_type'], true ) )
106 - )
107 - ) {
108 - // TODO: Introduce variable.
109 - $clauses['join'] .= ' LEFT JOIN `' . $this->wpdb->packetery_order . '` ON `' . $this->wpdb->packetery_order . '`.`id` = `' . $this->wpdb->posts . '`.`id`';
143 + public function processClauses( array $clauses, ?\WP_Query $queryObject, array $paramValues ): array {
144 + if ( ModuleHelper::isHposEnabled() ) {
145 + $clauses['join'] .= ' LEFT JOIN `' . $this->wpdbAdapter->packeteryOrder . '` ON `' . $this->wpdbAdapter->packeteryOrder . '`.`id` = `' . $this->wpdbAdapter->wcOrders . '`.`id`';
146 + } else {
147 + $clauses['join'] .= ' LEFT JOIN `' . $this->wpdbAdapter->packeteryOrder . '` ON `' . $this->wpdbAdapter->packeteryOrder . '`.`id` = `' . $this->wpdbAdapter->posts . '`.`id`';
148 + }
110 149
111 - if ( $paramValues['packetery_carrier_id'] ) {
112 - $clauses['where'] .= ' AND `' . $this->wpdb->packetery_order . '`.`carrier_id` = "' . $this->wpdb->_real_escape( $paramValues['packetery_carrier_id'] ) . '"';
113 - $this->applyCustomFilters( $clauses, $queryObject, $paramValues );
150 + if ( isset( $paramValues['packetery_carrier_id'] ) && $paramValues['packetery_carrier_id'] !== '' ) {
151 + $clauses['where'] .= ' AND `' . $this->wpdbAdapter->packeteryOrder . '`.`carrier_id` = "' . esc_sql( $paramValues['packetery_carrier_id'] ) . '"';
152 + $this->applyCustomFilters( $clauses, $queryObject, $paramValues );
153 + }
154 + if ( isset( $paramValues['packetery_to_submit'] ) && $paramValues['packetery_to_submit'] !== '' ) {
155 + $clauses['where'] .= ' AND `' . $this->wpdbAdapter->packeteryOrder . '`.`carrier_id` IS NOT NULL ';
156 + $clauses['where'] .= ' AND `' . $this->wpdbAdapter->packeteryOrder . '`.`is_exported` = false ';
157 + $this->applyCustomFilters( $clauses, $queryObject, $paramValues );
158 + }
159 + if ( isset( $paramValues['packetery_to_print'] ) && $paramValues['packetery_to_print'] !== '' ) {
160 + $clauses['where'] .= ' AND `' . $this->wpdbAdapter->packeteryOrder . '`.`packet_id` IS NOT NULL ';
161 + $clauses['where'] .= ' AND `' . $this->wpdbAdapter->packeteryOrder . '`.`is_label_printed` = false ';
162 + $this->applyCustomFilters( $clauses, $queryObject, $paramValues );
163 + }
164 + if ( isset( $paramValues['packetery_order_type'] ) && $paramValues['packetery_order_type'] !== '' ) {
165 + if ( $paramValues['packetery_order_type'] === Entity\Carrier::INTERNAL_PICKUP_POINTS_ID ) {
166 + $comparison = 'IN';
167 + } else {
168 + $comparison = 'NOT IN';
114 169 }
115 - if ( $paramValues['packetery_to_submit'] ) {
116 - $clauses['where'] .= ' AND `' . $this->wpdb->packetery_order . '`.`carrier_id` IS NOT NULL ';
117 - $clauses['where'] .= ' AND `' . $this->wpdb->packetery_order . '`.`is_exported` = false ';
118 - $this->applyCustomFilters( $clauses, $queryObject, $paramValues );
170 + $internalCarriers = array_keys( $this->carrierRepository->getNonFeedCarriers() );
171 + $internalCarriers[] = Entity\Carrier::INTERNAL_PICKUP_POINTS_ID;
172 + $clauses['where'] .= ' AND `' . $this->wpdbAdapter->packeteryOrder . '`.`carrier_id` ' . $comparison . ' (' . $this->wpdbAdapter->prepareInClause( $internalCarriers ) . ')';
173 + $this->applyCustomFilters( $clauses, $queryObject, $paramValues );
174 + }
175 + if ( isset( $paramValues['orderby'] ) && $paramValues['orderby'] === 'packetery_packet_stored_until' ) {
176 + if ( $paramValues['order'] === 'asc' ) {
177 + $clauses['orderby'] = '`' . $this->wpdbAdapter->packeteryOrder . '`.`stored_until` ASC';
119 178 }
120 - if ( $paramValues['packetery_to_print'] ) {
121 - $clauses['where'] .= ' AND `' . $this->wpdb->packetery_order . '`.`packet_id` IS NOT NULL ';
122 - $clauses['where'] .= ' AND `' . $this->wpdb->packetery_order . '`.`is_label_printed` = false ';
123 - $this->applyCustomFilters( $clauses, $queryObject, $paramValues );
179 + if ( $paramValues['order'] === 'desc' ) {
180 + $clauses['orderby'] = '`' . $this->wpdbAdapter->packeteryOrder . '`.`stored_until` DESC';
124 181 }
125 - if ( $paramValues['packetery_order_type'] ) {
126 - if ( Carrier\Repository::INTERNAL_PICKUP_POINTS_ID === $paramValues['packetery_order_type'] ) {
127 - $clauses['where'] .= ' AND `' . $this->wpdb->packetery_order . '`.`carrier_id` = "' . $this->wpdb->_real_escape( Carrier\Repository::INTERNAL_PICKUP_POINTS_ID ) . '"';
128 - } else {
129 - $clauses['where'] .= ' AND `' . $this->wpdb->packetery_order . '`.`carrier_id` != "' . $this->wpdb->_real_escape( Carrier\Repository::INTERNAL_PICKUP_POINTS_ID ) . '"';
130 - }
131 - $this->applyCustomFilters( $clauses, $queryObject, $paramValues );
132 - }
133 182 }
134 183
135 184 return $clauses;
136 185 }
@@ -139,38 +188,43 @@
139 188 * Create table to store orders.
140 189 *
141 190 * @return bool
142 191 */
143 - public function createTable(): bool {
144 - $wpdb = $this->wpdb;
192 + public function createOrAlterTable(): bool {
193 + $createTableQuery = 'CREATE TABLE ' . $this->wpdbAdapter->packeteryOrder . ' (
194 + `id` bigint(20) unsigned NOT NULL,
195 + `carrier_id` varchar(15) NOT NULL,
196 + `is_exported` tinyint(1) NOT NULL,
197 + `packet_id` varchar(15) NULL,
198 + `packet_claim_id` varchar(15) NULL,
199 + `packet_claim_password` varchar(10) NULL,
200 + `is_label_printed` tinyint(1) NOT NULL,
201 + `point_id` varchar(50) NULL,
202 + `point_name` varchar(150) NULL,
203 + `point_url` varchar(255) NULL,
204 + `point_street` varchar(120) NULL,
205 + `point_zip` varchar(10) NULL,
206 + `point_city` varchar(70) NULL,
207 + `address_validated` tinyint(1) NOT NULL,
208 + `delivery_address` text NULL,
209 + `weight` float NULL,
210 + `length` float NULL,
211 + `width` float NULL,
212 + `height` float NULL,
213 + `adult_content` tinyint(1) NULL,
214 + `value` double NULL,
215 + `cod` double NULL,
216 + `api_error_message` text NULL,
217 + `api_error_date` datetime NULL,
218 + `carrier_number` varchar(50) NULL,
219 + `packet_status` varchar(30) NULL,
220 + `stored_until` date NULL,
221 + `deliver_on` date NULL,
222 + `car_delivery_id` varchar(15) NULL,
223 + PRIMARY KEY (`id`)
224 + ) ' . $this->wpdbAdapter->get_charset_collate();
145 225
146 - return $wpdb->query(
147 - 'CREATE TABLE IF NOT EXISTS `' . $wpdb->packetery_order . '` (
148 - `id` bigint(20) unsigned NOT NULL,
149 - `carrier_id` varchar(255) NOT NULL,
150 - `is_exported` boolean NOT NULL,
151 - `packet_id` varchar(255) NULL,
152 - `is_label_printed` boolean NOT NULL,
153 - `point_id` varchar(255) NULL,
154 - `point_name` varchar(255) NULL,
155 - `point_url` varchar(255) NULL,
156 - `point_street` varchar(255) NULL,
157 - `point_zip` varchar(255) NULL,
158 - `point_city` varchar(255) NULL,
159 - `address_validated` boolean NOT NULL,
160 - `delivery_address` TEXT NULL,
161 - `weight` float NULL,
162 - `length` float NULL,
163 - `width` float NULL,
164 - `height` float NULL,
165 - `adult_content` boolean NULL,
166 - `value` double NULL,
167 - `cod` double NULL,
168 - `carrier_number` varchar(255) NULL,
169 - `packet_status` varchar(255) NULL,
170 - PRIMARY KEY (`id`)
171 - ) ' . $wpdb->get_charset_collate()
172 - );
226 + return $this->wpdbAdapter->dbDelta( $createTableQuery, $this->wpdbAdapter->packeteryOrder );
173 227 }
174 228
175 229 /**
176 230 * Drop table used to store orders.
@@ -175,22 +229,17 @@
175 229 /**
176 230 * Drop table used to store orders.
177 231 */
178 232 public function drop(): void {
179 - $wpdb = $this->wpdb;
180 - $wpdb->query( 'DROP TABLE IF EXISTS `' . $wpdb->packetery_order . '`' );
233 + $this->wpdbAdapter->query( 'DROP TABLE IF EXISTS `' . $this->wpdbAdapter->packeteryOrder . '`' );
181 234 }
182 235
183 236 /**
184 - * Gets order data.
185 - *
186 - * @param int $id Order id.
187 - *
188 - * @return Order|null
237 + * @throws InvalidCarrierException InvalidCarrierException.
189 238 */
190 239 public function getById( int $id ): ?Order {
191 - $wcOrder = wc_get_order( $id );
192 - if ( ! $wcOrder instanceof \WC_Order ) {
240 + $wcOrder = $this->getWcOrderById( $id );
241 + if ( $wcOrder === null ) {
193 242 return null;
194 243 }
195 244
196 245 return $this->getByWcOrder( $wcOrder );
@@ -196,127 +245,102 @@
196 245 return $this->getByWcOrder( $wcOrder );
197 246 }
198 247
199 248 /**
200 - * Gets order by wc order.
249 + * Ignores InvalidCarrierException.
250 + */
251 + public function getByIdWithValidCarrier( int $id ): ?Order {
252 + $wcOrder = $this->getWcOrderById( $id );
253 + if ( $wcOrder === null ) {
254 + return null;
255 + }
256 +
257 + return $this->getByWcOrderWithValidCarrier( $wcOrder );
258 + }
259 +
260 + /**
261 + * Returns Packeta order, or null.
201 262 *
202 - * @param \WC_Order $wcOrder WC Order.
263 + * @param int $id Order ID.
203 264 *
204 265 * @return Order|null
205 266 */
206 - public function getByWcOrder( \WC_Order $wcOrder ): ?Order {
207 - if ( ! $wcOrder->has_shipping_method( ShippingMethod::PACKETERY_METHOD_ID ) ) {
267 + public function findById( int $id ): ?Order {
268 + $wcOrder = $this->getWcOrderById( $id );
269 + if ( $wcOrder === null ) {
208 270 return null;
209 271 }
210 272
211 - $wpdb = $this->wpdb;
273 + $result = $this->getDataById( $wcOrder->get_id() );
274 + if ( $result === null ) {
275 + return null;
276 + }
212 277
213 - $result = $wpdb->get_row(
214 - $wpdb->prepare(
215 - '
216 - SELECT o.* FROM `' . $wpdb->packetery_order . '` o
217 - JOIN `' . $wpdb->posts . '` as wp_p ON wp_p.ID = o.id
218 - WHERE o.`id` = %d',
219 - $wcOrder->get_id()
220 - )
221 - );
222 -
223 - if ( ! $result ) {
278 + try {
279 + return $this->builder->build( $wcOrder, $result );
280 + } catch ( InvalidCarrierException $invalidCarrierException ) {
224 281 return null;
225 282 }
226 -
227 - $partialOrder = $this->createPartialOrder( $result );
228 - return $this->builder->finalize( $wcOrder, $partialOrder );
229 283 }
230 284
231 285 /**
232 - * Creates partial order.
286 + * Gets Packeta order data by id.
233 287 *
234 - * @param \stdClass $result DB result.
288 + * @param int $id Order id.
235 289 *
236 - * @return Order
290 + * @return object|null
237 291 */
238 - private function createPartialOrder( \stdClass $result ): Order {
239 - $partialOrder = new Order(
240 - $result->id,
241 - $result->carrier_id
292 + public function getDataById( int $id ): ?object {
293 + return $this->wpdbAdapter->get_row(
294 + $this->wpdbAdapter->prepare(
295 + '
296 + SELECT `o`.* FROM `' . $this->wpdbAdapter->packeteryOrder . '` `o`
297 + ' . $this->getWcOrderJoinClause() . '
298 + WHERE `o`.`id` = %d',
299 + $id
300 + )
242 301 );
302 + }
243 303
244 - $orderWeight = $this->parseFloat( $result->weight );
245 - if ( null !== $orderWeight ) {
246 - $partialOrder->setWeight( $orderWeight );
304 + private function getDataByWcOrder( WC_Order $wcOrder ): ?object {
305 + if ( ! ShippingProvider::wcOrderHasOurMethod( $wcOrder ) ) {
306 + return null;
247 307 }
248 - $partialOrder->setPacketId( $result->packet_id );
249 - $partialOrder->setSize( new Size( $this->parseFloat( $result->length ), $this->parseFloat( $result->width ), $this->parseFloat( $result->height ) ) );
250 - $partialOrder->setIsExported( (bool) $result->is_exported );
251 - $partialOrder->setIsLabelPrinted( (bool) $result->is_label_printed );
252 - $partialOrder->setCarrierNumber( $result->carrier_number );
253 - $partialOrder->setPacketStatus( $result->packet_status );
254 - $partialOrder->setAddressValidated( (bool) $result->address_validated );
255 - $partialOrder->setAdultContent( $this->parseBool( $result->adult_content ) );
256 - $partialOrder->setValue( $this->parseFloat( $result->value ) );
257 - $partialOrder->setCod( $this->parseFloat( $result->cod ) );
258 308
259 - if ( $result->delivery_address ) {
260 - $deliveryAddressDecoded = json_decode( $result->delivery_address, false );
261 - $deliveryAddress = new Address(
262 - $deliveryAddressDecoded->street,
263 - $deliveryAddressDecoded->city,
264 - $deliveryAddressDecoded->zip
265 - );
266 -
267 - $deliveryAddress->setHouseNumber( $deliveryAddressDecoded->houseNumber );
268 - $deliveryAddress->setLongitude( $deliveryAddressDecoded->longitude );
269 - $deliveryAddress->setLatitude( $deliveryAddressDecoded->latitude );
270 - $deliveryAddress->setCounty( $deliveryAddressDecoded->county );
271 -
272 - $partialOrder->setDeliveryAddress( $deliveryAddress );
309 + $packeteryOrderData = $this->getDataById( $wcOrder->get_id() );
310 + if ( ! is_object( $packeteryOrderData ) ) {
311 + return null;
273 312 }
274 313
275 - if ( null !== $result->point_id ) {
276 - $pickUpPoint = new PickupPoint(
277 - $result->point_id,
278 - $result->point_name,
279 - $result->point_city,
280 - $result->point_zip,
281 - $result->point_street,
282 - $result->point_url
283 - );
284 -
285 - $partialOrder->setPickupPoint( $pickUpPoint );
286 - }
287 -
288 - return $partialOrder;
314 + return $packeteryOrderData;
289 315 }
290 316
291 317 /**
292 - * Parses string value as float.
293 - *
294 - * @param string|float|null $value Value.
295 - *
296 - * @return float|null
318 + * @throws InvalidCarrierException
297 319 */
298 - private function parseFloat( $value ): ?float {
299 - if ( null === $value || '' === $value ) {
320 + public function getByWcOrder( WC_Order $wcOrder ): ?Order {
321 + $packeteryOrderData = $this->getDataByWcOrder( $wcOrder );
322 + if ( $packeteryOrderData === null ) {
300 323 return null;
301 324 }
302 325
303 - return (float) $value;
326 + return $this->builder->build( $wcOrder, $packeteryOrderData );
304 327 }
305 328
306 329 /**
307 - * Parses string value as float.
308 - *
309 - * @param string|int|null $value Value.
310 - *
311 - * @return bool|null
330 + * Ignores InvalidCarrierException.
312 331 */
313 - private function parseBool( $value ): ?bool {
314 - if ( null === $value || '' === $value ) {
332 + public function getByWcOrderWithValidCarrier( WC_Order $wcOrder ): ?Order {
333 + $packeteryOrderData = $this->getDataByWcOrder( $wcOrder );
334 + if ( $packeteryOrderData === null ) {
315 335 return null;
316 336 }
317 337
318 - return (bool) $value;
338 + try {
339 + return $this->builder->build( $wcOrder, $packeteryOrderData );
340 + } catch ( InvalidCarrierException $invalidCarrierException ) {
341 + return null;
342 + }
319 343 }
320 344
321 345 /**
322 346 * Transforms order to DB array.
@@ -322,44 +346,56 @@
322 346 * Transforms order to DB array.
323 347 *
324 348 * @param Order $order Order.
325 349 *
326 - * @return array
350 + * @return array<string, bool|float|int|string|null>
327 351 */
328 352 private function orderToDbArray( Order $order ): array {
329 353 $point = $order->getPickupPoint();
330 - if ( null === $point ) {
354 + if ( $point === null ) {
331 355 $point = new PickupPoint();
332 356 }
333 357
334 358 $deliveryAddress = null;
335 - if ( $order->isAddressValidated() && $order->getDeliveryAddress() ) {
359 + if ( $order->isAddressValidated() && $order->getDeliveryAddress() !== null ) {
336 360 $deliveryAddress = wp_json_encode( $order->getDeliveryAddress()->export() );
337 361 }
338 362
363 + $apiErrorDateTime = $order->getLastApiErrorDateTime();
364 + if ( $apiErrorDateTime !== null ) {
365 + $apiErrorDateTime = $apiErrorDateTime->format( CoreHelper::MYSQL_DATETIME_FORMAT );
366 + }
367 +
339 368 $data = [
340 - 'id' => (int) $order->getNumber(),
341 - 'carrier_id' => $order->getCarrierId(),
342 - 'is_exported' => (int) $order->isExported(),
343 - 'packet_id' => $order->getPacketId(),
344 - 'packet_status' => $order->getPacketStatus(),
345 - 'is_label_printed' => (int) $order->isLabelPrinted(),
346 - 'carrier_number' => $order->getCarrierNumber(),
347 - 'weight' => $order->getWeight(),
348 - 'point_id' => $point->getId(),
349 - 'point_name' => $point->getName(),
350 - 'point_url' => $point->getUrl(),
351 - 'point_street' => $point->getStreet(),
352 - 'point_zip' => $point->getZip(),
353 - 'point_city' => $point->getCity(),
354 - 'address_validated' => (int) $order->isAddressValidated(),
355 - 'delivery_address' => $deliveryAddress,
356 - 'length' => $order->getLength(),
357 - 'width' => $order->getWidth(),
358 - 'height' => $order->getHeight(),
359 - 'adult_content' => $order->containsAdultContent(),
360 - 'cod' => $order->getCod(),
361 - 'value' => $order->getValue(),
369 + 'id' => (int) $order->getNumber(),
370 + 'carrier_id' => $order->getCarrier()->getId(),
371 + 'is_exported' => (int) $order->isExported(),
372 + 'packet_id' => $order->getPacketId(),
373 + 'packet_claim_id' => $order->getPacketClaimId(),
374 + 'packet_claim_password' => $order->getPacketClaimPassword(),
375 + 'packet_status' => $order->getPacketStatus(),
376 + 'stored_until' => $this->coreHelper->getStringFromDateTime( $order->getStoredUntil(), CoreHelper::DATEPICKER_FORMAT ),
377 + 'is_label_printed' => (int) $order->isLabelPrinted(),
378 + 'carrier_number' => $order->getCarrierNumber(),
379 + 'weight' => $order->getWeight(),
380 + 'car_delivery_id' => $order->getCarDeliveryId(),
381 + 'point_id' => $point->getId(),
382 + 'point_name' => $point->getName(),
383 + 'point_url' => $point->getUrl(),
384 + 'point_street' => $point->getStreet(),
385 + 'point_zip' => $point->getZip(),
386 + 'point_city' => $point->getCity(),
387 + 'address_validated' => (int) $order->isAddressValidated(),
388 + 'delivery_address' => $deliveryAddress,
389 + 'length' => $order->getLength(),
390 + 'width' => $order->getWidth(),
391 + 'height' => $order->getHeight(),
392 + 'adult_content' => $order->containsAdultContent(),
393 + 'cod' => $order->getManualCod(),
394 + 'value' => $order->getManualValue(),
395 + 'api_error_message' => $order->getLastApiErrorMessage(),
396 + 'api_error_date' => $apiErrorDateTime,
397 + 'deliver_on' => $this->coreHelper->getStringFromDateTime( $order->getDeliverOn(), CoreHelper::DATEPICKER_FORMAT ),
362 398 ];
363 399
364 400 return $data;
365 401 }
@@ -371,37 +407,101 @@
371 407 *
372 408 * @return void
373 409 */
374 410 public function save( Order $order ): void {
375 - $this->wpdb->_insert_replace_helper( $this->wpdb->packetery_order, $this->orderToDbArray( $order ), null, 'REPLACE' );
411 + $this->saveData( $this->orderToDbArray( $order ) );
376 412 }
377 413
378 414 /**
415 + * Saves order data.
416 + *
417 + * @param array<string, int|string|null|DateTimeImmutable> $orderData Order data.
418 + *
419 + * @return void
420 + */
421 + public function saveData( array $orderData ): void {
422 + $this->onBeforeDataInsertion( $orderData );
423 + $this->wpdbAdapter->insertReplaceHelper( $this->wpdbAdapter->packeteryOrder, $orderData, null, 'REPLACE' );
424 + }
425 +
426 + /**
427 + * Calls logic before order data replace/insert.
428 + *
429 + * @param array<string, string|null|DateTimeImmutable> $orderData Order data.
430 + * @return void
431 + */
432 + private function onBeforeDataInsertion( array $orderData ): void {
433 + $pointId = $orderData['point_id'] ?? null;
434 + $carrierId = $orderData['carrier_id'] ?? null;
435 + /**
436 + * Tells if Packeta debug logs are enabled.
437 + *
438 + * @since 1.7.2
439 + */
440 + $isLoggingActive = (bool) apply_filters( 'packeta_enable_debug_logs', false );
441 +
442 + if ( ( $pointId !== null && $pointId !== '' ) || ! $isLoggingActive || ! $this->pickupPointsConfig->isInternalPickupPointCarrier( $carrierId ) ) {
443 + return;
444 + }
445 +
446 + $wcLogger = wc_get_logger();
447 + $dataToLog = [
448 + 'order' => $orderData,
449 + 'trace' => array_map(
450 + static function ( array $item ): array {
451 + return [
452 + 'file' => sprintf( '%s(%s)', $item['file'], $item['line'] ),
453 + 'method' => sprintf( '%s%s%s(...)', $item['class'] ?? '', $item['type'] ?? '', $item['function'] ),
454 + ];
455 + },
456 + ( new Exception() )->getTrace()
457 + ),
458 + ];
459 + $wcLogger->warning(
460 + sprintf(
461 + 'Required value for the pickup point order is not set: %s',
462 + wp_json_encode( $dataToLog, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE )
463 + ),
464 + [ 'source' => 'packeta' ]
465 + );
466 + }
467 +
468 + /**
379 469 * Loads order entities by list of ids.
380 470 *
381 - * @param array $orderIds Order ids.
471 + * @param string[] $orderIds Order ids.
382 472 *
383 473 * @return Order[]
384 474 */
385 475 public function getByIds( array $orderIds ): array {
476 + if ( count( $orderIds ) === 0 ) {
477 + return [];
478 + }
479 +
480 + $wpdbAdapter = $this->wpdbAdapter;
481 + $ordersIdsPlaceholder = implode( ', ', array_fill( 0, count( $orderIds ), '%d' ) );
482 + $packeteryOrdersResult = $wpdbAdapter->get_results(
483 + $wpdbAdapter->prepare(
484 + 'SELECT * FROM `' . $wpdbAdapter->packeteryOrder . '`
485 + WHERE `id` IN (' . $ordersIdsPlaceholder . ')',
486 + $orderIds
487 + ),
488 + OBJECT_K
489 + );
490 +
386 491 $orderEntities = [];
387 - $posts = get_posts(
388 - [
389 - 'post_type' => 'shop_order',
390 - 'post__in' => $orderIds,
391 - 'post_status' => [ 'any', 'trash' ],
392 - 'nopaging' => true,
393 - ]
394 - );
395 - foreach ( $posts as $post ) {
396 - $wcOrder = wc_get_order( $post );
397 - // In case WC_Order does not exist, result set is limited to existing records.
398 - if ( ! $wcOrder ) {
492 + foreach ( $orderIds as $orderId ) {
493 + if ( ! isset( $packeteryOrdersResult[ $orderId ] ) ) {
399 494 continue;
400 495 }
401 - $order = $this->getByWcOrder( $wcOrder );
402 - if ( $order ) {
403 - $orderEntities[ $order->getNumber() ] = $order;
496 +
497 + $wcOrder = $this->getWcOrderById( (int) $orderId );
498 + assert( $wcOrder !== null, 'WC order has to be present' );
499 +
500 + try {
501 + $orderEntities[ $orderId ] = $this->builder->build( $wcOrder, $packeteryOrdersResult[ $orderId ] );
502 + } catch ( InvalidCarrierException $exception ) {
503 + continue;
404 504 }
405 505 }
406 506
407 507 return $orderEntities;
@@ -407,87 +507,79 @@
407 507 return $orderEntities;
408 508 }
409 509
410 510 /**
411 - * Quote array of strings.
412 - *
413 - * @param array $input Input.
414 - *
415 - * @return array
416 - */
417 - private function quoteArrayOfStrings( array $input ): array {
418 - return array_map(
419 - function ( string $item ) {
420 - $wpdb = $this->wpdb;
421 - return $wpdb->prepare( '%s', $item );
422 - },
423 - $input
424 - );
425 - }
426 -
427 - /**
428 511 * Finds orders.
429 512 *
430 - * @param array $allowedPacketStatuses Allowed packet statuses.
431 - * @param array $allowedOrderStatuses Allowed order statuses.
432 - * @param int $maxDays Max number of days of single packet sync.
433 - * @param int $limit Number of records.
513 + * @param string[] $allowedPacketStatuses Allowed packet statuses.
514 + * @param string[] $allowedOrderStatuses Allowed order statuses.
515 + * @param int $maxDays Max number of days of single packet sync.
516 + * @param int $limit Number of records.
434 517 *
435 - * @return iterable|Order[]
518 + * @return \Generator<int>
519 + * @throws Exception Exception.
436 520 */
437 - public function findStatusSyncingOrders( array $allowedPacketStatuses, array $allowedOrderStatuses, int $maxDays, int $limit ): iterable {
438 - $wpdb = $this->wpdb;
439 - $dateLimit = Helper::now()->modify( '- ' . $maxDays . ' days' )->format( 'Y-m-d H:i:s' );
521 + public function findStatusSyncingOrderIds( array $allowedPacketStatuses, array $allowedOrderStatuses, int $maxDays, int $limit ): \Generator {
522 + $dateLimit = CoreHelper::now()->modify( '- ' . $maxDays . ' days' )->format( 'Y-m-d H:i:s' );
440 523
441 - $andWhere = [];
524 + $andWhere = [ '`o`.`packet_id` IS NOT NULL' ];
525 + $hposEnabled = ModuleHelper::isHposEnabled();
442 526
443 - $andWhere[] = 'o.`packet_id` IS NOT NULL';
444 - $andWhere[] = $wpdb->prepare( 'wp_p.`post_date_gmt` >= %s', $dateLimit );
527 + if ( $hposEnabled ) {
528 + $andWhere[] = $this->wpdbAdapter->prepare( '`wc_o`.`date_created_gmt` >= %s', $dateLimit );
529 + } else {
530 + $andWhere[] = $this->wpdbAdapter->prepare( '`wp_p`.`post_date_gmt` >= %s', $dateLimit );
531 + }
445 532
446 533 $orPacketStatus = [];
447 - $orPacketStatus[] = 'o.`packet_status` IS NULL';
534 + $orPacketStatus[] = '`o`.`packet_status` IS NULL';
448 535
449 - if ( $allowedPacketStatuses ) {
450 - $orPacketStatus[] = 'o.`packet_status` IN (' . implode( ',', $this->quoteArrayOfStrings( $allowedPacketStatuses ) ) . ')';
536 + if ( count( $allowedPacketStatuses ) > 0 ) {
537 + $orPacketStatus[] = '`o`.`packet_status` IN (' . $this->wpdbAdapter->prepareInClause( $allowedPacketStatuses ) . ')';
451 538 }
452 539
453 - if ( $orPacketStatus ) {
540 + if ( count( $orPacketStatus ) > 0 ) {
454 541 $andWhere[] = '(' . implode( ' OR ', $orPacketStatus ) . ')';
455 542 }
456 543
457 - if ( $allowedOrderStatuses ) {
458 - $andWhere[] = 'wp_p.`post_status` IN (' . implode( ',', $this->quoteArrayOfStrings( $allowedOrderStatuses ) ) . ')';
544 + if ( count( $allowedOrderStatuses ) > 0 && $hposEnabled ) {
545 + $andWhere[] = '`wc_o`.`status` IN (' . $this->wpdbAdapter->prepareInClause( $allowedOrderStatuses ) . ')';
546 + } elseif ( count( $allowedOrderStatuses ) > 0 && $hposEnabled === false ) {
547 + $andWhere[] = '`wp_p`.`post_status` IN (' . $this->wpdbAdapter->prepareInClause( $allowedOrderStatuses ) . ')';
459 548 } else {
460 549 $andWhere[] = '1 = 0';
461 550 }
462 551
463 552 $where = '';
464 - if ( $andWhere ) {
553 + if ( count( $andWhere ) > 0 ) {
465 554 $where = ' WHERE ' . implode( ' AND ', $andWhere );
466 555 }
467 556
468 - // @codingStandardsIgnoreStart
469 - $sql = $wpdb->prepare(
557 + if ( $hposEnabled ) {
558 + $orderBy = ' ORDER BY `wc_o`.`date_created_gmt` ';
559 + } else {
560 + $orderBy = ' ORDER BY `wp_p`.`post_date_gmt` ';
561 + }
562 +
563 + $sql = $this->wpdbAdapter->prepare(
470 564 '
471 - SELECT o.* FROM `' . $wpdb->packetery_order . '` o
472 - JOIN `' . $wpdb->posts . '` wp_p ON wp_p.`ID` = o.`id`
565 + SELECT `o`.`id` FROM `' . $this->wpdbAdapter->packeteryOrder . '` `o`
566 + ' . $this->getWcOrderJoinClause() . '
473 567 ' . $where . '
474 - ORDER BY wp_p.`post_date`
568 + ' . $orderBy . '
475 569 LIMIT %d',
476 570 $limit
477 571 );
478 572
479 - $rows = $wpdb->get_results( $sql );
480 - // @codingStandardsIgnoreEnd
573 + $rows = $this->wpdbAdapter->get_results( $sql );
481 574
482 575 foreach ( $rows as $row ) {
483 - $wcOrder = wc_get_order( $row->id );
484 - if ( false === $wcOrder || ! $wcOrder->has_shipping_method( ShippingMethod::PACKETERY_METHOD_ID ) ) {
576 + $wcOrder = $this->getWcOrderById( (int) $row->id );
577 + if ( $wcOrder === null || ! ShippingProvider::wcOrderHasOurMethod( $wcOrder ) ) {
485 578 continue;
486 579 }
487 580
488 - $partial = $this->createPartialOrder( $row );
489 - yield $this->builder->finalize( $wcOrder, $partial );
581 + yield (int) $row->id;
490 582 }
491 583 }
492 584
493 585 /**
@@ -495,15 +587,9 @@
495 587 *
496 588 * @return int
497 589 */
498 590 public function countOrdersToSubmit(): int {
499 - $wpdb = $this->wpdb;
500 -
501 - return (int) $wpdb->get_var(
502 - 'SELECT COUNT(DISTINCT o.id) FROM `' . $wpdb->packetery_order . '` o
503 - JOIN `' . $wpdb->posts . '` wp_p ON wp_p.`ID` = o.`id`
504 - WHERE o.`carrier_id` IS NOT NULL AND o.`is_exported` = false'
505 - );
591 + return $this->countOrders( [ 'packetery_to_submit' => '1' ] );
506 592 }
507 593
508 594 /**
509 595 * Counts order to be printed.
@@ -510,76 +596,104 @@
510 596 *
511 597 * @return int
512 598 */
513 599 public function countOrdersToPrint(): int {
514 - $wpdb = $this->wpdb;
515 -
516 - return (int) $wpdb->get_var(
517 - 'SELECT COUNT(DISTINCT o.id) FROM `' . $wpdb->packetery_order . '` o
518 - JOIN `' . $wpdb->posts . '` wp_p ON wp_p.`ID` = o.`id`
519 - WHERE o.`packet_id` IS NOT NULL AND o.`is_label_printed` = false'
520 - );
600 + return $this->countOrders( [ 'packetery_to_print' => '1' ] );
521 601 }
522 602
523 603 /**
524 - * Deletes all custom table records linked to permanently deleted orders.
604 + * Counts orders by given params.
525 605 *
526 - * @return void
606 + * @param array<string, string> $params Params.
607 + *
608 + * @return int
527 609 */
528 - public function deleteOrphans(): void {
529 - $wpdb = $this->wpdb;
610 + private function countOrders( array $params ): int {
611 + $clauses = [ 'join' => '' ];
530 612
531 - $wpdb->query(
532 - 'DELETE `' . $wpdb->packetery_order . '` FROM `' . $wpdb->packetery_order . '`
533 - LEFT JOIN `' . $wpdb->posts . '` ON `' . $wpdb->posts . '`.`ID` = `' . $wpdb->packetery_order . '`.`id`
534 - WHERE `' . $wpdb->posts . '`.`ID` IS NULL'
613 + if ( ModuleHelper::isHposEnabled() ) {
614 + $clauses['select'] = ' SELECT COUNT(DISTINCT `' . $this->wpdbAdapter->wcOrders . '`.`id`) ';
615 + $clauses['from'] = ' FROM `' . $this->wpdbAdapter->wcOrders . '` ';
616 + } else {
617 + $clauses['select'] = ' SELECT COUNT(DISTINCT `' . $this->wpdbAdapter->posts . '`.`ID`) ';
618 + $clauses['from'] = ' FROM `' . $this->wpdbAdapter->posts . '` ';
619 + }
620 +
621 + $clauses['where'] = ' WHERE `' . $this->wpdbAdapter->packeteryOrder . '`.`id` IS NOT NULL ';
622 + $clauses = $this->processClauses( $clauses, null, $params );
623 +
624 + return (int) $this->wpdbAdapter->get_var(
625 + $clauses['select'] .
626 + $clauses['from'] .
627 + $clauses['join'] .
628 + $clauses['where']
535 629 );
536 630 }
537 631
538 632 /**
539 - * Adds adult content column.
633 + * Gets WC Order join clause.
540 634 *
541 - * @return void
635 + * @return string
542 636 */
543 - public function addAdultContentColumn(): void {
544 - $wpdb = $this->wpdb;
637 + public function getWcOrderJoinClause(): string {
638 + $packeteryTableAlias = 'o';
545 639
546 - $wpdb->query( 'ALTER TABLE `' . $wpdb->packetery_order . '` ADD COLUMN `adult_content` boolean NULL DEFAULT NULL AFTER `height`' );
547 - }
640 + if ( ModuleHelper::isHposEnabled() ) {
641 + $sourceTableAlias = 'wc_o';
548 642
549 - /**
550 - * Adds value column.
551 - *
552 - * @return void
553 - */
554 - public function addValueColumn(): void {
555 - $wpdb = $this->wpdb;
643 + return sprintf(
644 + 'JOIN `%s` `%s` ON `%s`.`id` = `%s`.`id`',
645 + $this->wpdbAdapter->wcOrders,
646 + $sourceTableAlias,
647 + $sourceTableAlias,
648 + $packeteryTableAlias
649 + );
650 + }
556 651
557 - $wpdb->query( 'ALTER TABLE `' . $wpdb->packetery_order . '` ADD COLUMN `value` double NULL DEFAULT NULL AFTER `adult_content`' );
652 + $sourceTableAlias = 'wp_p';
653 +
654 + return sprintf(
655 + 'JOIN `%s` `%s` ON `%s`.`ID` = `%s`.`id`',
656 + $this->wpdbAdapter->posts,
657 + $sourceTableAlias,
658 + $sourceTableAlias,
659 + $packeteryTableAlias
660 + );
558 661 }
559 662
560 663 /**
561 - * Adds COD column.
664 + * Deletes all custom table records linked to permanently deleted orders.
562 665 *
563 666 * @return void
564 667 */
565 - public function addCodColumn(): void {
566 - $wpdb = $this->wpdb;
668 + public function deleteOrphans(): void {
669 + if ( ModuleHelper::isHposEnabled() ) {
670 + $this->wpdbAdapter->query(
671 + 'DELETE `' . $this->wpdbAdapter->packeteryOrder . '` FROM `' . $this->wpdbAdapter->packeteryOrder . '`
672 + LEFT JOIN `' . $this->wpdbAdapter->wcOrders . '` ON `' . $this->wpdbAdapter->wcOrders . '`.`id` = `' . $this->wpdbAdapter->packeteryOrder . '`.`id`
673 + WHERE `' . $this->wpdbAdapter->wcOrders . '`.`id` IS NULL'
674 + );
567 675
568 - $wpdb->query( 'ALTER TABLE `' . $wpdb->packetery_order . '` ADD COLUMN `cod` double NULL DEFAULT NULL AFTER `value`' );
676 + return;
677 + }
678 +
679 + $this->wpdbAdapter->query(
680 + 'DELETE `' . $this->wpdbAdapter->packeteryOrder . '` FROM `' . $this->wpdbAdapter->packeteryOrder . '`
681 + LEFT JOIN `' . $this->wpdbAdapter->posts . '` ON `' . $this->wpdbAdapter->posts . '`.`ID` = `' . $this->wpdbAdapter->packeteryOrder . '`.`id`
682 + WHERE `' . $this->wpdbAdapter->posts . '`.`ID` IS NULL'
683 + );
569 684 }
570 685
571 686 /**
572 - * Deletes data from custom table.
687 + * Deletes order data including customs declaration and its items from custom tables.
573 688 *
574 689 * @param int $orderId Order id.
575 690 *
576 691 * @return void
577 692 */
578 - private function delete( int $orderId ): void {
579 - $wpdb = $this->wpdb;
580 -
581 - $wpdb->delete( $wpdb->packetery_order, [ 'id' => $orderId ], '%d' );
693 + public function delete( int $orderId ): void {
694 + $this->customsDeclarationRepository->delete( (string) $orderId );
695 + $this->wpdbAdapter->delete( $this->wpdbAdapter->packeteryOrder, [ 'id' => $orderId ], '%d' );
582 696 }
583 697
584 698 /**
585 699 * Fires after post deletion.
@@ -589,10 +703,26 @@
589 703 *
590 704 * @return void
591 705 */
592 706 public function deletedPostHook( int $postId, WP_Post $post ): void {
593 - if ( 'shop_order' === $post->post_type ) {
707 + // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
708 + if ( $post->post_type === 'shop_order' ) {
594 709 $this->delete( $postId );
595 710 }
596 711 }
597 712
713 + /**
714 + * Gets WC_Order object.
715 + *
716 + * @param int $id Order id.
717 + *
718 + * @return WC_Order|null
719 + */
720 + public function getWcOrderById( int $id ): ?WC_Order {
721 + $result = wc_get_order( $id );
722 + if ( $result instanceof WC_Order ) {
723 + return $result;
724 + }
725 +
726 + return null;
727 + }
598 728 }