Skip to content

Commit 226599b

Browse files
Merge pull request #277 from magmodules/release/1.24.1
Release/1.24.1
2 parents eb71719 + 791f8d1 commit 226599b

13 files changed

Lines changed: 161 additions & 16 deletions

File tree

.github/workflows/e2e-test.yml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,30 @@ jobs:
8181
run: |
8282
curl --fail-with-body -k -v -w "%{http_code}" https://magento.test/admin/
8383
84+
- name: Get Playwright version
85+
id: playwright-version
86+
run: echo "version=$(cd Test/End-2-end && node -e "console.log(require('./package-lock.json').packages['node_modules/@playwright/test'].version)")" >> $GITHUB_OUTPUT
87+
88+
- name: Cache Playwright browsers
89+
id: playwright-cache
90+
uses: actions/cache@v4
91+
with:
92+
path: ~/.cache/ms-playwright
93+
key: playwright-${{ steps.playwright-version.outputs.version }}
94+
8495
- name: Install Playwright dependencies
8596
run: |
8697
cd Test/End-2-end
8798
npm install
8899
npx playwright install --with-deps
100+
if: steps.playwright-cache.outputs.cache-hit != 'true'
101+
102+
- name: Install Playwright (browsers cached)
103+
run: |
104+
cd Test/End-2-end
105+
npm install
106+
npx playwright install-deps
107+
if: steps.playwright-cache.outputs.cache-hit == 'true'
89108

90109
- name: Run Playwright tests
91110
run: |

.github/workflows/linting.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,8 @@ jobs:
2626
runs-on: ubuntu-latest
2727
steps:
2828
- uses: prestashop/github-action-php-lint/8.4@v2.3.1
29+
30+
php-85:
31+
runs-on: ubuntu-latest
32+
steps:
33+
- uses: prestashop/github-action-php-lint/8.5@master

