Set inventory and variationrow on EntryUpdating

Vote:
 

I running the following CommerceEventListener:

[ServiceConfiguration(typeof(CatalogEventListenerBase))]
    public class CommerceIndexingModule : CatalogEventListenerBase
    {
        private Injected _inventoryService;
        private Injected _warehouseService;

        public override void EntryUpdating(object source, EntryEventArgs args)
        {
            base.EntryUpdating(source, args);

            var entry = source as CatalogEntryDto;
            foreach (var catalogEntryRow in entry.CatalogEntry)
            {
                if (catalogEntryRow == null)
                    return;

                if (catalogEntryRow.ClassTypeId == "Variation")
                {
                    var inventoryRecords = _inventoryService.Service.QueryByEntry(new[] { catalogEntryRow.Code });

                    if (inventoryRecords.Count > 0)
                    {
                        var inventoryRecord = inventoryRecords.FirstOrDefault();
                        if (inventoryRecord.PurchaseAvailableQuantity != 100000)
                        {
                            inventoryRecord.PurchaseAvailableQuantity = 100000;
                            _inventoryService.Service.Update(new[] { inventoryRecord });
                        }
                    }
                    else
                    {
                        _inventoryService.Service.Insert(new[]
                        {
                        new InventoryRecord
                        {
                            AdditionalQuantity = 0,
                            BackorderAvailableQuantity = 0,
                            BackorderAvailableUtc = DateTime.UtcNow,
                            CatalogEntryCode = catalogEntryRow.Code,
                            IsTracked = true,
                            PreorderAvailableQuantity = 0,
                            PreorderAvailableUtc = DateTime.UtcNow,
                            PurchaseAvailableQuantity = 100000,
                            PurchaseAvailableUtc = DateTime.UtcNow,
                            WarehouseCode = _warehouseService.Service.GetDefaultWarehouse().Code
                        }
                    });
                    }

                    var variationRow = catalogEntryRow.GetVariationRows().FirstOrDefault();

                    if (variationRow != null)
                    {
                        variationRow.MinQuantity = 0m;
                        variationRow.MaxQuantity = 10000m;
                        variationRow.ListPrice = 0m;
                    }
                    else
                    {
                        CatalogEntryDto.VariationRow newVariationRow = entry.Variation.NewVariationRow();
                        newVariationRow.ListPrice = Convert.ToDecimal(0);
                        newVariationRow.MaxQuantity = 10000;
                        newVariationRow.SetMerchantIdNull();
                        newVariationRow.MinQuantity = 0;
                        newVariationRow.PackageId = 0;
                        newVariationRow.TaxCategoryId = 0;
                        newVariationRow.TrackInventory = false;
                        newVariationRow.WarehouseId = 0;
                        newVariationRow.Weight = Convert.ToDouble(1);
                        newVariationRow.CatalogEntryId = catalogEntryRow.CatalogEntryId;
                        if (newVariationRow.RowState == DataRowState.Detached)
                            entry.Variation.AddVariationRow(newVariationRow);
                    }

                }
            }
        }
    }

This sets or updates the inventory and variationrow for each variation so that the workflows connected to the cart will validate. It sets the information correctly and the inventory is updated correctly but I'm having issues with the 

var variationRow = catalogEntryRow.GetVariationRows().FirstOrDefault();

This always returns null even if the row exists in the Variation table. How to correctly check for existing VariationRow and if needed update the min/max quantity of it?

#147310
Apr 11, 2016 17:26
Vote:
 

Hi!

I haven't tried anything like this myself, but here is a guess: The DTO doesn't necessarily represent everything that is in the database. Most probably a DTO was loaded from the database, copied, modified and sent to the save method which raises the event you are listening to. But which tables' data are included in the DTO depends on the ResponseGroup used when it was loaded. But then you say that it is always null, and I'm pretty sure when something is edited from the UI it will use a response group that includes variations, so this might not be it at all. Have you checked if the Variation table contains anything at all?

#147313
Apr 11, 2016 19:03
Vote:
 

The Variation table has all the rows that was expected. Im trying this out with a Catalog.zip containing a mix of products and variations and all 182 variations is on the first run successfully added to the Variation table. However when I try to run the import again it fails since the check for existing VariationRow fails and the code for inserting a VariationRow is used when I would like either nothing happening or at least some kind of update of the VariationRow to happen



