Zend certified PHP/Magento developer

Ui component issue after upgrading from Magento 2.4.2 to 2.4.5-p2

After upgrading Magento version the custom checkout component created to handle customer informations does not work anymore, with the previous version everything was fine but now I am getting those errors in browser console:

  • [ERROR] Failed to load the “ui/form/field” template requested by “checkout.billing.address.lastname”.

  • [ERROR] Failed to load the “ui/group/group” template requested by “checkout.billing.address.street”.

The code is very simple there is a layout processor that adds the information about the fields reading the attribute data and then join them using Magento merger (MagentoCheckoutBlockCheckoutAttributeMerger):

<?php 
declare(strict_types=1);

namespace CustomModulesCheckoutBlockCheckout;

use MagentoEavApiDataAttributeInterface;
use MagentoCheckoutBlockCheckoutAttributeMerger;
use MagentoUiComponentFormAttributeMapper;
use MagentoCheckoutBlockCheckoutLayoutProcessorInterface;
use MagentoCustomerModelSession as CustomerSession;
use MagentoCheckoutModelSession as CheckoutSession;
use MagentoCustomerModelAttributeMetadataDataProvider;
use MagentoCustomerModelCustomer;
use MagentoQuoteModelQuote;
use MagentoFrameworkExceptionLocalizedException;
use MagentoFrameworkExceptionNoSuchEntityException;

class LayoutProcessor implements LayoutProcessorInterface
{
   /**
    * @var AttributeMetadataDataProvider
    */
   private $attributeMetadataDataProvider;

   /**
    * @var AttributeMapper
    */
   private $attributeMapper;

   /**
    * @var AttributeMerger
    */
   private $merger;

   /**
    * @var CustomerSession
    */
   private $customerSession;

   /**
    * @var CheckoutSession
    */
   private $checkoutSession;

   /**
    * @var array
    */
   private $previousLayout;

   /**
    * @var array
    */
   private $jsLayout;

   /**
    * @param CustomerSession $customerSession
    * @param CheckoutSession $checkoutSession
    * @param AttributeMerger $merger
    * @param AttributeMapper $attributeMapper
    * @param AttributeMetadataDataProvider $attributeMetadataDataProvider
    * @param array $jsLayout
    */
   public function __construct(
       CustomerSession $customerSession,
       CheckoutSession $checkoutSession,
       AttributeMerger $merger,
       AttributeMapper $attributeMapper,
       AttributeMetadataDataProvider $attributeMetadataDataProvider,
       array $jsLayout = []
   ) {
       $this->attributeMetadataDataProvider = $attributeMetadataDataProvider;
       $this->attributeMapper = $attributeMapper;
       $this->merger = $merger;
       $this->customerSession = $customerSession;
       $this->checkoutSession = $checkoutSession;
       $this->jsLayout = $jsLayout;
   }

   /**
    * @param array $previosLayout
    * @return array
    * @throws LocalizedException
    */
   public function process($previousLayout): array
   {
       $this->previousLayout= $previousLayout;
       $this->processAddress();
       $this->movePayment();
       $this->prefillBillingAddressForm();
       return $this->jsLayout;
   }

   /**
    * @return void
    * @throws LocalizedException
    */
   private function processAddress(): void
   {
       $fields = $this->jsLayout['components']['checkout']['children']['billing']['children']['address']['children'];
       $elements = $this->getAddressAttributes();
       $fields = $this->merger->merge(
           $elements,
           'checkoutProvider',
           'billingAddress',
           $fields
       );

       $this->jsLayout['components']['checkout']['children']['billing']['children']['address']['children'] = $fields;
   }


   /**
    * @return array
    * @throws LocalizedException
    */
   private function getAddressAttributes(): array
   {
       $attributes = $this->attributeMetadataDataProvider->loadAttributesCollection(
           'customer_address',
           'customer_register_address'
       );

       $elements = [];
       foreach ($attributes as $attribute) {
           if (!$attribute->getIsUserDefined()) {
               $code = $attribute->getAttributeCode();
               $elements[$code] = $this->attributeMapper->map($attribute);
               if (isset($elements[$code]['label'])) {
                   $elements[$code]['label'] = __($label);
               }
           }
       }
       return $elements;
   }


