1+ using Grand . Business . Core . Interfaces . Catalog . Brands ;
2+ using Grand . Business . Core . Interfaces . Catalog . Categories ;
3+ using Grand . Business . Core . Interfaces . Catalog . Collections ;
4+ using Grand . Business . Core . Interfaces . Catalog . Discounts ;
5+ using Grand . Business . Core . Interfaces . Customers ;
6+ using Grand . Business . Core . Utilities . Catalog ;
7+ using Grand . Domain . Catalog ;
8+ using Grand . Domain . Customers ;
9+ using Grand . Domain . Directory ;
10+ using Grand . Domain . Discounts ;
11+ using Grand . Domain . Stores ;
12+
13+ namespace Grand . Business . Catalog . Services . Discounts ;
14+
15+ public class DiscountHandlerService : IDiscountHandlerService
16+ {
17+ private readonly IDiscountService _discountService ;
18+ private readonly ICategoryService _categoryService ;
19+ private readonly IBrandService _brandService ;
20+ private readonly ICollectionService _collectionService ;
21+ private readonly IVendorService _vendorService ;
22+ private readonly IDiscountValidationService _discountValidationService ;
23+ private readonly CatalogSettings _catalogSettings ;
24+
25+ public DiscountHandlerService (
26+ IDiscountService discountService ,
27+ ICategoryService categoryService ,
28+ IBrandService brandService ,
29+ ICollectionService collectionService ,
30+ IVendorService vendorService ,
31+ IDiscountValidationService discountValidationService ,
32+ CatalogSettings catalogSettings )
33+ {
34+ _discountService = discountService ;
35+ _categoryService = categoryService ;
36+ _brandService = brandService ;
37+ _collectionService = collectionService ;
38+ _vendorService = vendorService ;
39+ _discountValidationService = discountValidationService ;
40+ _catalogSettings = catalogSettings ;
41+ }
42+
43+ /// <summary>
44+ /// Gets allowed discounts
45+ /// </summary>
46+ /// <param name="product">Product</param>
47+ /// <param name="customer">Customer</param>
48+ /// <param name="store">Store</param>
49+ /// <param name="currency">Currency</param>
50+ /// <returns>Discounts</returns>
51+ private async Task AddAllowedDiscounts ( IList < ApplyDiscount > allowedDiscounts , IEnumerable < string > appliedDiscounts , Customer customer , Store store , Currency currency , DiscountType discountType )
52+ {
53+ foreach ( var appliedDiscount in appliedDiscounts )
54+ {
55+ var discount = await _discountService . GetDiscountById ( appliedDiscount ) ;
56+ if ( discount == null ) continue ;
57+ var validDiscount = await _discountValidationService . ValidateDiscount ( discount , customer , store , currency ) ;
58+ if ( validDiscount . IsValid && discount . DiscountTypeId == discountType )
59+ allowedDiscounts . Add ( new ApplyDiscount {
60+ CouponCode = validDiscount . CouponCode ,
61+ DiscountId = discount . Id ,
62+ IsCumulative = discount . IsCumulative ,
63+ MaximumDiscountedQuantity = discount . MaximumDiscountedQuantity
64+ } ) ;
65+ }
66+ }
67+
68+ public virtual async Task < IList < ApplyDiscount > > GetAllowedDiscounts ( Product product , Customer customer , Store store , Currency currency )
69+ {
70+ var allowedDiscounts = new List < ApplyDiscount > ( ) ;
71+ if ( _catalogSettings . IgnoreDiscounts )
72+ return allowedDiscounts ;
73+
74+ //discounts applied to products
75+ await AddAllowedDiscounts ( allowedDiscounts , product . AppliedDiscounts , customer , store , currency , DiscountType . AssignedToSkus ) ;
76+
77+ //discounts applied to all products
78+ var allProductDiscounts = await _discountService . GetActiveDiscountsByContext ( DiscountType . AssignedToAllProducts , store . Id , currency . CurrencyCode ) ;
79+ await AddAllowedDiscounts ( allowedDiscounts , allProductDiscounts . Select ( d => d . Id ) , customer , store , currency , DiscountType . AssignedToAllProducts ) ;
80+
81+ //discounts applied to categories
82+ foreach ( var productCategory in product . ProductCategories )
83+ {
84+ var category = await _categoryService . GetCategoryById ( productCategory . CategoryId ) ;
85+ if ( category == null ) continue ;
86+ await AddAllowedDiscounts ( allowedDiscounts , category . AppliedDiscounts , customer , store , currency , DiscountType . AssignedToCategories ) ;
87+ }
88+
89+ //discounts applied to brands
90+ if ( ! string . IsNullOrEmpty ( product . BrandId ) )
91+ {
92+ var brand = await _brandService . GetBrandById ( product . BrandId ) ;
93+ if ( brand != null )
94+ await AddAllowedDiscounts ( allowedDiscounts , brand . AppliedDiscounts , customer , store , currency , DiscountType . AssignedToBrands ) ;
95+ }
96+
97+ //discounts applied to collections
98+ foreach ( var productCollection in product . ProductCollections )
99+ {
100+ var collection = await _collectionService . GetCollectionById ( productCollection . CollectionId ) ;
101+ if ( collection == null ) continue ;
102+ await AddAllowedDiscounts ( allowedDiscounts , collection . AppliedDiscounts , customer , store , currency , DiscountType . AssignedToCollections ) ;
103+ }
104+
105+ //discounts applied to vendors
106+ if ( ! string . IsNullOrEmpty ( product . VendorId ) )
107+ {
108+ var vendor = await _vendorService . GetVendorById ( product . VendorId ) ;
109+ if ( vendor != null )
110+ await AddAllowedDiscounts ( allowedDiscounts , vendor . AppliedDiscounts , customer , store , currency , DiscountType . AssignedToVendors ) ;
111+ }
112+
113+ return FilterDiscountsByMaxQuantity ( allowedDiscounts ) ;
114+ }
115+
116+ private static IList < ApplyDiscount > FilterDiscountsByMaxQuantity ( IList < ApplyDiscount > discounts )
117+ {
118+ if ( ! discounts . Any ( ) )
119+ return discounts ;
120+
121+ // Identify discounts that are cumulative and have a MaximumDiscountedQuantity > 0
122+ var cumulativeDiscounts = discounts
123+ . Where ( d => d . IsCumulative && ( d . MaximumDiscountedQuantity ?? 0 ) > 0 )
124+ . ToList ( ) ;
125+
126+ if ( ! cumulativeDiscounts . Any ( ) )
127+ return discounts ;
128+
129+ // Determine the maximum MaximumDiscountedQuantity among cumulative discounts
130+ var maxQuantity = cumulativeDiscounts . Max ( d => d . MaximumDiscountedQuantity ?? 0 ) ;
131+
132+ // Select cumulative discounts with the maximum MaximumDiscountedQuantity
133+ var maxCumulativeDiscounts = cumulativeDiscounts
134+ . Where ( d => d . MaximumDiscountedQuantity == maxQuantity )
135+ . ToList ( ) ;
136+
137+ // Select discounts that are not cumulative or have MaximumDiscountedQuantity <= 0
138+ var otherDiscounts = discounts
139+ . Where ( d => ! ( d . IsCumulative && ( d . MaximumDiscountedQuantity ?? 0 ) > 0 ) )
140+ . ToList ( ) ;
141+
142+ // Combine the filtered cumulative discounts with the other discounts
143+ return maxCumulativeDiscounts . Concat ( otherDiscounts ) . ToList ( ) ;
144+ }
145+
146+ public async Task < ( List < ApplyDiscount > appliedDiscount , double discountAmount ) > GetDiscountAmount ( Product product , Customer customer , Store store , Currency currency , double productPriceWithoutDiscount )
147+ {
148+ var allowedDiscounts = await GetAllowedDiscounts ( product , customer , store , currency ) ;
149+
150+ //no discounts
151+ if ( ! allowedDiscounts . Any ( ) )
152+ return ( [ ] , 0 ) ;
153+
154+ var preferredDiscount = await _discountService . GetPreferredDiscount ( allowedDiscounts , customer , currency ,
155+ product , productPriceWithoutDiscount ) ;
156+
157+ return ( preferredDiscount . appliedDiscount , preferredDiscount . discountAmount ) ;
158+ }
159+ }
0 commit comments