4/12/2016 8:15:38 AM: Violation of PRIMARY KEY constraint 'PK_ProductVariation'. Cannot insert duplicate key in object 'dbo.Variation'. The duplicate key value is (1265). The statement has been terminated. at System.Data.Common.DbDataAdapter.UpdatedRowStatusErrors(RowUpdatedEventArgs rowUpdatedEvent, BatchCommandInfo[] batchCommands, Int32 commandCount) at System.Data.Common.DbDataAdapter.UpdatedRowStatus(RowUpdatedEventArgs rowUpdatedEvent, BatchCommandInfo[] batchCommands, Int32 commandCount) at System.Data.Common.DbDataAdapter.Update(DataRow[] dataRows, DataTableMapping tableMapping) at System.Data.Common.DbDataAdapter.Update(DataRow[] dataRows) at EPiServer.Data.Providers.SqlTransientErrorsRetryPolicy.Execute[TResult](Func`1 method) at Mediachase.Data.Provider.SqlDataProvider.SaveRows(DataCommand command) at Mediachase.Commerce.Storage.DataHelper.SaveTableSimpleWorker(DataCommand cmd, DataTable table, DataViewRowState state) at Mediachase.Commerce.Storage.DataHelper.SaveDataSetSimple(DataCommand cmd, DataSet set, String[] tables) at Mediachase.Commerce.Catalog.Data.CatalogEntryAdmin.Save() at Mediachase.Commerce.Catalog.Managers.CatalogEntryManager.SaveCatalogEntry(CatalogEntryDto dataset) at Mediachase.Commerce.Catalog.ImportExport.CatalogImportExport.SaveEntryDto(CatalogEntryDto workingCatalogEntryDto, Dictionary`2 metaObjectsList, Dictionary`2 priceGroups, Dictionary`2 warehouseInventories) at Mediachase.Commerce.Catalog.ImportExport.CatalogImportExport.ReadEntries(Guid applicationId, Int32 catalogId, XmlReader reader, String baseFilePath, Int32 totalCount, String defaultCurrency, Boolean overwrite, IEnumerable`1 catalogLanguages) at Mediachase.Commerce.Catalog.ImportExport.CatalogImportExport.Import(Stream input, Guid applicationId, String baseFilePath, Boolean overwrite) at Mediachase.Commerce.Catalog.ImportExport.ImportJob.<>c__DisplayClass7.<DoImport>b__5() at Mediachase.Commerce.Catalog.ImportExport.ImportJob.WithApplicationId(Guid applicationId, Action action) at Mediachase.Commerce.Catalog.ImportExport.ImportJob.DoImport(String[] stages, Int32 stageIndex, String sourceFile, String sourceDirectory) at Mediachase.Commerce.Catalog.ImportExport.ImportJob.Execute(Action`1 addMessage, CancellationToken cancellationToken) at Mediachase.Commerce.BackgroundTasks.BackgroundTaskState.Execute(CancellationToken cancellationToken)
4/12/2016 8:15:38 AM: Task failed with exception.
4/12/2016 8:15:03 AM: Warning - Overwriting Entry with code '22017134'.

So do I need to load something in a different way to get the correct information?

#147322
Apr 12, 2016 8:20
Vote:
 

Solved it by getting the entryDto after clearing the cache and with ResponseGroup.CatalogEntryFull:

CatalogEntryDto.CatalogEntryRow cleanRow = null;
                    
                    string cacheKey = CatalogCache.CreateCacheKey("catalogentry", responseGroup.CacheKey, catalogEntryRow.CatalogEntryId.ToString());
                    CatalogCache.Remove(cacheKey);

                    CatalogEntryDto entryDto = CatalogContext.Current.GetCatalogEntryDto(catalogEntryRow.CatalogEntryId, responseGroup);
                    if (entryDto != null)
                    {
                        cleanRow = entryDto.CatalogEntry.FirstOrDefault();
                    }

                    var variationRow = cleanRow.GetVariationRows().FirstOrDefault();
#147323
Apr 12, 2016 8:34
Vote:
 

I don't think you have to clear the cache. It should work correctly with CatalogEntryFull only - if you have to remove the cache then it's a problem we have to look into

Regards,

/Q

#147328
Apr 12, 2016 9:44
Vote:
 

Ok yeah the cache clearance was not necessary. But can someone please assist me in the final touches to get this working properly.

