001// -------------------------------------------------------------------------------- 002// Copyright 2002-2025 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.sales.server.logic; 018 019import com.echothree.model.control.batch.common.BatchConstants; 020import com.echothree.model.control.batch.server.control.BatchControl; 021import com.echothree.model.control.batch.server.logic.BatchLogic; 022import com.echothree.model.control.core.server.control.EntityInstanceControl; 023import com.echothree.model.control.order.server.control.OrderBatchControl; 024import com.echothree.model.control.order.server.control.OrderControl; 025import com.echothree.model.control.sales.common.choice.SalesOrderBatchStatusChoicesBean; 026import com.echothree.model.control.sales.common.exception.CannotDeleteSalesOrderBatchInUseException; 027import com.echothree.model.control.sales.common.exception.IncorrectSalesOrderBatchAmountException; 028import com.echothree.model.control.sales.common.exception.IncorrectSalesOrderBatchCountException; 029import com.echothree.model.control.sales.common.exception.InvalidSalesOrderBatchStatusException; 030import com.echothree.model.control.sales.common.exception.InvalidSalesOrderStatusException; 031import com.echothree.model.control.sales.common.exception.UnknownSalesOrderBatchStatusChoiceException; 032import com.echothree.model.control.sales.common.workflow.SalesOrderBatchStatusConstants; 033import com.echothree.model.control.sales.common.workflow.SalesOrderStatusConstants; 034import com.echothree.model.control.sales.server.control.SalesOrderBatchControl; 035import com.echothree.model.control.workflow.server.control.WorkflowControl; 036import com.echothree.model.control.workflow.server.logic.WorkflowDestinationLogic; 037import com.echothree.model.control.workflow.server.logic.WorkflowLogic; 038import com.echothree.model.control.workflow.server.logic.WorkflowStepLogic; 039import com.echothree.model.data.accounting.server.entity.Currency; 040import com.echothree.model.data.batch.server.entity.Batch; 041import com.echothree.model.data.batch.server.entity.BatchEntity; 042import com.echothree.model.data.order.server.entity.Order; 043import com.echothree.model.data.party.common.pk.PartyPK; 044import com.echothree.model.data.party.server.entity.Language; 045import com.echothree.model.data.payment.server.entity.PaymentMethod; 046import com.echothree.util.common.message.ExecutionErrors; 047import com.echothree.util.common.persistence.BasePK; 048import com.echothree.util.server.control.BaseLogic; 049import com.echothree.util.server.message.ExecutionErrorAccumulator; 050import com.echothree.util.server.persistence.Session; 051import com.echothree.util.server.string.AmountUtils; 052import java.util.ArrayList; 053import java.util.List; 054import javax.enterprise.context.ApplicationScoped; 055import javax.enterprise.inject.spi.CDI; 056 057@ApplicationScoped 058public class SalesOrderBatchLogic 059 extends BaseLogic { 060 061 protected SalesOrderBatchLogic() { 062 super(); 063 } 064 065 public static SalesOrderBatchLogic getInstance() { 066 return CDI.current().select(SalesOrderBatchLogic.class).get(); 067 } 068 069 public Batch createBatch(final ExecutionErrorAccumulator eea, final Currency currency, final PaymentMethod paymentMethod, final Long count, 070 final Long amount, final BasePK createdBy) { 071 var salesOrderBatchControl = Session.getModelController(SalesOrderBatchControl.class); 072 var batch = BatchLogic.getInstance().createBatch(eea, BatchConstants.BatchType_SALES_ORDER, createdBy); 073 074 if(!eea.hasExecutionErrors()) { 075 var orderBatchControl = Session.getModelController(OrderBatchControl.class); 076 077 orderBatchControl.createOrderBatch(batch, currency, count, amount, createdBy); 078 salesOrderBatchControl.createSalesOrderBatch(batch, paymentMethod, createdBy); 079 } 080 081 return batch; 082 } 083 084 public boolean checkBatchInWorkflowSteps(final ExecutionErrorAccumulator eea, final Batch batch, final String... workflowStepNames) { 085 return !WorkflowStepLogic.getInstance().isEntityInWorkflowSteps(eea, SalesOrderBatchStatusConstants.Workflow_SALES_ORDER_BATCH_STATUS, 086 getEntityInstanceByBaseEntity(batch), workflowStepNames).isEmpty(); 087 } 088 089 public boolean checkBatchAvailableForEntry(final ExecutionErrorAccumulator eea, final Batch batch) { 090 return checkBatchInWorkflowSteps(eea, batch, SalesOrderBatchStatusConstants.WorkflowStep_ENTRY); 091 } 092 093 public BatchEntity createBatchEntity(final ExecutionErrorAccumulator eea, final Order order, final Batch batch, final BasePK createdBy) { 094 return BatchLogic.getInstance().createBatchEntity(eea, getEntityInstanceByBaseEntity(order), batch, createdBy); 095 } 096 097 public boolean batchEntryExists(final ExecutionErrorAccumulator eea, final Order order, final Batch batch, final String... workflowStepNames) { 098 var result = false; 099 100 if(workflowStepNames.length == 0) { 101 result = BatchLogic.getInstance().batchEntityExists(order, batch); 102 } else { 103 if(!WorkflowStepLogic.getInstance().isEntityInWorkflowSteps(eea, SalesOrderBatchStatusConstants.Workflow_SALES_ORDER_BATCH_STATUS, 104 getEntityInstanceByBaseEntity(order), workflowStepNames).isEmpty()) { 105 result = true; 106 } else { 107 handleExecutionError(InvalidSalesOrderBatchStatusException.class, eea, ExecutionErrors.InvalidSalesOrderBatchStatus.name(), 108 batch.getLastDetail().getBatchName()); 109 } 110 } 111 112 return result; 113 } 114 115 public void deleteBatch(final ExecutionErrorAccumulator eea, final Batch batch, final BasePK deletedBy) { 116 var batchControl = Session.getModelController(BatchControl.class); 117 118 if(batchControl.countBatchEntitiesByBatch(batch) == 0) { 119 BatchLogic.getInstance().deleteBatch(eea, batch, deletedBy); 120 121 if(eea == null || !eea.hasExecutionErrors()) { 122 var orderBatchControl = Session.getModelController(OrderBatchControl.class); 123 var salesOrderBatchControl = Session.getModelController(SalesOrderBatchControl.class); 124 125 orderBatchControl.deleteOrderBatch(batch, deletedBy); 126 salesOrderBatchControl.deleteSalesOrderBatch(batch, deletedBy); 127 } 128 } else { 129 handleExecutionError(CannotDeleteSalesOrderBatchInUseException.class, eea, ExecutionErrors.CannotDeleteSalesOrderBatchInUse.name(), 130 batch.getLastDetail().getBatchName()); 131 } 132 } 133 134 public Batch getBatchByName(final ExecutionErrorAccumulator eea, final String batchName) { 135 return BatchLogic.getInstance().getBatchByName(eea, BatchConstants.BatchType_SALES_ORDER, batchName); 136 } 137 138 public Batch getBatchByNameForUpdate(final ExecutionErrorAccumulator eea, final String batchName) { 139 return BatchLogic.getInstance().getBatchByNameForUpdate(eea, BatchConstants.BatchType_SALES_ORDER, batchName); 140 } 141 142 public SalesOrderBatchStatusChoicesBean getSalesOrderBatchStatusChoices(String defaultSalesOrderBatchStatusChoice, Language language, boolean allowNullChoice, 143 Batch batch, PartyPK partyPK) { 144 var workflowControl = Session.getModelController(WorkflowControl.class); 145 var salesOrderBatchStatusChoicesBean = new SalesOrderBatchStatusChoicesBean(); 146 147 if(batch == null) { 148 workflowControl.getWorkflowEntranceChoices(salesOrderBatchStatusChoicesBean, defaultSalesOrderBatchStatusChoice, language, allowNullChoice, 149 workflowControl.getWorkflowByName(SalesOrderBatchStatusConstants.Workflow_SALES_ORDER_BATCH_STATUS), partyPK); 150 } else { 151 var entityInstanceControl = Session.getModelController(EntityInstanceControl.class); 152 var entityInstance = entityInstanceControl.getEntityInstanceByBasePK(batch.getPrimaryKey()); 153 var workflowEntityStatus = workflowControl.getWorkflowEntityStatusByEntityInstanceUsingNames(SalesOrderBatchStatusConstants.Workflow_SALES_ORDER_BATCH_STATUS, 154 entityInstance); 155 156 workflowControl.getWorkflowDestinationChoices(salesOrderBatchStatusChoicesBean, defaultSalesOrderBatchStatusChoice, language, allowNullChoice, 157 workflowEntityStatus.getWorkflowStep(), partyPK); 158 } 159 160 return salesOrderBatchStatusChoicesBean; 161 } 162 163 public void setSalesOrderBatchStatus(final Session session, ExecutionErrorAccumulator eea, Batch batch, String salesOrderBatchStatusChoice, PartyPK modifiedBy) { 164 var entityInstanceControl = Session.getModelController(EntityInstanceControl.class); 165 var orderControl = Session.getModelController(OrderControl.class); 166 var workflowControl = Session.getModelController(WorkflowControl.class); 167 var workflow = WorkflowLogic.getInstance().getWorkflowByName(eea, SalesOrderBatchStatusConstants.Workflow_SALES_ORDER_BATCH_STATUS); 168 var entityInstance = entityInstanceControl.getEntityInstanceByBasePK(batch.getPrimaryKey()); 169 var workflowEntityStatus = workflowControl.getWorkflowEntityStatusByEntityInstanceForUpdate(workflow, entityInstance); 170 var workflowDestination = salesOrderBatchStatusChoice == null ? null : workflowControl.getWorkflowDestinationByName(workflowEntityStatus.getWorkflowStep(), salesOrderBatchStatusChoice); 171 172 if(workflowDestination != null || salesOrderBatchStatusChoice == null) { 173 var workflowDestinationLogic = WorkflowDestinationLogic.getInstance(); 174 var currentWorkflowStepName = workflowEntityStatus.getWorkflowStep().getLastDetail().getWorkflowStepName(); 175 var map = workflowDestinationLogic.getWorkflowDestinationsAsMap(workflowDestination); 176 Long triggerTime = null; 177 178 if(currentWorkflowStepName.equals(SalesOrderBatchStatusConstants.WorkflowStep_ENTRY)) { 179 if(workflowDestinationLogic.workflowDestinationMapContainsStep(map, SalesOrderBatchStatusConstants.Workflow_SALES_ORDER_BATCH_STATUS, SalesOrderBatchStatusConstants.WorkflowStep_AUDIT)) { 180 var batchControl = Session.getModelController(BatchControl.class); 181 var batchEntities = batchControl.getBatchEntitiesByBatch(batch); 182 183 // Verify all orders are in BATCH_AUDIT. 184 batchEntities.stream().map((batchEntity) -> workflowControl.getWorkflowEntityStatusByEntityInstanceUsingNames(SalesOrderStatusConstants.Workflow_SALES_ORDER_STATUS, batchEntity.getEntityInstance())).forEach((orderWorkflowEntityStatus) -> { 185 var orderEntityInstance = orderWorkflowEntityStatus.getEntityInstance(); 186 var orderWorkflowStepName = orderWorkflowEntityStatus.getWorkflowStep().getLastDetail().getWorkflowStepName(); 187 if (!orderWorkflowStepName.equals(SalesOrderStatusConstants.WorkflowStep_BATCH_AUDIT)) { 188 var order = orderControl.getOrderByEntityInstance(orderEntityInstance); 189 190 handleExecutionError(InvalidSalesOrderStatusException.class, eea, ExecutionErrors.InvalidSalesOrderStatus.name(), 191 order.getLastDetail().getOrderName(), orderWorkflowStepName); 192 } 193 }); 194 } 195 } else if(currentWorkflowStepName.equals(SalesOrderBatchStatusConstants.WorkflowStep_AUDIT)) { 196 if(workflowDestinationLogic.workflowDestinationMapContainsStep(map, SalesOrderBatchStatusConstants.Workflow_SALES_ORDER_BATCH_STATUS, SalesOrderBatchStatusConstants.WorkflowStep_COMPLETE)) { 197 var orderBatchControl = Session.getModelController(OrderBatchControl.class); 198 var orderBatch = orderBatchControl.getOrderBatch(batch); 199 var count = orderBatch.getCount(); 200 var amount = orderBatch.getAmount(); 201 202 if(count != null) { 203 var batchControl = Session.getModelController(BatchControl.class); 204 Long batchCount = batchControl.countBatchEntitiesByBatch(batch); 205 206 if(!count.equals(batchCount)) { 207 handleExecutionError(IncorrectSalesOrderBatchCountException.class, eea, ExecutionErrors.IncorrectSalesOrderBatchCount.name(), 208 count.toString(), batchCount.toString()); 209 } 210 } 211 212 if(amount != null) { 213 var batchAmount = getBatchOrderTotalsWithAdjustments(batch); 214 215 if(!amount.equals(batchAmount)) { 216 var currency = orderBatch.getCurrency(); 217 218 handleExecutionError(IncorrectSalesOrderBatchAmountException.class, eea, ExecutionErrors.IncorrectSalesOrderBatchAmount.name(), 219 AmountUtils.getInstance().formatPriceUnit(currency, amount), 220 AmountUtils.getInstance().formatPriceUnit(currency, batchAmount)); 221 } 222 } 223 } 224 } 225 226 if(eea == null || !eea.hasExecutionErrors()) { 227 workflowControl.transitionEntityInWorkflow(eea, workflowEntityStatus, workflowDestination, triggerTime, modifiedBy); 228 } 229 } else { 230 handleExecutionError(UnknownSalesOrderBatchStatusChoiceException.class, eea, ExecutionErrors.UnknownSalesOrderBatchStatusChoice.name(), salesOrderBatchStatusChoice); 231 } 232 } 233 234 public List<Order> getBatchOrders(final Batch batch) { 235 var batchControl = Session.getModelController(BatchControl.class); 236 var orderControl = Session.getModelController(OrderControl.class); 237 var batchEntities = batchControl.getBatchEntitiesByBatch(batch); 238 List<Order> orders = new ArrayList<>(batchEntities.size()); 239 240 batchEntities.forEach((batchEntity) -> { 241 orders.add(orderControl.convertEntityInstanceToOrder(batchEntity.getEntityInstance())); 242 }); 243 244 return orders; 245 } 246 247 public Long getBatchOrderTotalsWithAdjustments(Batch batch) { 248 var salesOrderLineLogic = SalesOrderLineLogic.getInstance(); 249 var orders = getBatchOrders(batch); 250 long total = 0; 251 252 total = orders.stream().map((order) -> salesOrderLineLogic.getOrderTotalWithAdjustments(order)).reduce(total, (accumulator, _item) -> accumulator + _item); 253 254 return total; 255 } 256 257}