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.accounting.common.exception.InvalidCurrencyException; 020import com.echothree.model.control.accounting.server.logic.CurrencyLogic; 021import com.echothree.model.control.associate.server.logic.AssociateReferralLogic; 022import com.echothree.model.control.batch.server.logic.BatchLogic; 023import com.echothree.model.control.cancellationpolicy.common.CancellationKinds; 024import com.echothree.model.control.cancellationpolicy.server.logic.CancellationPolicyLogic; 025import com.echothree.model.control.core.server.control.EntityInstanceControl; 026import com.echothree.model.control.customer.common.exception.MissingDefaultCustomerTypeException; 027import com.echothree.model.control.customer.server.control.CustomerControl; 028import com.echothree.model.control.offer.common.exception.MissingDefaultSourceException; 029import com.echothree.model.control.offer.server.control.OfferControl; 030import com.echothree.model.control.offer.server.control.SourceControl; 031import com.echothree.model.control.offer.server.logic.SourceLogic; 032import com.echothree.model.control.order.common.OrderRoleTypes; 033import com.echothree.model.control.order.common.OrderTypes; 034import com.echothree.model.control.order.common.exception.MissingDefaultOrderPriorityException; 035import com.echothree.model.control.order.common.exception.MissingRequiredBillToPartyException; 036import com.echothree.model.control.order.server.control.OrderBatchControl; 037import com.echothree.model.control.order.server.control.OrderControl; 038import com.echothree.model.control.order.server.control.OrderPriorityControl; 039import com.echothree.model.control.order.server.control.OrderRoleControl; 040import com.echothree.model.control.order.server.logic.BaseOrderLogic; 041import com.echothree.model.control.party.common.PartyTypes; 042import com.echothree.model.control.party.server.logic.PartyLogic; 043import com.echothree.model.control.returnpolicy.common.ReturnKinds; 044import com.echothree.model.control.returnpolicy.server.logic.ReturnPolicyLogic; 045import com.echothree.model.control.sales.common.choice.SalesOrderStatusChoicesBean; 046import com.echothree.model.control.sales.common.exception.InvalidSalesOrderBatchStatusException; 047import com.echothree.model.control.sales.common.exception.InvalidSalesOrderReferenceException; 048import com.echothree.model.control.sales.common.exception.InvalidSalesOrderStatusException; 049import com.echothree.model.control.sales.common.exception.SalesOrderDuplicateReferenceException; 050import com.echothree.model.control.sales.common.exception.SalesOrderReferenceRequiredException; 051import com.echothree.model.control.sales.common.exception.UnknownSalesOrderStatusChoiceException; 052import com.echothree.model.control.sales.common.workflow.SalesOrderStatusConstants; 053import com.echothree.model.control.sales.server.control.SalesOrderControl; 054import com.echothree.model.control.shipment.server.control.PartyFreeOnBoardControl; 055import com.echothree.model.control.shipment.server.logic.FreeOnBoardLogic; 056import com.echothree.model.control.term.server.control.TermControl; 057import com.echothree.model.control.term.server.logic.TermLogic; 058import com.echothree.model.control.user.server.control.UserControl; 059import com.echothree.model.control.workflow.server.control.WorkflowControl; 060import com.echothree.model.control.workflow.server.logic.WorkflowDestinationLogic; 061import com.echothree.model.control.workflow.server.logic.WorkflowLogic; 062import com.echothree.model.control.workflow.server.logic.WorkflowStepLogic; 063import com.echothree.model.data.accounting.server.entity.Currency; 064import com.echothree.model.data.batch.server.entity.Batch; 065import com.echothree.model.data.cancellationpolicy.server.entity.CancellationPolicy; 066import com.echothree.model.data.core.server.entity.EntityInstance; 067import com.echothree.model.data.customer.server.entity.Customer; 068import com.echothree.model.data.customer.server.entity.CustomerType; 069import com.echothree.model.data.offer.server.entity.Offer; 070import com.echothree.model.data.offer.server.entity.Source; 071import com.echothree.model.data.order.server.entity.Order; 072import com.echothree.model.data.order.server.entity.OrderPriority; 073import com.echothree.model.data.party.common.pk.PartyPK; 074import com.echothree.model.data.party.server.entity.Language; 075import com.echothree.model.data.party.server.entity.Party; 076import com.echothree.model.data.returnpolicy.server.entity.ReturnPolicy; 077import com.echothree.model.data.shipment.server.entity.FreeOnBoard; 078import com.echothree.model.data.term.server.entity.Term; 079import com.echothree.model.data.user.server.entity.UserVisit; 080import com.echothree.util.common.message.ExecutionErrors; 081import com.echothree.util.common.persistence.BasePK; 082import com.echothree.util.server.message.ExecutionErrorAccumulator; 083import com.echothree.util.server.persistence.Session; 084import javax.enterprise.context.ApplicationScoped; 085import javax.enterprise.inject.spi.CDI; 086 087@ApplicationScoped 088public class SalesOrderLogic 089 extends BaseOrderLogic { 090 091 protected SalesOrderLogic() { 092 super(); 093 } 094 095 public static SalesOrderLogic getInstance() { 096 return CDI.current().select(SalesOrderLogic.class).get(); 097 } 098 099 final static long AllocatedInventoryTimeout = 5 * 60 * 1000; // 5 Minutes 100 101 public CustomerType getCustomerType(final ExecutionErrorAccumulator eea, final Offer offer, final Customer customer) { 102 var customerControl = Session.getModelController(CustomerControl.class); 103 CustomerType customerType = null; 104 105 // 1) Try to get it from the customer, if one was supplied. 106 if(customer != null) { 107 customerType = customer.getCustomerType(); 108 } 109 110 // 2) Try to get it from the offer, if one was supplied. 111 if(customerType == null && offer != null) { 112 var offerControl = Session.getModelController(OfferControl.class); 113 var offerCustomerType = offerControl.getDefaultOfferCustomerType(offer); 114 115 if(offerCustomerType != null) { 116 customerType = offerCustomerType.getCustomerType(); 117 } 118 } 119 120 // 3) Try to get the default CustomerType, error if it isn't available. 121 if(customerType == null) { 122 customerType = customerControl.getDefaultCustomerType(); 123 124 if(customerType == null) { 125 handleExecutionError(MissingDefaultCustomerTypeException.class, eea, ExecutionErrors.MissingDefaultCustomerType.name()); 126 } 127 } 128 129 return customerType; 130 } 131 132 public void validateSalesOrderReference(final ExecutionErrorAccumulator eea, final String reference, final CustomerType customerType, final Customer billToCustomer) { 133 Boolean requireReference = null; 134 Boolean allowReferenceDuplicates = null; 135 String referenceValidationPattern = null; 136 137 if(billToCustomer != null) { 138 requireReference = billToCustomer.getRequireReference(); 139 allowReferenceDuplicates = billToCustomer.getAllowReferenceDuplicates(); 140 referenceValidationPattern = billToCustomer.getReferenceValidationPattern(); 141 } else if(customerType != null) { 142 var customerTypeDetail = customerType.getLastDetail(); 143 144 requireReference = customerTypeDetail.getDefaultRequireReference(); 145 allowReferenceDuplicates = customerTypeDetail.getDefaultAllowReferenceDuplicates(); 146 referenceValidationPattern = customerTypeDetail.getDefaultReferenceValidationPattern(); 147 } 148 149 if(requireReference != null) { 150 if(requireReference && reference == null) { 151 handleExecutionError(SalesOrderReferenceRequiredException.class, eea, ExecutionErrors.SalesOrderReferenceRequired.name()); 152 } else if(reference != null) { 153 var orderControl = Session.getModelController(OrderControl.class); 154 155 if(!allowReferenceDuplicates) { 156 if(billToCustomer == null) { 157 handleExecutionError(MissingRequiredBillToPartyException.class, eea, ExecutionErrors.MissingRequiredBillToParty.name()); 158 } else if(orderControl.countOrdersByBillToAndReference(billToCustomer.getParty(), reference) != 0) { 159 handleExecutionError(SalesOrderDuplicateReferenceException.class, eea, ExecutionErrors.SalesOrderDuplicateReference.name()); 160 } 161 } 162 163 if(referenceValidationPattern != null && !reference.matches(referenceValidationPattern)) { 164 handleExecutionError(InvalidSalesOrderReferenceException.class, eea, ExecutionErrors.InvalidSalesOrderReference.name()); 165 } 166 } 167 } 168 } 169 170 public CancellationPolicy getCancellationPolicy(final ExecutionErrorAccumulator eea, final CustomerType customerType, final Customer billToCustomer) { 171 return CancellationPolicyLogic.getInstance().getDefaultCancellationPolicyByKind(eea, CancellationKinds.CUSTOMER_CANCELLATION.name(), 172 new CancellationPolicy[]{ 173 billToCustomer == null ? null : billToCustomer.getCancellationPolicy(), 174 customerType.getLastDetail().getDefaultCancellationPolicy() 175 }); 176 } 177 178 public ReturnPolicy getReturnPolicy(final ExecutionErrorAccumulator eea, final CustomerType customerType, final Customer billToCustomer) { 179 return ReturnPolicyLogic.getInstance().getDefaultReturnPolicyByKind(eea, ReturnKinds.CUSTOMER_RETURN.name(), 180 new ReturnPolicy[]{ 181 billToCustomer == null ? null : billToCustomer.getReturnPolicy(), 182 customerType.getLastDetail().getDefaultReturnPolicy() 183 }); 184 } 185 186 /** 187 * Create a new Sales Order given a set of parameters. Default values will be determined as best as possible when 188 * a parameter is marked as Optional. If a previous preference has not been set then it may not be possible to 189 * to determine appropriate defaults. 190 * 191 * @param session Required. 192 * @param eea Required. 193 * @param userVisit Required. 194 * @param source Optional. 195 * @param billToParty Optional. 196 * @param orderPriority Optional. 197 * @param currency Optional. 198 * @param holdUntilComplete Optional. 199 * @param allowBackorders Optional. 200 * @param allowSubstitutions Optional. 201 * @param allowCombiningShipments Optional. 202 * @param reference Optional. 203 * @param term Optional. 204 * @param taxable Optional. 205 * @param workflowEntranceName Optional. 206 * @param createdByParty Required. 207 * @return The newly created Order, or null if there was an error. 208 */ 209 public Order createSalesOrder(final Session session, final ExecutionErrorAccumulator eea, final UserVisit userVisit, final Batch batch, Source source, 210 final Party billToParty, OrderPriority orderPriority, Currency currency, Boolean holdUntilComplete, Boolean allowBackorders, Boolean allowSubstitutions, 211 Boolean allowCombiningShipments, final String reference, Term term, FreeOnBoard freeOnBoard, Boolean taxable, final String workflowEntranceName, final Party createdByParty) { 212 var orderType = getOrderTypeByName(eea, OrderTypes.SALES_ORDER.name()); 213 var billToOrderRoleType = getOrderRoleTypeByName(eea, OrderRoleTypes.BILL_TO.name()); 214 var placingOrderRoleType = getOrderRoleTypeByName(eea, OrderRoleTypes.PLACING.name()); 215 Order order = null; 216 217 if(batch != null) { 218 if(SalesOrderBatchLogic.getInstance().checkBatchAvailableForEntry(eea, batch)) { 219 var orderBatchControl = Session.getModelController(OrderBatchControl.class); 220 var orderBatchCurrency = orderBatchControl.getOrderBatch(batch).getCurrency(); 221 222 if(currency == null) { 223 currency = orderBatchCurrency; 224 } else { 225 if(!currency.equals(orderBatchCurrency)) { 226 handleExecutionError(InvalidCurrencyException.class, eea, ExecutionErrors.InvalidCurrency.name(), currency.getCurrencyIsoName(), 227 orderBatchCurrency.getCurrencyIsoName()); 228 } 229 } 230 } else { 231 handleExecutionError(InvalidSalesOrderBatchStatusException.class, eea, ExecutionErrors.InvalidSalesOrderBatchStatus.name(), 232 batch.getLastDetail().getBatchName()); 233 } 234 } 235 236 if(eea == null || !eea.hasExecutionErrors()) { 237 if(source == null) { 238 var sourceControl = Session.getModelController(SourceControl.class); 239 240 source = sourceControl.getDefaultSource(); 241 242 if(source == null) { 243 handleExecutionError(MissingDefaultSourceException.class, eea, ExecutionErrors.MissingDefaultSource.name()); 244 } 245 } 246 247 if(orderPriority == null) { 248 var orderPriorityControl = Session.getModelController(OrderPriorityControl.class); 249 250 orderPriority = orderPriorityControl.getDefaultOrderPriority(orderType); 251 252 if(orderPriority == null) { 253 handleExecutionError(MissingDefaultOrderPriorityException.class, eea, ExecutionErrors.MissingDefaultOrderPriority.name(), OrderTypes.SALES_ORDER.name()); 254 } 255 } 256 257 if(currency == null) { 258 var userControl = Session.getModelController(UserControl.class); 259 260 if(billToParty != null) { 261 currency = userControl.getPreferredCurrencyFromParty(billToParty); 262 } 263 264 if(currency == null && userVisit != null) { 265 currency = userControl.getPreferredCurrencyFromUserVisit(userVisit); 266 } 267 268 if(currency == null) { 269 currency = CurrencyLogic.getInstance().getDefaultCurrency(eea); 270 } 271 } 272 273 if(billToParty != null) { 274 PartyLogic.getInstance().checkPartyType(eea, billToParty, PartyTypes.CUSTOMER.name()); 275 } 276 277 if(eea == null || !eea.hasExecutionErrors()) { 278 var customerControl = Session.getModelController(CustomerControl.class); 279 var billToCustomer = billToParty == null ? null : customerControl.getCustomer(billToParty); 280 var offerUse = source.getLastDetail().getOfferUse(); 281 var customerType = getCustomerType(eea, offerUse.getLastDetail().getOffer(), billToCustomer); 282 283 if(eea == null || !eea.hasExecutionErrors()) { 284 var cancellationPolicy = getCancellationPolicy(eea, customerType, billToCustomer); 285 var returnPolicy = getReturnPolicy(eea, customerType, billToCustomer); 286 287 if(billToCustomer != null) { 288 validateSalesOrderReference(eea, reference, customerType, billToCustomer); 289 } 290 291 if(eea == null || !eea.hasExecutionErrors()) { 292 var customerTypeDetail = customerType.getLastDetail(); 293 var sequence = offerUse.getLastDetail().getSalesOrderSequence(); 294 var createdBy = createdByParty == null ? null : createdByParty.getPrimaryKey(); 295 296 // If term or taxable were not set, then try to come up with sensible defaults, first from a PartyTerm if it 297 // was available, and then falling back on the CustomerType. 298 if(term == null || taxable == null) { 299 var termControl = Session.getModelController(TermControl.class); 300 var partyTerm = billToParty == null ? null : termControl.getPartyTerm(billToParty); 301 302 if(partyTerm == null) { 303 if(term == null) { 304 term = customerTypeDetail.getDefaultTerm(); 305 } 306 307 if(taxable == null) { 308 taxable = customerTypeDetail.getDefaultTaxable(); 309 } 310 } else { 311 if(term == null) { 312 term = partyTerm.getTerm(); 313 } 314 315 if(taxable == null) { 316 taxable = partyTerm.getTaxable(); 317 } 318 } 319 320 // If no better answer was found, use the default Term. 321 if(term == null) { 322 termControl.getDefaultTerm(); 323 } 324 325 // If no better answer was found, the order is taxable. 326 if(taxable == null) { 327 taxable = true; 328 } 329 } 330 331 // If FreeOnBoard wasn't specified, using the bill to Party, fir look for a preference for the Party, 332 // and then check the CustomerType. 333 if(freeOnBoard == null) { 334 var partFreeOnBoardControl = Session.getModelController(PartyFreeOnBoardControl.class); 335 var partyFreeOnBoard = billToParty == null ? null : partFreeOnBoardControl.getPartyFreeOnBoard(billToParty); 336 337 if(partyFreeOnBoard == null) { 338 if(freeOnBoard == null) { 339 freeOnBoard = customerTypeDetail.getDefaultFreeOnBoard(); 340 } 341 } else { 342 if(freeOnBoard == null) { 343 freeOnBoard = partyFreeOnBoard.getFreeOnBoard(); 344 } 345 } 346 347 // If no better answer was found, use the default FreeOnBoard. 348 if(freeOnBoard == null) { 349 freeOnBoard = FreeOnBoardLogic.getInstance().getDefaultFreeOnBoard(eea); 350 } 351 } 352 353 // If any of these flags were not set, try to get them from either the Customer, or the CustomerType. 354 if(holdUntilComplete == null || allowBackorders == null || allowSubstitutions == null || allowCombiningShipments == null) { 355 if(billToCustomer == null) { 356 if(holdUntilComplete == null) { 357 holdUntilComplete = customerTypeDetail.getDefaultHoldUntilComplete(); 358 } 359 360 if(allowBackorders == null) { 361 allowBackorders = customerTypeDetail.getDefaultAllowBackorders(); 362 } 363 364 if(allowSubstitutions == null) { 365 allowSubstitutions = customerTypeDetail.getDefaultAllowSubstitutions(); 366 } 367 368 if(allowCombiningShipments == null) { 369 allowCombiningShipments = customerTypeDetail.getDefaultAllowCombiningShipments(); 370 } 371 } else { 372 if(holdUntilComplete == null) { 373 holdUntilComplete = billToCustomer.getHoldUntilComplete(); 374 } 375 376 if(allowBackorders == null) { 377 allowBackorders = billToCustomer.getAllowBackorders(); 378 } 379 380 if(allowSubstitutions == null) { 381 allowSubstitutions = billToCustomer.getAllowSubstitutions(); 382 } 383 384 if(allowCombiningShipments == null) { 385 allowCombiningShipments = billToCustomer.getAllowCombiningShipments(); 386 } 387 } 388 } 389 390 order = createOrder(eea, orderType, sequence, orderPriority, currency, holdUntilComplete, allowBackorders, 391 allowSubstitutions, allowCombiningShipments, term, freeOnBoard, reference, null, 392 cancellationPolicy, returnPolicy, taxable, createdBy); 393 394 if(eea == null || !eea.hasExecutionErrors()) { 395 var entityInstanceControl = Session.getModelController(EntityInstanceControl.class); 396 var orderControl = Session.getModelController(OrderControl.class); 397 var orderRoleControl = Session.getModelController(OrderRoleControl.class); 398 var salesOrderControl = Session.getModelController(SalesOrderControl.class); 399 var workflowControl = Session.getModelController(WorkflowControl.class); 400 var associateReferral = AssociateReferralLogic.getInstance().getAssociateReferral(session, userVisit); 401 var entityInstance = entityInstanceControl.getEntityInstanceByBasePK(order.getPrimaryKey()); 402 403 salesOrderControl.createSalesOrder(order, offerUse, associateReferral, createdBy); 404 405 orderControl.createOrderUserVisit(order, userVisit); 406 407 workflowControl.addEntityToWorkflowUsingNames(null, SalesOrderStatusConstants.Workflow_SALES_ORDER_STATUS, workflowEntranceName, 408 entityInstance, null, session.START_TIME + AllocatedInventoryTimeout, createdBy); 409 410 if(billToParty != null) { 411 orderRoleControl.createOrderRole(order, billToParty, billToOrderRoleType, createdBy); 412 } 413 414 if(createdByParty != null) { 415 orderRoleControl.createOrderRole(order, createdByParty, placingOrderRoleType, createdBy); 416 } 417 418 if(batch != null) { 419 // eea is passed in as null to createBatchEntity(...) so that an Exception will be thrown is something 420 // goes wrong. No real way to back out at this point if it does, except by Exception. 421 BatchLogic.getInstance().createBatchEntity(null, entityInstance, batch, createdBy); 422 } 423 } 424 } 425 } 426 } 427 } 428 429 return order; 430 } 431 432 public Order createSalesOrder(final Session session, final ExecutionErrorAccumulator eea, final UserVisit userVisit, 433 final String batchName, final String sourceName, final String billToPartyName, final String orderPriorityName, 434 final String currencyIsoName, final String termName, final String strHoldUntilComplete, final String strAllowBackorders, 435 final String strAllowSubstitutions, final String strAllowCombiningShipments, final String reference, final String freeOnBoardName, 436 final String strTaxable, final String workflowEntranceName, final Party createdByParty) { 437 var batch = batchName == null ? null : SalesOrderBatchLogic.getInstance().getBatchByName(eea, batchName); 438 var source = sourceName == null ? null : SourceLogic.getInstance().getSourceByName(eea, sourceName); 439 var billToParty = billToPartyName == null ? null : PartyLogic.getInstance().getPartyByName(eea, billToPartyName, PartyTypes.CUSTOMER.name()); 440 var orderPriority = orderPriorityName == null ? null : SalesOrderLogic.getInstance().getOrderPriorityByName(eea, orderPriorityName); 441 var currency = currencyIsoName == null ? null : CurrencyLogic.getInstance().getCurrencyByName(eea, currencyIsoName); 442 var term = termName == null ? null : TermLogic.getInstance().getTermByName(eea, termName); 443 var freeOnBoard = freeOnBoardName == null ? null : FreeOnBoardLogic.getInstance().getFreeOnBoardByName(eea, freeOnBoardName); 444 Order order = null; 445 446 if(!eea.hasExecutionErrors()) { 447 var holdUntilComplete = strHoldUntilComplete == null ? null : Boolean.valueOf(strHoldUntilComplete); 448 var allowBackorders = strAllowBackorders == null ? null : Boolean.valueOf(strAllowBackorders); 449 var allowSubstitutions = strAllowSubstitutions == null ? null : Boolean.valueOf(strAllowSubstitutions); 450 var allowCombiningShipments = strAllowCombiningShipments == null ? null : Boolean.valueOf(strAllowCombiningShipments); 451 var taxable = strTaxable == null ? null : Boolean.valueOf(strTaxable); 452 453 order = createSalesOrder(session, eea, userVisit, batch, source, billToParty, orderPriority, currency, 454 holdUntilComplete, allowBackorders, allowSubstitutions, allowCombiningShipments, reference, term, 455 freeOnBoard, taxable, workflowEntranceName, createdByParty); 456 457 } 458 459 return order; 460 } 461 462 public boolean isOrderInWorkflowSteps(final ExecutionErrorAccumulator eea, final Order order, final String... workflowStepNames) { 463 return isOrderInWorkflowSteps(eea, getEntityInstanceByBaseEntity(order), workflowStepNames); 464 } 465 466 public boolean isOrderInWorkflowSteps(final ExecutionErrorAccumulator eea, final EntityInstance entityInstance, final String... workflowStepNames) { 467 return !WorkflowStepLogic.getInstance().isEntityInWorkflowSteps(eea, SalesOrderStatusConstants.Workflow_SALES_ORDER_STATUS, entityInstance, 468 workflowStepNames).isEmpty(); 469 } 470 471 public Order getOrderByName(final ExecutionErrorAccumulator eea, final String orderName) { 472 return getOrderByName(eea, OrderTypes.SALES_ORDER.name(), orderName); 473 } 474 475 public Order getOrderByNameForUpdate(final ExecutionErrorAccumulator eea, final String orderName) { 476 return getOrderByNameForUpdate(eea, OrderTypes.SALES_ORDER.name(), orderName); 477 } 478 479 public OrderPriority getOrderPriorityByName(final ExecutionErrorAccumulator eea, final String orderPriorityName) { 480 return SalesOrderPriorityLogic.getInstance().getOrderPriorityByName(eea, OrderTypes.SALES_ORDER.name(), orderPriorityName); 481 } 482 483 public OrderPriority getOrderPriorityByNameForUpdate(final ExecutionErrorAccumulator eea, final String orderPriorityName) { 484 return SalesOrderPriorityLogic.getInstance().getOrderPriorityByNameForUpdate(eea, OrderTypes.SALES_ORDER.name(), orderPriorityName); 485 } 486 487 public SalesOrderStatusChoicesBean getSalesOrderStatusChoices(final String defaultOrderStatusChoice, final Language language, final boolean allowNullChoice, 488 final Order order, final PartyPK partyPK) { 489 var workflowControl = Session.getModelController(WorkflowControl.class); 490 var salesOrderStatusChoicesBean = new SalesOrderStatusChoicesBean(); 491 492 if(order == null) { 493 workflowControl.getWorkflowEntranceChoices(salesOrderStatusChoicesBean, defaultOrderStatusChoice, language, allowNullChoice, 494 workflowControl.getWorkflowByName(SalesOrderStatusConstants.Workflow_SALES_ORDER_STATUS), partyPK); 495 } else { 496 var entityInstanceControl = Session.getModelController(EntityInstanceControl.class); 497 var entityInstance = entityInstanceControl.getEntityInstanceByBasePK(order.getPrimaryKey()); 498 var workflowEntityStatus = workflowControl.getWorkflowEntityStatusByEntityInstanceUsingNames(SalesOrderStatusConstants.Workflow_SALES_ORDER_STATUS, entityInstance); 499 500 workflowControl.getWorkflowDestinationChoices(salesOrderStatusChoicesBean, defaultOrderStatusChoice, language, allowNullChoice, workflowEntityStatus.getWorkflowStep(), partyPK); 501 } 502 503 return salesOrderStatusChoicesBean; 504 } 505 506 public void setSalesOrderStatus(final Session session, final ExecutionErrorAccumulator eea, final Order order, final String orderStatusChoice, final PartyPK modifiedBy) { 507 var entityInstanceControl = Session.getModelController(EntityInstanceControl.class); 508 var workflowControl = Session.getModelController(WorkflowControl.class); 509 var workflow = WorkflowLogic.getInstance().getWorkflowByName(eea, SalesOrderStatusConstants.Workflow_SALES_ORDER_STATUS); 510 var entityInstance = entityInstanceControl.getEntityInstanceByBasePK(order.getPrimaryKey()); 511 var workflowEntityStatus = workflowControl.getWorkflowEntityStatusByEntityInstanceForUpdate(workflow, entityInstance); 512 var workflowDestination = orderStatusChoice == null? null: workflowControl.getWorkflowDestinationByName(workflowEntityStatus.getWorkflowStep(), orderStatusChoice); 513 514 if(workflowDestination != null || orderStatusChoice == null) { 515 var workflowDestinationLogic = WorkflowDestinationLogic.getInstance(); 516 var currentWorkflowStepName = workflowEntityStatus.getWorkflowStep().getLastDetail().getWorkflowStepName(); 517 var map = workflowDestinationLogic.getWorkflowDestinationsAsMap(workflowDestination); 518 var handled = false; 519 Long triggerTime = null; 520 521 if(currentWorkflowStepName.equals(SalesOrderStatusConstants.WorkflowStep_ENTRY_ALLOCATED)) { 522 if(workflowDestinationLogic.workflowDestinationMapContainsStep(map, SalesOrderStatusConstants.Workflow_SALES_ORDER_STATUS, SalesOrderStatusConstants.WorkflowStep_ENTRY_UNALLOCATED)) { 523 // TODO: Unallocate inventory. 524 handled = true; 525 } else if(workflowDestinationLogic.workflowDestinationMapContainsStep(map, SalesOrderStatusConstants.Workflow_SALES_ORDER_STATUS, SalesOrderStatusConstants.WorkflowStep_ENTRY_COMPLETE)) { 526 // TODO: Verify the order is not part of a batch. 527 // TODO: Verify all aspects of the order are valid. 528 handled = true; 529 } else if(workflowDestinationLogic.workflowDestinationMapContainsStep(map, SalesOrderStatusConstants.Workflow_SALES_ORDER_STATUS, SalesOrderStatusConstants.WorkflowStep_BATCH_AUDIT)) { 530 // TODO: Verify the order is part of a batch. 531 // TODO: Verify all aspects of the order are valid. 532 handled = true; 533 } 534 } else if(currentWorkflowStepName.equals(SalesOrderStatusConstants.WorkflowStep_ENTRY_UNALLOCATED)) { 535 if(workflowDestinationLogic.workflowDestinationMapContainsStep(map, SalesOrderStatusConstants.Workflow_SALES_ORDER_STATUS, SalesOrderStatusConstants.WorkflowStep_ENTRY_ALLOCATED)) { 536 // TODO: Allocate inventory. 537 538 triggerTime = session.START_TIME + AllocatedInventoryTimeout; 539 handled = true; 540 } 541 } else if(currentWorkflowStepName.equals(SalesOrderStatusConstants.WorkflowStep_BATCH_AUDIT)) { 542 if(workflowDestinationLogic.workflowDestinationMapContainsStep(map, SalesOrderStatusConstants.Workflow_SALES_ORDER_STATUS, SalesOrderStatusConstants.WorkflowStep_ENTRY_COMPLETE)) { 543 handled = true; 544 } 545 } 546 547 if(eea == null || !eea.hasExecutionErrors()) { 548 if(handled) { 549 workflowControl.transitionEntityInWorkflow(eea, workflowEntityStatus, workflowDestination, triggerTime, modifiedBy); 550 } else { 551 // TODO: An error of some sort. 552 } 553 } 554 } else { 555 handleExecutionError(UnknownSalesOrderStatusChoiceException.class, eea, ExecutionErrors.UnknownSalesOrderStatusChoice.name(), orderStatusChoice); 556 } 557 } 558 559 /** Check to see if an Order is available for modification, and if it isn't, send back an error. 560 * 561 * @param session Required. 562 * @param eea Required. 563 * @param order Required. 564 * @param modifiedBy Required. 565 */ 566 public void checkOrderAvailableForModification(final Session session, final ExecutionErrorAccumulator eea, final Order order, final PartyPK modifiedBy) { 567 var workflowControl = Session.getModelController(WorkflowControl.class); 568 var workflowEntityStatus = workflowControl.getWorkflowEntityStatusByEntityInstanceForUpdateUsingNames(SalesOrderStatusConstants.Workflow_SALES_ORDER_STATUS, getEntityInstanceByBaseEntity(order)); 569 var workflowStepName = workflowEntityStatus.getWorkflowStep().getLastDetail().getWorkflowStepName(); 570 571 if(workflowStepName.equals(SalesOrderStatusConstants.WorkflowEntrance_ENTRY_ALLOCATED)) { 572 var workflowTrigger = workflowControl.getWorkflowTriggerForUpdate(workflowEntityStatus); 573 574 workflowTrigger.setTriggerTime(session.START_TIME + AllocatedInventoryTimeout); 575 } else if(workflowStepName.equals(SalesOrderStatusConstants.WorkflowEntrance_ENTRY_UNALLOCATED)) { 576 setSalesOrderStatus(session, eea, order, SalesOrderStatusConstants.WorkflowDestination_ENTRY_UNALLOCATED_TO_ALLOCATED, modifiedBy); 577 } else { 578 handleExecutionError(InvalidSalesOrderStatusException.class, eea, ExecutionErrors.InvalidSalesOrderStatus.name(), order.getLastDetail().getOrderName(), workflowStepName); 579 } 580 } 581 582 /** Find the BILL_TO Party for a given Order. 583 * 584 * @param order Required. 585 * @return The Party used for the BILL_TO OrderRoleType. May be null. 586 */ 587 public Party getOrderBillToParty(final Order order) { 588 var orderRoleControl = Session.getModelController(OrderRoleControl.class); 589 var billToOrderRole = orderRoleControl.getOrderRoleByOrderAndOrderRoleTypeUsingNames(order, OrderRoleTypes.BILL_TO.name()); 590 Party party = null; 591 592 if(billToOrderRole != null) { 593 party = billToOrderRole.getParty(); 594 } 595 596 return party; 597 } 598 599 /** Find the BILL_TO Party for a given Order. 600 * 601 * @param party Optional. 602 * @return The CustomerType for the Party. May be null. 603 */ 604 public CustomerType getCustomerTypeFromParty(final Party party) { 605 var customerControl = Session.getModelController(CustomerControl.class); 606 var customer = party == null ? null : customerControl.getCustomer(party); 607 CustomerType customerType = null; 608 609 if(customer != null) { 610 customerType = customer.getCustomerType(); 611 } 612 613 return customerType; 614 } 615 616 /** Find the BILL_TO Party for a given Order. 617 * 618 * @param order Required. 619 * @return The CustomerType for the BILL_TO Party. May be null. 620 */ 621 public CustomerType getOrderBillToCustomerType(final Order order) { 622 return getCustomerTypeFromParty(getOrderBillToParty(order)); 623 } 624 625 /** Attempt to find a SHIP_TO Party for the Order. If none is found, and billToFallback is set to true, then 626 * attempt to find the BILL_TO Party. If a BILL_TO Party is found, copy it to the SHIP_TO. 627 * 628 * @param order Required. 629 * @param billToFallback Required. 630 * @param createdBy Required if billToFallback is true. 631 * @return The Party that is to be used for the SHIP_TO OrderRoleType. May be null. 632 */ 633 public Party getOrderShipToParty(final Order order, final boolean billToFallback, final BasePK createdBy) { 634 var orderRoleControl = Session.getModelController(OrderRoleControl.class); 635 var shipToOrderRole = orderRoleControl.getOrderRoleByOrderAndOrderRoleTypeUsingNames(order, OrderRoleTypes.SHIP_TO.name()); 636 637 if(shipToOrderRole == null && billToFallback) { 638 shipToOrderRole = orderRoleControl.getOrderRoleByOrderAndOrderRoleTypeUsingNames(order, OrderRoleTypes.BILL_TO.name()); 639 640 if(shipToOrderRole != null) { 641 orderRoleControl.createOrderRoleUsingNames(order, shipToOrderRole.getParty(), OrderRoleTypes.SHIP_TO.name(), createdBy); 642 } 643 } 644 645 return shipToOrderRole == null ? null : shipToOrderRole.getParty(); 646 } 647 648}