   /**
    * @return void
    */
   private function movePayment(): void
   {
       $paymentComponents = $this->previousLayout['components']['checkout']['children']['steps']['children']
       ['billing-step']['children']['payment'];

       $this->jsLayout['components']['checkout']['children']['payment'] = array_replace_recursive(
           $paymentComponents,
           $this->jsLayout['components']['checkout']['children']['payment']
       );
       $this->removeUnusedPaymentForms();
   }

   /**
    * @return void
    */
   private function removeUnusedPaymentForms(): void
   {
       $unusedPaymentForms = array_keys($this->jsLayout['components']['checkout']
       ['children']['payment']['children']['payments-list']['children']);

       foreach ($unusedPaymentForms as $paymentFormName) {
           unset($this->jsLayout['components']['checkout']['children']['payment']
               ['children']['payments-list']['children'][$paymentFormName]);
       }
   }

   /**
    * @return void
    * @throws LocalizedException
    * @throws NoSuchEntityException
    */
   private function prefillBillingAddressForm(): void
   {
       $customer = $this->customerSession->getCustomer();
       $quote = $this->checkoutSession->getQuote();

       if (!$customer || !$quote) {
           return;
       }

       $billingAddress = $this->jsLayout['components']['checkout']['children']
       ['billing']['children']['address']['children'];
       if (!$this->customerSession->isLoggedIn()) {
           $this->prefillBillingAddressForGuest($billingAddress, $quote);
       } else {
           $this->prefillBillingAddressForCustomer($billingAddress, $customer);
       }

       $this->jsLayout['components']['checkout']['children']
       ['billing']['children']['address']['children'] = $billingAddress;
   }

   /**
    * @param array $billingAddress
    * @param Customer $customer
    * @return void
    */
   private function prefillBillingAddressForCustomer(array &$billingAddress, Customer $customer): void
   {
       $billingAddress['email']['value'] = $customer->getEmail();
       $billingAddress['telephone']['value'] = $customer->getPhonePrefix() . $customer->getPhone();
       $customerAddress = $customer->getDefaultBillingAddress();
       $billingAddress['street']['children'][0]['value'] = '';
       $billingAddress['street']['children'][1]['value'] = '';
       $billingAddress['city']['value'] = '';
       $billingAddress['country_id']['value'] = '';
       $billingAddress['region_id']['value'] = '';
       $billingAddress['postcode']['value'] = '';
       if ($customerAddress) {
           $billingAddress['street']['children'][0]['value'] = $customerAddress->getStreet()[0] ?? '';
           $billingAddress['street']['children'][1]['value'] = $customerAddress->getStreet()[1] ?? '';
           $billingAddress['postcode']['value'] = $customerAddress->getPostcode();
           $billingAddress['city']['value'] = $customerAddress->getCity();
           $billingAddress['country_id']['value'] = $customerAddress->getCountryId();
           $billingAddress['region_id']['value'] = $customerAddress->getRegionId();
       }
   }

   /**
    * @param array $billingAddress
    * @param Quote $quote
    * @return void
    */
   private function prefillBillingAddressForGuest(array &$billingAddress, Quote $quote): void
   {
       $billingAddress['firstname']['value'] = $quote->getCustomerFirstname();
       $billingAddress['lastname']['value'] = $quote->getCustomerLastname();
       $billingAddress['email']['value'] = $quote->getCustomerEmail();
       $billingAddressFromQuote = $quote->getBillingAddress();
       $billingAddress['telephone']['value'] = $billingAddressFromQuote->getTelephone();
   }
}