Im having problem getting the information to save into the Variation table

If I put the code in the EntryUpdating part as in the code above it won't work when there's new entrys to be written. I didn't see this at first since I hade the entries I tried with already in the database. What happens then is that the catalogEntryRow.CatalogEntryId is neagtive because it has not yet been saved to the database. However if the code is kept in the EntryUpdating the correct information will be saved if all items already exists in the database. I believe that might have to do with the entry being saved later on and all changes are then saved.

However putting it in the EntryUpdated solves the inventory setting and the code for variation row works but nothing ends up in the Variation table. How do I save it correctly?

if (variationRow == null)
                    {
                        CatalogEntryDto.VariationRow newVariationRow = entry.Variation.NewVariationRow();
                        newVariationRow.ListPrice = Convert.ToDecimal(0);
                        newVariationRow.MaxQuantity = 10000;
                        newVariationRow.SetMerchantIdNull();
                        newVariationRow.MinQuantity = 0;
                        newVariationRow.PackageId = 0;
                        newVariationRow.TaxCategoryId = 0;
                        newVariationRow.TrackInventory = false;
                        newVariationRow.WarehouseId = 0;
                        newVariationRow.Weight = Convert.ToDouble(1);
                        newVariationRow.CatalogEntryId = catalogEntryRow.CatalogEntryId;
                        if (newVariationRow.RowState == DataRowState.Detached)
                            entry.Variation.AddVariationRow(newVariationRow);
                    }

Also I'm a bit curious into why I must do the 

