001// -------------------------------------------------------------------------------- 002// Copyright 2002-2026 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.invoice.server.logic; 018 019import com.echothree.model.control.accounting.common.AccountingConstants; 020import com.echothree.model.control.accounting.server.logic.GlAccountLogic; 021import com.echothree.model.control.core.server.control.EntityInstanceControl; 022import com.echothree.model.control.invoice.common.InvoiceTypes; 023import com.echothree.model.control.invoice.common.choice.PurchaseInvoiceStatusChoicesBean; 024import com.echothree.model.control.invoice.common.workflow.PurchaseInvoiceStatusConstants; 025import com.echothree.model.control.invoice.server.control.InvoiceControl; 026import com.echothree.model.control.vendor.server.control.VendorControl; 027import com.echothree.model.control.workflow.server.control.WorkflowControl; 028import com.echothree.model.data.accounting.server.entity.Currency; 029import com.echothree.model.data.accounting.server.entity.GlAccount; 030import com.echothree.model.data.contact.server.entity.PartyContactMechanism; 031import com.echothree.model.data.invoice.server.entity.Invoice; 032import com.echothree.model.data.invoice.server.entity.InvoiceLine; 033import com.echothree.model.data.invoice.server.entity.InvoiceLineType; 034import com.echothree.model.data.party.common.pk.PartyPK; 035import com.echothree.model.data.party.server.entity.Language; 036import com.echothree.model.data.party.server.entity.Party; 037import com.echothree.model.data.shipment.server.entity.FreeOnBoard; 038import com.echothree.model.data.term.server.entity.Term; 039import com.echothree.model.data.vendor.server.entity.Vendor; 040import com.echothree.util.common.message.ExecutionErrors; 041import com.echothree.util.common.persistence.BasePK; 042import com.echothree.util.server.message.ExecutionErrorAccumulator; 043import com.echothree.util.server.persistence.Session; 044import javax.enterprise.context.ApplicationScoped; 045import javax.inject.Inject; 046 047@ApplicationScoped 048public class PurchaseInvoiceLogic { 049 050 @Inject 051 EntityInstanceControl entityInstanceControl; 052 053 @Inject 054 InvoiceControl invoiceControl; 055 056 @Inject 057 VendorControl vendorControl; 058 059 @Inject 060 WorkflowControl workflowControl; 061 062 @Inject 063 GlAccountLogic glAccountLogic; 064 065 @Inject 066 InvoiceLogic invoiceLogic; 067 068 protected PurchaseInvoiceLogic() { 069 super(); 070 } 071 072 public Invoice getInvoiceByName(String invoiceName) { 073 return invoiceControl.getInvoiceByNameUsingNames(InvoiceTypes.PURCHASE_INVOICE.name(), invoiceName); 074 } 075 076 protected void validateReference(final ExecutionErrorAccumulator eea, final Party billFrom, final String reference, final Vendor vendor) { 077 if(vendor.getRequireReference() && reference == null) { 078 eea.addExecutionError(ExecutionErrors.PurchaseInvoiceReferenceRequired.name()); 079 } else if(reference != null) { 080 if(!vendor.getAllowReferenceDuplicates() && invoiceControl.countInvoicesByInvoiceFromAndReference(billFrom, reference) != 0) { 081 eea.addExecutionError(ExecutionErrors.PurchaseInvoiceDuplicateReference.name()); 082 } else { 083 var referenceValidationPattern = vendor.getReferenceValidationPattern(); 084 085 if(referenceValidationPattern != null && !reference.matches(referenceValidationPattern)) { 086 eea.addExecutionError(ExecutionErrors.InvalidPurchaseInvoiceReference.name()); 087 } 088 } 089 } 090 } 091 092 protected GlAccount getApGlAccount(final ExecutionErrorAccumulator eea, final Vendor vendor) { 093 var vendorApGlAccount = vendor.getApGlAccount(); 094 var vendorTypeDefaultApGlAccount = vendor.getVendorType().getLastDetail().getDefaultApGlAccount(); 095 var glAccounts = new GlAccount[]{vendorApGlAccount, vendorTypeDefaultApGlAccount}; 096 097 return glAccountLogic.getDefaultGlAccountByCategory(eea, new GlAccount[]{ 098 vendor.getApGlAccount(), 099 vendor.getVendorType().getLastDetail().getDefaultApGlAccount() 100 }, AccountingConstants.GlAccountCategory_ACCOUNTS_PAYABLE, ExecutionErrors.UnknownDefaultApGlAccount.name()); 101 } 102 103 public Invoice createInvoice(final Session session, final ExecutionErrorAccumulator eea, final Party billFrom, 104 final PartyContactMechanism billFromPartyContactMechanism, final Party billTo, final PartyContactMechanism billToPartyContactMechanism, 105 final Currency currency, final Term term, final FreeOnBoard freeOnBoard, final String reference, 106 final String description, final Long invoicedTime, final Long dueTime, 107 final Long paidTime, final String workflowEntranceName, final BasePK createdBy) { 108 Invoice invoice = null; 109 var vendor = vendorControl.getVendor(billFrom); 110 var glAccount = getApGlAccount(eea, vendor); 111 112 validateReference(eea, billFrom, reference, vendor); 113 114 if(eea == null || !eea.hasExecutionErrors()) { 115 invoice = invoiceLogic.createInvoice(session, eea, InvoiceTypes.PURCHASE_INVOICE.name(), billFrom, 116 billFromPartyContactMechanism, billTo, billToPartyContactMechanism, currency, glAccount, term, 117 freeOnBoard, reference, description, invoicedTime, dueTime, paidTime, createdBy); 118 119 if(!eea.hasExecutionErrors() && workflowEntranceName != null) { 120 var entityInstance = entityInstanceControl.getEntityInstanceByBasePK(invoice.getPrimaryKey()); 121 122 workflowControl.addEntityToWorkflowUsingNames(null, PurchaseInvoiceStatusConstants.Workflow_PURCHASE_INVOICE_STATUS, workflowEntranceName, 123 entityInstance, null, null, createdBy); 124 } 125 } 126 127 return invoice; 128 } 129 130 public InvoiceLine createInvoiceLine(final ExecutionErrorAccumulator eea, final Invoice invoice, final Integer invoiceLineSequence, final InvoiceLine parentInvoiceLine, 131 final Long amount, final InvoiceLineType invoiceLineType, final GlAccount glAccount, final String description, final BasePK createdBy) { 132 InvoiceLine invoiceLine = null; 133 var entityInstance = entityInstanceControl.getEntityInstanceByBasePK(invoice.getPrimaryKey()); 134 var workflowEntityStatus = workflowControl.getWorkflowEntityStatusByEntityInstanceUsingNames(PurchaseInvoiceStatusConstants.Workflow_PURCHASE_INVOICE_STATUS, entityInstance); 135 var workflowStepName = workflowEntityStatus.getWorkflowStep().getLastDetail().getWorkflowStepName(); 136 137 if(workflowStepName.equals(PurchaseInvoiceStatusConstants.WorkflowStep_ENTRY)) { 138 invoiceLine = invoiceLogic.createInvoiceLine(eea, invoice, invoiceLineSequence, parentInvoiceLine, amount, invoiceLineType, glAccount, description, createdBy); 139 } else { 140 eea.addExecutionError(ExecutionErrors.InvalidPurchaseInvoiceStatus.name(), invoice.getLastDetail().getInvoiceName(), workflowStepName); 141 } 142 143 return invoiceLine; 144 } 145 146 public PurchaseInvoiceStatusChoicesBean getPurchaseInvoiceStatusChoices(final String defaultInvoiceStatusChoice, final Language language, final boolean allowNullChoice, 147 final Invoice invoice, final PartyPK partyPK) { 148 var purchaseInvoiceStatusChoicesBean = new PurchaseInvoiceStatusChoicesBean(); 149 150 if(invoice == null) { 151 workflowControl.getWorkflowEntranceChoices(purchaseInvoiceStatusChoicesBean, defaultInvoiceStatusChoice, language, allowNullChoice, 152 workflowControl.getWorkflowByName(PurchaseInvoiceStatusConstants.Workflow_PURCHASE_INVOICE_STATUS), partyPK); 153 } else { 154 var entityInstance = entityInstanceControl.getEntityInstanceByBasePK(invoice.getPrimaryKey()); 155 var workflowEntityStatus = workflowControl.getWorkflowEntityStatusByEntityInstanceUsingNames(PurchaseInvoiceStatusConstants.Workflow_PURCHASE_INVOICE_STATUS, entityInstance); 156 157 workflowControl.getWorkflowDestinationChoices(purchaseInvoiceStatusChoicesBean, defaultInvoiceStatusChoice, language, allowNullChoice, workflowEntityStatus.getWorkflowStep(), partyPK); 158 } 159 160 return purchaseInvoiceStatusChoicesBean; 161 } 162 163 public void setPurchaseInvoiceStatus(final ExecutionErrorAccumulator eea, final Invoice invoice, final String invoiceStatusChoice, final PartyPK modifiedBy) { 164 var entityInstance = entityInstanceControl.getEntityInstanceByBasePK(invoice.getPrimaryKey()); 165 var workflowEntityStatus = workflowControl.getWorkflowEntityStatusByEntityInstanceForUpdateUsingNames(PurchaseInvoiceStatusConstants.Workflow_PURCHASE_INVOICE_STATUS, 166 entityInstance); 167 var workflowDestination = invoiceStatusChoice == null? null: workflowControl.getWorkflowDestinationByName(workflowEntityStatus.getWorkflowStep(), invoiceStatusChoice); 168 169 if(workflowDestination != null || invoiceStatusChoice == null) { 170 workflowControl.transitionEntityInWorkflow(eea, workflowEntityStatus, workflowDestination, null, modifiedBy); 171 } else { 172 eea.addExecutionError(ExecutionErrors.UnknownPurchaseInvoiceStatusChoice.name(), invoiceStatusChoice); 173 } 174 } 175 176}