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