Skip to content

Commit

Permalink
🔃 [Magento Community Engineering] Community Contributions - 2.4-devel…
Browse files Browse the repository at this point in the history
…op expedited

Accepted Community Pull Requests:
 - magento#27414: Resolved Cannot create shipment for remaining items (by @niravkrish)
 - magento#27876: Add GraphQL mutations for Reset password for MyAccount  (by @Usik2203)


Fixed GitHub Issues:
 - magento#27141: Cannot create shipment for remaining items (reported by @MLucaciu) has been fixed in magento#27414 by @niravkrish in 2.4-develop branch
   Related commits:
     1. 8e4c3bf
     2. ec55a63
     3. b7e99de
     4. cbc352e
     5. c85bcd0
     6. 01aeae6
     7. 20e30c6
     8. 2d34f05
     9. f6938af
     10. f5c4dc4
  • Loading branch information
magento-engcom-team authored May 18, 2020
2 parents 0e743f2 + f98ef04 commit 5a09115
Show file tree
Hide file tree
Showing 9 changed files with 665 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\CustomerGraphQl\Model\Resolver;

use Magento\Customer\Api\AccountManagementInterface;
use Magento\Customer\Api\CustomerRepositoryInterface;
use Magento\Customer\Model\AccountManagement;
use Magento\Customer\Model\AuthenticationInterface;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\GraphQl\Config\Element\Field;
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
use Magento\Framework\GraphQl\Query\Resolver\ContextInterface;
use Magento\Framework\GraphQl\Query\Resolver\Value;
use Magento\Framework\GraphQl\Query\ResolverInterface;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Magento\Framework\Validator\EmailAddress as EmailValidator;