All the informations about the components are injected in the constructor parameter jsLayout from a di.xml file:

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<preference for="MagentoCheckoutBlockOnepage"
type="CustomModulesCheckoutBlockCheckoutOnepage"/>
<type name="CustomModulesCheckoutBlockCheckoutOnepage">
<arguments>
<argument name="layoutProcessors" xsi:type="array">
<item name="CustomModules_Checkout::CheckoutLayoutProcessor" xsi:type="object">
CustomModulesCheckoutBlockCheckoutLayoutProcessor
</item>
<item name="directoryData" xsi:type="object">
MagentoCheckoutBlockCheckoutDirectoryDataProcessor
</item>
</argument>
</arguments>
</type>
<type name="CustomModulesCheckoutBlockCheckoutLayoutProcessor">
<arguments>
<argument name="jsLayout" xsi:type="array">
<item name="components" xsi:type="array">
<item name="checkout" xsi:type="array">
<item name="component" xsi:type="string">uiComponent</item>
<item name="config" xsi:type="array">
<item name="template" xsi:type="string">CustomModules_Checkout/onepage</item>
</item>
<item name="children" xsi:type="array">
<item name="billing" xsi:type="array">
<item name="component" xsi:type="string">
CustomModules_Checkout/js/view/billing
</item>
<item name="config" xsi:type="array">
<item name="template" xsi:type="string">CustomModules_Checkout/onepage/billing</item>
</item>
<item name="provider" xsi:type="string">checkoutProvider</item>
<item name="deps" xsi:type="array">
<item name="0" xsi:type="string">checkoutProvider</item>
</item>
<item name="dataScopePrefix" xsi:type="string">billingAddress</item>
<item name="children" xsi:type="array">
<item name="address" xsi:type="array">
<item name="component" xsi:type="string">uiComponent</item>
<item name="displayArea" xsi:type="string">billingAddress</item>
<item name="dataScopePrefix" xsi:type="string">billingAddress</item>
<item name="children" xsi:type="array">
<item name="firstname" xsi:type="array">
<item name="config" xsi:type="array">
<item name="disabled" xsi:type="boolean">true</item>
</item>
</item>
<item name="lastname" xsi:type="array">
<item name="config" xsi:type="array">
<item name="disabled" xsi:type="boolean">true</item>
</item>
</item>
<item name="email" xsi:type="array">
<item name="config" xsi:type="array">
<item name="disabled" xsi:type="boolean">true</item>
<item name="template" xsi:type="string">
ui/form/field
</item>
<item name="elementTmpl" xsi:type="string">
ui/form/element/input
</item>
</item>
<item name="label" xsi:type="string" translate="true">
Email
</item>
</item>
<item name="telephone" xsi:type="array">
<item name="config" xsi:type="array">
<item name="disabled" xsi:type="boolean">true</item>
</item>
<item name="validation" xsi:type="array">
<item name="required-entry" xsi:type="boolean">
false
</item>
</item>
</item>
<item name="region" xsi:type="array">
<item name="visible" xsi:type="boolean">false</item>
</item>
<item name="region_id" xsi:type="array">
<item name="component" xsi:type="string">
Magento_Ui/js/form/element/region
</item>
<item name="visible" xsi:type="boolean">false</item>
<item name="config" xsi:type="array">
<item name="elementTmpl" xsi:type="string">
ui/form/element/select
</item>
<item name="customEntry" xsi:type="string">
billingAddress.region
</item>
<item name="additionalClasses" xsi:type="string">
select-option
</item>
</item>
<item name="validation" xsi:type="array">
<item name="required-entry" xsi:type="boolean">
true
</item>
</item>
<item name="filterBy" xsi:type="array">
<item name="target" xsi:type="string">
${ $.provider }:${ $.parentScope }.country_id
</item>
<item name="field" xsi:type="string">
country_id
</item>
</item>
</item>
<item name="country_id" xsi:type="array">
<item name="config" xsi:type="array">
<item name="disabled" xsi:type="boolean">true</item>
</item>
</item>
<item name="postcode" xsi:type="array">
<item name="validation" xsi:type="array">
<item name="required-entry" xsi:type="boolean">
true
</item>
</item>
</item>
</item>
</item>
</item>
</item>
</item>
</item>
<item name="checkoutProvider" xsi:type="array">
<item name="component" xsi:type="string">uiComponent</item>
</item>
</item>
</argument>
</arguments>
</type>
</config>