foreach (var catalogEntryRow in entry.CatalogEntry)
            {

Because when I started out trying to get this to work I followed jondjones example of this at http://jondjones.com/how-to-hook-into-episerver-commerces-8-catalog-event-handlers/ and I thought the the EntryUpdated would be triggered for each and every entry. But it seems to come in batches because when I tried with the import file which contains 44 nodes and 343 entries it was just triggered twice.

#147356
Apr 12, 2016 21:04
Vote:
 

Adding the rows referencing the negative ids should work too, I don't know why it doesn't...

In an EntryUpdated handler the DTO is already saved and won't be saved again, so you have to save yourself. The suggested approach is to always copy a DTO before modifying and saving it:

var entry = (CatalogEntryDto)source;
entry = (CatalogEntryDto)entry.Copy();
// do your updates
CatalogContext.Current.SaveCatalogEntry(entry); // Note: Beware of recursion as this will trigger your event handler again!

The reason it behaves differently from that example is that from Commerce 9 entries in the import are saved in batches (a performance optimization).

#147357
Edited, Apr 12, 2016 21:18
Vote:
 

The problem with the negative ids is on this row:

CatalogEntryDto entryDto = CatalogContext.Current.GetCatalogEntryDto(catalogEntryRow.CatalogEntryId, responseGroup);

It doesn't return an entryDto when the database is empty. So thats stopping med from having the code in EntryUpdating. 

But your final comments seems to have corrected the VariationRow saving and it works. Here is the final code:

using EPiServer.ServiceLocation;
using Mediachase.Commerce.Catalog;
using Mediachase.Commerce.Catalog.Dto;
using Mediachase.Commerce.Catalog.Events;
using Mediachase.Commerce.Catalog.Managers;
using Mediachase.Commerce.Inventory;
using Mediachase.Commerce.InventoryService;
using System;
using System.Data;
using System.Linq;

namespace EPiServer.Reference.Commerce.Manager.Infrastructure.Indexing
{
    [ServiceConfiguration(typeof(CatalogEventListenerBase))]
    public class CommerceIndexingModule : CatalogEventListenerBase
    {
        private Injected<IInventoryService> _inventoryService;
        private Injected<IWarehouseRepository> _warehouseService;

        public override void EntryUpdated(object source, EntryEventArgs args)
        {
            base.EntryUpdated(source, args);

            var entry = (CatalogEntryDto)source;
            entry = (CatalogEntryDto)entry.Copy();

            var responseGroup = new CatalogEntryResponseGroup(CatalogEntryResponseGroup.ResponseGroup.CatalogEntryFull);

            foreach (var catalogEntryRow in entry.CatalogEntry)
            {
                if (catalogEntryRow == null)
                    continue;

                if (catalogEntryRow.ClassTypeId == "Variation")
                {
                    var inventoryRecords = _inventoryService.Service.QueryByEntry(new[] { catalogEntryRow.Code });

                    if (inventoryRecords.Count > 0)
                    {
                        var inventoryRecord = inventoryRecords.FirstOrDefault();
                        if (inventoryRecord.PurchaseAvailableQuantity != 100000)
                        {
                            inventoryRecord.PurchaseAvailableQuantity = 100000;
                            _inventoryService.Service.Update(new[] { inventoryRecord });
                        }
                    }
                    else
                    {
                        _inventoryService.Service.Insert(new[]
                        {
                        new InventoryRecord
                        {
                            AdditionalQuantity = 0,
                            BackorderAvailableQuantity = 0,
                            BackorderAvailableUtc = DateTime.UtcNow,
                            CatalogEntryCode = catalogEntryRow.Code,
                            IsTracked = true,
                            PreorderAvailableQuantity = 0,
                            PreorderAvailableUtc = DateTime.UtcNow,
                            PurchaseAvailableQuantity = 100000,
                            PurchaseAvailableUtc = DateTime.UtcNow,
                            WarehouseCode = _warehouseService.Service.GetDefaultWarehouse().Code
                        }
                    });
                    }

                    CatalogEntryDto.CatalogEntryRow cleanRow = null;
                    CatalogEntryDto entryDto = CatalogContext.Current.GetCatalogEntryDto(catalogEntryRow.CatalogEntryId, responseGroup);
                    if (entryDto == null) continue;

                    cleanRow = entryDto.CatalogEntry.FirstOrDefault();

                    if (cleanRow == null) continue;

                    var variationRow = cleanRow.GetVariationRows().FirstOrDefault();

                    if (variationRow == null)
                    {
                        CatalogEntryDto.VariationRow newVariationRow = entry.Variation.NewVariationRow();
                        newVariationRow.ListPrice = Convert.ToDecimal(0);
                        newVariationRow.MaxQuantity = 10000;
                        newVariationRow.SetMerchantIdNull();
                        newVariationRow.MinQuantity = 0;
                        newVariationRow.PackageId = 0;
                        newVariationRow.TaxCategoryId = 0;
                        newVariationRow.TrackInventory = false;
                        newVariationRow.WarehouseId = 0;
                        newVariationRow.Weight = Convert.ToDouble(1);
                        newVariationRow.CatalogEntryId = catalogEntryRow.CatalogEntryId;
                        if (newVariationRow.RowState == DataRowState.Detached)
                            entry.Variation.AddVariationRow(newVariationRow);
                    }
                }
            }
            CatalogContext.Current.SaveCatalogEntry(entry);
        }
    }
}
#147386
Apr 13, 2016 14:06
Vote:
 

Ah, I thought you were back to the first approach where you didn't fetch an extra DTO. Fetching from DB with the negative ID will not work.

It still troubles me a bit that you can't get it working without re-fetching and saving a second dto. The code in your first example looks like it would work, though the absense of a variation row doesn't mean there is none in the database (the responsegroup thing). But I'm still surprised you never saw anything in the table.

What you could probably do though, if you care to investigate the EntryUpdating approach more, is to check if the CatalogEntryRow you currently have in the loop has rowstate added. If so, and there is no variation row corresponding to it, it should mean there is no variation row in db and you can create one. If the entry row is instead in state modified and you can't find a variation row in the table it either means there is none in the db or the DTO wasn't loaded with that response group. In this case you should have the ID and you can fetch a separate DTO. For rows with state deleted you should of course not try to add any variation rows.

#147388
Apr 13, 2016 14:17
Vote:
 

I will check the EntryUpdating a bit more but I can't get the inventory update to work in EntryUpdating. Getting 

An exception of type 'System.Data.SqlClient.SqlException' occurred in EPiServer.Data.dll but was not handled in user code

Additional information: The INSERT statement conflicted with the FOREIGN KEY constraint "FK_ManagedInventory_CatalogEntry"

Im guessing the entry has not yet benn saved and therefor I can't connect inventory to it.

#147391
Apr 13, 2016 14:37
Vote:
 

I think you can make that code run after the entry dto is saved by adding a TransactionScope.OnCommit handler:

TransactionScope.OnCommit(() =>
{
    // Code here
});

Beware though that when that runs the DTO will have been saved so the state of it may be different. If you want to act exactly on the state as it is in the EntryUpdating handler you should extract any you need from the DTO into other variables and and use those in the oncommit action to make sure you capture the expected data for the closure.

#147393
Apr 13, 2016 14:42
Vote:
 

Ok got the EntryUpdating path working using this code. I also came into checking if the row exists and then try to update it. But the following code does not correctly save a new value in the variation row. How does that work? If I look at the rowstate of the variationRow before AcceptChanges it is Modified and after AcceptChanges Unchanged but no updated value has been aded to the database.

public override void EntryUpdating(object source, EntryEventArgs args)
        {
            base.EntryUpdating(source, args);

            var entry = (CatalogEntryDto)source;
            //entry = (CatalogEntryDto)entry.Copy();

            var responseGroup = new CatalogEntryResponseGroup(CatalogEntryResponseGroup.ResponseGroup.CatalogEntryFull);

            foreach (var catalogEntryRow in entry.CatalogEntry)
            {
                if (catalogEntryRow == null)
                    continue;

                if (catalogEntryRow.ClassTypeId == "Variation")
                {
                    if (catalogEntryRow.RowState == DataRowState.Added)
                    {
                        CatalogEntryDto.VariationRow newVariationRow = entry.Variation.NewVariationRow();
                        newVariationRow.ListPrice = Convert.ToDecimal(0);
                        newVariationRow.MaxQuantity = 10000;
                        newVariationRow.SetMerchantIdNull();
                        newVariationRow.MinQuantity = 0;
                        newVariationRow.PackageId = 0;
                        newVariationRow.TaxCategoryId = 0;
                        newVariationRow.TrackInventory = false;
                        newVariationRow.WarehouseId = 0;
                        newVariationRow.Weight = Convert.ToDouble(1);
                        newVariationRow.CatalogEntryId = catalogEntryRow.CatalogEntryId;
                        if (newVariationRow.RowState == DataRowState.Detached)
                            entry.Variation.AddVariationRow(newVariationRow);
                    }
                    else if (catalogEntryRow.RowState == DataRowState.Modified || catalogEntryRow.RowState == DataRowState.Unchanged)
                    {
                        CatalogEntryDto.CatalogEntryRow cleanRow = null;
                        CatalogEntryDto entryDto = CatalogContext.Current.GetCatalogEntryDto(catalogEntryRow.CatalogEntryId, responseGroup);
                        if (entryDto == null) continue;

                        cleanRow = entryDto.CatalogEntry.FirstOrDefault();

                        if (cleanRow == null) continue;

                        var variationRow = cleanRow.GetVariationRows().FirstOrDefault();
                        if (variationRow == null)
                        {
                            CatalogEntryDto.VariationRow newVariationRow = entry.Variation.NewVariationRow();
                            newVariationRow.ListPrice = Convert.ToDecimal(0);
                            newVariationRow.MaxQuantity = 10000;
                            newVariationRow.SetMerchantIdNull();
                            newVariationRow.MinQuantity = 0;
                            newVariationRow.PackageId = 0;
                            newVariationRow.TaxCategoryId = 0;
                            newVariationRow.TrackInventory = false;
                            newVariationRow.WarehouseId = 0;
                            newVariationRow.Weight = Convert.ToDouble(1);
                            newVariationRow.CatalogEntryId = catalogEntryRow.CatalogEntryId;
                            if (newVariationRow.RowState == DataRowState.Detached)
                                entry.Variation.AddVariationRow(newVariationRow);
                        }
                        else {
                            variationRow.MaxQuantity = 20000;
                            variationRow.AcceptChanges();
                        }
                    }
                }
            }
        }
#147398
Apr 13, 2016 15:14
Vote:
 

You should not call AcceptChanges. As you noticed that resets the row state so it will be skipped when the DTO is saved. However since that row is coming from a separate DTO you need to import it into the dto chat is going to be saved after the event completes:

entry.Variation.ImportRow(variationRow)

#147401
Apr 13, 2016 15:26
Vote:
 

Why not just simply hook up with published event to achieve this? I have done this recently by using published event, and everything works like a charm.

#147403
Apr 13, 2016 15:43
Vote:
 

I haven't thought much about what you are trying to achieve, just advising on what to do to make the code work :)

Anyway, adding to my last post, you should preferrably import the row before modifying it, otherwise you are modifying on a potentially shared resource since the DTOs are loaded through cache. 

else
{
entry.Variation.ImportRow(variationRow);
variationRow = entry.Variation[entry.Variation.Count - 1];
variationRow.MaxQuantity = 20000;
}
Of all the horrible parts of the Microsoft DataTable stuff this is IMO one of the worst, that ImportRow doesn't return any reference to the imported data so you have to resort to picking the last row on the table. But so far I haven't found any other way, though nor have I found a case where it doesn't work as expected.
#147404
Apr 13, 2016 16:27
Vote:
 

This is running as part of the manager code in a Quicksilver based solution to set this information since inRiver doesn't set it when publishing information to Episerver and the basic workflows for the cart don't work without them. But is there a better way to do this I would gladly look into it. This approach was suggested to me from inRiver and after all the initial struggles it feels like a rather effective way to achive it with regards to the batch based approach of the importer. But maybe there's better ways. 

#147408
Edited, Apr 13, 2016 17:27
Vote:
 

Yeah the syntax of the DTOs and their lifecycles is a lot to get your head around and require you to get your hands dirty and produce rather verbose code but when you begin to understand all the idiosyncrasies of it it works pretty well and the batching capabilities are very powerful compared to the content model.

#147409
Apr 13, 2016 17:58
Vote:
 

Hi Tobias

Below is what I recently implemented in my solution (I am bit lazy to go to Commerce manager every time when I need to add inventory for a test product. :)) I hope this can help you solve your problem. 

 public void PublishedContent(object sender, ContentEventArgs e)
        {
#if DEBUG
            var entry = this.LoadEntry();

            var firstWarehouse = EPiServer.ServiceLocation.ServiceLocator.Current
                                .GetInstance<Mediachase.Commerce.Inventory.IWarehouseRepository>()
                                .List().FirstOrDefault();
            var warehouseInventoryService = EPiServer.ServiceLocation.ServiceLocator.Current.GetInstance<Mediachase.Commerce.Inventory.IWarehouseInventoryService>();

            var currentInventory = warehouseInventoryService.Get(new CatalogKey(entry), firstWarehouse);

            if(currentInventory == null)
            {
                var inventoryService = EPiServer.ServiceLocation.ServiceLocator.Current.GetInstance<Mediachase.Commerce.InventoryService.IInventoryService>();
                inventoryService.Insert(new[]
                {
                    new Mediachase.Commerce.InventoryService.InventoryRecord
                    {
                        AdditionalQuantity = 0,
                        BackorderAvailableQuantity = 0,
                        BackorderAvailableUtc = DateTime.UtcNow,
                        CatalogEntryCode = new CatalogKey(entry).CatalogEntryCode,
                        IsTracked = true,
                        PreorderAvailableQuantity = 0,
                        PreorderAvailableUtc = DateTime.UtcNow,
                        PurchaseAvailableQuantity = 1000,
                        PurchaseAvailableUtc = DateTime.UtcNow,
                        WarehouseCode = firstWarehouse.Code
                    }
                });
            }
            else
            {
                if (currentInventory.InStockQuantity < 100)
                {
                    var inventory = new Mediachase.Commerce.Inventory.WarehouseInventory()
                    {
                        CatalogKey = new CatalogKey(entry),
                        WarehouseCode = firstWarehouse.Code,
                        InStockQuantity = 1000,
                        InventoryStatus = Mediachase.Commerce.Inventory.InventoryTrackingStatus.Enabled
                    };

                    warehouseInventoryService.Save(inventory);
                }
            }            
            
#endif
        }
#147417
Edited, Apr 14, 2016 4:19
Vote:
 

Thanks for the input, works like a charm.

Is there a simple way to get hold of the properties of the specific entry without causing to much overhead? When the information is synced from inRiver we get a metafield called ItemWeight. What I would like to do is to put that value into the Weight property of the VariationRow. But can those values be read even if the entry has not been saved to the database? If this can not be done without causing to much overhead we will solve it in another way, more related to the cart/checkout.

#147421
Apr 14, 2016 8:39
Vote:
 

Basically no, the events are only fired when the entry is actually saved - I don't think it's a common case to read a value when it's not been persisted.

#147422
Apr 14, 2016 8:44
This topic was created over six months ago and has been resolved. If you have a similar question, please create a new topic and refer to this one.