001// --------------------------------------------------------------------------------
002// Copyright 2002-2024 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.DuplicateOrderLineSequenceException;
020import com.echothree.model.control.order.common.exception.UnknownOrderLineSequenceException;
021import com.echothree.model.control.order.server.control.OrderAdjustmentControl;
022import com.echothree.model.control.order.server.control.OrderControl;
023import com.echothree.model.control.order.server.control.OrderLineAdjustmentControl;
024import com.echothree.model.control.order.server.control.OrderLineControl;
025import com.echothree.model.data.cancellationpolicy.server.entity.CancellationPolicy;
026import com.echothree.model.data.inventory.server.entity.InventoryCondition;
027import com.echothree.model.data.item.server.entity.Item;
028import com.echothree.model.data.order.server.entity.Order;
029import com.echothree.model.data.order.server.entity.OrderLine;
030import com.echothree.model.data.order.server.entity.OrderShipmentGroup;
031import com.echothree.model.data.returnpolicy.server.entity.ReturnPolicy;
032import com.echothree.model.data.uom.server.entity.UnitOfMeasureType;
033import com.echothree.util.common.message.ExecutionErrors;
034import com.echothree.util.common.persistence.BasePK;
035import com.echothree.util.server.control.BaseLogic;
036import com.echothree.util.server.message.ExecutionErrorAccumulator;
037import com.echothree.util.server.persistence.EntityPermission;
038import com.echothree.util.server.persistence.Session;
039
040public class OrderLineLogic
041        extends BaseLogic {
042
043    protected OrderLineLogic() {
044        super();
045    }
046
047    private static class OrderLogicHolder {
048        static OrderLineLogic instance = new OrderLineLogic();
049    }
050
051    public static OrderLineLogic getInstance() {
052        return OrderLogicHolder.instance;
053    }
054
055    public OrderLine createOrderLine(final Session session, final ExecutionErrorAccumulator eea, final Order order, Integer orderLineSequence,
056            final OrderLine parentOrderLine, final OrderShipmentGroup orderShipmentGroup, final Item item, final InventoryCondition inventoryCondition,
057            final UnitOfMeasureType unitOfMeasureType, final Long quantity, final Long unitAmount, final String description,
058            final CancellationPolicy cancellationPolicy, final ReturnPolicy returnPolicy, final Boolean taxable, final BasePK createdBy) {
059        var orderLogic = OrderLogic.getInstance();
060        OrderLine orderLine = null;
061
062        // Make sure any OrderPaymentPreferences associated with this order are OK with this Item.
063        orderLogic.checkItemAgainstOrderPaymentPreferences(session, eea, order, item, createdBy);
064        
065        if(orderShipmentGroup != null) {
066            // Make sure the ShippingMethod associated with the OrderShipmentGroup is OK with this Item.
067            orderLogic.checkItemAgainstShippingMethod(session, eea, orderShipmentGroup, item, createdBy);
068        }
069        
070        if(eea == null || !eea.hasExecutionErrors()) {
071            var orderControl = Session.getModelController(OrderControl.class);
072            var orderLineControl = Session.getModelController(OrderLineControl.class);
073            var orderStatus = orderControl.getOrderStatusForUpdate(order);
074
075            if(orderLineSequence == null) {
076                orderLineSequence = orderStatus.getOrderLineSequence() + 1;
077                orderStatus.setOrderLineSequence(orderLineSequence);
078            } else {
079                orderLine = orderLineControl.getOrderLineBySequence(order, orderLineSequence);
080
081                if(orderLine == null) {
082                    // If the orderLineSequence is > the last one that was recorded in the OrderStatus, jump the
083                    // one in OrderStatus forward - it should always record the greatest orderLineSequence used.
084                    if(orderLineSequence > orderStatus.getOrderLineSequence()) {
085                        orderStatus.setOrderLineSequence(orderLineSequence);
086                    }
087                } else {
088                    handleExecutionError(DuplicateOrderLineSequenceException.class, eea, ExecutionErrors.DuplicateOrderLineSequence.name(),
089                            order.getLastDetail().getOrderName(), orderLineSequence.toString());
090                }
091            }
092
093            if(orderLine == null) {
094                orderLine = orderLineControl.createOrderLine(order, orderLineSequence, parentOrderLine, orderShipmentGroup, item, inventoryCondition,
095                        unitOfMeasureType, quantity, unitAmount, description, cancellationPolicy, returnPolicy, taxable, createdBy);
096            }
097        }
098
099        return orderLine;
100    }
101
102    private OrderLine getOrderLineByName(final ExecutionErrorAccumulator eea, final String orderTypeName, final String orderName, final String orderLineSequence,
103            final EntityPermission entityPermission) {
104        var order = OrderLogic.getInstance().getOrderByName(eea, orderTypeName, orderName);
105        OrderLine orderLine = null;
106        
107        if(eea == null || !eea.hasExecutionErrors()) {
108            var orderLineControl = Session.getModelController(OrderLineControl.class);
109
110            orderLine = orderLineControl.getOrderLineBySequence(order, Integer.valueOf(orderLineSequence), entityPermission);
111            
112            if(orderLine == null) {
113                handleExecutionError(UnknownOrderLineSequenceException.class, eea, ExecutionErrors.UnknownOrderLineSequence.name(), orderTypeName, orderName, orderLineSequence);
114            }
115        }
116
117        return orderLine;
118    }
119
120    public OrderLine getOrderLineByName(final ExecutionErrorAccumulator eea, final String orderTypeName, final String orderName, final String orderLineSequence) {
121        return getOrderLineByName(eea, orderTypeName, orderName, orderLineSequence, EntityPermission.READ_ONLY);
122    }
123
124    public OrderLine getOrderLineByNameForUpdate(final ExecutionErrorAccumulator eea, final String orderTypeName, final String orderName, final String orderLineSequence) {
125        return getOrderLineByName(eea, orderTypeName, orderName, orderLineSequence, EntityPermission.READ_WRITE);
126    }
127    
128    public Long getOrderTotalWithAdjustments(final Order order) {
129        var orderAdjustmentControl = Session.getModelController(OrderAdjustmentControl.class);
130        long total = 0;
131        var orderAdjustments = orderAdjustmentControl.getOrderAdjustmentsByOrder(order);
132
133        total = orderAdjustments.stream().map((orderAdjustment) -> orderAdjustment.getLastDetail().getAmount()).reduce(total, (accumulator, _item) -> accumulator + _item);
134
135        return total + getOrderLineTotalsWithAdjustments(order);
136    }
137
138    public Long getOrderLineTotalsWithAdjustments(final Order order) {
139        var orderLineControl = Session.getModelController(OrderLineControl.class);
140        var orderLines = orderLineControl.getOrderLinesByOrder(order);
141        long total = 0;
142
143        total = orderLines.stream().map((orderLine) -> getOrderLineTotalWithAdjustments(orderLine)).reduce(total, (accumulator, _item) -> accumulator + _item);
144
145        return total;
146    }
147
148    public Long getOrderLineTotalWithAdjustments(final OrderLine orderLine) {
149        var orderLineAdjustmentControl = Session.getModelController(OrderLineAdjustmentControl.class);
150        var orderLineDetail = orderLine.getLastDetail();
151        long total = orderLineDetail.getQuantity() * orderLineDetail.getUnitAmount();
152        var orderLineAdjustments = orderLineAdjustmentControl.getOrderLineAdjustmentsByOrderLine(orderLine);
153
154        total = orderLineAdjustments.stream().map((orderLineAdjustment) -> orderLineAdjustment.getLastDetail().getAmount()).reduce(total, (accumulator, _item) -> accumulator + _item);
155
156        return total;
157    }
158
159}