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