Adding a new Product via code

Vote:
 

Hi,

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).

Thanks

#81250
Feb 12, 2014 15:52
Vote:
 

Hi,

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?

Regards.

/Q

 

 

#81265
Feb 13, 2014 4:51
Vote:
 

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.

#81272
Feb 13, 2014 9:52
Vote:
 

Hi,

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).

/Q

#81273
Feb 13, 2014 9:58
Vote:
 

Hi,

I think IPriceDetailService and IWarehouseInventoryService would be perfect for us - do you know of any examples on how the code works?

#81274
Feb 13, 2014 10:02
Vote:
 

Any examples?

#81276
Feb 13, 2014 10:41
Vote:
 

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);

        inventory....

        inventories.Add(inventory);

        _inventoryService.Save(inventories);

 

Same for IPriceDetailService

#81277
Feb 13, 2014 10:42
Vote:
 

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>();

Thanks again,

Jon

#81278
Edited, Feb 13, 2014 10:53
Vote:
 

Yes, For the IPriceDetailService, you can call Save(IEnumerable<IPriceDetailValue> priceValues), which you can use PriceDetailValue as implementation for IPriceDetailValue.

Regards.

/Q

#81281
Feb 13, 2014 11:05
Vote:
 

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);

    

#81282
Feb 13, 2014 11:09
Vote:
 

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 :)
Regards.
/Q

 

 

 


 

 

#81286
Edited, Feb 13, 2014 11:19
Vote:
 

Hi,

Many thanks that is perfect.

#81288
Feb 13, 2014 11:31
Vote:
 

Hi,

Sorry to bother you again, the priceDetailService.Save(newPriceList); is returning an Object reference is null exception - is there something we need to add?

#81290
Feb 13, 2014 12:11
Vote:
 

Hi,

Can you send me the stacktrace? I can't say much without it :)

/Q

#81291
Feb 13, 2014 12:15
Vote:
 

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,

Jon

#81292
Feb 13, 2014 12:22
Vote:
 

Hi,

You can use CatalogContentDraftStore.Save(IContent, SaveAction) to save your Entry/Node content, regardless of it's new or existing.

Regards.

/Q

#81302
Feb 13, 2014 15:41
Vote:
 

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?

#81306
Feb 13, 2014 18:25
Vote:
 

Hi, Thanks fpr your help yesterday - wow - on page 2 now of this thread :).

Did you have any sample code that you could share?

Thanks

#81313
Feb 14, 2014 9:15
Vote:
 

Hi,

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>()

contentProvider.Save(entryContent, SaveAction.Publish);

Regards,

/Q

#81314
Feb 14, 2014 9:32
Vote:
 

Thanks. How do you create a new Product using entryContent?

#81315
Feb 14, 2014 9:39
Vote:
 

It's funny - using Entry there is no option to add in a DisplayName

#81317
Feb 14, 2014 9:44
Vote:
 

Just like this:

var entryContent = new VariationContent();

entryContent.Name =

entryContent.DisplayName =

 

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.

Regards.

/Q

#81318
Feb 14, 2014 9:47
Vote:
 

Hi, thanks. Sorry to keep asking simple questions but how would you Update an existing Product this way?

#81319
Feb 14, 2014 9:58
Vote:
 

Also, does the entryContent require a ContentLink and if so how would you give it one if the product is new?

#81320
Feb 14, 2014 10:01
Vote:
 

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?

#81321
Feb 14, 2014 10:16
Vote:
 

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

    

#81325
Feb 14, 2014 10:40
Vote:
 

Hi

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:

  • Get product using IContentRepository
  • Call CreateWriteableClone on the product to make it writeable
  • Set your updated values
  • Save it using the IContentRepository

 

You can see some examples of how to use IContentLoader/IContentRepository in the following articles:

http://world.episerver.com/Documentation/Items/Developers-Guide/EPiServer-Commerce1/751/Content/Working-with-the-Catalog-as-IContent/

http://world.episerver.com/Documentation/Items/Developers-Guide/EPiServer-Commerce1/751/Content/Catalog-content-provider/

Regards

Per Gunsarfs

#81330
Feb 14, 2014 11:18
Vote:
 

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)

#81332
Feb 14, 2014 11:23
Vote:
 

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

#81333
Feb 14, 2014 11:31
Vote:
 

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.

#81334
Feb 14, 2014 11:41
Vote:
 

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.

#81336
Feb 14, 2014 11:49
Vote:
 

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?

#81337
Feb 14, 2014 11:58
Vote:
 

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);

    

#81339
Feb 14, 2014 12:22
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.
* You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.