Skip to content

Commit 38b2fe8

Browse files
authored
Cross sell (#190)
* added cross-sell products
1 parent c5beae4 commit 38b2fe8

File tree

10 files changed

+121
-43
lines changed

10 files changed

+121
-43
lines changed

src/Modules/SimplCommerce.Module.Catalog/Controllers/ProductApiController.cs

Lines changed: 62 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -141,12 +141,23 @@ public async Task<IActionResult> Get(long id)
141141
});
142142
}
143143

144-
foreach (var relatedProduct in product.ProductLinks.Where(x => x.LinkType == ProductLinkType.Relation).Select(x => x.LinkedProduct).Where(x => !x.IsDeleted).OrderBy(x => x.Id))
144+
foreach (var relatedProduct in product.ProductLinks.Where(x => x.LinkType == ProductLinkType.Related).Select(x => x.LinkedProduct).Where(x => !x.IsDeleted).OrderBy(x => x.Id))
145145
{
146-
productVm.RelatedProducts.Add(new RelatedProductVm
146+
productVm.RelatedProducts.Add(new ProductLinkVm
147147
{
148148
Id = relatedProduct.Id,
149-
Name = relatedProduct.Name
149+
Name = relatedProduct.Name,
150+
IsPublished = relatedProduct.IsPublished
151+
});
152+
}
153+
154+
foreach (var crossSellProduct in product.ProductLinks.Where(x => x.LinkType == ProductLinkType.CrossSell).Select(x => x.LinkedProduct).Where(x => !x.IsDeleted).OrderBy(x => x.Id))
155+
{
156+
productVm.CrossSellProducts.Add(new ProductLinkVm
157+
{
158+
Id = crossSellProduct.Id,
159+
Name = crossSellProduct.Name,
160+
IsPublished = crossSellProduct.IsPublished
150161
});
151162
}
152163

@@ -316,7 +327,7 @@ public async Task<IActionResult> Post(ProductForm model)
316327
SaveProductMedias(model, product);
317328

318329
MapProductVariationVmToProduct(model, product);
319-
MapProductRelationVmToProduct(model, product);
330+
MapProductLinkVmToProduct(model, product);
320331

321332
_productService.Create(product);
322333

@@ -385,7 +396,7 @@ public async Task<IActionResult> Put(long id, ProductForm model)
385396
AddOrDeleteProductAttribute(model, product);
386397
AddOrDeleteCategories(model, product);
387398
AddOrDeleteProductVariation(model, product);
388-
AddOrDeleteProductRelation(model, product);
399+
AddOrDeleteProductLinks(model, product);
389400

390401
_productService.Update(product);
391402

@@ -468,15 +479,27 @@ private static void MapProductVariationVmToProduct(ProductForm model, Product pr
468479
}
469480
}
470481

