LotSizingDataModel.Core 2.0.1
Core domain model, shared abstractions and XML-serializable entities.
Loading...
Searching...
No Matches
DistributionCenterSourcing.DecisionModel.cs
Go to the documentation of this file.
1using System;
2using System.Collections.Generic;
3using System.ComponentModel;
4using System.Xml.Serialization;
9
11
12/// <summary>
13/// Adds the decision-model parameters associated with the
14/// sourcing of an item by a distribution center from a warehouse.
15/// </summary>
16public sealed partial class DistributionCenterSourcing :
17 IPlanningHorizonAware
18{
19 private BacklogConstraint? _backlogConstraint;
20 private BacklogCost? _backlogCost;
21
22 private ShortageConstraint? _shortageConstraint;
23 private ShortageCost? _shortageCost;
24
25 private SellingPrice? _sellingPrice;
26
27 /// <summary>
28 /// Gets or sets the maximum backlog quantity allowed
29 /// during each planning period.
30 ///
31 /// Backlog demand remains to be fulfilled during
32 /// a later planning period.
33 ///
34 /// A null value means that backlog is not authorized
35 /// for this sourcing relationship.
36 /// </summary>
37 [XmlElement("backlogConstraint")]
39 {
40 get => _backlogConstraint;
41 set => SetDecisionParameter(
42 ref _backlogConstraint,
43 value,
44 nameof(BacklogConstraint));
45 }
46
47 /// <summary>
48 /// Gets or sets the unit cost applied to each item unit
49 /// remaining in backlog during a planning period.
50 ///
51 /// This parameter should only be defined when backlog
52 /// is authorized.
53 /// </summary>
54 [XmlElement("backlogCost")]
56 {
57 get => _backlogCost;
58 set => SetDecisionParameter(
59 ref _backlogCost,
60 value,
61 nameof(BacklogCost));
62 }
63
64 /// <summary>
65 /// Gets or sets the maximum quantity of demand that may
66 /// be permanently lost during each planning period.
67 ///
68 /// A null value means that shortage is not authorized
69 /// for this sourcing relationship.
70 /// </summary>
71 [XmlElement("shortageConstraint")]
73 {
74 get => _shortageConstraint;
75 set => SetDecisionParameter(
76 ref _shortageConstraint,
77 value,
78 nameof(ShortageConstraint));
79 }
80
81 /// <summary>
82 /// Gets or sets the unit cost applied to each item unit
83 /// permanently lost as shortage.
84 ///
85 /// This parameter should only be defined when shortage
86 /// is authorized.
87 /// </summary>
88 [XmlElement("shortageCost")]
90 {
91 get => _shortageCost;
92 set => SetDecisionParameter(
93 ref _shortageCost,
94 value,
95 nameof(ShortageCost));
96 }
97
98 /// <summary>
99 /// Gets or sets the unit selling price of the item supplied
100 /// by the warehouse to the distribution center during each
101 /// planning period.
102 /// </summary>
103 [XmlElement("sellingPrice")]
105 {
106 get => _sellingPrice;
107 set => SetDecisionParameter(
108 ref _sellingPrice,
109 value,
110 nameof(SellingPrice));
111 }
112
113 /// <summary>
114 /// Gets the planning horizon represented by the first
115 /// active period-dependent parameter.
116 ///
117 /// Returns zero when no decision parameter is active.
118 /// </summary>
119 [XmlIgnore]
121 {
122 get
123 {
124 // Return the first active parameter's horizon.
125 foreach (IPlanningHorizonAware parameter
126 in GetDecisionParameters())
127 {
128 return parameter.PlanningHorizon;
129 }
130
131 // No active parameter → horizon is zero.
132 return 0;
133 }
134 }
135
136 /// <summary>
137 /// Gets a value indicating whether backlog is authorized.
138 /// </summary>
139 [XmlIgnore]
140 public bool AllowsBacklog =>
141 BacklogConstraint is not null;
142
143 /// <summary>
144 /// Gets a value indicating whether shortage is authorized.
145 /// </summary>
146 [XmlIgnore]
147 public bool AllowsShortage =>
148 ShortageConstraint is not null;
149
150 /// <summary>
151 /// Gets a value indicating whether at least one
152 /// decision-model parameter is active.
153 /// </summary>
154 [XmlIgnore]
156 BacklogConstraint is not null ||
157 BacklogCost is not null ||
158 ShortageConstraint is not null ||
159 ShortageCost is not null ||
160 SellingPrice is not null;
161
162 /// <summary>
163 /// Gets a value indicating whether all active parameters
164 /// use the same planning horizon.
165 /// </summary>
166 [XmlIgnore]
168 {
169 get
170 {
171 int? expectedHorizon = null;
172
173 // Compare each active parameter's horizon against the first.
174 foreach (IPlanningHorizonAware parameter
175 in GetDecisionParameters())
176 {
177 if (expectedHorizon is null)
178 {
179 expectedHorizon =
180 parameter.PlanningHorizon;
181
182 continue;
183 }
184
185 if (parameter.PlanningHorizon !=
186 expectedHorizon.Value)
187 {
188 return false;
189 }
190 }
191
192 return true;
193 }
194 }
195
196 /// <summary>
197 /// Gets a value indicating whether the dependencies
198 /// between service constraints and costs are respected.
199 ///
200 /// A cost cannot be defined if the corresponding
201 /// service option is not authorized.
202 /// </summary>
203 [XmlIgnore]
205 (BacklogCost is null ||
206 BacklogConstraint is not null) &&
207
208 (ShortageCost is null ||
209 ShortageConstraint is not null);
210
211 /// <summary>
212 /// Resizes every active period-dependent parameter.
213 ///
214 /// Existing values are preserved.
215 /// </summary>
216 /// <param name="periodCount">
217 /// New number of planning periods.
218 /// </param>
219 public void ResizeTimeSeries(int periodCount)
220 {
221 if (periodCount < 0)
222 {
223 throw new ArgumentOutOfRangeException(
224 nameof(periodCount),
225 periodCount,
226 "The planning horizon cannot be negative.");
227 }
228
229 // Resize every active decision parameter.
230 foreach (IPlanningHorizonAware parameter
231 in GetDecisionParameters())
232 {
233 parameter.ResizeTimeSeries(periodCount);
234 }
235
236 // Notify dependent computed properties.
240 }
241
242 /// <summary>
243 /// Removes all optional decision-model parameters from
244 /// this sourcing relationship.
245 /// </summary>
247 {
248 BacklogConstraint = null;
249 BacklogCost = null;
250
251 ShortageConstraint = null;
252 ShortageCost = null;
253
254 SellingPrice = null;
255 }
256
257 private void SetDecisionParameter<T>(
258 ref T? field,
259 T? value,
260 string propertyName)
262 {
263 if (ReferenceEquals(field, value))
264 {
265 return;
266 }
267
268 // Unsubscribe from old parameter.
269 if (field is not null)
270 {
271 field.PropertyChanged -=
272 OnDecisionParameterPropertyChanged;
273 }
274
275 field = value;
276
277 // Subscribe to new parameter.
278 if (field is not null)
279 {
280 field.PropertyChanged +=
281 OnDecisionParameterPropertyChanged;
282 }
283
284 // Notify the specific property plus all dependent ones.
285 OnPropertyChanged(propertyName);
294 }
295
296 private IEnumerable<IPlanningHorizonAware>
297 GetDecisionParameters()
298 {
299 if (BacklogConstraint is not null)
300 {
301 yield return BacklogConstraint;
302 }
303
304 if (BacklogCost is not null)
305 {
306 yield return BacklogCost;
307 }
308
309 if (ShortageConstraint is not null)
310 {
311 yield return ShortageConstraint;
312 }
313
314 if (ShortageCost is not null)
315 {
316 yield return ShortageCost;
317 }
318
319 if (SellingPrice is not null)
320 {
321 yield return SellingPrice;
322 }
323 }
324
325 private void OnDecisionParameterPropertyChanged(
326 object? sender,
327 PropertyChangedEventArgs e)
328 {
329 // Forward notification for the specific parameter that changed.
330 if (ReferenceEquals(sender, BacklogConstraint))
331 {
333 }
334 else if (ReferenceEquals(sender, BacklogCost))
335 {
337 }
338 else if (ReferenceEquals(sender, ShortageConstraint))
339 {
341 }
342 else if (ReferenceEquals(sender, ShortageCost))
343 {
345 }
346 else if (ReferenceEquals(sender, SellingPrice))
347 {
349 }
350
351 // Update all computed properties that may be affected.
357 }
358}
Base class for model objects that notify listeners when one of their properties changes.
virtual void OnPropertyChanged([CallerMemberName] string? propertyName=null)
Raises the PropertyChanged event.
Represents the ability of a distribution center to source a given item from a given warehouse invento...
SellingPrice? SellingPrice
Gets or sets the unit selling price of the item supplied by the warehouse to the distribution center ...
ShortageCost? ShortageCost
Gets or sets the unit cost applied to each item unit permanently lost as shortage.
void ClearDecisionParameters()
Removes all optional decision-model parameters from this sourcing relationship.
bool HasConsistentPlanningHorizon
Gets a value indicating whether all active parameters use the same planning horizon.
ShortageConstraint? ShortageConstraint
Gets or sets the maximum quantity of demand that may be permanently lost during each planning period.
bool HasDecisionParameters
Gets a value indicating whether at least one decision-model parameter is active.
void ResizeTimeSeries(int periodCount)
Resizes every active period-dependent parameter.
bool HasValidDecisionConfiguration
Gets a value indicating whether the dependencies between service constraints and costs are respected.
int PlanningHorizon
Gets the planning horizon represented by the first active period-dependent parameter.
bool AllowsShortage
Gets a value indicating whether shortage is authorized.
BacklogConstraint? BacklogConstraint
Gets or sets the maximum backlog quantity allowed during each planning period.
BacklogCost? BacklogCost
Gets or sets the unit cost applied to each item unit remaining in backlog during a planning period.
Defines a model object containing data indexed by planning period.
int PlanningHorizon
Gets the number of planning periods currently represented by the object's time-dependent data.
void ResizeTimeSeries(int periodCount)
Resizes every time-dependent series contained in the object.