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