471-
private static void MapProductRelationVmToProduct(ProductForm model, Product product)
482+
private static void MapProductLinkVmToProduct(ProductForm model, Product product)
472483
{
473-
foreach (var relationVm in model.Product.RelatedProducts)
484+
foreach (var relatedProductVm in model.Product.RelatedProducts)
485+
{
486+
var productLink = new ProductLink
487+
{
488+
LinkType = ProductLinkType.Related,
489+
Product = product,
490+
LinkedProductId = relatedProductVm.Id
491+
};
492+
493+
product.AddProductLinks(productLink);
494+
}
495+
496+
foreach (var crossSellProductVm in model.Product.CrossSellProducts)
474497
{
475498
var productLink = new ProductLink
476499
{
477-
LinkType = ProductLinkType.Relation,
500+
LinkType = ProductLinkType.CrossSell,
478501
Product = product,
479-
LinkedProductId = relationVm.Id
502+
LinkedProductId = crossSellProductVm.Id
480503
};
481504

482505
product.AddProductLinks(productLink);
@@ -597,31 +620,55 @@ private void AddOrDeleteProductVariation(ProductForm model, Product product)
597620
}
598621

599622
// Due to some issue with EF Core, we have to use _productLinkRepository in this case.
600-
private void AddOrDeleteProductRelation(ProductForm model, Product product)
623+
private void AddOrDeleteProductLinks(ProductForm model, Product product)
601624
{
602-
foreach (var productRelationVm in model.Product.RelatedProducts)
625+
foreach (var relatedProductVm in model.Product.RelatedProducts)
603626
{
604-
var productLink = product.ProductLinks.Where(x => x.LinkType == ProductLinkType.Relation).FirstOrDefault(x => x.LinkedProductId == productRelationVm.Id);
627+
var productLink = product.ProductLinks.Where(x => x.LinkType == ProductLinkType.Related).FirstOrDefault(x => x.LinkedProductId == relatedProductVm.Id);
605628
if (productLink == null)
606629
{
607630
productLink = new ProductLink
608631
{
609-
LinkType = ProductLinkType.Relation,
632+
LinkType = ProductLinkType.Related,
610633
Product = product,
611-
LinkedProductId = productRelationVm.Id,
634+
LinkedProductId = relatedProductVm.Id,
612635
};
613636

614637
_productLinkRepository.Add(productLink);
615638
}
616639
}
617640

618-
foreach (var productLink in product.ProductLinks.Where(x => x.LinkType == ProductLinkType.Relation))
641+
foreach (var productLink in product.ProductLinks.Where(x => x.LinkType == ProductLinkType.Related))
619642
{
620643
if (model.Product.RelatedProducts.All(x => x.Id != productLink.LinkedProductId))
621644
{
622645
_productLinkRepository.Remove(productLink);
623646
}
624647
}
648+
649+
foreach (var crossSellProductVm in model.Product.CrossSellProducts)
650+
{
651+
var productLink = product.ProductLinks.Where(x => x.LinkType == ProductLinkType.CrossSell).FirstOrDefault(x => x.LinkedProductId == crossSellProductVm.Id);
652+
if (productLink == null)
653+
{
654+
productLink = new ProductLink
655+
{
656+
LinkType = ProductLinkType.CrossSell,
657+
Product = product,
658+
LinkedProductId = crossSellProductVm.Id,
659+
};
660+
661+
_productLinkRepository.Add(productLink);
662+
}
663+
}
664+
665+
foreach (var productLink in product.ProductLinks.Where(x => x.LinkType == ProductLinkType.CrossSell))
666+
{
667+
if (model.Product.CrossSellProducts.All(x => x.Id != productLink.LinkedProductId))
668+
{
669+
_productLinkRepository.Remove(productLink);
670+
}
671+
}
625672
}
626673

627674
private void AddOrDeleteProductAttribute(ProductForm model, Product product)

src/Modules/SimplCommerce.Module.Catalog/Controllers/ProductController.cs

Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -113,32 +113,24 @@ private void MapProductVariantToProductVm(Product product, ProductDetail model)
113113

114114
private void MapRelatedProductToProductVm(Product product, ProductDetail model)
115115
{
116-
foreach(var productLink in product.ProductLinks.Where(x => x.LinkType == ProductLinkType.Relation))
116+
var publishedProductLinks = product.ProductLinks.Where(x => x.LinkedProduct.IsPublished && (x.LinkType == ProductLinkType.Related || x.LinkType == ProductLinkType.CrossSell));
117+
foreach(var productLink in publishedProductLinks)
117118
{
118-
var relatedProduct = productLink.LinkedProduct;
119-
var productThumbnail = new ProductThumbnail
120-
{
121-
Id = relatedProduct.Id,
122-
Name = relatedProduct.Name,
123-
SeoTitle = relatedProduct.SeoTitle,
124-
Price = relatedProduct.Price,
125-
OldPrice = relatedProduct.OldPrice,
126-
SpecialPrice = relatedProduct.SpecialPrice,
127-
SpecialPriceStart = relatedProduct.SpecialPriceStart,
128-
SpecialPriceEnd = relatedProduct.SpecialPriceEnd,
129-
StockQuantity = relatedProduct.StockQuantity,
130-
IsAllowToOrder = relatedProduct.IsAllowToOrder,
131-
IsCallForPricing = relatedProduct.IsCallForPricing,
132-
ThumbnailImage = relatedProduct.ThumbnailImage,
133-
NumberVariation = relatedProduct.ProductLinks.Count,
134-
ReviewsCount = relatedProduct.ReviewsCount,
135-
RatingAverage = relatedProduct.RatingAverage
136-
};
119+
var linkedProduct = productLink.LinkedProduct;
120+
var productThumbnail = ProductThumbnail.FromProduct(linkedProduct);
137121

138-
productThumbnail.ThumbnailUrl = _mediaService.GetThumbnailUrl(relatedProduct.ThumbnailImage);
139-
productThumbnail.CalculatedProductPrice = _productPricingService.CalculateProductPrice(relatedProduct);
122+
productThumbnail.ThumbnailUrl = _mediaService.GetThumbnailUrl(linkedProduct.ThumbnailImage);
123+
productThumbnail.CalculatedProductPrice = _productPricingService.CalculateProductPrice(linkedProduct);
140124

141-
model.RelatedProducts.Add(productThumbnail);
125+
if(productLink.LinkType == ProductLinkType.Related)
126+
{
127+
model.RelatedProducts.Add(productThumbnail);
128+
}
129+
130+
if(productLink.LinkType == ProductLinkType.CrossSell)
131+
{
132+
model.CrossSellProducts.Add(productThumbnail);
133+
}
142134
}
143135
}
144136
}

