001// --------------------------------------------------------------------------------
002// Copyright 2002-2026 Echo Three, LLC
003//
004// Licensed under the Apache License, Version 2.0 (the "License");
005// you may not use this file except in compliance with the License.
006// You may obtain a copy of the License at
007//
008//     http://www.apache.org/licenses/LICENSE-2.0
009//
010// Unless required by applicable law or agreed to in writing, software
011// distributed under the License is distributed on an "AS IS" BASIS,
012// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013// See the License for the specific language governing permissions and
014// limitations under the License.
015// --------------------------------------------------------------------------------
016
017package com.echothree.model.control.order.server.logic;
018
019import com.echothree.model.control.order.common.exception.CannotSpecifyPartyPaymentMethodException;
020import com.echothree.model.control.order.common.exception.CannotSpecifyPaymentMethodAndPartyPaymentMethodException;
021import com.echothree.model.control.order.common.exception.CannotSpecifyWasPresentException;
022import com.echothree.model.control.order.common.exception.DuplicateOrderPaymentPreferenceSequenceException;
023import com.echothree.model.control.order.common.exception.MustSpecifyPartyPaymentMethodException;
024import com.echothree.model.control.order.common.exception.MustSpecifyPaymentMethodOrPartyPaymentMethodException;
025import com.echothree.model.control.order.common.exception.MustSpecifyWasPresentException;
026import com.echothree.model.control.order.common.exception.UnknownOrderAliasTypeNameException;
027import com.echothree.model.control.order.common.exception.UnknownOrderNameException;
028import com.echothree.model.control.order.common.exception.UnknownOrderPaymentPreferenceSequenceException;
029import com.echothree.model.control.order.common.exception.UnknownOrderRoleTypeNameException;
030import com.echothree.model.control.order.common.exception.UnknownOrderSequenceException;
031import com.echothree.model.control.order.common.exception.UnknownOrderSequenceTypeException;
032import com.echothree.model.control.order.common.exception.UnknownOrderTypeNameException;
033import com.echothree.model.control.order.server.control.OrderAliasControl;
034import com.echothree.model.control.order.server.control.OrderControl;
035import com.echothree.model.control.order.server.control.OrderLineControl;
036import com.echothree.model.control.order.server.control.OrderPaymentPreferenceControl;
037import com.echothree.model.control.order.server.control.OrderRoleControl;
038import com.echothree.model.control.order.server.control.OrderTypeControl;
039import com.echothree.model.control.payment.common.PaymentMethodTypes;
040import com.echothree.model.control.payment.server.logic.PaymentMethodLogic;
041import com.echothree.model.control.sequence.server.control.SequenceControl;
042import com.echothree.model.control.sequence.server.logic.SequenceGeneratorLogic;
043import com.echothree.model.control.shipping.server.logic.ShippingMethodLogic;
044import com.echothree.model.data.accounting.server.entity.Currency;
045import com.echothree.model.data.cancellationpolicy.server.entity.CancellationPolicy;
046import com.echothree.model.data.item.server.entity.Item;
047import com.echothree.model.data.order.server.entity.Order;
048import com.echothree.model.data.order.server.entity.OrderAliasType;
049import com.echothree.model.data.order.server.entity.OrderPaymentPreference;
050import com.echothree.model.data.order.server.entity.OrderPriority;
051import com.echothree.model.data.order.server.entity.OrderRoleType;
052import com.echothree.model.data.order.server.entity.OrderShipmentGroup;
053import com.echothree.model.data.order.server.entity.OrderType;
054import com.echothree.model.data.payment.server.entity.PartyPaymentMethod;
055import com.echothree.model.data.payment.server.entity.PaymentMethod;
056import com.echothree.model.data.returnpolicy.server.entity.ReturnPolicy;
057import com.echothree.model.data.sequence.server.entity.Sequence;
058import com.echothree.model.data.sequence.server.entity.SequenceType;
059import com.echothree.model.data.shipment.server.entity.FreeOnBoard;
060import com.echothree.model.data.term.server.entity.Term;
061import com.echothree.util.common.message.ExecutionErrors;
062import com.echothree.util.common.persistence.BasePK;
063import com.echothree.util.server.control.BaseLogic;
064import com.echothree.util.server.message.ExecutionErrorAccumulator;
065import com.echothree.util.server.persistence.EntityPermission;
066import com.echothree.util.server.persistence.Session;
067import java.util.HashSet;
068import java.util.Set;
069import javax.enterprise.context.ApplicationScoped;
070import javax.enterprise.inject.spi.CDI;
071import javax.inject.Inject;
072
073public class BaseOrderLogic
074        extends BaseLogic {
075
076    @Inject
077    OrderAliasControl orderAliasControl;
078
079    @Inject
080    OrderControl orderControl;
081
082    @Inject
083    OrderLineControl orderLineControl;
084
085    @Inject
086    OrderPaymentPreferenceControl orderPaymentPreferenceControl;
087
088    @Inject
089    OrderRoleControl orderRoleControl;
090
091    @Inject
092    OrderTypeControl orderTypeControl;
093
094    @Inject
095    SequenceControl sequenceControl;
096
097    @Inject
098    PaymentMethodLogic paymentMethodLogic;
099
100    @Inject
101    SequenceGeneratorLogic sequenceGeneratorLogic;
102
103    @Inject
104    ShippingMethodLogic shippingMethodLogic;
105
106    protected BaseOrderLogic() {
107        super();
108    }
109
110    public Currency getOrderCurrency(final Order order) {
111        return order.getLastDetail().getCurrency();
112    }
113    
114    public OrderType getOrderTypeByName(final ExecutionErrorAccumulator eea, final String orderTypeName) {
115        var orderType = orderTypeControl.getOrderTypeByName(orderTypeName);
116
117        if(orderType == null) {
118            handleExecutionError(UnknownOrderTypeNameException.class, eea, ExecutionErrors.UnknownOrderTypeName.name(), orderTypeName);
119        }
120
121        return orderType;
122    }
123
124    public OrderRoleType getOrderRoleTypeByName(final ExecutionErrorAccumulator eea, final String orderRoleTypeName) {
125        var orderRoleType = orderRoleControl.getOrderRoleTypeByName(orderRoleTypeName);
126
127        if(orderRoleType == null) {
128            handleExecutionError(UnknownOrderRoleTypeNameException.class, eea, ExecutionErrors.UnknownOrderRoleTypeName.name(), orderRoleTypeName);
129        }
130
131        return orderRoleType;
132    }
133
134    public SequenceType getOrderSequenceType(final ExecutionErrorAccumulator eea, final OrderType orderType) {
135        var orderTypeDetail = orderType.getLastDetail();
136        var sequenceType = orderTypeDetail.getOrderSequenceType();
137
138        if(sequenceType == null) {
139            sequenceType = sequenceControl.getDefaultSequenceType();
140        }
141
142        if(sequenceType == null) {
143            handleExecutionError(UnknownOrderSequenceTypeException.class, eea, ExecutionErrors.UnknownOrderSequenceType.name(), orderType.getLastDetail().getOrderTypeName());
144        }
145
146        return sequenceType;
147    }
148
149    public Sequence getOrderSequence(final ExecutionErrorAccumulator eea, final OrderType orderType) {
150        var sequenceType = getOrderSequenceType(eea, orderType);
151        Sequence sequence = null;
152
153        if(eea == null || !eea.hasExecutionErrors()) {
154            sequence = sequenceControl.getDefaultSequence(sequenceType);
155        }
156
157        if(sequence == null) {
158            handleExecutionError(UnknownOrderSequenceException.class, eea, ExecutionErrors.UnknownOrderSequence.name(), orderType.getLastDetail().getOrderTypeName());
159        }
160
161        return sequence;
162    }
163
164    public String getOrderName(final ExecutionErrorAccumulator eea, final OrderType orderType, Sequence sequence) {
165        String orderName = null;
166
167        if(sequence == null) {
168            sequence = getOrderSequence(eea, orderType);
169        }
170
171        if(eea == null || !eea.hasExecutionErrors()) {
172            orderName = sequenceGeneratorLogic.getNextSequenceValue(sequence);
173        }
174
175        return orderName;
176    }
177
178    public Order createOrder(final ExecutionErrorAccumulator eea, OrderType orderType, Sequence sequence, final OrderPriority orderPriority,
179            final Currency currency, final Boolean holdUntilComplete, final Boolean allowBackorders, final Boolean allowSubstitutions,
180            final Boolean allowCombiningShipments, final Term term, final FreeOnBoard freeOnBoard, final String reference, final String description,
181            final CancellationPolicy cancellationPolicy, final ReturnPolicy returnPolicy, final Boolean taxable, final BasePK createdBy) {
182        var orderName = getOrderName(eea, orderType, sequence);
183        Order order = null;
184
185        if(eea == null || !eea.hasExecutionErrors()) {
186            order = orderControl.createOrder(orderType, orderName, orderPriority, currency, holdUntilComplete, allowBackorders, allowSubstitutions,
187                    allowCombiningShipments, term, freeOnBoard, reference, description, cancellationPolicy, returnPolicy, taxable, createdBy);
188        }
189
190        return order;
191    }
192
193    public OrderAliasType getOrderAliasTypeByName(final ExecutionErrorAccumulator eea, final OrderType orderType, final String orderAliasTypeName) {
194        var orderAliasType = orderAliasControl.getOrderAliasTypeByName(orderType, orderAliasTypeName);
195
196        if(orderAliasType == null) {
197            handleExecutionError(UnknownOrderAliasTypeNameException.class, eea, ExecutionErrors.UnknownOrderAliasTypeName.name(),
198                    orderType.getLastDetail().getOrderTypeName(), orderAliasTypeName);
199        }
200
201        return orderAliasType;
202    }
203
204    private Order getOrderByName(final ExecutionErrorAccumulator eea, final String orderTypeName, final String orderName,
205            final EntityPermission entityPermission) {
206        var orderType = getOrderTypeByName(eea, orderTypeName);
207        Order order = null;
208
209        if(eea == null || !eea.hasExecutionErrors()) {
210            order = orderControl.getOrderByName(orderType, orderName, entityPermission);
211
212            if(order == null) {
213                handleExecutionError(UnknownOrderNameException.class, eea, ExecutionErrors.UnknownOrderName.name(), orderTypeName, orderName);
214            }
215        }
216
217        return order;
218    }
219
220    public Order getOrderByName(final ExecutionErrorAccumulator eea, final String orderTypeName, final String orderName) {
221        return getOrderByName(eea, orderTypeName, orderName, EntityPermission.READ_ONLY);
222    }
223
224    public Order getOrderByNameForUpdate(final ExecutionErrorAccumulator eea, final String orderTypeName, final String orderName) {
225        return getOrderByName(eea, orderTypeName, orderName, EntityPermission.READ_WRITE);
226    }
227    
228    public Set<Item> getItemsFromOrder(final Order order) {
229        var orderLines = orderLineControl.getOrderLinesByOrder(order);
230        var items = new HashSet<Item>(orderLines.size());
231
232        orderLines.forEach((orderLine) -> {
233            items.add(orderLine.getLastDetail().getItem());
234        });
235        
236        return items;
237    }
238    
239    public OrderPaymentPreference createOrderPaymentPreference(final Session session, final ExecutionErrorAccumulator eea, final Order order,
240            Integer orderPaymentPreferenceSequence, PaymentMethod paymentMethod, final PartyPaymentMethod partyPaymentMethod,
241            final Boolean wasPresent, final Long maximumAmount, final Integer sortOrder, final BasePK createdBy) {
242        var parameterCount = (paymentMethod == null ? 0 : 1) + (partyPaymentMethod == null ? 0 : 1);
243        OrderPaymentPreference orderPaymentPreference = null;
244
245        // Either the paymentMethod or the partyPaymentMethod must be specified.
246        if(parameterCount == 1) {
247            String paymentMethodTypeName;
248
249            if(paymentMethod == null) {
250                paymentMethod = partyPaymentMethod.getLastDetail().getPaymentMethod();
251            }
252            
253            paymentMethodTypeName = paymentMethod.getLastDetail().getPaymentMethodType().getLastDetail().getPaymentMethodTypeName();
254            
255            // If the type is CREDIT_CARD, GIFT_CARD, or GIFT_CERTIFICATE, then the partyPaymentMethod must be specified.
256            if((paymentMethodTypeName.equals(PaymentMethodTypes.CREDIT_CARD.name())
257                    || paymentMethodTypeName.equals(PaymentMethodTypes.GIFT_CARD.name())
258                    || paymentMethodTypeName.equals(PaymentMethodTypes.GIFT_CERTIFICATE.name())) && partyPaymentMethod == null) {
259                handleExecutionError(MustSpecifyPartyPaymentMethodException.class, eea, ExecutionErrors.MustSpecifyPartyPaymentMethod.name());
260            } else if(partyPaymentMethod != null) {
261                // Otherwise, the partyPaymentMethod should always be null.
262                handleExecutionError(CannotSpecifyPartyPaymentMethodException.class, eea, ExecutionErrors.CannotSpecifyPartyPaymentMethod.name());
263            }
264            
265            // If the type is CREDIT_CARD then wasPresent must be specified.
266            if(paymentMethodTypeName.equals(PaymentMethodTypes.CREDIT_CARD.name()) && wasPresent == null) {
267                handleExecutionError(MustSpecifyWasPresentException.class, eea, ExecutionErrors.MustSpecifyWasPresent.name());
268            } else if(wasPresent != null) {
269                // Otherwise, the partyPaymentMethod should always be null.
270                handleExecutionError(CannotSpecifyWasPresentException.class, eea, ExecutionErrors.CannotSpecifyWasPresent.name());
271            }
272            
273            if(eea == null || !eea.hasExecutionErrors()) {
274                paymentMethodLogic.checkAcceptanceOfItems(session, eea, paymentMethod, getItemsFromOrder(order), createdBy);
275                
276                if(eea == null || !eea.hasExecutionErrors()) {
277                    var orderStatus = orderControl.getOrderStatusForUpdate(order);
278
279                    if(orderPaymentPreferenceSequence == null) {
280                        orderPaymentPreferenceSequence = orderStatus.getOrderPaymentPreferenceSequence() + 1;
281                        orderStatus.setOrderPaymentPreferenceSequence(orderPaymentPreferenceSequence);
282                    } else {
283                        orderPaymentPreference = orderPaymentPreferenceControl.getOrderPaymentPreferenceBySequence(order, orderPaymentPreferenceSequence);
284
285                        if(orderPaymentPreference == null) {
286                            // If the orderPaymentPreferenceSequence is > the last one that was recorded in the OrderStatus, jump the
287                            // one in OrderStatus forward - it should always record the greatest orderPaymentPreferenceSequence used.
288                            if(orderPaymentPreferenceSequence > orderStatus.getOrderPaymentPreferenceSequence()) {
289                                orderStatus.setOrderPaymentPreferenceSequence(orderPaymentPreferenceSequence);
290                            }
291                        } else {
292                            handleExecutionError(DuplicateOrderPaymentPreferenceSequenceException.class, eea, ExecutionErrors.DuplicateOrderPaymentPreferenceSequence.name(),
293                                    order.getLastDetail().getOrderName(), orderPaymentPreferenceSequence.toString());
294                        }
295                    }
296
297                    if(orderPaymentPreference == null) {
298                        orderPaymentPreference = orderPaymentPreferenceControl.createOrderPaymentPreference(order, orderPaymentPreferenceSequence, paymentMethod, partyPaymentMethod,
299                                wasPresent, maximumAmount, sortOrder, createdBy);
300                    }
301                }
302            }
303        } else {
304            if(parameterCount == 0) {
305                handleExecutionError(MustSpecifyPaymentMethodOrPartyPaymentMethodException.class, eea, ExecutionErrors.MustSpecifyPaymentMethodOrPartyPaymentMethod.name());
306            } else {
307                handleExecutionError(CannotSpecifyPaymentMethodAndPartyPaymentMethodException.class, eea, ExecutionErrors.CannotSpecifyPaymentMethodAndPartyPaymentMethod.name());
308            }
309        }
310
311        return orderPaymentPreference;
312    }
313
314    private OrderPaymentPreference getOrderPaymentPreferenceByName(final ExecutionErrorAccumulator eea, final String orderTypeName, final String orderName,
315            final String orderPaymentPreferenceSequence, final EntityPermission entityPermission) {
316        var order = getOrderByName(eea, orderTypeName, orderName);
317        OrderPaymentPreference orderPaymentPreference = null;
318        
319        if(eea == null || !eea.hasExecutionErrors()) {
320            orderPaymentPreference = orderPaymentPreferenceControl.getOrderPaymentPreferenceBySequence(order, Integer.valueOf(orderPaymentPreferenceSequence), entityPermission);
321            
322            if(orderPaymentPreference == null) {
323                handleExecutionError(UnknownOrderPaymentPreferenceSequenceException.class, eea, ExecutionErrors.UnknownOrderPaymentPreferenceSequence.name(), orderTypeName,
324                        orderName, orderPaymentPreferenceSequence);
325            }
326        }
327
328        return orderPaymentPreference;
329    }
330
331    public OrderPaymentPreference getOrderPaymentPreferenceByName(final ExecutionErrorAccumulator eea, final String orderTypeName, final String orderName,
332            final String orderPaymentPreferenceSequence) {
333        return getOrderPaymentPreferenceByName(eea, orderTypeName, orderName, orderPaymentPreferenceSequence, EntityPermission.READ_ONLY);
334    }
335
336    public OrderPaymentPreference getOrderPaymentPreferenceByNameForUpdate(final ExecutionErrorAccumulator eea, final String orderTypeName, final String orderName,
337            final String orderPaymentPreferenceSequence) {
338        return getOrderPaymentPreferenceByName(eea, orderTypeName, orderName, orderPaymentPreferenceSequence, EntityPermission.READ_WRITE);
339    }
340    
341    public void checkItemAgainstOrderPaymentPreferences(final Session session, final ExecutionErrorAccumulator eea, final Order order, final Item item,
342            final BasePK evaluatedBy) {
343        var orderPaymentPreferences = orderPaymentPreferenceControl.getOrderPaymentPreferencesByOrder(order);
344        
345        orderPaymentPreferences.forEach((orderPaymentPreference) -> {
346            paymentMethodLogic.checkAcceptanceOfItem(session, eea, orderPaymentPreference.getLastDetail().getPaymentMethod(), item, evaluatedBy);
347        });
348    }
349    
350    public void checkItemAgainstShippingMethod(final Session session, final ExecutionErrorAccumulator eea, final OrderShipmentGroup orderShipmentGroup,
351            final Item item, final BasePK evaluatedBy) {
352        var shippingMethod = orderShipmentGroup.getLastDetail().getShippingMethod();
353        
354        if(shippingMethod != null) {
355            shippingMethodLogic.checkAcceptanceOfItem(session, eea, shippingMethod, item, evaluatedBy);
356        }
357    }
358
359}