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