src/Modules/SimplCommerce.Module.Catalog/Models/ProductLinkType.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ public enum ProductLinkType
44
{
55
Super = 1,
66

7-
Relation = 2,
7+
Related = 2,
88

99
CrossSell = 3,
1010

src/Modules/SimplCommerce.Module.Catalog/ViewModels/ProductDetail.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,5 +62,7 @@ into g
6262
public IList<ProductDetailCategory> Categories { get; set; } = new List<ProductDetailCategory>();
6363

6464
public IList<ProductThumbnail> RelatedProducts { get; set; } = new List<ProductThumbnail>();
65+
66+
public IList<ProductThumbnail> CrossSellProducts { get; set; } = new List<ProductThumbnail>();
6567
}
6668
}

src/Modules/SimplCommerce.Module.Catalog/ViewModels/RelatedProductVm.cs renamed to src/Modules/SimplCommerce.Module.Catalog/ViewModels/ProductLinkVm.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
namespace SimplCommerce.Module.Catalog.ViewModels
22
{
3-
public class RelatedProductVm
3+
public class ProductLinkVm
44
{
55
public long Id { get; set; }
66

77
public string Name { get; set; }
8+
9+
public bool IsPublished { get; set; }
810
}
911
}

src/Modules/SimplCommerce.Module.Catalog/ViewModels/ProductVm.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ public ProductVm()
6464

6565
public long? BrandId { get; set; }
6666

67-
public List<RelatedProductVm> RelatedProducts { get; set; } = new List<RelatedProductVm>();
67+
public List<ProductLinkVm> RelatedProducts { get; set; } = new List<ProductLinkVm>();
68+
69+
public List<ProductLinkVm> CrossSellProducts { get; set; } = new List<ProductLinkVm>();
6870
}
6971
}

src/Modules/SimplCommerce.Module.Catalog/Views/Product/ProductDetail.cshtml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,19 @@
214214
</div>
215215
}
216216

217+
@if (Model.CrossSellProducts.Any())
218+
{
219+
<h2 class="page-header">Cross-sell Products</h2>
220+
<div class="row product-list">
221+
@foreach (var product in Model.CrossSellProducts)
222+
{
223+
<div class="col-xs-6 col-md-3">
224+
@await Html.PartialAsync("_ProductThumbnail", product)
225+
</div>
226+
}
227+
</div>
228+
}
229+
217230
<div>
218231
<h3>@Localizer["Customer reviews"]</h3>
219232
@await Component.InvokeAsync("Review", new { entityId = Model.Id, entityTypeId = (long)3 })

