PageSourceSearch

https://recroommasters.com/wp-content/plugins/woocommerce-mkl-pc-s…/js/stock-management.js?ver=1786074168

js recroommasters.com collected 2026-10-06 03:34:39 UTC 26,539 bytes, 621 lines download raw bytes

1( function( $, wp, PC ) {
2
3	wp.hooks.addAction( 'PC.fe.init.modal', 'mkl/product_configurator', function( Configurator ) {
4		if ( PC_config.config.hide_outofstock_items ) {
5			Configurator.$el.addClass( 'hide-outofstock-items' );
6		}
7	} );
8
9	wp.hooks.addAction( 'PC.fe.start', 'mkl/product_configurator', function( Configurator ) {
10		if ( PC_config.config.hide_outofstock_items ) {
11			Configurator.toolbar.$( 'li.out-of-stock' ).each( function( ind, item ) {
12				$parent = $( item ).closest( 'ul' );
13				$( item ).remove();
14				if ( ! $parent.find( 'li' ).length ) {
15					$parent.append( '<li class="out-of-stock--no-more-choice">' + PC_config.lang.outofstock_nomorechoice + '</li>' );
16				}
17			} );
18		}
19	} );
20 
21	wp.hooks.addAction( 'PC.fe.start', 'mkl/product_configurator', function( Configurator ) {
22		linked_stock_ready = true;
23		refresh_linked_stock_state();
24	}, 20000 );
25
26	/**
27	 * Create new PC.fe.currentProductData.linked_products collection, if it wasn't done before
28	 */
29	wp.hooks.addAction( 'PC.fe.setContent.parse.before', 'mkl/product_configurator', function() {
30		linked_stock_ready = false;
31		if ( PC.fe.currentProductData.linked_products_data && ! PC.fe.currentProductData.linked_products ) {
32			PC.fe.currentProductData.linked_products = new Backbone.Collection( PC.fe.currentProductData.linked_products_data );
33		}
34	} );
35
36	wp.hooks.addAction( 'PC.fe.reset_configurator', 'mkl/product_configurator', function() {
37		linked_stock_ready = true;
38		refresh_linked_stock_state();
39	} );
40
41	const get_choice_qty = function( model ) {
42		const default_min_qty = wp.hooks.applyFilters( 'PC.fe.linked_product.default_min_qty', 1, model );
43		let qty = parseFloat( model.get( 'linked_product_qty' ) );
44		if ( Number.isNaN( qty ) || qty <= 0 ) {
45			qty = default_min_qty;
46		}
47		return Math.abs( qty );
48	};
49
50	const is_linked_choice = function( model ) {
51		return (
52			model
53			&& 'linked' === model.get( 'manage_stock' )
54			&& model.get( 'linked_product_id' )
55			&& false !== model.get( 'cshow' )
56			&& ! model.get( 'is_group' )
57		);
58	};
59
60	const get_choice_value = function( choice, key ) {
61		if ( choice && 'function' === typeof choice.get ) return choice.get( key );
62		return choice ? choice[key] : undefined;
63	};
64
65	const set_choice_value = function( choice, key, value ) {
66		if ( choice && 'function' === typeof choice.set ) {
67			choice.set( key, value );
68		} else if ( choice ) {
69			choice[key] = value;
70		}
71	};
72
73	const should_use_linked_product_text = function( choice, field ) {
74		const global_settings = {
75			name: 'link_choice_name_from_product',
76			description: 'link_choice_description_from_product',
77			advanced_description: 'link_advanced_description_from_product',
78		};
79		const choice_settings = {
80			name: 'linked_product_name_override',
81			description: 'linked_product_description_override',
82			advanced_description: 'linked_product_advanced_description_override',
83		};
84
85		if ( ! global_settings[field] || ! choice_settings[field] ) return false;
86		if ( 'advanced_description' === field && get_choice_value( choice, 'is_advanced_description_disabled' ) ) return false;
87
88		const mode = get_choice_value( choice, choice_settings[field] ) || 'inherit';
89		if ( 'use_from_product' === mode ) return true;
90		if ( 'no_override' === mode ) return false;
91
92		return !! PC.fe.config[global_settings[field]];
93	};
94
95	const update_linked_product_text = function( choice, product ) {
96		if ( should_use_linked_product_text( choice, 'name' ) ) {
97			const linked_choice_name = product.get( 'extensions' )?.mkl_pc_stock?.linked_choice_name;
98			if ( null !== linked_choice_name && 'undefined' !== typeof linked_choice_name ) {
99				set_choice_value( choice, 'name', linked_choice_name );
100			}
101		}
102		if ( should_use_linked_product_text( choice, 'description' ) ) {
103			const linked_choice_description = product.get( 'extensions' )?.mkl_pc_stock?.linked_choice_description;
104			if ( null !== linked_choice_description && 'undefined' !== typeof linked_choice_description ) {
105				set_choice_value( choice, 'description', linked_choice_description );
106			}
107		}
108		if ( should_use_linked_product_text( choice, 'advanced_description' ) ) {
109			const linked_choice_advanced_description = product.get( 'extensions' )?.mkl_pc_stock?.linked_choice_advanced_description;
110			if ( null !== linked_choice_advanced_description && 'undefined' !== typeof linked_choice_advanced_description ) {
111				set_choice_value( choice, 'advanced_description', linked_choice_advanced_description );
112			}
113		}
114	};
115
116	let linked_stock_ready = false;
117
118	const get_linked_choices_map = function() {
119		const map = {};
120		if ( ! PC.fe.layers ) return map;
121		PC.fe.layers.each( function( layer ) {
122			const content = PC.fe.getLayerContent( layer.id );
123			if ( ! content ) return;
124			content.each( function( choice ) {
125				if ( ! is_linked_choice( choice ) ) return;
126				const linked_product_id = choice.get( 'linked_product_id' );
127				if ( ! map[linked_product_id] ) {
128					map[linked_product_id] = [];
129				}
130				map[linked_product_id].push( choice );
131			} );
132		} );
133		return map;
134	};
135
136	const get_linked_stock = function( choice ) {
137		if ( ! choice || ! PC.fe.currentProductData.linked_products ) return null;
138		const linked_product_id = choice.get( 'linked_product_id' );
139		if ( ! linked_product_id ) return null;
140		const product = PC.fe.currentProductData.linked_products.get( linked_product_id );
141		if ( ! product ) return null;
142		const maximum = product.get( 'is_in_stock' ) ? parseFloat( product.get( 'add_to_cart' ).maximum ) || 0 : 0;
143		return {
144			product: product,
145			maximum: maximum,
146		};
147	};
148
149	const get_required_quantity = function( linked_choices, target_choice = null, include_target = false ) {
150		return linked_choices.reduce( function( required_qty, choice ) {
151			let is_active = !! choice.get( 'active' );
152			if ( target_choice && choice.cid === target_choice.cid ) {
153				is_active = include_target;
154			}
155			if ( ! is_active ) return required_qty;
156			return required_qty + get_choice_qty( choice );
157		}, 0 );
158	};
159
160	const refresh_linked_stock_state = function() {
161		return;
162		if ( ! linked_stock_ready ) return;
163		if ( ! PC.fe.currentProductData.linked_products ) return;
164		const linked_choices_map = get_linked_choices_map();
165		Object.values( linked_choices_map ).forEach( function( linked_choices ) {
166			if ( ! linked_choices.length ) return;
167			const stock_data = get_linked_stock( linked_choices[0] );
168			if ( ! stock_data ) return;
169			let used_stock = 0;
170			
171			linked_choices.forEach( function( choice ) {
172				const own_qty = get_choice_qty( choice );
173				const is_active = !! choice.get( 'active' );
174				const remaining_for_choice = Math.max( 0, stock_data.maximum - used_stock );
175				const can_backorder = 'yes' === choice.get( 'wc_backorder' ) || 'notify' === choice.get( 'wc_backorder' );
176
177				choice.set( 'stock', stock_data.maximum, { silent: true } );
178				choice.set( 'available', can_backorder || own_qty <= remaining_for_choice );
179
180				if ( typeof choice.get( 'linked_product_max_qty_raw' ) === 'undefined' ) {
181					const raw_max_qty = choice.get( 'linked_product_max_qty' );
182					choice.set( 'linked_product_max_qty_raw', raw_max_qty, { silent: true } );
183				}
184
185				const configured_max = parseFloat( choice.get( 'linked_product_max_qty_raw' ) );
186				let max_qty = remaining_for_choice;
187				if ( ! Number.isNaN( configured_max ) && configured_max > 0 ) {
188					max_qty = Math.min( max_qty, configured_max );
189				}
190				choice.set( 'linked_product_max_qty', max_qty );
191
192				if ( is_active ) {
193					used_stock += own_qty;
194				}
195			} );
196		} );
197	};
198
199	/**
200	 * Update product data
201	 */
202	wp.hooks.addAction( 'PC.fe.models.choice.init', 'mkl/product_configurator', function( Model ) {
203
204		var linked_product_id = Model.get( 'linked_product_id');
205		var stock_management = Model.get( 'manage_stock' );
206		// Update prices and stock
207		if ( linked_product_id && PC.fe.currentProductData.linked_products ) {
208			var product = PC.fe.currentProductData.linked_products.get( linked_product_id );
209			if ( product ) {
210				
211				const prices = product.get( 'prices' );
212
213				Model.set( 'linked_product_price', prices.price / 10 ** prices.currency_minor_unit );
214
215				if ( 'linked' === stock_management ) {
216					update_linked_product_text( Model, product );
217					
218					// Only set prices if we link the extra price, and do not override it
219					if (
220						// if we Link the extra price
221						PC.fe.config.link_extra_price
222						&& !PC.fe.config.extra_price_overrides_product_price
223						&& ( 
224							!Model.get( 'override_linked_price' )
225							|| (
226								Model.get( 'override_linked_price' )
227								&& 'replace' !== Model.get( 'override_linked_price_mode' )
228							)
229						)
230
231					) {
232
233						if ( PC_config.config.tax_settings && 'incl' == PC_config.config.tax_settings.tax_display_mode && ! PC_config.config.tax_settings.prices_include_tax && product.get( 'extensions' ).mkl_pc_stock.price_excl_tax ) {
234							Model.set( 'linked_product_price', product.get( 'extensions' ).mkl_pc_stock.price_excl_tax );
235							if ( product.get( 'extensions' ).mkl_pc_stock?.regular_price_excl_tax ) Model.set( 'linked_product_regular_price', product.get( 'extensions' ).mkl_pc_stock?.regular_price_excl_tax );
236						} else if ( PC_config.config.tax_settings && 'excl' === PC_config.config.tax_settings.tax_display_mode && PC_config.config.tax_settings.prices_include_tax && product.get( 'extensions' ).mkl_pc_stock.price_incl_tax ) {
237							Model.set( 'linked_product_price', product.get( 'extensions' ).mkl_pc_stock.price_incl_tax );
238							if ( product.get( 'extensions' ).mkl_pc_stock?.regular_price_incl_tax ) Model.set( 'linked_product_regular_price', product.get( 'extensions' ).mkl_pc_stock?.regular_price_incl_tax );
239						} else {
240							Model.set( 'linked_product_price', prices.price / 10 ** prices.currency_minor_unit );
241							Model.set( 'linked_product_regular_price', prices.regular_price / 10 ** prices.currency_minor_unit );
242						}
243
244						// Wholesale price
245						if ( product.get( 'extensions' ).mkl_pc_stock.wholesale_price ) {
246							const wholesale_price = product.get( 'extensions' ).mkl_pc_stock.wholesale_price;
247							// If the tax settings are not consistent, we need to reset the wholesale price, as the plugin provides the price with the "tax display mode" applied.
248							if ( PC_config.config.tax_enabled ) {
249								if ( PC_config.config.tax_settings.prices_include_tax ) {
250									var price = wholesale_price.wholesale_price_with_tax;
251								} else {
252									var price = wholesale_price.wholesale_price_with_no_tax;
253								}
254							} else {
255								var price = wholesale_price.wholesale_price;
256							}
257
258							Model.set( 'linked_product_price', price );
259						}
260					}
261
262					if ( product.get( 'is_in_stock' ) ) {
263						Model.set( 'stock', product.get( 'add_to_cart' ).maximum );
264					} else {
265						Model.set( 'stock', 0 );
266					}
267
268					// Maybe change linked_product_max_qty
269					if ( product.get( 'add_to_cart' ).maximum < Model.get( 'linked_product_max_qty' ) ) {
270						Model.set( 'linked_product_max_qty', product.get( 'add_to_cart' ).maximum );
271					}
272					
273					// Maybe change linked_product_default_qty
274					if ( Model.get( 'linked_product_default_qty' ) && product.get( 'add_to_cart' ).maximum < Model.get( 'linked_product_default_qty' ) ) {
275						Model.set( 'linked_product_default_qty', product.get( 'add_to_cart' ).maximum );
276					}
277
278					if ( product.get( 'is_on_backorder' ) ) {
279						if ( product.get( 'extensions' ) && product.get( 'extensions' ).mkl_pc_stock && product.get( 'extensions' ).mkl_pc_stock.wc_backorder ) {
280							Model.set( 'wc_backorder', product.get( 'extensions' ).mkl_pc_stock.wc_backorder );
281						} else {
282							Model.set( 'wc_backorder', 'yes' );
283						}
284					} else { 
285						Model.set( 'wc_backorder', 'no' );
286					}
287
288					// Sale
289					if ( !Model.get( 'override_linked_price' ) && product.get( 'on_sale' ) ) {
290						Model.set( 'on_sale', true );
291					}
292				}
293			} else {
294				// If product is not found, while stock_management is enabled, set stock to 0
295				if ( 'linked' === stock_management ) {
296					Model.set( 'stock', 0 );
297					Model.set( 'available', false );
298					Model.set( 'hide-item', true );
299					console.warn( 'Product #' + linked_product_id + ' - ' + Model.get( 'linked_product_name' ) + ' was not found in linked product data. The product is linked to Choice ' + Model.get( 'name' ) );
300				}
301			}
302		}
303		
304		var quantity_to_decrease = Model.get( 'linked_product_qty' ) || 1;
305		quantity_to_decrease = ( quantity_to_decrease == null || stock_management != 'linked' ) ? 1 : Math.abs( quantity_to_decrease );
306		if ( 'none' != stock_management && quantity_to_decrease > Model.get( 'stock' ) ) {
307			if ( 'simple' == stock_management ) {
308				if ( ! Model.get( 'backorder' ) ) {
309					Model.set( 'available', false );
310				}
311			} else if ( Model.get( 'wc_backorder' ) ) {
312				if ( 'no' == Model.get( 'wc_backorder' ) ) {
313					Model.set( 'available', false );
314				}
315			} else {
316				Model.set( 'available', false );
317			}
318		}
319
320	} );
321
322	wp.hooks.addFilter( 'PC.fe.configurator.choice_data', 'mkl/pc/stock_management_linked_product_text', function( attributes ) {
323		if ( ! attributes || 'linked' !== attributes.manage_stock || ! attributes.linked_product_id || ! PC.fe.currentProductData.linked_products ) return attributes;
324
325		const product = PC.fe.currentProductData.linked_products.get( attributes.linked_product_id );
326		if ( product ) update_linked_product_text( attributes, product );
327
328		return attributes;
329	}, 20 );
330
331	wp.hooks.addAction( 'PC.fe.choice.init', 'mkl/pc/stock_management', function( view ) {
332		view.listenTo( view.model, 'change:linked_product_qty', function( item, value ) {
333			if ( view.$( '.mkl-pc-choice-qty' ).val() != value  ) {
334				view.$( '.mkl-pc-choice-qty' ).val( value );
335			}
336		} );
337	} );
338
339	/**
340	 * Override price displayed
341	 */
342	wp.hooks.addFilter( 'PC.extra_price_to_display', 'mkl/product_configurator', function( extra_price_do_display, choice ) {
343		if ( 'linked' != choice.get( 'manage_stock' ) || ! PC.fe.config.link_extra_price ) return extra_price_do_display;
344		let qty = 1;
345		const default_min_qty = wp.hooks.applyFilters( 'PC.fe.linked_product.default_min_qty', 1, choice );
346
347		// Check if we should multiply with the quantity 
348		if ( 
349			// quantity_multiplies_price is enabled
350			( ( ! choice.get( 'linked_product_qty_mode' ) || 'multiply' === choice.get( 'linked_product_qty_mode' ) ) && choice.get( 'quantity_multiplies_price' ) ) 
351			// OR we do not override prices, so we multiply by default
352			|| ( ! PC_config.extra_price_overrides_product_price && choice.get( 'linked_product_qty') && ! choice.get( 'override_linked_price' ) )
353		) qty = parseFloat( choice.get( 'linked_product_qty') ) || default_min_qty;
354		
355		return parseFloat( extra_price_do_display ) / qty;
356	} );
357
358	/**
359	 * Override a choice's Extra price, depending on the various settings
360	 */
361	wp.hooks.addFilter( 'pc.get_extra_price', 'mkl/product_configurator', function( extra_price, choice, context ) {
362		if ( 'linked' != choice.get( 'manage_stock' ) || ! PC.fe.config.link_extra_price ) return extra_price;
363
364		const forRegularTotal = context && context.forRegularTotal;
365		const linked_regular = choice.get( 'linked_product_regular_price' );
366		const use_regular_linked = forRegularTotal && 'undefined' != typeof linked_regular && null !== linked_regular && '' !== linked_regular && ! isNaN( parseFloat( linked_regular ) );
367
368		const default_min_qty = wp.hooks.applyFilters( 'PC.fe.linked_product.default_min_qty', 1, choice );
369		let qty = default_min_qty;
370
371		// Check if we should multiply with the quantity 
372		if ( 
373			// quantity_multiplies_price is enabled
374			( ( ! choice.get( 'linked_product_qty_mode' ) || 'multiply' === choice.get( 'linked_product_qty_mode' ) ) && choice.get( 'quantity_multiplies_price' ) ) 
375			// OR we do not override prices, so we multiply by default
376			|| ( ! PC_config.extra_price_overrides_product_price && choice.get( 'linked_product_qty') && ! choice.get( 'override_linked_price' ) )
377		) qty = parseFloat( choice.get( 'linked_product_qty') ) || default_min_qty;
378
379
380		if ( choice.get( 'override_linked_price' ) ) {
381			if ( 'multiply' == choice.get( 'override_linked_price_mode' ) ) {
382				let multiply_factor = choice.get( 'linked_product_price' );
383				if ( use_regular_linked ) {
384					multiply_factor = parseFloat( linked_regular );
385				}
386				extra_price = multiply_factor * extra_price;
387			}
388		} else if ( 'undefined' != typeof choice.get( 'linked_product_price' ) || use_regular_linked ) {
389			extra_price = use_regular_linked ? parseFloat( linked_regular ) : choice.get( 'linked_product_price' );
390
391			// Tiered pricing checks
392			const linked_product_id = choice.get( 'linked_product_id' );
393
394			if ( linked_product_id && PC.fe.currentProductData.linked_products ) {
395				const product = PC.fe.currentProductData.linked_products.get( linked_product_id );
396				
397				const quantity = parseFloat( choice.get( 'linked_product_qty') );
398				
399				// Tiered pricing 
400				if ( product && product.get( 'extensions' )?.mkl_pc_stock?.price_tiers ) {
401					const product_is_on_sale = product?.get( 'on_sale' );
402
403					const price_tiers = product.get( 'extensions' ).mkl_pc_stock.price_tiers;
404					const update_price_to_display = ! forRegularTotal && ! ( PC_config.config.relative_price && 'simple' == choice.collection.layer_type );
405					let tiered_pricing_used = false;
406					const tier = price_tiers.find( ( item ) => quantity >= parseInt( item.start ) );
407					if ( tier ) {
408						// Percentage or fixed price
409						if ( tier.type.includes( 'percent' ) ) {
410							extra_price = extra_price - ( parseFloat( tier.price ) * parseFloat( extra_price ) / 100 );
411						} else {
412							extra_price = parseFloat( tier.price || 0 );
413						}
414						
415						if ( update_price_to_display && ( extra_price * qty ) != choice.get( 'extra_price_to_display' ) ) {
416							choice.set( 'extra_price_to_display', extra_price * qty );
417						}
418						tiered_pricing_used = true;
419					}
420					
421					// Maybe update displayed price
422					if ( update_price_to_display && !tiered_pricing_used && ( extra_price * qty ) != choice.get( 'extra_price_to_display' ) ) {
423						choice.set( 'extra_price_to_display', extra_price * qty );
424					}
425					
426					// Set on sale flag
427					if ( ! forRegularTotal ) {
428						if ( tiered_pricing_used && !choice.get( 'on_sale' ) ) {
429							choice.set( 'on_sale', true ) ;
430						} else if ( !tiered_pricing_used && choice.get( 'on_sale' ) && !product_is_on_sale ) {
431							choice.set( 'on_sale', false ) ;
432						}
433					}
434				}
435			}
436		}
437		
438		return parseFloat( extra_price ) * qty;
439	} );
440
441	/**
442	 * Hook the data used in the Extra Price view
443	 */
444	wp.hooks.addFilter( 'PC.ExtraPrice.view.data', 'mkl/pc/stock_management', function( data, choice, view ) {
445		if ( PC_config.config.relative_price && 'simple' == choice.collection.layer_type ) return data;
446		if ( choice.get( 'linked_product_regular_price' ) ) {
447			data.formatted_regular_price = PC.utils.formatMoney( Math.abs( pc_maybe_apply_taxes( choice.get( 'linked_product_regular_price' ) ) ) );
448		}
449		return data;
450	} );
451
452	wp.hooks.addAction( 'PC.fe.configurator.choice-item.render.after-template', 'mkl/pc/stock_management', function( view ) {
453		
454		if ( view.$( '.mkl-pc-choice-qty' ).length ) view.$el.addClass( 'has-qty' );
455
456		view.$( '.mkl-pc-choice-qty' ).on( 'change', function() {
457			view.model.set( 'linked_product_qty', $( this ).val() );
458			wp.hooks.doAction( 'PC.fe.linked_product_qty.updated', view );
459		} );
460
461		const default_min_qty = wp.hooks.applyFilters( 'PC.fe.linked_product.default_min_qty', 1, view.model );
462
463		view.$( '.mkl-pc-choice-qty--btn' ).on( 'click', function() {
464			var current_val = parseInt( view.$( '.mkl-pc-choice-qty' ).val() );
465			if ( $( this ).is( '.mkl-minus' ) ) {
466				var min = view.model.get( 'linked_product_min_qty' ) || default_min_qty;
467				// If the current value is 1, maybe deselect the choice
468				if ( current_val == min ) {
469					view.model.collection.selectChoice( view.model.id, false );
470					// if ( 'multiple' === view.model.collection.layer_type ) {
471					// 	view.$( '> .choice-item').trigger( 'mousedown' );
472					// }
473					return;
474				}
475				view.$( '.mkl-pc-choice-qty' ).val( --current_val ).trigger( 'change' );
476			} else {
477				if ( view.model.get( 'linked_product_max_qty' ) && current_val == view.model.get( 'linked_product_max_qty' ) ) return;
478				view.$( '.mkl-pc-choice-qty' ).val( ++current_val ).trigger( 'change' );
479				//.trigger( 'change' );
480			}
481		} );
482	} );
483
484	wp.hooks.addAction( 'PC.fe.configurator.choice-item.render', 'MKL/PC/stock_management', function( view ) {
485		const choice = view.model;
486		if ( 'user_defined' === choice.get( 'linked_product_qty_mode' ) ) {
487			view.$( 'input.mkl-pc-choice-qty' ).trigger( 'change' );
488		}
489	} );
490	
491	wp.hooks.addFilter( 'PC.fe.choices.add_one', 'MKL/PC/stock_management', function( add_one, model ) {
492		if ( model.get( 'hide-item' ) ) return false;
493		return add_one;
494	} );
495
496	wp.hooks.addAction( 'PC.fe.setConfig.setItem', 'MKL/PC/stock_management', function( config_item, layer_content ) {
497		var choice = layer_content.get( config_item.choice_id );
498		if ( ! choice || ! config_item.hasOwnProperty( 'linked_product_qty' ) ) return;
499
500		var qty = choice.set( 'linked_product_qty', config_item.linked_product_qty );
501		refresh_linked_stock_state();
502	} );
503
504	/*
505		Add data for fields attached to normal layers (not form layers)
506	*/
507	wp.hooks.addFilter( 'PC.fe.save_data.parse_choices.added_choice', 'MKL/PC/stock_management', function( parsed_choice, choice_model ) {
508		if ( ! choice_model || ! choice_model.get( 'linked_product_qty' ) ) return parsed_choice;
509		if ( 'user_defined' === choice_model.get( 'linked_product_qty_mode' ) ) {
510			parsed_choice.linked_product_qty = choice_model.get( 'linked_product_qty' );
511		}
512		return parsed_choice;
513	} );
514
515	/*
516		Extra price Quantities
517	*/
518	// wp.hooks.addFilter( 'PC.fe.extra_price.item_price', 'MKL/PC/stock_management', function( item_price, choice, layer ) {
519	// 	if ( ! choice || ! choice.get( 'linked_product_qty' ) ) return item_price;
520	// 	if ( 'user_defined' !== choice.get( 'linked_product_qty_mode' ) ) {
521	// 		return item_price * choice.get( 'linked_product_qty' );
522	// 	}
523
524	// 	return item_price;
525	// } );
526
527	/**
528	 * Override a choice's Extra price when calculating the price
529	 */
530	// wp.hooks.addFilter( 'PC.fe.extra_price.item_price', 'mkl/product_configurator', function( item_price, choice, layer ) {
531	// 	if ( 'linked' != choice.get( 'manage_stock' ) ) return item_price;
532	// 	if ( PC.fe.config.extra_price_overrides_product_price && ! PC.fe.config.link_extra_price ) return item_price;
533	// 	// if ( ! choice.get( 'linked_product_qty' ) || 
534	// 	var qty = parseFloat( choice.get( 'linked_product_qty' ) ) || 1;
535	// 	return parseFloat( item_price ) * qty;
536	// } );
537
538	wp.hooks.addFilter( 'PC.fe.close_choices_after_selection', 'MKL/PC/stock_management', function( close_choices, choice_model ) {
539		if ( choice_model.get( 'linked_product_qty_mode' ) && 'user_defined' === choice_model.get( 'linked_product_qty_mode' ) ) return false;
540		return close_choices;
541	} );
542
543	/**
544	 * Multiple choice max selection compatibility
545	 */
546
547	/**
548	 * Reset quantity on choice deactivation, to prevent quantity miscalculation
549	 */
550	wp.hooks.addAction( 'PC.fe.choice.change', 'MKL/PC/stock_management', function( choice ) {
551		if ( 'multiple' === choice.collection.layer_type ) {
552			/**
553			 * Check if the choice is part of a limited layer / group
554			 */
555			var has_max_selection = false;
556			if ( 
557				choice.get( 'parent' ) 
558				&& choice.collection.get( choice.get( 'parent' ) ) 
559				&& choice.collection.get( choice.get( 'parent' ) ).get( 'max_selection' ) 
560			) has_max_selection = true;
561
562			if ( choice.collection.layer.get( 'max_selection' ) ) has_max_selection = true;
563			
564			if ( has_max_selection && choice.get( 'linked_product_qty_mode' ) && 'user_defined' === choice.get( 'linked_product_qty_mode' ) ) {
565				// If the choice was deactivated
566				if ( ! choice.get( 'active' ) ) choice.set( 'linked_product_qty', choice.get( 'linked_product_min_qty' ) || 1 );
567			}
568		}
569		refresh_linked_stock_state();
570	} );
571
572	wp.hooks.addAction( 'PC.fe.linked_product_qty.updated', 'MKL/PC/stock_management', function( { model } ) {
573		if ( model.get( 'linked_product_qty_mode' ) && 'user_defined' === model.get( 'linked_product_qty_mode' ) ) {
574			// If the linked_product_qty changed, check whether it was possible
575			var can_change = wp.hooks.applyFilters( 'PC.choices.canSelectChoice', true, model, model.collection, 'linked_product_quantity' );
576			if ( ! can_change ) {
577				// Revert field value
578				model.set( 'linked_product_qty', model._previousAttributes.linked_product_qty || 1 );
579			}
580
581		}
582		refresh_linked_stock_state();
583	} );
584
585	wp.hooks.addFilter( 'PC.choices.canSelectChoice', 'MKL/PC/stock_management/shared_stock', function( canSelect, choice, choices, context ) {
586		if ( ! canSelect || ! is_linked_choice( choice ) ) return canSelect;
587		if ( 'choice' === context && ! linked_stock_ready ) return canSelect;
588		const stock_data = get_linked_stock( choice );
589		if ( ! stock_data ) return canSelect;
590		const can_backorder = 'yes' === choice.get( 'wc_backorder' ) || 'notify' === choice.get( 'wc_backorder' );
591		if ( can_backorder ) return canSelect;
592		const linked_choices_map = get_linked_choices_map();
593		const linked_choices = linked_choices_map[choice.get( 'linked_product_id' )] || [ choice ];
594		return get_required_quantity( linked_choices, choice, true ) <= stock_data.maximum;
595	}, 110 );
596
597	/**
598	 * Revert quantity change if the choice "cannot" be selected (result of PC.choices.canSelectChoice)
599	 */
600	// wp.hooks.addAction( 'PC.fe.form.item.change', 'MKL/PC/stock_management', function( model, event ) {
601	// } );
602
603	/**
604	 * Include the linked_product_qty in the items_selected_count
605	 */
606	wp.hooks.addFilter( 'PC.fe.multiple_choice.item_quantity', 'MKL/PC/stock_management', function( current_count, item, choice ) {
607		if ( !item.get( 'manage_stock' ) || 'linked' !== item.get( 'manage_stock' ) || 'user_defined' !== item.get( 'linked_product_qty_mode' ) ) return current_count;
608		
609		let count = 0;
610		const default_min_qty = wp.hooks.applyFilters( 'PC.fe.linked_product.default_min_qty', 1, item );
611		// Choice is only set when checking if an item is selectable. So we reduce by one to have a "pre-selection" count - if that makes sense!
612		if ( choice === item ) {
613			count += ( parseInt( item.get( 'linked_product_qty' ) ) || default_min_qty ) - 1;
614		} else {
615			count = parseInt( item.get( 'linked_product_qty' ) ) || default_min_qty;
616		}
617
618		return count;
619	}, 100 );
620
621} ) ( jQuery, window.wp, window.PC );

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.