DisplayName is just (another) property of EntryContent/NodeContent. You can set it as you like, as long as your content is inherited from NodeContent or EntryContentBase.
For the second question, I don't really get "do not show in the Catalog view". Which is the Catalog view you mentioned? And what is the code you're using to adding variant?
Hi, there seems to be two ways to add/update products.
Doing it the CatalogEntryDto way gives us most of the options we need apart from DisplayName and we can view the Variations in the Catalog view in the Commerce CMS 7.5 (Not Catalog Manager) view. THere needs to be some way of Publishing the Variant to view these.
If we chose to go down the route of IContentRepository route we cant add in new prices and Inventories.
Do you update the variant to have IsActive is true?
For the route of IContentRepository: Sure you can. Just need to use IPriceDetailService and IWarehouseInventoryService services (which can get through ServiceLocator).
I think IPriceDetailService and IWarehouseInventoryService would be perfect for us - do you know of any examples on how the code works?
They're quite simple. Assuming you just need to save values (pseudo-code, forgive the syntax errors etc):
IWarehouseInventoryService inventoryService = ServiceLocator.Current.GetInstance<IWarehouseRepositoryService>();
List<WarehouseInventory> inventories = new List<WarehouseInventory>();
WarehouseInventory inventory = new WarehouseInventory();
inventory.CatalogKey = new CatalogKey(//ApplicationId, CatalogEntryCode);
Same for IPriceDetailService
This is very kind of you. Many thanks for your help.
Can I ask, would WarehouseInventory be PriceDetailValue for the prices?
for example: List<PriceDetailValue> inventories = new List<PriceDetailValue>();
Yes, For the IPriceDetailService, you can call Save(IEnumerable<IPriceDetailValue> priceValues), which you can use PriceDetailValue as implementation for IPriceDetailValue.
HI, Thanks again.
We are using this code now - does this look right?
CatalogEntryDto.CatalogEntryRow dtoRow = CatalogContext.Current.GetCatalogEntryDto(prod.CatalogEntryId).CatalogEntry.FirstOrDefault(); CatalogKey ck = new CatalogKey(dtoRow); Currency currency = new Currency("GBP"); IPriceDetailService priceDetailService = ServiceLocator.Current.GetInstance<IPriceDetailService>(); List<PriceDetailValue> newPriceList = new List<PriceDetailValue>(); PriceDetailValue newPriceEntry = new PriceDetailValue(); newPriceEntry.CatalogKey = ck; newPriceEntry.MinQuantity = 0; newPriceEntry.MarketId = MarketId.Default.Value; newPriceEntry.UnitPrice = new Money(price, currency); newPriceEntry.ValidFrom = DateTime.Now; newPriceEntry.ValidUntil = DateTime.Now.AddYears(20); newPriceList.Add(newPriceEntry); priceDetailService.Save(newPriceList);
Looks right. But I prefer not to use GetCatalogEntryDto just to construct the CatalogKey. The preferred approach is to use
var catalogKey = new CatalogKey(AppContext.Current.ApplicationId, entryCode)
well, unless you need CatalogEntryDto for other purposes :)
Many thanks that is perfect.
Sorry to bother you again, the
priceDetailService.Save(newPriceList); is returning an Object reference is null exception - is there something we need to add?
Can you send me the stacktrace? I can't say much without it :)
I think I know what is causing it - the first time it went in well, but because the second time we tried it the product already existed so we need to check for an existing product then do a sort of Update - not sure how you can update an existing product. Do you know how to update an existing Product?
Thanks for all your help,
You can use CatalogContentDraftStore.Save(IContent, SaveAction) to save your Entry/Node content, regardless of it's new or existing.
Hi, sounds like a slightly different way of doing it - I have not tried that way before - do you have any samples from start to the save function?
Hi, Thanks fpr your help yesterday - wow - on page 2 now of this thread :).
Did you have any sample code that you could share?
My mistake. I would suggest to use CatalogContentProvider.Save(IContent content, SaveAction action) instead. CatalogContentDraftStore is the underlying class.
EntryContentBase/NodeContent contents are inherits from IContent, then you can pass it to the method. You can get CatalogContentProvider instance from ServiceLocator. Pseudo-code:
var contentProvider = ServiceLocator.Current.GetInstance<CatalogContentProvider>()
Thanks. How do you create a new Product using entryContent?
It's funny - using Entry there is no option to add in a DisplayName
Just like this:
var entryContent = new VariationContent();
you can use VariationContent, ProductContent or other classes (inherited from EntryContentBase/NodeContent) as you need :).
Note that Entry is the "concept" from eCF API and should not be used with CatalogContentProvider. You should/need to use content types defined in EPiServer.Commerce.Catalog.ContentTypes instead.
Hi, thanks. Sorry to keep asking simple questions but how would you Update an existing Product this way?
Also, does the entryContent require a ContentLink and if so how would you give it one if the product is new?
Hi Q, this is my code so far - Im getting an Object Reference instance of NULL on the Save:
var contentProvider = ServiceLocator.Current.GetInstance<EPiServer.Commerce.Catalog.Provider.CatalogContentProvider>(); var entryContent = new EPiServer.Commerce.Catalog.ContentTypes.ProductContent(); var referenceConverter = ServiceLocator.Current.GetInstance<ReferenceConverter>(); //// EPISERVER VALUES entryContent.ApplicationId = AppContext.Current.ApplicationId.ToString(); entryContent.CatalogId = _catalogId; entryContent.DisplayName = productName; entryContent.Name = productName; entryContent.Code = productCode; entryContent.StartPublish = DateTime.UtcNow; entryContent.StopPublish = DateTime.UtcNow.AddYears(20); //entryContent.SeoInformation entryContent.ContentLink = referenceConverter.GetContentLink(entryContent.CatalogId, CatalogContentType.CatalogNode, 0); contentProvider.Save(entryContent, DataAccess.SaveAction.Publish);
Have I missed anything?
The Stack trace is:
[NullReferenceException: Object reference not set to an instance of an object.] EPiServer.Commerce.Catalog.Provider.<>c__DisplayClass29.<CreateDraft>b__27(ContentVersion v) +106 System.Linq.Enumerable.FirstOrDefault(IEnumerable`1 source, Func`2 predicate) +168 EPiServer.Commerce.Catalog.Provider.CatalogContentDraftStore.CreateDraft(CatalogContentBase content, Int32 workId, VersionStatus status, Int32 masterVersionId) +438 EPiServer.Commerce.Catalog.Provider.CatalogContentDraftStore.SaveInternal(CatalogContentBase content, SaveAction saveAction, String userName, Boolean forceCurrentVersion, Boolean newVersionRequired, Boolean delayedPublish) +943 EPiServer.Commerce.Catalog.Provider.CatalogContentDraftStore.Save(IContent content, SaveAction saveAction) +1029 EPiServer.Commerce.Catalog.Provider.CatalogContentProvider.Save(IContent content, SaveAction action) +469 EPiServer.Templates.RCOG.Business.CatalogEntryImportManager.CreateProduct(String productName, String productCategory, String productCode, String productBooking_Document, String productBooking_Email, String productBooking_Type, String productOrganiserEmail, String productProgrammeLink, String productThumbnail, String productVenue, Boolean productAllowBookings, Nullable`1 productStartDate, Nullable`1 productEndDate, String productStartTime, String productEndTime, String productAudience, String productRegion, String productType, String productEventStatus, Int32 productIntegraref) in z:\Projects\D\RCOG\Website\Development\rcog-uat\Business\Core\CatalogEntryImportManager.cs:157 [Exception: Something is wrong with Catalog.] EPiServer.Templates.RCOG.Business.CatalogEntryImportManager.CreateProduct(String productName, String productCategory, String productCode, String productBooking_Document, String productBooking_Email, String productBooking_Type, String productOrganiserEmail, String productProgrammeLink, String productThumbnail, String productVenue, Boolean productAllowBookings, Nullable`1 productStartDate, Nullable`1 productEndDate, String productStartTime, String productEndTime, String productAudience, String productRegion, String productType, String productEventStatus, Int32 productIntegraref) in z:\Projects\D\RCOG\Website\Development\rcog-uat\Business\Core\CatalogEntryImportManager.cs:368 Episerver.Templates.RCOG.Business.ScheduledJobs.ImportEvents.Execute() in z:\Projects\D\RCOG\Website\Development\rcog-uat\Business\ScheduledJobs\ImportEvents.cs:127 Episerver.Templates.RCOG.testpage.Page_Load(Object sender, EventArgs e) in z:\Projects\D\RCOG\Website\Development\rcog-uat\testpage.aspx.cs:35 System.Web.UI.Control.LoadRecursive() +116 System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +2910
Your problem is the following:
var entryContent = new EPiServer.Commerce.Catalog.ContentTypes.ProductContent();
You should never use new on an IContent. Instead, use the IContentRepository to get your product. Use the Get method to get an existing one, or GetDefault if you want to create a new one. The flow to update an existing product would be:
You can see some examples of how to use IContentLoader/IContentRepository in the following articles:
Im sorry, but I am not a seaoned Episerver programmer - I still dont know what you mean. I can read the forum links but there is nothing about creating a brand new Product (NOT SKU)
WHat is wrong with this code:
var parentLink = new ContentReference(); var contentRepository = ServiceLocator.Current.GetInstance<IContentRepository>(); //Create a new instance of CatalogContentTypeSample that will be a child to the specified parentNode. var entryContent = contentRepository.GetDefault<EPiServer.Commerce.Catalog.ContentTypes.ProductContent>(parentLink);
As it is asking for a ContentLink
One important thing you've missed is that you need a model class for your product. Ie a class that inherits from ProductContent and defines the properties the product needs. That's the only difference when working with Products instead of SKU:s. The SKU:s model type should inherit from VariationContent.
The contentLink in this case should be a reference to the parent of this product. It's usually a category. If you dont have a ContentReference to the category, maybe you just have its ID or code, you can use the ReferenceConverter to get one.
That makes sense. On the Save (as we are going to run this as a scheduled job) we are getting an Access is Denied error - we have tried Undefined, NoAccess, Administer and Full Access but none of these work.
If you call IContentRepository.Save with NoAccess as a parameter, it should save without checking access.
What stack trace do you for you Access Denied error?
Hi, Yes - NoAccess does work - I had to move it up to Live and it now works - Many thanks both for all your help.
This is our working code on how to ADD a new product:
var referenceConverter = ServiceLocator.Current.GetInstance<ReferenceConverter>(); var contentRepository = ServiceLocator.Current.GetInstance<IContentRepository>(); var productLink = referenceConverter.GetContentLink(node.CatalogNodeId, CatalogContentType.CatalogNode, 0); //Create a new instance of CatalogContentTypeSample that will be a child to the specified parentNode. var newProduct = contentRepository.GetDefault<ProductContentType>(productLink); newProduct.CreateWritableClone(); //var referenceConverter = ServiceLocator.Current.GetInstance<ReferenceConverter>(); ////var productLink = referenceConverter.GetContentLink(productCode); //var contentRepository = ServiceLocator.Current.GetInstance<IContentRepository>(); //var parentLink = new ContentReference(); //var newProduct = contentRepository.GetDefault<ProductContentType>(parentLink); //ProductContentType newProduct = new ProductContentType(); // EPISERVER VALUES newProduct.ApplicationId = AppContext.Current.ApplicationId.ToString(); newProduct.CatalogId = "INT ID"; newProduct.DisplayName = "STRING NAME";
contentRepository.Save(newProduct, DataAccess.SaveAction.Publish, Security.AccessLevel.NoAccess);
When adding a new Product via c# code using similar code that Khurram Khan uses in his blog http://kkhan-episerver.blogspot.co.uk/ how can the Display Name get populated? We can populate the Internal name ok but not the Display Name.
Also, when adding Variants, they do not show in the Catalog view and will only show if you go in each one and publish them. (they show up on the Catalog Manager view first time).