src/Modules/SimplCommerce.Module.Catalog/wwwroot/admin/product/product-form.html

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,9 +347,25 @@ <h2 ng-if="vm.isEditMode">{{::vm.translate.get('Edit Product')}} {{vm.product.na
347347
<table class="table table-striped">
348348
<tr>
349349
<th>{{::vm.translate.get('Name')}}</th>
350+
<th class="text-center">{{::vm.translate.get('Is Published')}}</th>
350351
</tr>
351352
<tr ng-repeat="product in vm.product.relatedProducts">
352353
<td>{{product.name}}</td>
354+
<td class="text-center"><i ng-attr-class="{{product.isPublished && 'fa fa-circle' || 'fa fa-circle-o'}}"></i></td>
355+
</tr>
356+
</table>
357+
</uib-tab>
358+
<uib-tab index="5" heading="{{::vm.translate.get('Cross-sell Products')}}">
359+
<product-selection-directive model-id="productCrossSellSelector" title="Add cross-sell products" selected-products="vm.product.crossSellProducts"></product-selection-directive>
360+
<p><button type="button" class="btn btn-default" data-toggle="modal" data-target="#productCrossSellSelector">{{::vm.translate.get('Manage Cross-sell Products')}}</button></p>
361+
<table class="table table-striped">
362+
<tr>
363+
<th>{{::vm.translate.get('Name')}}</th>
364+
<th class="text-center">{{::vm.translate.get('Is Published')}}</th>
365+
</tr>
366+
<tr ng-repeat="product in vm.product.crossSellProducts">
367+
<td>{{product.name}}</td>
368+
<td class="text-center"><i ng-attr-class="{{product.isPublished && 'fa fa-circle' || 'fa fa-circle-o'}}"></i></td>
353369
</tr>
354370
</table>
355371
</uib-tab>

src/Modules/SimplCommerce.Module.Catalog/wwwroot/admin/product/product-form.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
vm.product.variations = [];
1616
vm.product.attributes = [];
1717
vm.product.relatedProducts = [];
18+
vm.product.crossSellProducts = [];
1819
vm.categories = [];
1920
vm.thumbnailImage = null;
2021
vm.productImages = [];

src/Modules/SimplCommerce.Module.Catalog/wwwroot/admin/product/product-selection-directive.html

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ <h4 class="modal-title">{{vm.title}}</h4>
1212
<th>Selected</th>
1313
<th st-sort="Name" class="sortable">Name</th>
1414
<th class="text-center">Is Allowed<br />To Order</th>
15+
<th class="text-center">Is Published</th>
1516
</tr>
1617
<tr>
1718
<th></th>
@@ -21,6 +22,7 @@ <h4 class="modal-title">{{vm.title}}</h4>
2122
</div>
2223
</th>
2324
<th></th>
25+
<th></th>
2426
</tr>
2527
</thead>
2628
<tbody ng-show="!vm.isLoading">
@@ -34,16 +36,17 @@ <h4 class="modal-title">{{vm.title}}</h4>
3436
</td>
3537
<td>{{product.name}}</td>
3638
<td class="text-center"><i ng-attr-class="{{product.isAllowToOrder && 'fa fa-circle' || 'fa fa-circle-o'}}"></i></td>
39+
<td class="text-center"><i ng-attr-class="{{product.isPublished && 'fa fa-circle' || 'fa fa-circle-o'}}"></i></td>
3740
</tr>
3841
</tbody>
3942
<tbody ng-show="vm.isLoading">
4043
<tr>
41-
<td colspan="3" class="text-center">Loading ... </td>
44+
<td colspan="4" class="text-center">Loading ... </td>
4245
</tr>
4346
</tbody>
4447
<tfoot>
4548
<tr>
46-
<td class="text-center" st-pagination="" st-items-by-page="5" st-displayed-pages="10" colspan="3"></td>
49+
<td class="text-center" st-pagination="" st-items-by-page="5" st-displayed-pages="10" colspan="4"></td>
4750
</tr>
4851
</tfoot>
4952
</table>

0 commit comments

Comments
 (0)