Console/Command/ItemUpdate.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ public function configure()
7373
/**
7474
* @inheritdoc
7575
*/
76-
public function execute(InputInterface $input, OutputInterface $output)
76+
public function execute(InputInterface $input, OutputInterface $output): int
7777
{
7878
$this->appState->setAreaCode('frontend');
7979
$itemModel = $this->itemFactory->create();

Console/Command/OrderSimulate.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ public function configure()
108108
/**
109109
* @inheritdoc
110110
*/
111-
public function execute(InputInterface $input, OutputInterface $output)
111+
public function execute(InputInterface $input, OutputInterface $output): int
112112
{
113113
if (!$input->getOption(self::INPUT_KEY_STORE_ID)) {
114114
throw new \InvalidArgumentException('Please add ' . self::INPUT_KEY_STORE_ID . ' param.');

Console/Command/Selftest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public function configure()
5656
/**
5757
* {@inheritdoc}
5858
*/
59-
public function execute(InputInterface $input, OutputInterface $output)
59+
public function execute(InputInterface $input, OutputInterface $output): int
6060
{
6161
$result = $this->selftestRepository->test();
6262
foreach ($result as $test) {
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?php
2+
/**
3+
* Copyright © Magmodules.eu. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magmodules\Channable\Plugin\Order;
9+
10+
use Magento\Checkout\Model\Session as CheckoutSession;
11+
use Magento\Customer\Model\Address\Validator\Country;
12+
13+
/**
14+
* Skip region validation for Channable order imports.
15+
*
16+
* Channable sends state_code values (e.g. "099" for LV) that don't exist in
17+
* Magento's directory_country_region table. This plugin filters out regionId
18+
* validation errors during Channable imports to prevent order creation failures.
19+
*/
20+
class SkipRegionValidation
21+
{
22+
private CheckoutSession $checkoutSession;
23+
24+
public function __construct(
25+
CheckoutSession $checkoutSession
26+
) {
27+
$this->checkoutSession = $checkoutSession;
28+
}
29+
30+
/**
31+
* Filter out regionId-related validation errors during Channable order imports.
32+
*
33+
* @param Country $subject
34+
* @param array $result
35+
* @return array
36+
*/
37+
public function afterValidate(Country $subject, array $result): array
38+
{
39+
if (!$this->checkoutSession->getChannableEnabled()) {
40+
return $result;
41+
}
42+
43+
return array_values(array_filter($result, function ($error) {
44+
$message = $error instanceof \Magento\Framework\Phrase ? $error->render() : (string)$error;
45+
return stripos($message, 'regionId') === false
46+
&& stripos($message, 'region_id') === false
47+
&& stripos($message, '"region" is required') === false;
48+
}));
49+
}
50+
}

Service/Order/Quote/AddressHandler.php

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,13 @@ public function getAddressData(string $type, array $orderData, Quote $quote): ar
109109
$email = $this->cleanEmail($orderData['customer']['email'] ?? '');
110110
}
111111

112+
$stateCode = $address['state_code'] ?? '';
113+
$stateName = $address['state'] ?? '';
114+
$countryCode = $address['country_code'];
115+
$regionId = !empty($stateCode) || !empty($stateName)
116+
? $this->getRegionId($stateCode, $countryCode, $stateName)
117+
: null;
118+
112119
$addressData = [
113120
'customer_id' => $customerId,
114121
'company' => $this->sanitize($company, self::PATTERN_NAME, 255),
@@ -117,10 +124,9 @@ public function getAddressData(string $type, array $orderData, Quote $quote): ar
117124
'lastname' => $this->sanitize($address['last_name'], self::PATTERN_NAME, 255) ?? '-',
118125
'street' => $this->getStreet($address, (int)$storeId),
119126
'city' => $this->sanitize($address['city'], self::PATTERN_CITY, 100),
120-
'country_id' => $address['country_code'],
121-
'region' => !empty($address['state_code'])
122-
? $this->getRegionId($address['state_code'], $address['country_code'])
123-
: null,
127+
'country_id' => $countryCode,
128+
'region_id' => $regionId,
129+
'region' => $regionId ? null : ($stateName ?: null),
124130
'postcode' => $address['zip_code'],
125131
'telephone' => $this->sanitize($telephone, self::PATTERN_TELEPHONE, 20) ?? '000',
126132
'vat_id' => $this->getVatId($type, $orderData, $storeId),
@@ -267,14 +273,40 @@ public function getStreet(array $address, int $storeId): string
267273
}
268274

269275
/**
276+
* Multi-strategy region lookup:
277+
* 1. loadByCode($code, $countryId) — e.g. "NH" + "US"
278+
* 2. loadByCode($countryId-$code, $countryId) — e.g. "LV-099" + "LV"
279+
* 3. loadByName($stateName, $countryId) — e.g. "Tukuma novads" + "LV"
280+
* 4. Return null — graceful fallback
281+
*
270282
* @param string $code
271283
* @param string $countryId
272-
* @return mixed
284+
* @param string $stateName
285+
* @return int|null
273286
*/
274-
private function getRegionId(string $code, string $countryId)
287+
private function getRegionId(string $code, string $countryId, string $stateName): ?int
275288
{
276-
$region = $this->regionFactory->create();
277-
return $region->loadByCode($code, $countryId)->getId();
289+
if (!empty($code)) {
290+
$region = $this->regionFactory->create()->loadByCode($code, $countryId);
291+
if ($region->getId()) {
292+
return (int)$region->getId();
293+
}
294+
295+
$prefixedCode = $countryId . '-' . $code;
296+
$region = $this->regionFactory->create()->loadByCode($prefixedCode, $countryId);
297+
if ($region->getId()) {
298+
return (int)$region->getId();
299+
}
300+
}
301+
302+
if (!empty($stateName)) {
303+
$region = $this->regionFactory->create()->loadByName($stateName, $countryId);
304+
if ($region->getId()) {
305+
return (int)$region->getId();
306+
}
307+
}
308+
309+
return null;
278310
}
279311

280312
/**
@@ -297,7 +329,7 @@ private function saveAddress(array $addressData, int $customerId, string $type):
297329
->setStreet(explode("\n", (string)$addressData['street']))
298330
->setCity($addressData['city'])
299331
->setCountryId($addressData['country_id'])
300-
->setRegionId($addressData['region'])
332+
->setRegionId($addressData['region_id'])
301333
->setPostcode($addressData['postcode'])
302334
->setVatId($addressData['vat_id'])
303335
->setTelephone($addressData['telephone']);

Service/Product/PriceData.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -321,8 +321,11 @@ public function formatPrice($price, array $config): string
321321
''
322322
);
323323

324-
// Append currency if configured.
325-
if ($useCurrency && $formattedPrice >= 0) {
324+
// Clamp negative prices to 0 and always append currency if configured.
325+
if ($useCurrency) {
326+
if ($formattedPrice < 0) {
327+
$formattedPrice = '0.00';
328+
}
326329
$formattedPrice .= ' ' . $currency;
327330
}
328331

Test/End-2-end/support/services/ChannableApi.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ export default class ChannableApi extends BaseApi {
6868
*/
6969
buildOrderData(overrides: {
7070
country?: string;
71+
state?: string;
72+
stateCode?: string;
7173
price?: number;
7274
priceTax?: number;
7375
productId?: number;
@@ -105,12 +107,23 @@ export default class ChannableApi extends BaseApi {
105107
'AT': '1010',
106108
'BE': '1000',
107109
'FR': '75001',
110+
'LV': 'LV-3101',
111+
'PL': '00-001',
108112
};
109113
if (zipCodes[country]) {
110114
data.billing.zip_code = zipCodes[country];
111115
data.shipping.zip_code = zipCodes[country];
112116
}
113117

118+
if (overrides.state !== undefined) {
119+
data.billing.state = overrides.state;
120+
data.shipping.state = overrides.state;
121+
}
122+
if (overrides.stateCode !== undefined) {
123+
data.billing.state_code = overrides.stateCode;
124+
data.shipping.state_code = overrides.stateCode;
125+
}
126+
114127
if (productId) {
115128
data.products[0].id = productId;
116129
}

Test/End-2-end/tests/order/order-import.spec.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,26 @@ const testCases = [
165165
expect(grandTotal).toBeCloseTo(12.10 * 3, 1);
166166
},
167167
},
168+
{
169+
title: 'LV order with pycountry region code',
170+
config: {
171+
'general/region/state_required': 'LV',
172+
},
173+
orderOverrides: { country: 'LV', stateCode: '099', state: 'Tukuma novads' },
174+
assert: async (page, incrementId) => {
175+
const displayedId = await orderViewPage.getOrderIncrementId(page);
176+
expect(displayedId).toBeTruthy();
177+
},
178+
},
179+
{
180+
title: 'PL order without region',
181+
config: {},
182+
orderOverrides: { country: 'PL', stateCode: '', state: '' },
183+
assert: async (page, incrementId) => {
184+
const displayedId = await orderViewPage.getOrderIncrementId(page);
185+
expect(displayedId).toBeTruthy();
186+
},
187+
},
168188
{
169189
title: 'Multi-currency order (PLN)',
170190
config: {

0 commit comments

Comments
 (0)