AutoMapper error when trying to create Shipment

I’m trying to create a shipment and move it to processing state using this code:

_eventBroker.Subscribe<SalesOrderConfirmed>(async x =>
{
    var shipment = await _shipmentManager.Create(x.Item.SystemId, new CreateShipmentArgs
    {
        Rows = x.Item.Rows.Where(salesOrderRow => salesOrderRow.OrderRowType == OrderRowType.Product)
            .Select(salesOrderRow => new ProductToShipRow
            {
                Quantity = salesOrderRow.Quantity,
                ArticleNumber = salesOrderRow.ArticleNumber
            }).ToList(),
        ShippingMethod = x.Item.ShippingInfo.First().ShippingOption.OptionId,
        Address = x.Item.ShippingInfo.First().ShippingAddress,
    });

    using (_securityContextService.ActAsSystem())
    {
        _shipmentService.Create(shipment);
    }

    var stateTransitionResult1 = _stateTransitionsService.SetState<Sales.Shipment>(shipment.SystemId, ShipmentState.Processing);
});

But i get this strange AutoMapper error in the log and I can’t figure out what’s wrong. It’s complaining about FirstName but from what I can see there’s nothing strange about FirstName on the ShippingInfo.First().ShippingAddress that I provide as CreateShipmentArgs.Address.

Any ideas?

2023-09-11 17:16:21.4522 [App:01] [ERROR] [.NET ThreadPool Worker] Litium.Application.Events.EventBrokerImpl - Error mapping types.

Mapping types:
ShipmentEntity → Shipment
Litium.Application.Sales.Data.ShipmentEntity → Litium.Sales.Shipment

Type Map configuration:
ShipmentEntity → Shipment
Litium.Application.Sales.Data.ShipmentEntity → Litium.Sales.Shipment

Destination Member:
ReceiverAddress
AutoMapper.AutoMapperMappingException: Error mapping types.

Mapping types:
ShipmentEntity → Shipment
Litium.Application.Sales.Data.ShipmentEntity → Litium.Sales.Shipment

Type Map configuration:
ShipmentEntity → Shipment
Litium.Application.Sales.Data.ShipmentEntity → Litium.Sales.Shipment

Destination Member:
ReceiverAddress

—> AutoMapper.AutoMapperMappingException: Error mapping types.

Mapping types:
AddressEntity → Address
Litium.Application.Sales.Data.AddressEntity → Litium.Sales.Address

Type Map configuration:
AddressEntity → Address
Litium.Application.Sales.Data.AddressEntity → Litium.Sales.Address

Destination Member:
FirstName

—> System.NotSupportedException: Object is read only.
at Litium.ComponentModel.ObjectBase.ThrowIfReadOnly()
at Litium.ComponentModel.ReadOnlyExtensions.ThrowIfReadOnly[T](ObjectBase self, T item)
at Litium.Sales.Address.set_FirstName(String value)
at lambda_method5614(Closure , ShipmentEntity , Shipment , ResolutionContext )
— End of inner exception stack trace —
at lambda_method5614(Closure , ShipmentEntity , Shipment , ResolutionContext )
— End of inner exception stack trace —
at lambda_method5614(Closure , ShipmentEntity , Shipment , ResolutionContext )
at lambda_method5615(Closure , Object , Object , ResolutionContext )
at Litium.Runtime.AutoMapper.AutoMapperExtensions.MapFrom[TResult](TResult target, Object source)
at Litium.Application.Data.Batching.BatchDataProcessor2.<>c__DisplayClass16_0.<Add>b__0() at Litium.Application.Data.Batching.BatchDataImpl.Commit() at Litium.Application.Sales.ShipmentServiceImpl.Create(Shipment shipment) at Litium.Accelerator.StateTransitions.SalesOrderEventListener.<Litium.Runtime.IAsyncAutostart.StartAsync>b__8_1(SalesOrderConfirmed x) in C:\Repos\SoftubeLitium8\Src\Litium.Accelerator\StateTransitions\SalesOrderEventListener.cs:line 66 at Litium.Application.Events.EventBrokerImpl.<>c__DisplayClass11_11.b__4()
at Litium.Application.Common.FifoExecution.WorkItem.Execute()
at Litium.Application.Common.FifoExecution.ProcessQueuedItems(Object ignored)

Litium version: 8.10.0

It’s hard to understand why this exception is happening but I should try to explain.

First, the root-cause for the exception is the System.NotSupportedException: Object is read only. part and first after I was reading that I understand why you are getting this exception.

When we creating the shipment with the following code

    var shipment = await _shipmentManager.Create(x.Item.SystemId, new CreateShipmentArgs
    {
        Rows = x.Item.Rows.Where(salesOrderRow => salesOrderRow.OrderRowType == OrderRowType.Product)
            .Select(salesOrderRow => new ProductToShipRow
            {
                Quantity = salesOrderRow.Quantity,
                ArticleNumber = salesOrderRow.ArticleNumber
            }).ToList(),
        ShippingMethod = x.Item.ShippingInfo.First().ShippingOption.OptionId,
        Address = x.Item.ShippingInfo.First().ShippingAddress,
    });

we are reusing the ShippingInfo.ShippingAddress instance, that is fired inside the event system. To ensure that all entities that listen on this object are getting the same information, the event is marked as read only and no property on the SalesOrder or any of their child objects can be changed.

To solve this issue you have 2 options,

  1. create your own instance of the address class and assign to the args.Address property from the ShippingInfo.First().ShippingAddress information
  2. don’t assign the args.Address at all, the ShipmentManager will automatic take the ShippingAddress from the first ShippingInfo with the same ShippingMethod from the SalesOrder
1 Like

This works now.

I notice though that the Id of the created shipment is null. Can that cause problems? What would be the best practice way of giving a shipment an Id in that case?

This topic was automatically closed 28 days after the last reply. New replies are no longer allowed.

You need to set an Id.
The usual way to set an Id easily is, (as you had correctly suggested in another thread), to use the OrderId and prefix it with some letters and adding a sequential number at the end.
For the sequential number, you can just use the Number of shipment objects already present. (use the OrderOverview object which gets the shipments connected to the order)

1 Like