/**
* Class Resolver for RequestPasswordResetEmail
*/
class RequestPasswordResetEmail implements ResolverInterface
{
/**
* @var AuthenticationInterface
*/
private $authentication;

/**
* @var CustomerRepositoryInterface
*/
private $customerRepository;

/**
* @var AccountManagementInterface
*/
private $customerAccountManagement;

/**
* @var EmailValidator
*/
private $emailValidator;

/**
* RequestPasswordResetEmail constructor.
*
* @param AuthenticationInterface $authentication
* @param CustomerRepositoryInterface $customerRepository
* @param AccountManagementInterface $customerAccountManagement
* @param EmailValidator $emailValidator
*/
public function __construct(
AuthenticationInterface $authentication,
CustomerRepositoryInterface $customerRepository,
AccountManagementInterface $customerAccountManagement,
EmailValidator $emailValidator
) {
$this->authentication = $authentication;
$this->customerRepository = $customerRepository;
$this->customerAccountManagement = $customerAccountManagement;
$this->emailValidator = $emailValidator;
}

/**
* Send password email request
*
* @param Field $field
* @param ContextInterface $context
* @param ResolveInfo $info
* @param array|null $value
* @param array|null $args
*
* @return bool|Value|mixed
*
* @throws GraphQlInputException
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function resolve(
Field $field,
$context,
ResolveInfo $info,
array $value = null,
array $args = null
) {
if (empty($args['email'])) {
throw new GraphQlInputException(__('You must specify an email address.'));
}

if (!$this->emailValidator->isValid($args['email'])) {
throw new GraphQlInputException(__('The email address has an invalid format.'));
}

try {
$customer = $this->customerRepository->get($args['email']);
} catch (LocalizedException $e) {
throw new GraphQlInputException(__('Cannot reset the customer\'s password'), $e);
}

if (true === $this->authentication->isLocked($customer->getId())) {
throw new GraphQlInputException(__('The account is locked'));
}

try {
return $this->customerAccountManagement->initiatePasswordReset(
$args['email'],
AccountManagement::EMAIL_RESET
);
} catch (LocalizedException $e) {
throw new GraphQlInputException(__('Cannot reset the customer\'s password'), $e);
}
}
}
124 changes: 124 additions & 0 deletions app/code/Magento/CustomerGraphQl/Model/Resolver/ResetPassword.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\CustomerGraphQl\Model\Resolver;

use Magento\Customer\Api\AccountManagementInterface;
use Magento\Customer\Api\CustomerRepositoryInterface;
use Magento\Customer\Model\AuthenticationInterface;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\GraphQl\Config\Element\Field;
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
use Magento\Framework\GraphQl\Query\Resolver\ContextInterface;
use Magento\Framework\GraphQl\Query\Resolver\Value;
use Magento\Framework\GraphQl\Query\ResolverInterface;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Magento\Framework\Validator\EmailAddress as EmailValidator;

/**
* Class Resolver for ResetPassword
*/
class ResetPassword implements ResolverInterface
{
/**
* @var AccountManagementInterface
*/
private $customerAccountManagement;

/**
* @var EmailValidator
*/
private $emailValidator;

/**
* @var AuthenticationInterface
*/
private $authentication;

/**
* @var CustomerRepositoryInterface
*/
private $customerRepository;

/**
* ResetPassword constructor.
*
* @param AuthenticationInterface $authentication
* @param CustomerRepositoryInterface $customerRepository
* @param AccountManagementInterface $customerAccountManagement
* @param EmailValidator $emailValidator
*/
public function __construct(
AuthenticationInterface $authentication,
CustomerRepositoryInterface $customerRepository,
AccountManagementInterface $customerAccountManagement,
EmailValidator $emailValidator
) {
$this->authentication = $authentication;
$this->customerRepository = $customerRepository;
$this->customerAccountManagement = $customerAccountManagement;
$this->emailValidator = $emailValidator;
}

/**
* Reset old password and set new
*
* @param Field $field
* @param ContextInterface $context
* @param ResolveInfo $info
* @param array|null $value
* @param array|null $args
*
* @return bool|Value|mixed
*
* @throws GraphQlInputException
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function resolve(
Field $field,
$context,
ResolveInfo $info,
array $value = null,
array $args = null
) {
if (empty($args['email'])) {
throw new GraphQlInputException(__('You must specify an email address.'));
}

if (!$this->emailValidator->isValid($args['email'])) {
throw new GraphQlInputException(__('The email address has an invalid format.'));
}

if (empty($args['resetPasswordToken'])) {
throw new GraphQlInputException(__('resetPasswordToken must be specified'));
}

if (empty($args['newPassword'])) {
throw new GraphQlInputException(__('newPassword must be specified'));
}

try {
$customer = $this->customerRepository->get($args['email']);
} catch (LocalizedException $e) {
throw new GraphQlInputException(__('Cannot set the customer\'s password'), $e);
}

if (true === $this->authentication->isLocked($customer->getId())) {
throw new GraphQlInputException(__('The account is locked'));
}

try {
return $this->customerAccountManagement->resetPassword(
$args['email'],
$args['resetPasswordToken'],
$args['newPassword']
);
} catch (LocalizedException $e) {
throw new GraphQlInputException(__('Cannot set the customer\'s password'), $e);
}
}
}
9 changes: 9 additions & 0 deletions app/code/Magento/CustomerGraphQl/etc/graphql/di.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,13 @@
</argument>
</arguments>
</type>
<type name="Magento\StoreGraphQl\Model\Resolver\Store\StoreConfigDataProvider">
<arguments>
<argument name="extendedConfigData" xsi:type="array">
<item name="required_character_classes_number" xsi:type="string">customer/password/required_character_classes_number</item>
<item name="minimum_password_length" xsi:type="string">customer/password/minimum_password_length</item>
<item name="autocomplete_on_storefront" xsi:type="string">customer/password/autocomplete_on_storefront</item>
</argument>
</arguments>
</type>
</config>
8 changes: 8 additions & 0 deletions app/code/Magento/CustomerGraphQl/etc/schema.graphqls
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
# Copyright © Magento, Inc. All rights reserved.
# See COPYING.txt for license details.

type StoreConfig {
required_character_classes_number : String @doc(description: "The number of different character classes required in a password (lowercase, uppercase, digits, special characters).")
minimum_password_length : String @doc(description: "The minimum number of characters required for a valid password.")
autocomplete_on_storefront : Boolean @doc(description: "Enable autocomplete on login and forgot password forms")
}

type Query {
customer: Customer @resolver(class: "Magento\\CustomerGraphQl\\Model\\Resolver\\Customer") @doc(description: "The customer query returns information about a customer account") @cache(cacheable: false)
isEmailAvailable (
Expand All @@ -17,6 +23,8 @@ type Mutation {
createCustomerAddress(input: CustomerAddressInput!): CustomerAddress @resolver(class: "Magento\\CustomerGraphQl\\Model\\Resolver\\CreateCustomerAddress") @doc(description: "Create customer address")
updateCustomerAddress(id: Int!, input: CustomerAddressInput): CustomerAddress @resolver(class: "Magento\\CustomerGraphQl\\Model\\Resolver\\UpdateCustomerAddress") @doc(description: "Update customer address")
deleteCustomerAddress(id: Int!): Boolean @resolver(class: "Magento\\CustomerGraphQl\\Model\\Resolver\\DeleteCustomerAddress") @doc(description: "Delete customer address")
requestPasswordResetEmail(email: String!): Boolean @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\RequestPasswordResetEmail") @doc(description: "Request an email with a reset password token for the registered customer identified by the specified email.")
resetPassword(email: String!, resetPasswordToken: String!, newPassword: String!): Boolean @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\ResetPassword") @doc(description: "Reset a customer's password using the reset password token that the customer received in an email after requesting it using requestPasswordResetEmail.")
}

input CustomerAddressInput {
Expand Down
2 changes: 1 addition & 1 deletion app/code/Magento/Sales/Model/Order/Item.php
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ public function getQtyToShip()
*/
public function getSimpleQtyToShip()
{
$qty = $this->getQtyOrdered() - $this->getQtyShipped() - $this->getQtyRefunded() - $this->getQtyCanceled();
$qty = $this->getQtyOrdered() - max($this->getQtyShipped(), $this->getQtyRefunded()) - $this->getQtyCanceled();
return max(round($qty, 8), 0);
}

Expand Down
4 changes: 2 additions & 2 deletions app/code/Magento/Sales/Test/Unit/Model/Order/ItemTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ public function getItemQtyVariants()
'qty_ordered' => 12, 'qty_invoiced' => 12, 'qty_refunded' => 5, 'qty_shipped' => 4,
'qty_canceled' => 0
],
'expectedResult' => ['to_ship' => 3.0, 'to_invoice' => 0.0]
'expectedResult' => ['to_ship' => 7.0, 'to_invoice' => 0.0]
],
'complete' => [
'options' => [
Expand All @@ -342,7 +342,7 @@ public function getItemQtyVariants()
'qty_ordered' => 4.4, 'qty_invoiced' => 0.4, 'qty_refunded' => 0.4, 'qty_shipped' => 4,
'qty_canceled' => 0,
],
'expectedResult' => ['to_ship' => 0.0, 'to_invoice' => 4.0]
'expectedResult' => ['to_ship' => 0.4, 'to_invoice' => 4.0]
],
'completely_invoiced_using_decimals' => [
'options' => [
Expand Down
Loading

0 comments on commit 5a09115

Please sign in to comment.