001    /*
002     * Copyright 2011 The Kuali Foundation.
003     * 
004     * Licensed under the Educational Community 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.opensource.org/licenses/ecl2.php
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    package org.kuali.kfs.module.ar.document.service.impl;
017    
018    import java.util.ArrayList;
019    import java.util.Collection;
020    import java.util.HashMap;
021    import java.util.List;
022    import java.util.Map;
023    
024    import org.apache.commons.lang.StringUtils;
025    import org.kuali.kfs.coa.businessobject.ObjectCode;
026    import org.kuali.kfs.coa.service.ObjectCodeService;
027    import org.kuali.kfs.module.ar.ArConstants;
028    import org.kuali.kfs.module.ar.ArPropertyConstants;
029    import org.kuali.kfs.module.ar.batch.service.CustomerInvoiceWriteoffBatchService;
030    import org.kuali.kfs.module.ar.batch.vo.CustomerInvoiceWriteoffBatchVO;
031    import org.kuali.kfs.module.ar.businessobject.AccountsReceivableDocumentHeader;
032    import org.kuali.kfs.module.ar.businessobject.CustomerInvoiceDetail;
033    import org.kuali.kfs.module.ar.businessobject.CustomerInvoiceWriteoffLookupResult;
034    import org.kuali.kfs.module.ar.businessobject.InvoicePaidApplied;
035    import org.kuali.kfs.module.ar.businessobject.OrganizationAccountingDefault;
036    import org.kuali.kfs.module.ar.businessobject.lookup.CustomerInvoiceWriteoffLookupUtil;
037    import org.kuali.kfs.module.ar.document.CustomerCreditMemoDocument;
038    import org.kuali.kfs.module.ar.document.CustomerInvoiceDocument;
039    import org.kuali.kfs.module.ar.document.CustomerInvoiceWriteoffDocument;
040    import org.kuali.kfs.module.ar.document.service.AccountsReceivableDocumentHeaderService;
041    import org.kuali.kfs.module.ar.document.service.CustomerInvoiceDocumentService;
042    import org.kuali.kfs.module.ar.document.service.CustomerInvoiceWriteoffDocumentService;
043    import org.kuali.kfs.module.ar.document.service.CustomerService;
044    import org.kuali.kfs.module.ar.document.service.InvoicePaidAppliedService;
045    import org.kuali.kfs.sys.KFSConstants;
046    import org.kuali.kfs.sys.businessobject.ChartOrgHolder;
047    import org.kuali.kfs.sys.context.SpringContext;
048    import org.kuali.kfs.sys.service.FinancialSystemUserService;
049    import org.kuali.kfs.sys.service.UniversityDateService;
050    import org.kuali.rice.kew.exception.WorkflowException;
051    import org.kuali.rice.kim.bo.Person;
052    import org.kuali.rice.kns.exception.UnknownDocumentIdException;
053    import org.kuali.rice.kns.service.BusinessObjectService;
054    import org.kuali.rice.kns.service.DateTimeService;
055    import org.kuali.rice.kns.service.DocumentService;
056    import org.kuali.rice.kns.service.ParameterService;
057    import org.kuali.rice.kns.util.GlobalVariables;
058    import org.kuali.rice.kns.util.KualiDecimal;
059    import org.kuali.rice.kns.util.ObjectUtils;
060    import org.kuali.rice.kns.workflow.service.KualiWorkflowDocument;
061    import org.kuali.rice.kns.workflow.service.WorkflowDocumentService;
062    import org.springframework.transaction.annotation.Transactional;
063    
064    @Transactional
065    public class CustomerInvoiceWriteoffDocumentServiceImpl implements CustomerInvoiceWriteoffDocumentService {
066    
067        private ParameterService parameterService;
068        private UniversityDateService universityDateService;
069        private BusinessObjectService businessObjectService;
070        private AccountsReceivableDocumentHeaderService accountsReceivableDocumentHeaderService;
071        private CustomerInvoiceDocumentService customerInvoiceDocumentService;
072        private CustomerService customerService;
073        private DocumentService documentService;
074        private CustomerInvoiceWriteoffBatchService invoiceWriteoffBatchService;
075        private DateTimeService dateTimeService;
076        private InvoicePaidAppliedService<CustomerInvoiceDetail> paidAppliedService;
077    
078        
079        /**
080         * 
081         * @see org.kuali.kfs.module.ar.document.service.CustomerInvoiceWriteoffDocumentService#completeWriteoffProcess(org.kuali.kfs.module.ar.document.CustomerInvoiceWriteoffDocument)
082         */
083        public void completeWriteoffProcess(CustomerInvoiceWriteoffDocument writeoff) {
084            
085            //  retrieve the document and make sure its not already closed, crash if so 
086            String invoiceNumber = writeoff.getFinancialDocumentReferenceInvoiceNumber();
087            CustomerInvoiceDocument invoice;
088            try {
089                 invoice = (CustomerInvoiceDocument) documentService.getByDocumentHeaderId(invoiceNumber);
090            }
091            catch (WorkflowException e) {
092                throw new RuntimeException("A WorkflowException was generated when trying to load Customer Invoice #" + invoiceNumber + ".", e);
093            }
094            if (!invoice.isOpenInvoiceIndicator()) {
095                throw new UnsupportedOperationException("The Invoice Writeoff Document #" + writeoff.getDocumentNumber() + " attempted to writeoff " + 
096                        "an Invoice [#" + invoiceNumber + "] that was already closed.  This is not supported.");
097            }
098            
099            Integer paidAppliedItemNumber = 0;
100            KualiDecimal totalApplied = KualiDecimal.ZERO;
101            
102            //  retrieve the customer invoice details, and generate paid applieds for each 
103            List<CustomerInvoiceDetail> invoiceDetails = invoice.getCustomerInvoiceDetailsWithoutDiscounts();
104            for (CustomerInvoiceDetail invoiceDetail : invoiceDetails) {
105    
106                //   if no open amount on the detail, then do nothing
107                if (invoiceDetail.getAmountOpen().isZero()) {
108                    continue;
109                }
110                
111                //  retrieve the number of current paid applieds, so we dont have item number overlap
112                if (paidAppliedItemNumber == 0) {
113                    paidAppliedItemNumber = paidAppliedService.getNumberOfInvoicePaidAppliedsForInvoiceDetail(invoiceNumber, 
114                            invoiceDetail.getInvoiceItemNumber());
115                }
116                
117                //  create and save the paidApplied
118                InvoicePaidApplied invoicePaidApplied = new InvoicePaidApplied();
119                invoicePaidApplied.setDocumentNumber(writeoff.getDocumentNumber());
120                invoicePaidApplied.setPaidAppliedItemNumber(paidAppliedItemNumber++);
121                invoicePaidApplied.setFinancialDocumentReferenceInvoiceNumber(invoiceNumber);
122                invoicePaidApplied.setInvoiceItemNumber(invoiceDetail.getInvoiceItemNumber());
123                invoicePaidApplied.setUniversityFiscalYear(universityDateService.getCurrentFiscalYear());
124                invoicePaidApplied.setUniversityFiscalPeriodCode(universityDateService.getCurrentUniversityDate().getUniversityFiscalAccountingPeriod());
125                invoicePaidApplied.setInvoiceItemAppliedAmount(invoiceDetail.getAmountOpen());
126                businessObjectService.save(invoicePaidApplied);
127    
128                //  record how much we're applying
129                totalApplied = totalApplied.add(invoicePaidApplied.getInvoiceItemAppliedAmount());
130            }
131            
132            //  close the document
133            invoice.setOpenInvoiceIndicator(false);
134            invoice.setClosedDate(dateTimeService.getCurrentSqlDate());
135            documentService.updateDocument(invoice);
136            
137            //  set the final document total for the invoice
138            writeoff.setInvoiceWriteoffAmount(totalApplied);
139            writeoff.getDocumentHeader().setFinancialDocumentTotalAmount(totalApplied);
140            documentService.updateDocument(writeoff);
141        }
142        
143        /**
144         * @see org.kuali.kfs.module.ar.document.service.CustomerInvoiceWriteoffDocumentService#setupDefaultValuesForNewCustomerInvoiceWriteoffDocument(org.kuali.kfs.module.ar.document.CustomerInvoiceWriteoffDocument)
145         */
146        public void setupDefaultValuesForNewCustomerInvoiceWriteoffDocument(CustomerInvoiceWriteoffDocument customerInvoiceWriteoffDocument) {
147    
148            // update status
149            customerInvoiceWriteoffDocument.setStatusCode(ArConstants.CustomerInvoiceWriteoffStatuses.IN_PROCESS);
150    
151            // set accounts receivable document header
152            AccountsReceivableDocumentHeader accountsReceivableDocumentHeader = accountsReceivableDocumentHeaderService.getNewAccountsReceivableDocumentHeaderForCurrentUser();
153            accountsReceivableDocumentHeader.setDocumentNumber(customerInvoiceWriteoffDocument.getDocumentNumber());
154            accountsReceivableDocumentHeader.setCustomerNumber(customerInvoiceWriteoffDocument.getCustomerInvoiceDocument().getAccountsReceivableDocumentHeader().getCustomerNumber());
155            customerInvoiceWriteoffDocument.setAccountsReceivableDocumentHeader(accountsReceivableDocumentHeader);
156    
157            // if writeoffs are generated based on organization accounting default, populate those fields now
158            String writeoffGenerationOption = parameterService.getParameterValue(CustomerInvoiceWriteoffDocument.class, ArConstants.GLPE_WRITEOFF_GENERATION_METHOD);
159            boolean isUsingOrgAcctDefaultWriteoffFAU = ArConstants.GLPE_WRITEOFF_GENERATION_METHOD_ORG_ACCT_DEFAULT.equals(writeoffGenerationOption);
160    
161            String writeoffTaxGenerationOption = SpringContext.getBean(ParameterService.class).getParameterValue(CustomerInvoiceWriteoffDocument.class, ArConstants.ALLOW_SALES_TAX_LIABILITY_ADJUSTMENT_IND);
162            boolean isUsingTaxGenerationMethodDisallow = ArConstants.ALLOW_SALES_TAX_LIABILITY_ADJUSTMENT_IND_NO.equals( writeoffTaxGenerationOption );
163            if (isUsingOrgAcctDefaultWriteoffFAU || isUsingTaxGenerationMethodDisallow) {
164    
165                Integer currentUniversityFiscalYear = universityDateService.getCurrentFiscalYear();
166                ChartOrgHolder currentUser = SpringContext.getBean(FinancialSystemUserService.class).getPrimaryOrganization(GlobalVariables.getUserSession().getPerson(), ArConstants.AR_NAMESPACE_CODE);
167    
168                Map<String, Object> criteria = new HashMap<String, Object>();
169                criteria.put("universityFiscalYear", currentUniversityFiscalYear);
170                criteria.put("chartOfAccountsCode", customerInvoiceWriteoffDocument.getCustomerInvoiceDocument().getBillByChartOfAccountCode());
171                criteria.put("organizationCode", customerInvoiceWriteoffDocument.getCustomerInvoiceDocument().getBilledByOrganizationCode());
172    
173                OrganizationAccountingDefault organizationAccountingDefault = (OrganizationAccountingDefault) businessObjectService.findByPrimaryKey(OrganizationAccountingDefault.class, criteria);
174                if (ObjectUtils.isNotNull(organizationAccountingDefault)) {
175                    customerInvoiceWriteoffDocument.setChartOfAccountsCode(organizationAccountingDefault.getWriteoffChartOfAccountsCode());
176                    customerInvoiceWriteoffDocument.setAccountNumber(organizationAccountingDefault.getWriteoffAccountNumber());
177                    customerInvoiceWriteoffDocument.setSubAccountNumber(organizationAccountingDefault.getWriteoffSubAccountNumber());
178                    customerInvoiceWriteoffDocument.setFinancialObjectCode(organizationAccountingDefault.getWriteoffFinancialObjectCode());
179                    customerInvoiceWriteoffDocument.setFinancialSubObjectCode(organizationAccountingDefault.getWriteoffFinancialSubObjectCode());
180                    customerInvoiceWriteoffDocument.setProjectCode(organizationAccountingDefault.getWriteoffProjectCode());
181                    customerInvoiceWriteoffDocument.setOrganizationReferenceIdentifier(organizationAccountingDefault.getWriteoffOrganizationReferenceIdentifier());
182                }
183            }
184        }
185        
186    
187        public boolean isCustomerInvoiceWriteoffDocumentApproved(String customerInvoiceWriteoffDocumentNumber) {
188            Map criteria = new HashMap();
189            criteria.put("documentNumber", customerInvoiceWriteoffDocumentNumber);
190            criteria.put("documentHeader.financialDocumentStatusCode", KFSConstants.DocumentStatusCodes.APPROVED);
191            return businessObjectService.countMatching(CustomerInvoiceWriteoffDocument.class, criteria) == 1;
192        }    
193    /*
194        public Collection<CustomerInvoiceWriteoffLookupResult> getCustomerInvoiceDocumentsForInvoiceWriteoffLookup() {
195            // change this service call to a service method that actually takes in the lookup parameters
196            Collection<CustomerInvoiceDocument> customerInvoiceDocuments = customerInvoiceDocumentService.getAllCustomerInvoiceDocumentsWithoutWorkflowInfo();
197            
198            Collection<CustomerInvoiceDocument> customerInvoiceDocumentsWithOpenBalance = new ArrayList<CustomerInvoiceDocument>();
199            for (Iterator itr = customerInvoiceDocuments.iterator(); itr.hasNext();) {
200                CustomerInvoiceDocument invoice = (CustomerInvoiceDocument) itr.next();
201                if (invoice.getOpenAmount().isGreaterThan(KualiDecimal.ZERO))
202                    customerInvoiceDocumentsWithOpenBalance.add(invoice);
203            }
204    
205            return CustomerInvoiceWriteoffLookupUtil.getPopulatedCustomerInvoiceWriteoffLookupResults(customerInvoiceDocumentsWithOpenBalance);
206        }
207        */
208        public Collection<CustomerInvoiceWriteoffLookupResult> getCustomerInvoiceDocumentsForInvoiceWriteoffLookup(Map<String, String> fieldValues) {
209            
210            //  only one of these four will be used, based on priority
211            String customerNumber = fieldValues.get(ArPropertyConstants.CustomerInvoiceWriteoffLookupResultFields.CUSTOMER_NUMBER);
212            String customerName = fieldValues.get(ArPropertyConstants.CustomerInvoiceWriteoffLookupResultFields.CUSTOMER_NAME);
213            String customerTypeCode = fieldValues.get(ArPropertyConstants.CustomerInvoiceWriteoffLookupResultFields.CUSTOMER_TYPE_CODE);
214            String customerInvoiceNumber = fieldValues.get(ArPropertyConstants.CustomerInvoiceWriteoffLookupResultFields.CUSTOMER_INVOICE_NUMBER);
215            
216            //  this may be combined with any of the four above
217            String age = fieldValues.get(ArPropertyConstants.CustomerInvoiceWriteoffLookupResultFields.AGE);
218    
219            //  this is the priority order for searching if multiples are entered
220            Collection<CustomerInvoiceDocument> customerInvoiceDocuments;
221            if (StringUtils.isNotEmpty(customerInvoiceNumber)) {
222                customerInvoiceDocuments = new ArrayList<CustomerInvoiceDocument>();
223                CustomerInvoiceDocument customerInvoiceDocument = customerInvoiceDocumentService.getInvoiceByInvoiceDocumentNumber(customerInvoiceNumber);
224                if (customerInvoiceDocument.isOpenInvoiceIndicator())
225                    customerInvoiceDocuments.add(customerInvoiceDocument);
226                else
227                    customerInvoiceDocuments = new ArrayList<CustomerInvoiceDocument>();
228            }
229            else if (StringUtils.isNotEmpty(customerNumber)) {
230                customerInvoiceDocuments = customerInvoiceDocumentService.getOpenInvoiceDocumentsByCustomerNumber(customerNumber);
231            }
232            else if (StringUtils.isNotEmpty(customerName) && StringUtils.isNotEmpty(customerTypeCode)) {
233                customerInvoiceDocuments = customerInvoiceDocumentService.getOpenInvoiceDocumentsByCustomerNameByCustomerType(customerName, customerTypeCode);
234            }
235            else if (StringUtils.isNotEmpty(customerName)) {
236                customerInvoiceDocuments = customerInvoiceDocumentService.getOpenInvoiceDocumentsByCustomerName(customerName);
237            }
238            else if (StringUtils.isNotEmpty(customerTypeCode)) {
239                customerInvoiceDocuments = customerInvoiceDocumentService.getOpenInvoiceDocumentsByCustomerType(customerTypeCode);
240            }
241            else {
242                 customerInvoiceDocuments = customerInvoiceDocumentService.getAllOpenCustomerInvoiceDocumentsWithoutWorkflow();
243            }
244            
245            // attach headers
246            customerInvoiceDocuments = customerInvoiceDocumentService.attachWorkflowHeadersToTheInvoices(customerInvoiceDocuments);
247            // filter invoices which have related CRMs and writeoffs in route.
248            Collection<CustomerInvoiceDocument> filteredCustomerInvoiceDocuments = filterInvoices(customerInvoiceDocuments);
249            
250            //  if no age value was specified, then we're done!
251            if (StringUtils.isEmpty(age)) {
252                return CustomerInvoiceWriteoffLookupUtil.getPopulatedCustomerInvoiceWriteoffLookupResults(filteredCustomerInvoiceDocuments);
253            }
254            
255            // walk through what we have, and do any extra filtering based on age, if necessary
256            boolean eligibleInvoiceFlag;
257            Collection<CustomerInvoiceDocument> eligibleInvoices = new ArrayList<CustomerInvoiceDocument>();
258            for (CustomerInvoiceDocument invoice : filteredCustomerInvoiceDocuments) {
259                eligibleInvoiceFlag = true;
260                
261                if (ObjectUtils.isNotNull(invoice.getAge())) {
262                    eligibleInvoiceFlag &=((new Integer(age)).compareTo(invoice.getAge()) <= 0);
263                } else {
264                    eligibleInvoiceFlag = false;
265                }
266            
267                if (eligibleInvoiceFlag) {
268                    eligibleInvoices.add(invoice);
269                }
270            }
271    
272            return CustomerInvoiceWriteoffLookupUtil.getPopulatedCustomerInvoiceWriteoffLookupResults(eligibleInvoices);
273        }
274        
275        /**
276         * This method returns invoices which are in FINAL status and have no related CRMs and writeoffs in route
277         * 
278         * @param customerInvoiceDocuments
279         * @return filteredInvoices
280         */    
281        public Collection<CustomerInvoiceDocument> filterInvoices(Collection<CustomerInvoiceDocument> customerInvoiceDocuments) {
282            Collection<CustomerInvoiceDocument> filteredInvoices = new ArrayList<CustomerInvoiceDocument>();
283            boolean hasNoDocumentsInRouteFlag;
284            
285            for (CustomerInvoiceDocument invoice : customerInvoiceDocuments) {
286                if (invoice.getDocumentHeader().getWorkflowDocument().stateIsFinal()) {
287                    hasNoDocumentsInRouteFlag = checkIfThereIsNoAnotherCRMInRouteForTheInvoice(invoice.getDocumentNumber());
288                    if (hasNoDocumentsInRouteFlag)
289                        hasNoDocumentsInRouteFlag = checkIfThereIsNoAnotherWriteoffInRouteForTheInvoice(invoice.getDocumentNumber());
290                    if (hasNoDocumentsInRouteFlag)
291                        filteredInvoices.add(invoice);
292                }
293            }
294            return filteredInvoices;
295        }
296        
297        /**
298         * This method checks if there is no another CRM in route for the invoice
299         * Not in route if CRM status is one of the following: processed, cancelled, or disapproved
300         * 
301         * @param invoice
302         * @return
303         */
304        public boolean checkIfThereIsNoAnotherCRMInRouteForTheInvoice(String invoiceDocumentNumber) {
305    
306            KualiWorkflowDocument workflowDocument;
307            boolean success = true;
308    
309            Map<String, String> fieldValues = new HashMap<String, String>();
310            fieldValues.put("financialDocumentReferenceInvoiceNumber", invoiceDocumentNumber);
311    
312            BusinessObjectService businessObjectService = SpringContext.getBean(BusinessObjectService.class);
313            Collection<CustomerCreditMemoDocument> customerCreditMemoDocuments = businessObjectService.findMatching(CustomerCreditMemoDocument.class, fieldValues);
314    
315            // no CRMs associated with the invoice are found
316            if (customerCreditMemoDocuments.isEmpty())
317                return success;
318    
319            Person user = GlobalVariables.getUserSession().getPerson();
320    
321            for (CustomerCreditMemoDocument customerCreditMemoDocument : customerCreditMemoDocuments) {
322                try {
323                    workflowDocument = SpringContext.getBean(WorkflowDocumentService.class).createWorkflowDocument(Long.valueOf(customerCreditMemoDocument.getDocumentNumber()), user);
324                }
325                catch (WorkflowException e) {
326                    throw new UnknownDocumentIdException("no document found for documentHeaderId '" + customerCreditMemoDocument.getDocumentNumber() + "'", e);
327                }
328    
329                if (!(workflowDocument.stateIsApproved() || workflowDocument.stateIsCanceled() || workflowDocument.stateIsDisapproved())) {
330                    success = false;
331                    break;
332                }
333            }
334            return success;
335        }
336    
337        /**
338         * This method checks if there is no another writeoff in route for the invoice
339         * Not in route if writeoff status is one of the following: processed, cancelled, or disapproved
340         * 
341         * @param invoice
342         * @return
343         */
344        public boolean checkIfThereIsNoAnotherWriteoffInRouteForTheInvoice(String invoiceDocumentNumber) {
345    
346            KualiWorkflowDocument workflowDocument;
347            boolean success = true;
348    
349            Map<String, String> fieldValues = new HashMap<String, String>();
350            fieldValues.put("financialDocumentReferenceInvoiceNumber", invoiceDocumentNumber);
351    
352            BusinessObjectService businessObjectService = SpringContext.getBean(BusinessObjectService.class);
353            Collection<CustomerInvoiceWriteoffDocument> customerInvoiceWriteoffDocuments = businessObjectService.findMatching(CustomerInvoiceWriteoffDocument.class, fieldValues);
354    
355    
356            // no writeoffs associated with the invoice are found
357            if (customerInvoiceWriteoffDocuments.isEmpty())
358                return success;
359    
360            Person user = GlobalVariables.getUserSession().getPerson();
361    
362            for (CustomerInvoiceWriteoffDocument customerInvoiceWriteoffDocument : customerInvoiceWriteoffDocuments) {
363                try {
364                    workflowDocument = SpringContext.getBean(WorkflowDocumentService.class).createWorkflowDocument(Long.valueOf(customerInvoiceWriteoffDocument.getDocumentNumber()), user);
365                }
366                catch (WorkflowException e) {
367                    throw new UnknownDocumentIdException("no document found for documentHeaderId '" + customerInvoiceWriteoffDocument.getDocumentNumber() + "'", e);
368                }
369    
370                if (!(workflowDocument.stateIsApproved() || workflowDocument.stateIsCanceled() || workflowDocument.stateIsDisapproved())) {
371                    success = false;
372                    break;
373                }
374            }
375            return success;
376        }
377    
378        
379        /**
380         * 
381         * @see org.kuali.kfs.module.ar.document.service.CustomerInvoiceWriteoffDocumentService#sendCustomerInvoiceWriteoffDocumentsToBatch(org.kuali.rice.kim.bo.Person, java.util.Collection)
382         */
383        public String sendCustomerInvoiceWriteoffDocumentsToBatch(Person person, Collection<CustomerInvoiceWriteoffLookupResult> customerInvoiceWriteoffLookupResults) {
384            
385            CustomerInvoiceWriteoffBatchVO batch = new CustomerInvoiceWriteoffBatchVO(person.getPrincipalName());
386            
387            //  add the date
388            batch.setSubmittedOn(dateTimeService.getCurrentTimestamp().toString());
389            
390            //  add the customer note, if one was added
391            String note = null;
392            for( CustomerInvoiceWriteoffLookupResult customerInvoiceWriteoffLookupResult : customerInvoiceWriteoffLookupResults ){
393                note = customerInvoiceWriteoffLookupResult.getCustomerNote();
394            }
395            if (StringUtils.isNotBlank(note)) {
396                batch.setNote(note);
397            }
398            
399            //  add the document numbers
400            for( CustomerInvoiceWriteoffLookupResult customerInvoiceWriteoffLookupResult : customerInvoiceWriteoffLookupResults ){
401                for( CustomerInvoiceDocument customerInvoiceDocument : customerInvoiceWriteoffLookupResult.getCustomerInvoiceDocuments() ){
402                    batch.addInvoiceNumber(customerInvoiceDocument.getDocumentNumber());
403                }
404            }
405            
406            // use the batch service to create the XML and drop it in the directory
407            return invoiceWriteoffBatchService.createBatchDrop(person, batch);
408        }
409        
410        public String createCustomerInvoiceWriteoffDocument(Person initiator, String invoiceNumber, String note) throws WorkflowException {
411    
412            //  force the initiating user into the header
413            GlobalVariables.getUserSession().setBackdoorUser(initiator.getPrincipalName());
414            
415            //  create the new writeoff document
416            CustomerInvoiceWriteoffDocument document = (CustomerInvoiceWriteoffDocument) documentService.getNewDocument(CustomerInvoiceWriteoffDocument.class);
417            
418            //  setup the defaults and tie it to the Invoice document
419            document.setFinancialDocumentReferenceInvoiceNumber(invoiceNumber);
420            setupDefaultValuesForNewCustomerInvoiceWriteoffDocument( document );
421            document.getDocumentHeader().setDocumentDescription(ArConstants.CUSTOMER_INVOICE_WRITEOFF_DOCUMENT_DESCRIPTION + " " + invoiceNumber + ".");
422            
423            document.setCustomerNote(note);
424            
425            //  satisfy silly > 10 chars explanation rule
426            if (StringUtils.isBlank(note)) {
427                note = "Document created by batch process.";
428            }
429            else if (note.length() <= 10) {
430                note = "Document created by batch process.  " + note;
431            }
432            document.getDocumentHeader().setExplanation(note);
433            
434            //  route the document
435            documentService.routeDocument(document, "Routed by Customer Invoice Writeoff Document Batch Service", null);
436    
437            //  clear the user overrid
438            GlobalVariables.getUserSession().clearBackdoorUser();
439            
440            return document.getDocumentNumber();
441        }
442        
443        public String getFinancialObjectCode(CustomerInvoiceDetail postable, CustomerInvoiceWriteoffDocument poster, boolean isUsingOrgAcctDefaultWriteoffFAU, boolean isUsingChartForWriteoff, String chartOfAccountsCode) {
444    
445            if ( isUsingOrgAcctDefaultWriteoffFAU ){
446                return poster.getFinancialObjectCode();
447            } else if ( isUsingChartForWriteoff ) {
448                return SpringContext.getBean(ParameterService.class).getParameterValue(CustomerInvoiceWriteoffDocument.class, ArConstants.GLPE_WRITEOFF_OBJECT_CODE_BY_CHART, chartOfAccountsCode);
449            } else {
450                return postable.getAccountsReceivableObjectCode();
451            }
452        }
453    
454        public ObjectCode getObjectCode(CustomerInvoiceDetail postable, CustomerInvoiceWriteoffDocument poster, boolean isUsingOrgAcctDefaultWriteoffFAU, boolean isUsingChartForWriteoff, String chartOfAccountsCode) {
455            
456            return SpringContext.getBean(ObjectCodeService.class).getByPrimaryIdForCurrentYear(chartOfAccountsCode, this.getFinancialObjectCode(postable, poster, isUsingOrgAcctDefaultWriteoffFAU,isUsingChartForWriteoff, chartOfAccountsCode));
457        }
458        
459        public ParameterService getParameterService() {
460            return parameterService;
461        }
462    
463        public void setParameterService(ParameterService parameterService) {
464            this.parameterService = parameterService;
465        }
466    
467        public UniversityDateService getUniversityDateService() {
468            return universityDateService;
469        }
470    
471        public void setUniversityDateService(UniversityDateService universityDateService) {
472            this.universityDateService = universityDateService;
473        }
474    
475        public BusinessObjectService getBusinessObjectService() {
476            return businessObjectService;
477        }
478    
479        public void setBusinessObjectService(BusinessObjectService businessObjectService) {
480            this.businessObjectService = businessObjectService;
481        }
482    
483        public CustomerInvoiceDocumentService getCustomerInvoiceDocumentService() {
484            return customerInvoiceDocumentService;
485        }
486    
487        public void setCustomerInvoiceDocumentService(CustomerInvoiceDocumentService customerInvoiceDocumentService) {
488            this.customerInvoiceDocumentService = customerInvoiceDocumentService;
489        }
490    
491        public AccountsReceivableDocumentHeaderService getAccountsReceivableDocumentHeaderService() {
492            return accountsReceivableDocumentHeaderService;
493        }
494    
495        public void setAccountsReceivableDocumentHeaderService(AccountsReceivableDocumentHeaderService accountsReceivableDocumentHeaderService) {
496            this.accountsReceivableDocumentHeaderService = accountsReceivableDocumentHeaderService;
497        }
498    
499    
500        public CustomerService getCustomerService() {
501            return customerService;
502        }
503    
504    
505        public void setCustomerService(CustomerService customerService) {
506            this.customerService = customerService;
507        }
508    
509    
510        public DocumentService getDocumentService() {
511            return documentService;
512        }
513    
514    
515        public void setDocumentService(DocumentService documentService) {
516            this.documentService = documentService;
517        }
518    
519    
520        public void setInvoiceWriteoffBatchService(CustomerInvoiceWriteoffBatchService invoiceWriteoffBatchService) {
521            this.invoiceWriteoffBatchService = invoiceWriteoffBatchService;
522        }
523    
524    
525        public void setDateTimeService(DateTimeService dateTimeService) {
526            this.dateTimeService = dateTimeService;
527        }
528    
529        public void setPaidAppliedService(InvoicePaidAppliedService<CustomerInvoiceDetail> paidAppliedService) {
530            this.paidAppliedService = paidAppliedService;
531        }
532    
533    }
534