bring in public functions
authorSteve Hackbarth <stephenhackbarth@gmail.com>
Tue, 8 Apr 2014 20:10:36 +0000 (16:10 -0400)
committerSteve Hackbarth <stephenhackbarth@gmail.com>
Tue, 8 Apr 2014 20:10:36 +0000 (16:10 -0400)
855 files changed:
foundation-database/manifest.js
foundation-database/public/functions/acknowledgemessage.sql [new file with mode: 0644]
foundation-database/public/functions/actcost.sql [new file with mode: 0644]
foundation-database/public/functions/addrusecount.sql [new file with mode: 0644]
foundation-database/public/functions/addtaxtoglseries.sql [new file with mode: 0644]
foundation-database/public/functions/addtopackinglistbatch.sql [new file with mode: 0644]
foundation-database/public/functions/adjustinvvalue.sql [new file with mode: 0644]
foundation-database/public/functions/adjustments.sql [new file with mode: 0644]
foundation-database/public/functions/allocatedforso.sql [new file with mode: 0644]
foundation-database/public/functions/allocatedforwo.sql [new file with mode: 0644]
foundation-database/public/functions/alterencrypt.sql [new file with mode: 0644]
foundation-database/public/functions/apaging.sql [new file with mode: 0644]
foundation-database/public/functions/apapplied.sql [new file with mode: 0644]
foundation-database/public/functions/apcheckpending.sql [new file with mode: 0644]
foundation-database/public/functions/apcurrgain.sql [new file with mode: 0644]
foundation-database/public/functions/applyapcreditmemotobalance.sql [new file with mode: 0644]
foundation-database/public/functions/applyapcredits.sql [new file with mode: 0644]
foundation-database/public/functions/applyarcreditmemotobalance.sql [new file with mode: 0644]
foundation-database/public/functions/applycashreceiptlinebalance.sql [new file with mode: 0644]
foundation-database/public/functions/applycashreceipttobalance.sql [new file with mode: 0644]
foundation-database/public/functions/araging.sql [new file with mode: 0644]
foundation-database/public/functions/arapplied.sql [new file with mode: 0644]
foundation-database/public/functions/archivesaleshistory.sql [new file with mode: 0644]
foundation-database/public/functions/arcurrgain.sql [new file with mode: 0644]
foundation-database/public/functions/asofinvbal.sql [new file with mode: 0644]
foundation-database/public/functions/asofinvnn.sql [new file with mode: 0644]
foundation-database/public/functions/asofinvqty.sql [new file with mode: 0644]
foundation-database/public/functions/attachcontact.sql [new file with mode: 0644]
foundation-database/public/functions/attachquotetoopportunity.sql [new file with mode: 0644]
foundation-database/public/functions/attachsalesordertoopportunity.sql [new file with mode: 0644]
foundation-database/public/functions/averagesalesprice.sql [new file with mode: 0644]
foundation-database/public/functions/avgcost.sql [new file with mode: 0644]
foundation-database/public/functions/balanceitemsite.sql [new file with mode: 0644]
foundation-database/public/functions/basecurrid.sql [new file with mode: 0644]
foundation-database/public/functions/bomcontains.sql [new file with mode: 0644]
foundation-database/public/functions/bomhistsequence.sql [new file with mode: 0644]
foundation-database/public/functions/bomitem.sql [new file with mode: 0644]
foundation-database/public/functions/bomlevelbyitem.sql [new file with mode: 0644]
foundation-database/public/functions/bomworkeffective.sql [new file with mode: 0644]
foundation-database/public/functions/bomworkexpired.sql [new file with mode: 0644]
foundation-database/public/functions/bomworkitemsequence.sql [new file with mode: 0644]
foundation-database/public/functions/bomworksequence.sql [new file with mode: 0644]
foundation-database/public/functions/buildinvbal.sql [new file with mode: 0644]
foundation-database/public/functions/buildsearchpath.sql [new file with mode: 0644]
foundation-database/public/functions/calcbillingamts.sql [new file with mode: 0644]
foundation-database/public/functions/calccashbudget.sql [new file with mode: 0644]
foundation-database/public/functions/calccreditmemoamts.sql [new file with mode: 0644]
foundation-database/public/functions/calcpendingarapplications.sql [new file with mode: 0644]
foundation-database/public/functions/calcquoteamt.sql [new file with mode: 0644]
foundation-database/public/functions/calcsalesorderamt.sql [new file with mode: 0644]
foundation-database/public/functions/calcshipfreight.sql [new file with mode: 0644]
foundation-database/public/functions/calctotalslipqty.sql [new file with mode: 0644]
foundation-database/public/functions/calculatefreightdetail.sql [new file with mode: 0644]
foundation-database/public/functions/calculatesubtax.sql [new file with mode: 0644]
foundation-database/public/functions/calculatetax.sql [new file with mode: 0644]
foundation-database/public/functions/calculatetaxdetail.sql [new file with mode: 0644]
foundation-database/public/functions/calculatetaxdetailline.sql [new file with mode: 0644]
foundation-database/public/functions/calculatetaxdetailsummary.sql [new file with mode: 0644]
foundation-database/public/functions/calculatetaxhist.sql [new file with mode: 0644]
foundation-database/public/functions/calcvoucheramts.sql [new file with mode: 0644]
foundation-database/public/functions/calcwooperstartstub.sql [new file with mode: 0644]
foundation-database/public/functions/cancelbillingselection.sql [new file with mode: 0644]
foundation-database/public/functions/changeaccountingperioddates.sql [new file with mode: 0644]
foundation-database/public/functions/changeaccountingyearperioddates.sql [new file with mode: 0644]
foundation-database/public/functions/changefkeypointers.sql [new file with mode: 0644]
foundation-database/public/functions/changepoitemduedate.sql [new file with mode: 0644]
foundation-database/public/functions/changepoitemqty.sql [new file with mode: 0644]
foundation-database/public/functions/changeprdate.sql [new file with mode: 0644]
foundation-database/public/functions/changeprqty.sql [new file with mode: 0644]
foundation-database/public/functions/changepseudofkeypointers.sql [new file with mode: 0644]
foundation-database/public/functions/changewodates.sql [new file with mode: 0644]
foundation-database/public/functions/changewoproject.sql [new file with mode: 0644]
foundation-database/public/functions/changewoqty.sql [new file with mode: 0644]
foundation-database/public/functions/characteristicstostring.sql [new file with mode: 0644]
foundation-database/public/functions/checkcreditmemositeprivs.sql [new file with mode: 0644]
foundation-database/public/functions/checkdetailformatted.sql [new file with mode: 0644]
foundation-database/public/functions/checkinvoicesiteprivs.sql [new file with mode: 0644]
foundation-database/public/functions/checkpositeprivs.sql [new file with mode: 0644]
foundation-database/public/functions/checkprivilege.sql [new file with mode: 0644]
foundation-database/public/functions/checkquotesiteprivs.sql [new file with mode: 0644]
foundation-database/public/functions/checkrasiteprivs.sql [new file with mode: 0644]
foundation-database/public/functions/checkshipmentsiteprivs.sql [new file with mode: 0644]
foundation-database/public/functions/checksositeprivs.sql [new file with mode: 0644]
foundation-database/public/functions/checkvouchersiteprivs.sql [new file with mode: 0644]
foundation-database/public/functions/clearnumberissue.sql [new file with mode: 0644]
foundation-database/public/functions/clearpayment.sql [new file with mode: 0644]
foundation-database/public/functions/closeaccountingperiod.sql [new file with mode: 0644]
foundation-database/public/functions/closeaccountingyearperiod.sql [new file with mode: 0644]
foundation-database/public/functions/closepo.sql [new file with mode: 0644]
foundation-database/public/functions/closewo.sql [new file with mode: 0644]
foundation-database/public/functions/cntct.sql [new file with mode: 0644]
foundation-database/public/functions/cntctdups.sql [new file with mode: 0644]
foundation-database/public/functions/cntctmerge.sql [new file with mode: 0644]
foundation-database/public/functions/cntctrestore.sql [new file with mode: 0644]
foundation-database/public/functions/cntctselect.sql [new file with mode: 0644]
foundation-database/public/functions/cntctselectcol.sql [new file with mode: 0644]
foundation-database/public/functions/cntctused.sql [new file with mode: 0644]
foundation-database/public/functions/coheadstatecolor.sql [new file with mode: 0644]
foundation-database/public/functions/concatagg.sql [new file with mode: 0644]
foundation-database/public/functions/consolidatelocations.sql [new file with mode: 0644]
foundation-database/public/functions/convertcustomertoprospect.sql [new file with mode: 0644]
foundation-database/public/functions/convertprospecttocustomer.sql [new file with mode: 0644]
foundation-database/public/functions/convertquote.sql [new file with mode: 0644]
foundation-database/public/functions/convertquotetoinvoice.sql [new file with mode: 0644]
foundation-database/public/functions/copybom.sql [new file with mode: 0644]
foundation-database/public/functions/copybudget.sql [new file with mode: 0644]
foundation-database/public/functions/copycmd.sql [new file with mode: 0644]
foundation-database/public/functions/copycontract.sql [new file with mode: 0644]
foundation-database/public/functions/copyfinancialgroup.sql [new file with mode: 0644]
foundation-database/public/functions/copyglseries.sql [new file with mode: 0644]
foundation-database/public/functions/copyincdt.sql [new file with mode: 0644]
foundation-database/public/functions/copyinvoice.sql [new file with mode: 0644]
foundation-database/public/functions/copyitem.sql [new file with mode: 0644]
foundation-database/public/functions/copyitemsite.sql [new file with mode: 0644]
foundation-database/public/functions/copylocale.sql [new file with mode: 0644]
foundation-database/public/functions/copypo.sql [new file with mode: 0644]
foundation-database/public/functions/copypricingschedule.sql [new file with mode: 0644]
foundation-database/public/functions/copyprj.sql [new file with mode: 0644]
foundation-database/public/functions/copyproject.sql [new file with mode: 0644]
foundation-database/public/functions/copyquote.sql [new file with mode: 0644]
foundation-database/public/functions/copyso.sql [new file with mode: 0644]
foundation-database/public/functions/copytodoitem.sql [new file with mode: 0644]
foundation-database/public/functions/copyvoucher.sql [new file with mode: 0644]
foundation-database/public/functions/correctporeceipt.sql [new file with mode: 0644]
foundation-database/public/functions/correctproduction.sql [new file with mode: 0644]
foundation-database/public/functions/correctreceipt.sql [new file with mode: 0644]
foundation-database/public/functions/cosbycustomervalue.sql [new file with mode: 0644]
foundation-database/public/functions/costsbycustomerbyitemsite.sql [new file with mode: 0644]
foundation-database/public/functions/costsbycustomervalue.sql [new file with mode: 0644]
foundation-database/public/functions/createaccountingperiod.sql [new file with mode: 0644]
foundation-database/public/functions/createaccountingyearperiod.sql [new file with mode: 0644]
foundation-database/public/functions/createapchecks.sql [new file with mode: 0644]
foundation-database/public/functions/createapcreditmemo.sql [new file with mode: 0644]
foundation-database/public/functions/createapcreditmemoapplication.sql [new file with mode: 0644]
foundation-database/public/functions/createapdebitmemo.sql [new file with mode: 0644]
foundation-database/public/functions/createapdiscount.sql [new file with mode: 0644]
foundation-database/public/functions/createarcashdeposit.sql [new file with mode: 0644]
foundation-database/public/functions/createarcreditmemo.sql [new file with mode: 0644]
foundation-database/public/functions/createardebitmemo.sql [new file with mode: 0644]
foundation-database/public/functions/createbillingheader.sql [new file with mode: 0644]
foundation-database/public/functions/createbomitem.sql [new file with mode: 0644]
foundation-database/public/functions/createcheck.sql [new file with mode: 0644]
foundation-database/public/functions/createchecks.sql [new file with mode: 0644]
foundation-database/public/functions/createcounttag.sql [new file with mode: 0644]
foundation-database/public/functions/createcyclecountsbywarehouse.sql [new file with mode: 0644]
foundation-database/public/functions/createcyclecountsbywarehousebyclasscode.sql [new file with mode: 0644]
foundation-database/public/functions/createcyclecountsbywarehousebyplannercode.sql [new file with mode: 0644]
foundation-database/public/functions/createfile.sql [new file with mode: 0644]
foundation-database/public/functions/createinvoice.sql [new file with mode: 0644]
foundation-database/public/functions/createinvoiceconsolidated.sql [new file with mode: 0644]
foundation-database/public/functions/createinvoices.sql [new file with mode: 0644]
foundation-database/public/functions/createmiscapcheck.sql [new file with mode: 0644]
foundation-database/public/functions/createpkgschema.sql [new file with mode: 0644]
foundation-database/public/functions/createpr.sql [new file with mode: 0644]
foundation-database/public/functions/createpriv.sql [new file with mode: 0644]
foundation-database/public/functions/createpurchasetosale.sql [new file with mode: 0644]
foundation-database/public/functions/createrecurringinvoices.sql [new file with mode: 0644]
foundation-database/public/functions/createrecurringitems.sql [new file with mode: 0644]
foundation-database/public/functions/createtodoitem.sql [new file with mode: 0644]
foundation-database/public/functions/createurl.sql [new file with mode: 0644]
foundation-database/public/functions/createuser.sql [new file with mode: 0644]
foundation-database/public/functions/createwo.sql [new file with mode: 0644]
foundation-database/public/functions/createwomaterial.sql [new file with mode: 0644]
foundation-database/public/functions/creditmemototal.sql [new file with mode: 0644]
foundation-database/public/functions/crmacct.sql [new file with mode: 0644]
foundation-database/public/functions/currconcat.sql [new file with mode: 0644]
foundation-database/public/functions/currentapmemonumber.sql [new file with mode: 0644]
foundation-database/public/functions/currentarmemonumber.sql [new file with mode: 0644]
foundation-database/public/functions/currentcashrcptnumber.sql [new file with mode: 0644]
foundation-database/public/functions/currentnumber.sql [new file with mode: 0644]
foundation-database/public/functions/currgain.sql [new file with mode: 0644]
foundation-database/public/functions/currrate.sql [new file with mode: 0644]
foundation-database/public/functions/currtobase.sql [new file with mode: 0644]
foundation-database/public/functions/currtocurr.sql [new file with mode: 0644]
foundation-database/public/functions/currtolocal.sql [new file with mode: 0644]
foundation-database/public/functions/custitem.sql [new file with mode: 0644]
foundation-database/public/functions/customercanpurchase.sql [new file with mode: 0644]
foundation-database/public/functions/defaultlocationname.sql [new file with mode: 0644]
foundation-database/public/functions/deleteaccount.sql [new file with mode: 0644]
foundation-database/public/functions/deleteaccountingperiod.sql [new file with mode: 0644]
foundation-database/public/functions/deleteaccountingyearperiod.sql [new file with mode: 0644]
foundation-database/public/functions/deleteaddress.sql [new file with mode: 0644]
foundation-database/public/functions/deleteapcheck.sql [new file with mode: 0644]
foundation-database/public/functions/deletebankadjustmenttype.sql [new file with mode: 0644]
foundation-database/public/functions/deletebankreconciliation.sql [new file with mode: 0644]
foundation-database/public/functions/deletebom.sql [new file with mode: 0644]
foundation-database/public/functions/deletebomworkset.sql [new file with mode: 0644]
foundation-database/public/functions/deletebudget.sql [new file with mode: 0644]
foundation-database/public/functions/deletebudgetitems.sql [new file with mode: 0644]
foundation-database/public/functions/deletecashrcpt.sql [new file with mode: 0644]
foundation-database/public/functions/deletecharacteristic.sql [new file with mode: 0644]
foundation-database/public/functions/deletecheck.sql [new file with mode: 0644]
foundation-database/public/functions/deleteclasscode.sql [new file with mode: 0644]
foundation-database/public/functions/deletecompany.sql [new file with mode: 0644]
foundation-database/public/functions/deletecreditmemo.sql [new file with mode: 0644]
foundation-database/public/functions/deletecustomer.sql [new file with mode: 0644]
foundation-database/public/functions/deletecustomertype.sql [new file with mode: 0644]
foundation-database/public/functions/deleteempgrp.sql [new file with mode: 0644]
foundation-database/public/functions/deleteexpiredips.sql [new file with mode: 0644]
foundation-database/public/functions/deletefile.sql [new file with mode: 0644]
foundation-database/public/functions/deleteflgroup.sql [new file with mode: 0644]
foundation-database/public/functions/deleteform.sql [new file with mode: 0644]
foundation-database/public/functions/deletefreightclass.sql [new file with mode: 0644]
foundation-database/public/functions/deleteglseries.sql [new file with mode: 0644]
foundation-database/public/functions/deleteincident.sql [new file with mode: 0644]
foundation-database/public/functions/deleteinvoice.sql [new file with mode: 0644]
foundation-database/public/functions/deleteipsitem.sql [new file with mode: 0644]
foundation-database/public/functions/deleteipsprodcat.sql [new file with mode: 0644]
foundation-database/public/functions/deleteitem.sql [new file with mode: 0644]
foundation-database/public/functions/deleteitemcost.sql [new file with mode: 0644]
foundation-database/public/functions/deleteitemsite.sql [new file with mode: 0644]
foundation-database/public/functions/deleteitemuom.sql [new file with mode: 0644]
foundation-database/public/functions/deleteitemuomconv.sql [new file with mode: 0644]
foundation-database/public/functions/deletelocation.sql [new file with mode: 0644]
foundation-database/public/functions/deletemetasql.sql [new file with mode: 0644]
foundation-database/public/functions/deleteopenrecurringitems.sql [new file with mode: 0644]
foundation-database/public/functions/deleteopportunity.sql [new file with mode: 0644]
foundation-database/public/functions/deletepackage.sql [new file with mode: 0644]
foundation-database/public/functions/deletepo.sql [new file with mode: 0644]
foundation-database/public/functions/deletepoitem.sql [new file with mode: 0644]
foundation-database/public/functions/deletepr.sql [new file with mode: 0644]
foundation-database/public/functions/deleteproductcategory.sql [new file with mode: 0644]
foundation-database/public/functions/deleteprofitcenter.sql [new file with mode: 0644]
foundation-database/public/functions/deleteproject.sql [new file with mode: 0644]
foundation-database/public/functions/deleteprojecttask.sql [new file with mode: 0644]
foundation-database/public/functions/deleteqryhead.sql [new file with mode: 0644]
foundation-database/public/functions/deletequote.sql [new file with mode: 0644]
foundation-database/public/functions/deleterecvfororder.sql [new file with mode: 0644]
foundation-database/public/functions/deletesalescategory.sql [new file with mode: 0644]
foundation-database/public/functions/deleteshippingcharge.sql [new file with mode: 0644]
foundation-database/public/functions/deleteshippingchargetype.sql [new file with mode: 0644]
foundation-database/public/functions/deleteshipto.sql [new file with mode: 0644]
foundation-database/public/functions/deleteso.sql [new file with mode: 0644]
foundation-database/public/functions/deletesoitem.sql [new file with mode: 0644]
foundation-database/public/functions/deletestandardjournal.sql [new file with mode: 0644]
foundation-database/public/functions/deletestandardjournalgroup.sql [new file with mode: 0644]
foundation-database/public/functions/deletesubaccount.sql [new file with mode: 0644]
foundation-database/public/functions/deletesubaccounttype.sql [new file with mode: 0644]
foundation-database/public/functions/deletetax.sql [new file with mode: 0644]
foundation-database/public/functions/deletetaxclass.sql [new file with mode: 0644]
foundation-database/public/functions/deletetaxtype.sql [new file with mode: 0644]
foundation-database/public/functions/deletetaxzone.sql [new file with mode: 0644]
foundation-database/public/functions/deletetodoitem.sql [new file with mode: 0644]
foundation-database/public/functions/deleteunusedclasscodes.sql [new file with mode: 0644]
foundation-database/public/functions/deleteunusedfreightclasses.sql [new file with mode: 0644]
foundation-database/public/functions/deleteunusedproductcategories.sql [new file with mode: 0644]
foundation-database/public/functions/deleteuom.sql [new file with mode: 0644]
foundation-database/public/functions/deleteuomconv.sql [new file with mode: 0644]
foundation-database/public/functions/deleteurl.sql [new file with mode: 0644]
foundation-database/public/functions/deleteuserpreference.sql [new file with mode: 0644]
foundation-database/public/functions/deletevendoraddr.sql [new file with mode: 0644]
foundation-database/public/functions/deletevendortype.sql [new file with mode: 0644]
foundation-database/public/functions/deletewo.sql [new file with mode: 0644]
foundation-database/public/functions/deletewomaterial.sql [new file with mode: 0644]
foundation-database/public/functions/detachccpayfromso.sql [new file with mode: 0644]
foundation-database/public/functions/detachcontact.sql [new file with mode: 0644]
foundation-database/public/functions/detag.sql [new file with mode: 0644]
foundation-database/public/functions/detailednnqoh.sql [new file with mode: 0644]
foundation-database/public/functions/detailedqoh.sql [new file with mode: 0644]
foundation-database/public/functions/determinediscountdate.sql [new file with mode: 0644]
foundation-database/public/functions/determineduedate.sql [new file with mode: 0644]
foundation-database/public/functions/disablepackage.sql [new file with mode: 0644]
foundation-database/public/functions/distributeitemlocseries.sql [new file with mode: 0644]
foundation-database/public/functions/distributetodefault.sql [new file with mode: 0644]
foundation-database/public/functions/distributetodefaultitemloc.sql [new file with mode: 0644]
foundation-database/public/functions/distributetolocations.sql [new file with mode: 0644]
foundation-database/public/functions/distributevoucherline.sql [new file with mode: 0644]
foundation-database/public/functions/dopostcosts.sql [new file with mode: 0644]
foundation-database/public/functions/doupdatecosts.sql [new file with mode: 0644]
foundation-database/public/functions/dropifexists.sql [new file with mode: 0644]
foundation-database/public/functions/editccnumber.sql [new file with mode: 0644]
foundation-database/public/functions/enablepackage.sql [new file with mode: 0644]
foundation-database/public/functions/endoftime.sql [new file with mode: 0644]
foundation-database/public/functions/entercount.sql [new file with mode: 0644]
foundation-database/public/functions/enterporeceipt.sql [new file with mode: 0644]
foundation-database/public/functions/enterporeturn.sql [new file with mode: 0644]
foundation-database/public/functions/enterreceipt.sql [new file with mode: 0644]
foundation-database/public/functions/expirecreditcard.sql [new file with mode: 0644]
foundation-database/public/functions/explodebom.sql [new file with mode: 0644]
foundation-database/public/functions/explodekit.sql [new file with mode: 0644]
foundation-database/public/functions/explodephantomorder.sql [new file with mode: 0644]
foundation-database/public/functions/explodewo.sql [new file with mode: 0644]
foundation-database/public/functions/explodewoeffective.sql [new file with mode: 0644]
foundation-database/public/functions/fetchapmemonumber.sql [new file with mode: 0644]
foundation-database/public/functions/fetcharmemonumber.sql [new file with mode: 0644]
foundation-database/public/functions/fetchcashrcptnumber.sql [new file with mode: 0644]
foundation-database/public/functions/fetchcmnumber.sql [new file with mode: 0644]
foundation-database/public/functions/fetchcrmaccountnumber.sql [new file with mode: 0644]
foundation-database/public/functions/fetchdefaultfob.sql [new file with mode: 0644]
foundation-database/public/functions/fetchdefaultshipvia.sql [new file with mode: 0644]
foundation-database/public/functions/fetchglsequence.sql [new file with mode: 0644]
foundation-database/public/functions/fetchincidentnumber.sql [new file with mode: 0644]
foundation-database/public/functions/fetchinvcnumber.sql [new file with mode: 0644]
foundation-database/public/functions/fetchitemuomconvtypes.sql [new file with mode: 0644]
foundation-database/public/functions/fetchjournalnumber.sql [new file with mode: 0644]
foundation-database/public/functions/fetchmetricbool.sql [new file with mode: 0644]
foundation-database/public/functions/fetchmetrictext.sql [new file with mode: 0644]
foundation-database/public/functions/fetchmetricvalue.sql [new file with mode: 0644]
foundation-database/public/functions/fetchnextchecknumber.sql [new file with mode: 0644]
foundation-database/public/functions/fetchnextnumber.sql [new file with mode: 0644]
foundation-database/public/functions/fetchponumber.sql [new file with mode: 0644]
foundation-database/public/functions/fetchprefwarehousid.sql [new file with mode: 0644]
foundation-database/public/functions/fetchprnumber.sql [new file with mode: 0644]
foundation-database/public/functions/fetchqunumber.sql [new file with mode: 0644]
foundation-database/public/functions/fetchshipmentnumber.sql [new file with mode: 0644]
foundation-database/public/functions/fetchsonumber.sql [new file with mode: 0644]
foundation-database/public/functions/fetchtonumber.sql [new file with mode: 0644]
foundation-database/public/functions/fetchusrprefbool.sql [new file with mode: 0644]
foundation-database/public/functions/fetchvonumber.sql [new file with mode: 0644]
foundation-database/public/functions/fetchwonumber.sql [new file with mode: 0644]
foundation-database/public/functions/financialreport.sql [new file with mode: 0644]
foundation-database/public/functions/findapaccount.sql [new file with mode: 0644]
foundation-database/public/functions/findapdiscountaccount.sql [new file with mode: 0644]
foundation-database/public/functions/findapprepaidaccount.sql [new file with mode: 0644]
foundation-database/public/functions/findaraccount.sql [new file with mode: 0644]
foundation-database/public/functions/findardiscountaccount.sql [new file with mode: 0644]
foundation-database/public/functions/findcalendarorigin.sql [new file with mode: 0644]
foundation-database/public/functions/findcustomerform.sql [new file with mode: 0644]
foundation-database/public/functions/finddeferredaccount.sql [new file with mode: 0644]
foundation-database/public/functions/findfreightaccount.sql [new file with mode: 0644]
foundation-database/public/functions/findperiodend.sql [new file with mode: 0644]
foundation-database/public/functions/findperiodstart.sql [new file with mode: 0644]
foundation-database/public/functions/findprepaidaccount.sql [new file with mode: 0644]
foundation-database/public/functions/findsalesaccnt.sql [new file with mode: 0644]
foundation-database/public/functions/findspecialfinancial.sql [new file with mode: 0644]
foundation-database/public/functions/first_agg.sql [new file with mode: 0644]
foundation-database/public/functions/firstline.sql [new file with mode: 0644]
foundation-database/public/functions/fixacl.sql [new file with mode: 0644]
foundation-database/public/functions/formatabachecks.sql [new file with mode: 0644]
foundation-database/public/functions/formatachchecks.sql [new file with mode: 0644]
foundation-database/public/functions/formatachcompanyid.sql [new file with mode: 0644]
foundation-database/public/functions/formataddr.sql [new file with mode: 0644]
foundation-database/public/functions/formatboolyn.sql [new file with mode: 0644]
foundation-database/public/functions/formatbooseq.sql [new file with mode: 0644]
foundation-database/public/functions/formatbytea.sql [new file with mode: 0644]
foundation-database/public/functions/formatccdashes.sql [new file with mode: 0644]
foundation-database/public/functions/formatccnumber.sql [new file with mode: 0644]
foundation-database/public/functions/formatcntctname.sql [new file with mode: 0644]
foundation-database/public/functions/formatcost.sql [new file with mode: 0644]
foundation-database/public/functions/formatcounttagbarcode.sql [new file with mode: 0644]
foundation-database/public/functions/formatcreditmemonumber.sql [new file with mode: 0644]
foundation-database/public/functions/formatdate.sql [new file with mode: 0644]
foundation-database/public/functions/formatdatetime.sql [new file with mode: 0644]
foundation-database/public/functions/formatextprice.sql [new file with mode: 0644]
foundation-database/public/functions/formatflitemdescrip.sql [new file with mode: 0644]
foundation-database/public/functions/formatglaccount.sql [new file with mode: 0644]
foundation-database/public/functions/formatglaccountlong.sql [new file with mode: 0644]
foundation-database/public/functions/formatindent.sql [new file with mode: 0644]
foundation-database/public/functions/formatinterval.sql [new file with mode: 0644]
foundation-database/public/functions/formatinvcnumber.sql [new file with mode: 0644]
foundation-database/public/functions/formatitemsitebarcode.sql [new file with mode: 0644]
foundation-database/public/functions/formatlocationbarcode.sql [new file with mode: 0644]
foundation-database/public/functions/formatlocationcontentsbarcode.sql [new file with mode: 0644]
foundation-database/public/functions/formatlocationissuebarcode.sql [new file with mode: 0644]
foundation-database/public/functions/formatlocationname.sql [new file with mode: 0644]
foundation-database/public/functions/formatlotserialnumber.sql [new file with mode: 0644]
foundation-database/public/functions/formatmoney.sql [new file with mode: 0644]
foundation-database/public/functions/formatnumeric.sql [new file with mode: 0644]
foundation-database/public/functions/formatperiodname.sql [new file with mode: 0644]
foundation-database/public/functions/formatplonumber.sql [new file with mode: 0644]
foundation-database/public/functions/formatprcnt.sql [new file with mode: 0644]
foundation-database/public/functions/formatprice.sql [new file with mode: 0644]
foundation-database/public/functions/formatpurchprice.sql [new file with mode: 0644]
foundation-database/public/functions/formatqty.sql [new file with mode: 0644]
foundation-database/public/functions/formatqtyper.sql [new file with mode: 0644]
foundation-database/public/functions/formatratio.sql [new file with mode: 0644]
foundation-database/public/functions/formatrevnumber.sql [new file with mode: 0644]
foundation-database/public/functions/formatsalesprice.sql [new file with mode: 0644]
foundation-database/public/functions/formatscrap.sql [new file with mode: 0644]
foundation-database/public/functions/formatshipmentnumber.sql [new file with mode: 0644]
foundation-database/public/functions/formatsobarcode.sql [new file with mode: 0644]
foundation-database/public/functions/formatsoitembarcode.sql [new file with mode: 0644]
foundation-database/public/functions/formatsoitemnumber.sql [new file with mode: 0644]
foundation-database/public/functions/formatsolinenumber.sql [new file with mode: 0644]
foundation-database/public/functions/formatsonumber.sql [new file with mode: 0644]
foundation-database/public/functions/formattime.sql [new file with mode: 0644]
foundation-database/public/functions/formatuomratio.sql [new file with mode: 0644]
foundation-database/public/functions/formatuserbarcode.sql [new file with mode: 0644]
foundation-database/public/functions/formatweight.sql [new file with mode: 0644]
foundation-database/public/functions/formatwobarcode.sql [new file with mode: 0644]
foundation-database/public/functions/formatwonumber.sql [new file with mode: 0644]
foundation-database/public/functions/formatwooperseq.sql [new file with mode: 0644]
foundation-database/public/functions/forwardupdateaccount.sql [new file with mode: 0644]
foundation-database/public/functions/forwardupdateinvbalance.sql [new file with mode: 0644]
foundation-database/public/functions/forwardupdateitemsite.sql [new file with mode: 0644]
foundation-database/public/functions/forwardupdatetrialbalance.sql [new file with mode: 0644]
foundation-database/public/functions/freezeaccountingperiod.sql [new file with mode: 0644]
foundation-database/public/functions/freightdetail.sql [new file with mode: 0644]
foundation-database/public/functions/freightdetailquote.sql [new file with mode: 0644]
foundation-database/public/functions/freightforrecv.sql [new file with mode: 0644]
foundation-database/public/functions/getactiverevid.sql [new file with mode: 0644]
foundation-database/public/functions/getaddrid.sql [new file with mode: 0644]
foundation-database/public/functions/getadjustmenttaxtypeid.sql [new file with mode: 0644]
foundation-database/public/functions/getaropenid.sql [new file with mode: 0644]
foundation-database/public/functions/getbankaccntid.sql [new file with mode: 0644]
foundation-database/public/functions/getbomitemid.sql [new file with mode: 0644]
foundation-database/public/functions/getbooitemseqid.sql [new file with mode: 0644]
foundation-database/public/functions/getbudgheadid.sql [new file with mode: 0755]
foundation-database/public/functions/getcashrcptid.sql [new file with mode: 0644]
foundation-database/public/functions/getcharid.sql [new file with mode: 0644]
foundation-database/public/functions/getclasscodeid.sql [new file with mode: 0644]
foundation-database/public/functions/getcmheadid.sql [new file with mode: 0644]
foundation-database/public/functions/getcmnttypeid.sql [new file with mode: 0644]
foundation-database/public/functions/getcntctid.sql [new file with mode: 0644]
foundation-database/public/functions/getcoheadid.sql [new file with mode: 0644]
foundation-database/public/functions/getcoitemid.sql [new file with mode: 0644]
foundation-database/public/functions/getcontrcteffective.sql [new file with mode: 0644]
foundation-database/public/functions/getcontrctexpires.sql [new file with mode: 0644]
foundation-database/public/functions/getcontrctid.sql [new file with mode: 0644]
foundation-database/public/functions/getcostcatid.sql [new file with mode: 0644]
foundation-database/public/functions/getcostelemid.sql [new file with mode: 0644]
foundation-database/public/functions/getcrmacctid.sql [new file with mode: 0644]
foundation-database/public/functions/getcurrid.sql [new file with mode: 0644]
foundation-database/public/functions/getcustid.sql [new file with mode: 0644]
foundation-database/public/functions/getcustnamefrominfo.sql [new file with mode: 0644]
foundation-database/public/functions/getcusttypeid.sql [new file with mode: 0644]
foundation-database/public/functions/getdeptid.sql [new file with mode: 0644]
foundation-database/public/functions/getediprofileid.sql [new file with mode: 0644]
foundation-database/public/functions/getediprofilename.sql [new file with mode: 0644]
foundation-database/public/functions/geteffectivextuser.sql [new file with mode: 0644]
foundation-database/public/functions/getempid.sql [new file with mode: 0644]
foundation-database/public/functions/getexpcatid.sql [new file with mode: 0644]
foundation-database/public/functions/getflcoldata.sql [new file with mode: 0644]
foundation-database/public/functions/getflstmthead.sql [new file with mode: 0644]
foundation-database/public/functions/getfltrendhead.sql [new file with mode: 0644]
foundation-database/public/functions/getfreightclassid.sql [new file with mode: 0644]
foundation-database/public/functions/getfreighttaxtypeid.sql [new file with mode: 0644]
foundation-database/public/functions/getgainlossaccntid.sql [new file with mode: 0644]
foundation-database/public/functions/getglaccntid.sql [new file with mode: 0644]
foundation-database/public/functions/getimageid.sql [new file with mode: 0644]
foundation-database/public/functions/getincdtcatid.sql [new file with mode: 0644]
foundation-database/public/functions/getincdtcrmacctid.sql [new file with mode: 0644]
foundation-database/public/functions/getincdtpriorityid.sql [new file with mode: 0644]
foundation-database/public/functions/getincdtresolutionid.sql [new file with mode: 0644]
foundation-database/public/functions/getincdtseverityid.sql [new file with mode: 0644]
foundation-database/public/functions/getincidentid.sql [new file with mode: 0644]
foundation-database/public/functions/getinvcheadid.sql [new file with mode: 0644]
foundation-database/public/functions/getinvcitemlotserial.sql [new file with mode: 0644]
foundation-database/public/functions/getipsheadid.sql [new file with mode: 0644]
foundation-database/public/functions/getipsitemid.sql [new file with mode: 0644]
foundation-database/public/functions/getipsprodcatid.sql [new file with mode: 0644]
foundation-database/public/functions/getitemid.sql [new file with mode: 0644]
foundation-database/public/functions/getitemidfromupc.sql [new file with mode: 0644]
foundation-database/public/functions/getitemsiteid.sql [new file with mode: 0644]
foundation-database/public/functions/getitemsrcid.sql [new file with mode: 0644]
foundation-database/public/functions/getitemtaxtype.sql [new file with mode: 0644]
foundation-database/public/functions/getlasttrialbalid.sql [new file with mode: 0644]
foundation-database/public/functions/getlocationid.sql [new file with mode: 0644]
foundation-database/public/functions/getlotserialid.sql [new file with mode: 0644]
foundation-database/public/functions/getopheadid.sql [new file with mode: 0644]
foundation-database/public/functions/getpacklistcharname.sql [new file with mode: 0644]
foundation-database/public/functions/getpacklistcharvalue.sql [new file with mode: 0644]
foundation-database/public/functions/getpacklistitemlotserial.sql [new file with mode: 0644]
foundation-database/public/functions/getperiodid.sql [new file with mode: 0644]
foundation-database/public/functions/getpkgheadid.sql [new file with mode: 0644]
foundation-database/public/functions/getplancodeid.sql [new file with mode: 0644]
foundation-database/public/functions/getpoheadid.sql [new file with mode: 0644]
foundation-database/public/functions/getpoitemid.sql [new file with mode: 0644]
foundation-database/public/functions/getprjaccntid.sql [new file with mode: 0644]
foundation-database/public/functions/getprjid.sql [new file with mode: 0644]
foundation-database/public/functions/getprjtaskid.sql [new file with mode: 0644]
foundation-database/public/functions/getprodcatid.sql [new file with mode: 0644]
foundation-database/public/functions/getprospectid.sql [new file with mode: 0644]
foundation-database/public/functions/getquoteid.sql [new file with mode: 0644]
foundation-database/public/functions/getquotelineitemid.sql [new file with mode: 0644]
foundation-database/public/functions/getrevid.sql [new file with mode: 0644]
foundation-database/public/functions/getrsnid.sql [new file with mode: 0644]
foundation-database/public/functions/getsalescatid.sql [new file with mode: 0644]
foundation-database/public/functions/getsaleslineitemid.sql [new file with mode: 0644]
foundation-database/public/functions/getsalesorderid.sql [new file with mode: 0644]
foundation-database/public/functions/getsalesrepid.sql [new file with mode: 0644]
foundation-database/public/functions/getsaletypeid.sql [new file with mode: 0644]
foundation-database/public/functions/getshiftid.sql [new file with mode: 0644]
foundation-database/public/functions/getshipchrgid.sql [new file with mode: 0644]
foundation-database/public/functions/getshipformid.sql [new file with mode: 0644]
foundation-database/public/functions/getshipheadid.sql [new file with mode: 0644]
foundation-database/public/functions/getshiptoid.sql [new file with mode: 0644]
foundation-database/public/functions/getshiptonumberfrominfo.sql [new file with mode: 0644]
foundation-database/public/functions/getshipviaid.sql [new file with mode: 0644]
foundation-database/public/functions/getshipzoneid.sql [new file with mode: 0644]
foundation-database/public/functions/getsitetypeid.sql [new file with mode: 0644]
foundation-database/public/functions/getsoscheddate.sql [new file with mode: 0644]
foundation-database/public/functions/getsostatus.sql [new file with mode: 0644]
foundation-database/public/functions/getsubtax.sql [new file with mode: 0644]
foundation-database/public/functions/gettaxauthid.sql [new file with mode: 0644]
foundation-database/public/functions/gettaxid.sql [new file with mode: 0644]
foundation-database/public/functions/gettaxtypeid.sql [new file with mode: 0644]
foundation-database/public/functions/gettaxzoneid.sql [new file with mode: 0644]
foundation-database/public/functions/gettermsid.sql [new file with mode: 0644]
foundation-database/public/functions/getunassignedaccntid.sql [new file with mode: 0644]
foundation-database/public/functions/getuomid.sql [new file with mode: 0644]
foundation-database/public/functions/getuomtypeid.sql [new file with mode: 0644]
foundation-database/public/functions/getusrid.sql [new file with mode: 0644]
foundation-database/public/functions/getusrlocaleid.sql [new file with mode: 0644]
foundation-database/public/functions/getvendaddrid.sql [new file with mode: 0644]
foundation-database/public/functions/getvendid.sql [new file with mode: 0644]
foundation-database/public/functions/getvendtypeid.sql [new file with mode: 0644]
foundation-database/public/functions/getwarehousid.sql [new file with mode: 0644]
foundation-database/public/functions/getwhsezoneid.sql [new file with mode: 0644]
foundation-database/public/functions/grantallmodulecmnttypesource.sql [new file with mode: 0644]
foundation-database/public/functions/grantallmodulepriv.sql [new file with mode: 0644]
foundation-database/public/functions/grantallmoduleprivgroup.sql [new file with mode: 0644]
foundation-database/public/functions/grantcmnttypesource.sql [new file with mode: 0644]
foundation-database/public/functions/grantgroup.sql [new file with mode: 0644]
foundation-database/public/functions/grantpriv.sql [new file with mode: 0644]
foundation-database/public/functions/grantprivgroup.sql [new file with mode: 0644]
foundation-database/public/functions/hasalarms.sql [new file with mode: 0644]
foundation-database/public/functions/hasevents.sql [new file with mode: 0644]
foundation-database/public/functions/hasmessages.sql [new file with mode: 0644]
foundation-database/public/functions/haspriv.sql [new file with mode: 0644]
foundation-database/public/functions/hasprivonobject.sql [new file with mode: 0644]
foundation-database/public/functions/implodewo.sql [new file with mode: 0644]
foundation-database/public/functions/incdt.sql [new file with mode: 0644]
foundation-database/public/functions/indentedbom.sql [new file with mode: 0644]
foundation-database/public/functions/indentedwhereused.sql [new file with mode: 0644]
foundation-database/public/functions/indentedwo.sql [new file with mode: 0644]
foundation-database/public/functions/indentedwomatl.sql [new file with mode: 0644]
foundation-database/public/functions/initeffectivextuser.sql [new file with mode: 0644]
foundation-database/public/functions/initialdistribution.sql [new file with mode: 0644]
foundation-database/public/functions/insertIntoGLSeries.sql [new file with mode: 0644]
foundation-database/public/functions/insertccard.sql [new file with mode: 0644]
foundation-database/public/functions/insertflgroup.sql [new file with mode: 0644]
foundation-database/public/functions/insertgltransaction.sql [new file with mode: 0644]
foundation-database/public/functions/insertitemcost.sql [new file with mode: 0644]
foundation-database/public/functions/intervaltominutes.sql [new file with mode: 0644]
foundation-database/public/functions/invadjustment.sql [new file with mode: 0644]
foundation-database/public/functions/invexpense.sql [new file with mode: 0644]
foundation-database/public/functions/invhistsense.sql [new file with mode: 0644]
foundation-database/public/functions/invoicetotal.sql [new file with mode: 0644]
foundation-database/public/functions/invreceipt.sql [new file with mode: 0644]
foundation-database/public/functions/invscrap.sql [new file with mode: 0644]
foundation-database/public/functions/isdba.sql [new file with mode: 0644]
foundation-database/public/functions/ismulticurr.sql [new file with mode: 0644]
foundation-database/public/functions/isnumeric.sql [new file with mode: 0644]
foundation-database/public/functions/issueallbalancetoshipping.sql [new file with mode: 0644]
foundation-database/public/functions/issuelinebalancetoshipping.sql [new file with mode: 0644]
foundation-database/public/functions/issues.sql [new file with mode: 0644]
foundation-database/public/functions/issuetoshipping.sql [new file with mode: 0644]
foundation-database/public/functions/issuewomaterial.sql [new file with mode: 0644]
foundation-database/public/functions/issuewomaterialbatch.sql [new file with mode: 0644]
foundation-database/public/functions/itemaltcapinvrat.sql [new file with mode: 0644]
foundation-database/public/functions/itemaltcapuom.sql [new file with mode: 0644]
foundation-database/public/functions/itemcapinvrat.sql [new file with mode: 0644]
foundation-database/public/functions/itemcapuom.sql [new file with mode: 0644]
foundation-database/public/functions/itemcharprice.sql [new file with mode: 0644]
foundation-database/public/functions/itemcharvalue.sql [new file with mode: 0644]
foundation-database/public/functions/itemcost.sql [new file with mode: 0644]
foundation-database/public/functions/iteminventoryuominuse.sql [new file with mode: 0644]
foundation-database/public/functions/iteminvpricerat.sql [new file with mode: 0644]
foundation-database/public/functions/itemipsprice.sql [new file with mode: 0644]
foundation-database/public/functions/itemlocdistqty.sql [new file with mode: 0644]
foundation-database/public/functions/itemprice.sql [new file with mode: 0644]
foundation-database/public/functions/itemsellinguom.sql [new file with mode: 0644]
foundation-database/public/functions/itemsrcprice.sql [new file with mode: 0644]
foundation-database/public/functions/itemuombytype.sql [new file with mode: 0644]
foundation-database/public/functions/itemuomfractionalbytype.sql [new file with mode: 0644]
foundation-database/public/functions/itemuomfractionalbyuom.sql [new file with mode: 0644]
foundation-database/public/functions/itemuomratiobytype.sql [new file with mode: 0644]
foundation-database/public/functions/itemuomtouom.sql [new file with mode: 0644]
foundation-database/public/functions/itemuomtouomratio.sql [new file with mode: 0644]
foundation-database/public/functions/last_agg.sql [new file with mode: 0644]
foundation-database/public/functions/login.sql [new file with mode: 0644]
foundation-database/public/functions/logout.sql [new file with mode: 0644]
foundation-database/public/functions/lowercost.sql [new file with mode: 0644]
foundation-database/public/functions/maintainbomworkspace.sql [new file with mode: 0644]
foundation-database/public/functions/markapcheckasposted.sql [new file with mode: 0644]
foundation-database/public/functions/markapcheckasprinted.sql [new file with mode: 0644]
foundation-database/public/functions/markcheckasposted.sql [new file with mode: 0644]
foundation-database/public/functions/markcheckasprinted.sql [new file with mode: 0644]
foundation-database/public/functions/massexpirebomitem.sql [new file with mode: 0644]
foundation-database/public/functions/massreplacebomitem.sql [new file with mode: 0644]
foundation-database/public/functions/merge2crmaccts.sql [new file with mode: 0644]
foundation-database/public/functions/mergecrmaccts.sql [new file with mode: 0644]
foundation-database/public/functions/movebomitemdown.sql [new file with mode: 0644]
foundation-database/public/functions/movebomitemup.sql [new file with mode: 0644]
foundation-database/public/functions/moveccarddown.sql [new file with mode: 0644]
foundation-database/public/functions/moveccardup.sql [new file with mode: 0644]
foundation-database/public/functions/moveflgroupdown.sql [new file with mode: 0644]
foundation-database/public/functions/moveflgroupup.sql [new file with mode: 0644]
foundation-database/public/functions/moveflitemdown.sql [new file with mode: 0644]
foundation-database/public/functions/moveflitemup.sql [new file with mode: 0644]
foundation-database/public/functions/moveflspecdown.sql [new file with mode: 0644]
foundation-database/public/functions/moveflspecup.sql [new file with mode: 0644]
foundation-database/public/functions/movescript.sql [new file with mode: 0644]
foundation-database/public/functions/moveuiform.sql [new file with mode: 0644]
foundation-database/public/functions/moveupdown.sql [new file with mode: 0644]
foundation-database/public/functions/nextperiodbyinterval.sql [new file with mode: 0644]
foundation-database/public/functions/nextprsubnumber.sql [new file with mode: 0644]
foundation-database/public/functions/nextwosubnumber.sql [new file with mode: 0644]
foundation-database/public/functions/noneg.sql [new file with mode: 0644]
foundation-database/public/functions/nopos.sql [new file with mode: 0644]
foundation-database/public/functions/normalizetrialbal.sql [new file with mode: 0644]
foundation-database/public/functions/numofdatabaseusers.sql [new file with mode: 0644]
foundation-database/public/functions/numofserverusers.sql [new file with mode: 0644]
foundation-database/public/functions/openaccountingperiod.sql [new file with mode: 0644]
foundation-database/public/functions/openaccountingyearperiod.sql [new file with mode: 0644]
foundation-database/public/functions/openapitemsvalue.sql [new file with mode: 0644]
foundation-database/public/functions/openaritemsvalue.sql [new file with mode: 0644]
foundation-database/public/functions/openrecurringitems.sql [new file with mode: 0644]
foundation-database/public/functions/ophead.sql [new file with mode: 0644]
foundation-database/public/functions/orderedbypo.sql [new file with mode: 0644]
foundation-database/public/functions/orderedbywo.sql [new file with mode: 0644]
foundation-database/public/functions/orderhead.sql [new file with mode: 0644]
foundation-database/public/functions/orderitem.sql [new file with mode: 0644]
foundation-database/public/functions/orderitemdata.sql [new file with mode: 0644]
foundation-database/public/functions/packageisenabled.sql [new file with mode: 0644]
foundation-database/public/functions/pkgmaybemodified.sql [new file with mode: 0644]
foundation-database/public/functions/postapcheck.sql [new file with mode: 0644]
foundation-database/public/functions/postapchecks.sql [new file with mode: 0644]
foundation-database/public/functions/postapcreditmemoapplication.sql [new file with mode: 0644]
foundation-database/public/functions/postapopenitems.sql [new file with mode: 0644]
foundation-database/public/functions/postarcreditmemoapplication.sql [new file with mode: 0644]
foundation-database/public/functions/postaropenitems.sql [new file with mode: 0644]
foundation-database/public/functions/postbankadjustment.sql [new file with mode: 0644]
foundation-database/public/functions/postbankreconciliation.sql [new file with mode: 0644]
foundation-database/public/functions/postbillingselection.sql [new file with mode: 0644]
foundation-database/public/functions/postbillingselectionconsolidated.sql [new file with mode: 0644]
foundation-database/public/functions/postbillingselections.sql [new file with mode: 0644]
foundation-database/public/functions/postcashreceipt.sql [new file with mode: 0644]
foundation-database/public/functions/postcashreceiptdisc.sql [new file with mode: 0644]
foundation-database/public/functions/postcccashreceipt.sql [new file with mode: 0644]
foundation-database/public/functions/postcccredit.sql [new file with mode: 0644]
foundation-database/public/functions/postccvoid.sql [new file with mode: 0644]
foundation-database/public/functions/postcheck.sql [new file with mode: 0644]
foundation-database/public/functions/postchecks.sql [new file with mode: 0644]
foundation-database/public/functions/postcomment.sql [new file with mode: 0644]
foundation-database/public/functions/postcost.sql [new file with mode: 0644]
foundation-database/public/functions/postcountslip.sql [new file with mode: 0644]
foundation-database/public/functions/postcounttag.sql [new file with mode: 0644]
foundation-database/public/functions/postcounttaglocation.sql [new file with mode: 0644]
foundation-database/public/functions/postcounttags.sql [new file with mode: 0644]
foundation-database/public/functions/postcreditmemo.sql [new file with mode: 0644]
foundation-database/public/functions/postcreditmemos.sql [new file with mode: 0644]
foundation-database/public/functions/postglseries.sql [new file with mode: 0644]
foundation-database/public/functions/postglseriesnosumm.sql [new file with mode: 0644]
foundation-database/public/functions/postintoinvbalance.sql [new file with mode: 0644]
foundation-database/public/functions/postintotrialbalance.sql [new file with mode: 0644]
foundation-database/public/functions/postinvhist.sql [new file with mode: 0644]
foundation-database/public/functions/postinvoice.sql [new file with mode: 0644]
foundation-database/public/functions/postinvoices.sql [new file with mode: 0644]
foundation-database/public/functions/postinvtrans.sql [new file with mode: 0644]
foundation-database/public/functions/postitemlocseries.sql [new file with mode: 0644]
foundation-database/public/functions/postjournals.sql [new file with mode: 0644]
foundation-database/public/functions/postmessage.sql [new file with mode: 0644]
foundation-database/public/functions/postmisccount.sql [new file with mode: 0644]
foundation-database/public/functions/postpogltransactions.sql [new file with mode: 0644]
foundation-database/public/functions/postporeceipt.sql [new file with mode: 0644]
foundation-database/public/functions/postporeceipts.sql [new file with mode: 0644]
foundation-database/public/functions/postporeturncreditmemo.sql [new file with mode: 0644]
foundation-database/public/functions/postporeturns.sql [new file with mode: 0644]
foundation-database/public/functions/postproduction.sql [new file with mode: 0644]
foundation-database/public/functions/postreceipt.sql [new file with mode: 0644]
foundation-database/public/functions/postreceipts.sql [new file with mode: 0644]
foundation-database/public/functions/postsogltransactions.sql [new file with mode: 0644]
foundation-database/public/functions/postsoitemproduction.sql [new file with mode: 0644]
foundation-database/public/functions/poststandardjournal.sql [new file with mode: 0644]
foundation-database/public/functions/poststandardjournalgroup.sql [new file with mode: 0644]
foundation-database/public/functions/postvalueintoinvbalance.sql [new file with mode: 0644]
foundation-database/public/functions/postvoucher.sql [new file with mode: 0644]
foundation-database/public/functions/postvouchers.sql [new file with mode: 0644]
foundation-database/public/functions/primarykeyfields.sql [new file with mode: 0644]
foundation-database/public/functions/prj.sql [new file with mode: 0644]
foundation-database/public/functions/prjtask.sql [new file with mode: 0644]
foundation-database/public/functions/purgecreditmemos.sql [new file with mode: 0644]
foundation-database/public/functions/purgecrmacctmerge.sql [new file with mode: 0644]
foundation-database/public/functions/purgeinvoicerecord.sql [new file with mode: 0644]
foundation-database/public/functions/purgeinvoicerecords.sql [new file with mode: 0644]
foundation-database/public/functions/purgepostedcountslips.sql [new file with mode: 0644]
foundation-database/public/functions/purgepostedcounttags.sql [new file with mode: 0644]
foundation-database/public/functions/purgeshipments.sql [new file with mode: 0644]
foundation-database/public/functions/qtyallocated.sql [new file with mode: 0644]
foundation-database/public/functions/qtyatshipping.sql [new file with mode: 0644]
foundation-database/public/functions/qtyavailable.sql [new file with mode: 0644]
foundation-database/public/functions/qtyinshipment.sql [new file with mode: 0644]
foundation-database/public/functions/qtylocation.sql [new file with mode: 0644]
foundation-database/public/functions/qtyordered.sql [new file with mode: 0644]
foundation-database/public/functions/qtypr.sql [new file with mode: 0644]
foundation-database/public/functions/qtytoreceive.sql [new file with mode: 0644]
foundation-database/public/functions/recallshipment.sql [new file with mode: 0644]
foundation-database/public/functions/recallwo.sql [new file with mode: 0644]
foundation-database/public/functions/receipts.sql [new file with mode: 0644]
foundation-database/public/functions/releaseapmemonumber.sql [new file with mode: 0644]
foundation-database/public/functions/releasearmemonumber.sql [new file with mode: 0644]
foundation-database/public/functions/releasecashrcptnumber.sql [new file with mode: 0644]
foundation-database/public/functions/releasecmnumber.sql [new file with mode: 0644]
foundation-database/public/functions/releasecrmaccountnumber.sql [new file with mode: 0644]
foundation-database/public/functions/releaseincidentnumber.sql [new file with mode: 0644]
foundation-database/public/functions/releaseinvcnumber.sql [new file with mode: 0644]
foundation-database/public/functions/releasenumber.sql [new file with mode: 0644]
foundation-database/public/functions/releaseponumber.sql [new file with mode: 0644]
foundation-database/public/functions/releaseprnumber.sql [new file with mode: 0644]
foundation-database/public/functions/releasepurchaseorder.sql [new file with mode: 0644]
foundation-database/public/functions/releasequnumber.sql [new file with mode: 0644]
foundation-database/public/functions/releaseshipmentnumber.sql [new file with mode: 0644]
foundation-database/public/functions/releasesohead.sql [new file with mode: 0644]
foundation-database/public/functions/releasesonumber.sql [new file with mode: 0644]
foundation-database/public/functions/releaseunusedbillingheader.sql [new file with mode: 0644]
foundation-database/public/functions/releasevonumber.sql [new file with mode: 0644]
foundation-database/public/functions/releasewo.sql [new file with mode: 0644]
foundation-database/public/functions/releasewonumber.sql [new file with mode: 0644]
foundation-database/public/functions/relocateinventory.sql [new file with mode: 0644]
foundation-database/public/functions/reorderdate.sql [new file with mode: 0644]
foundation-database/public/functions/replaceallvoidedapchecks.sql [new file with mode: 0644]
foundation-database/public/functions/replaceallvoidedchecks.sql [new file with mode: 0644]
foundation-database/public/functions/replacevoidedapcheck.sql [new file with mode: 0644]
foundation-database/public/functions/replacevoidedcheck.sql [new file with mode: 0644]
foundation-database/public/functions/reprioritizewo.sql [new file with mode: 0644]
foundation-database/public/functions/resetdbobjperms.sql [new file with mode: 0644]
foundation-database/public/functions/resetlowlevelcode.sql [new file with mode: 0644]
foundation-database/public/functions/resetqohbalance.sql [new file with mode: 0644]
foundation-database/public/functions/resolvecosaccount.sql [new file with mode: 0644]
foundation-database/public/functions/resolvecowaccount.sql [new file with mode: 0644]
foundation-database/public/functions/resolvecreditaccount.sql [new file with mode: 0644]
foundation-database/public/functions/resolvesalesaccount.sql [new file with mode: 0644]
foundation-database/public/functions/restoresaleshistory.sql [new file with mode: 0644]
foundation-database/public/functions/returncompleteshipment.sql [new file with mode: 0644]
foundation-database/public/functions/returnitemshipments.sql [new file with mode: 0644]
foundation-database/public/functions/returnshipmenttransaction.sql [new file with mode: 0644]
foundation-database/public/functions/returnwomaterial.sql [new file with mode: 0644]
foundation-database/public/functions/returnwomaterialbatch.sql [new file with mode: 0644]
foundation-database/public/functions/reversecashreceipt.sql [new file with mode: 0644]
foundation-database/public/functions/reversecashreceiptdisc.sql [new file with mode: 0644]
foundation-database/public/functions/reverseglseries.sql [new file with mode: 0644]
foundation-database/public/functions/revokeallmodulecmnttypesource.sql [new file with mode: 0644]
foundation-database/public/functions/revokeallmodulepriv.sql [new file with mode: 0644]
foundation-database/public/functions/revokeallmoduleprivgroup.sql [new file with mode: 0644]
foundation-database/public/functions/revokecmnttypesource.sql [new file with mode: 0644]
foundation-database/public/functions/revokegroup.sql [new file with mode: 0644]
foundation-database/public/functions/revokepriv.sql [new file with mode: 0644]
foundation-database/public/functions/revokeprivgroup.sql [new file with mode: 0644]
foundation-database/public/functions/rollupactualcost.sql [new file with mode: 0644]
foundation-database/public/functions/rollupsoracost.sql [new file with mode: 0644]
foundation-database/public/functions/rollupstandardcost.sql [new file with mode: 0644]
foundation-database/public/functions/roundcost.sql [new file with mode: 0644]
foundation-database/public/functions/roundlocale.sql [new file with mode: 0644]
foundation-database/public/functions/roundqty.sql [new file with mode: 0644]
foundation-database/public/functions/roundsale.sql [new file with mode: 0644]
foundation-database/public/functions/roundup.sql [new file with mode: 0644]
foundation-database/public/functions/saveaddr.sql [new file with mode: 0644]
foundation-database/public/functions/savealarm.sql [new file with mode: 0644]
foundation-database/public/functions/savebomhead.sql [new file with mode: 0644]
foundation-database/public/functions/savecntct.sql [new file with mode: 0644]
foundation-database/public/functions/saveimageass.sql [new file with mode: 0644]
foundation-database/public/functions/saveipsitem.sql [new file with mode: 0644]
foundation-database/public/functions/saveipsprodcat.sql [new file with mode: 0644]
foundation-database/public/functions/saveitemimage.sql [new file with mode: 0644]
foundation-database/public/functions/saveitemuomconv.sql [new file with mode: 0644]
foundation-database/public/functions/savemetasql.sql [new file with mode: 0644]
foundation-database/public/functions/scraps.sql [new file with mode: 0644]
foundation-database/public/functions/scrapwomaterial.sql [new file with mode: 0644]
foundation-database/public/functions/selectbalanceforbilling.sql [new file with mode: 0644]
foundation-database/public/functions/selectdiscountitemsforpayment.sql [new file with mode: 0644]
foundation-database/public/functions/selectdueitemsforpayment.sql [new file with mode: 0644]
foundation-database/public/functions/selectforbilling.sql [new file with mode: 0644]
foundation-database/public/functions/selectpayment.sql [new file with mode: 0644]
foundation-database/public/functions/selectuninvoicedshipment.sql [new file with mode: 0644]
foundation-database/public/functions/selectuninvoicedshipments.sql [new file with mode: 0644]
foundation-database/public/functions/setapjournalnumber.sql [new file with mode: 0644]
foundation-database/public/functions/setarjournalnumber.sql [new file with mode: 0644]
foundation-database/public/functions/setbudget.sql [new file with mode: 0644]
foundation-database/public/functions/setbytea.sql [new file with mode: 0644]
foundation-database/public/functions/setccbankaccnt.sql [new file with mode: 0644]
foundation-database/public/functions/seteffectivextuser.sql [new file with mode: 0644]
foundation-database/public/functions/setgljournalnumber.sql [new file with mode: 0644]
foundation-database/public/functions/setmetric.sql [new file with mode: 0644]
foundation-database/public/functions/setmetricenc.sql [new file with mode: 0644]
foundation-database/public/functions/setnextapmemonumber.sql [new file with mode: 0644]
foundation-database/public/functions/setnextarmemonumber.sql [new file with mode: 0644]
foundation-database/public/functions/setnextcashrcptnumber.sql [new file with mode: 0644]
foundation-database/public/functions/setnextchecknumber.sql [new file with mode: 0644]
foundation-database/public/functions/setnextcmnumber.sql [new file with mode: 0644]
foundation-database/public/functions/setnextcrmaccountnumber.sql [new file with mode: 0644]
foundation-database/public/functions/setnextincidentnumber.sql [new file with mode: 0644]
foundation-database/public/functions/setnextinvcnumber.sql [new file with mode: 0644]
foundation-database/public/functions/setnextnumber.sql [new file with mode: 0644]
foundation-database/public/functions/setnextponumber.sql [new file with mode: 0644]
foundation-database/public/functions/setnextprnumber.sql [new file with mode: 0644]
foundation-database/public/functions/setnextqunumber.sql [new file with mode: 0644]
foundation-database/public/functions/setnextshipmentnumber.sql [new file with mode: 0644]
foundation-database/public/functions/setnextsonumber.sql [new file with mode: 0644]
foundation-database/public/functions/setnextvcnumber.sql [new file with mode: 0644]
foundation-database/public/functions/setnextwonumber.sql [new file with mode: 0644]
foundation-database/public/functions/setusercancreateusers.sql [new file with mode: 0644]
foundation-database/public/functions/setuserpreference.sql [new file with mode: 0644]
foundation-database/public/functions/shipments.sql [new file with mode: 0644]
foundation-database/public/functions/shipshipment.sql [new file with mode: 0644]
foundation-database/public/functions/singlecharacteristicstostring.sql [new file with mode: 0644]
foundation-database/public/functions/singlelevelbom.sql [new file with mode: 0644]
foundation-database/public/functions/site.sql [new file with mode: 0644]
foundation-database/public/functions/snoozemessage.sql [new file with mode: 0644]
foundation-database/public/functions/spellamount.sql [new file with mode: 0644]
foundation-database/public/functions/splitreceipt.sql [new file with mode: 0644]
foundation-database/public/functions/splitrecurrence.sql [new file with mode: 0644]
foundation-database/public/functions/startoftime.sql [new file with mode: 0644]
foundation-database/public/functions/stdcost.sql [new file with mode: 0644]
foundation-database/public/functions/sufficientinventorytoshipitem.sql [new file with mode: 0644]
foundation-database/public/functions/sufficientinventorytoshiporder.sql [new file with mode: 0644]
foundation-database/public/functions/summarizedbom.sql [new file with mode: 0644]
foundation-database/public/functions/summarizetransactions.sql [new file with mode: 0644]
foundation-database/public/functions/summdemand.sql [new file with mode: 0644]
foundation-database/public/functions/summprod.sql [new file with mode: 0644]
foundation-database/public/functions/summtransa.sql [new file with mode: 0644]
foundation-database/public/functions/summtransc.sql [new file with mode: 0644]
foundation-database/public/functions/summtransi.sql [new file with mode: 0644]
foundation-database/public/functions/summtransr.sql [new file with mode: 0644]
foundation-database/public/functions/summtranss.sql [new file with mode: 0644]
foundation-database/public/functions/summtranst.sql [new file with mode: 0644]
foundation-database/public/functions/taxassignments.sql [new file with mode: 0644]
foundation-database/public/functions/thawaccountingperiod.sql [new file with mode: 0644]
foundation-database/public/functions/thawitemsite.sql [new file with mode: 0644]
foundation-database/public/functions/todoitem.sql [new file with mode: 0644]
foundation-database/public/functions/todoitemmove.sql [new file with mode: 0644]
foundation-database/public/functions/todoitemmovedown.sql [new file with mode: 0644]
foundation-database/public/functions/todoitemmoveup.sql [new file with mode: 0644]
foundation-database/public/functions/togglebankreccleared.sql [new file with mode: 0644]
foundation-database/public/functions/togglebomitemcost.sql [new file with mode: 0644]
foundation-database/public/functions/tonumeric.sql [new file with mode: 0644]
foundation-database/public/functions/transitwhs.sql [new file with mode: 0644]
foundation-database/public/functions/transtype.sql [new file with mode: 0644]
foundation-database/public/functions/trylock.sql [new file with mode: 0644]
foundation-database/public/functions/undomerge.sql [new file with mode: 0644]
foundation-database/public/functions/uomusedforitem.sql [new file with mode: 0644]
foundation-database/public/functions/updateabcclass.sql [new file with mode: 0644]
foundation-database/public/functions/updatecharassignment.sql [new file with mode: 0644]
foundation-database/public/functions/updatecost.sql [new file with mode: 0644]
foundation-database/public/functions/updatecustomprivs.sql [new file with mode: 0644]
foundation-database/public/functions/updateitemcost.sql [new file with mode: 0644]
foundation-database/public/functions/updateitemsiteleadtime.sql [new file with mode: 0644]
foundation-database/public/functions/updatelistprice.sql [new file with mode: 0644]
foundation-database/public/functions/updatelowerusercosts.sql [new file with mode: 0644]
foundation-database/public/functions/updatelowlevel.sql [new file with mode: 0644]
foundation-database/public/functions/updateoutlevel.sql [new file with mode: 0644]
foundation-database/public/functions/updatereorderlevel.sql [new file with mode: 0644]
foundation-database/public/functions/updateretainedearnings.sql [new file with mode: 0644]
foundation-database/public/functions/updatesoracost.sql [new file with mode: 0644]
foundation-database/public/functions/updatestdcost.sql [new file with mode: 0644]
foundation-database/public/functions/updatetodoitem.sql [new file with mode: 0644]
foundation-database/public/functions/usedefaultlocation.sql [new file with mode: 0644]
foundation-database/public/functions/usercancreateusers.sql [new file with mode: 0644]
foundation-database/public/functions/usercanlogin.sql [new file with mode: 0644]
foundation-database/public/functions/userid.sql [new file with mode: 0644]
foundation-database/public/functions/validateorderqty.sql [new file with mode: 0644]
foundation-database/public/functions/validlocation.sql [new file with mode: 0644]
foundation-database/public/functions/valueatshipping.sql [new file with mode: 0644]
foundation-database/public/functions/voidapcheck.sql [new file with mode: 0644]
foundation-database/public/functions/voidapopenvoucher.sql [new file with mode: 0644]
foundation-database/public/functions/voidcheck.sql [new file with mode: 0644]
foundation-database/public/functions/voidcreditmemo.sql [new file with mode: 0644]
foundation-database/public/functions/voidinvoice.sql [new file with mode: 0644]
foundation-database/public/functions/voidpostedapcheck.sql [new file with mode: 0644]
foundation-database/public/functions/voidpostedcheck.sql [new file with mode: 0644]
foundation-database/public/functions/woeffectivedate.sql [new file with mode: 0644]
foundation-database/public/functions/woinvavail.sql [new file with mode: 0644]
foundation-database/public/functions/woinvavailmatl.sql [new file with mode: 0644]
foundation-database/public/functions/wostarted.sql [new file with mode: 0644]

index aa9330a..3a89605 100644 (file)
@@ -2,6 +2,855 @@
   "name": "_foundation_database_",
   "databaseScripts": [
     "public/tables/metric.sql",
+    "public/functions/acknowledgemessage.sql",
+    "public/functions/actcost.sql",
+    "public/functions/addrusecount.sql",
+    "public/functions/addtaxtoglseries.sql",
+    "public/functions/addtopackinglistbatch.sql",
+    "public/functions/adjustinvvalue.sql",
+    "public/functions/adjustments.sql",
+    "public/functions/allocatedforso.sql",
+    "public/functions/allocatedforwo.sql",
+    "public/functions/alterencrypt.sql",
+    "public/functions/apaging.sql",
+    "public/functions/apapplied.sql",
+    "public/functions/apcheckpending.sql",
+    "public/functions/apcurrgain.sql",
+    "public/functions/applyapcreditmemotobalance.sql",
+    "public/functions/applyapcredits.sql",
+    "public/functions/applyarcreditmemotobalance.sql",
+    "public/functions/applycashreceiptlinebalance.sql",
+    "public/functions/applycashreceipttobalance.sql",
+    "public/functions/araging.sql",
+    "public/functions/arapplied.sql",
+    "public/functions/archivesaleshistory.sql",
+    "public/functions/arcurrgain.sql",
+    "public/functions/asofinvbal.sql",
+    "public/functions/asofinvnn.sql",
+    "public/functions/asofinvqty.sql",
+    "public/functions/attachcontact.sql",
+    "public/functions/attachquotetoopportunity.sql",
+    "public/functions/attachsalesordertoopportunity.sql",
+    "public/functions/averagesalesprice.sql",
+    "public/functions/avgcost.sql",
+    "public/functions/balanceitemsite.sql",
+    "public/functions/basecurrid.sql",
+    "public/functions/bomcontains.sql",
+    "public/functions/bomhistsequence.sql",
+    "public/functions/bomitem.sql",
+    "public/functions/bomlevelbyitem.sql",
+    "public/functions/bomworkeffective.sql",
+    "public/functions/bomworkexpired.sql",
+    "public/functions/bomworkitemsequence.sql",
+    "public/functions/bomworksequence.sql",
+    "public/functions/buildinvbal.sql",
+    "public/functions/buildsearchpath.sql",
+    "public/functions/calcbillingamts.sql",
+    "public/functions/calccashbudget.sql",
+    "public/functions/calccreditmemoamts.sql",
+    "public/functions/calcpendingarapplications.sql",
+    "public/functions/calcquoteamt.sql",
+    "public/functions/calcsalesorderamt.sql",
+    "public/functions/calcshipfreight.sql",
+    "public/functions/calctotalslipqty.sql",
+    "public/functions/calculatefreightdetail.sql",
+    "public/functions/calculatesubtax.sql",
+    "public/functions/calculatetax.sql",
+    "public/functions/calculatetaxdetail.sql",
+    "public/functions/calculatetaxdetailline.sql",
+    "public/functions/calculatetaxdetailsummary.sql",
+    "public/functions/calculatetaxhist.sql",
+    "public/functions/calcvoucheramts.sql",
+    "public/functions/calcwooperstartstub.sql",
+    "public/functions/cancelbillingselection.sql",
+    "public/functions/changeaccountingperioddates.sql",
+    "public/functions/changeaccountingyearperioddates.sql",
+    "public/functions/changefkeypointers.sql",
+    "public/functions/changepoitemduedate.sql",
+    "public/functions/changepoitemqty.sql",
+    "public/functions/changeprdate.sql",
+    "public/functions/changeprqty.sql",
+    "public/functions/changepseudofkeypointers.sql",
+    "public/functions/changewodates.sql",
+    "public/functions/changewoproject.sql",
+    "public/functions/changewoqty.sql",
+    "public/functions/characteristicstostring.sql",
+    "public/functions/checkcreditmemositeprivs.sql",
+    "public/functions/checkdetailformatted.sql",
+    "public/functions/checkinvoicesiteprivs.sql",
+    "public/functions/checkpositeprivs.sql",
+    "public/functions/checkprivilege.sql",
+    "public/functions/checkquotesiteprivs.sql",
+    "public/functions/checkrasiteprivs.sql",
+    "public/functions/checkshipmentsiteprivs.sql",
+    "public/functions/checksositeprivs.sql",
+    "public/functions/checkvouchersiteprivs.sql",
+    "public/functions/clearnumberissue.sql",
+    "public/functions/clearpayment.sql",
+    "public/functions/closeaccountingperiod.sql",
+    "public/functions/closeaccountingyearperiod.sql",
+    "public/functions/closepo.sql",
+    "public/functions/closewo.sql",
+    "public/functions/cntct.sql",
+    "public/functions/cntctdups.sql",
+    "public/functions/cntctmerge.sql",
+    "public/functions/cntctrestore.sql",
+    "public/functions/cntctselect.sql",
+    "public/functions/cntctselectcol.sql",
+    "public/functions/cntctused.sql",
+    "public/functions/coheadstatecolor.sql",
+    "public/functions/concatagg.sql",
+    "public/functions/consolidatelocations.sql",
+    "public/functions/convertcustomertoprospect.sql",
+    "public/functions/convertprospecttocustomer.sql",
+    "public/functions/convertquote.sql",
+    "public/functions/convertquotetoinvoice.sql",
+    "public/functions/copybom.sql",
+    "public/functions/copybudget.sql",
+    "public/functions/copycmd.sql",
+    "public/functions/copycontract.sql",
+    "public/functions/copyfinancialgroup.sql",
+    "public/functions/copyglseries.sql",
+    "public/functions/copyincdt.sql",
+    "public/functions/copyinvoice.sql",
+    "public/functions/copyitem.sql",
+    "public/functions/copyitemsite.sql",
+    "public/functions/copylocale.sql",
+    "public/functions/copypo.sql",
+    "public/functions/copypricingschedule.sql",
+    "public/functions/copyprj.sql",
+    "public/functions/copyproject.sql",
+    "public/functions/copyquote.sql",
+    "public/functions/copyso.sql",
+    "public/functions/copytodoitem.sql",
+    "public/functions/copyvoucher.sql",
+    "public/functions/correctporeceipt.sql",
+    "public/functions/correctproduction.sql",
+    "public/functions/correctreceipt.sql",
+    "public/functions/cosbycustomervalue.sql",
+    "public/functions/costsbycustomerbyitemsite.sql",
+    "public/functions/costsbycustomervalue.sql",
+    "public/functions/createaccountingperiod.sql",
+    "public/functions/createaccountingyearperiod.sql",
+    "public/functions/createapchecks.sql",
+    "public/functions/createapcreditmemo.sql",
+    "public/functions/createapcreditmemoapplication.sql",
+    "public/functions/createapdebitmemo.sql",
+    "public/functions/createapdiscount.sql",
+    "public/functions/createarcashdeposit.sql",
+    "public/functions/createarcreditmemo.sql",
+    "public/functions/createardebitmemo.sql",
+    "public/functions/createbillingheader.sql",
+    "public/functions/createbomitem.sql",
+    "public/functions/createcheck.sql",
+    "public/functions/createchecks.sql",
+    "public/functions/createcounttag.sql",
+    "public/functions/createcyclecountsbywarehouse.sql",
+    "public/functions/createcyclecountsbywarehousebyclasscode.sql",
+    "public/functions/createcyclecountsbywarehousebyplannercode.sql",
+    "public/functions/createfile.sql",
+    "public/functions/createinvoice.sql",
+    "public/functions/createinvoiceconsolidated.sql",
+    "public/functions/createinvoices.sql",
+    "public/functions/createmiscapcheck.sql",
+    "public/functions/createpkgschema.sql",
+    "public/functions/createpr.sql",
+    "public/functions/createpriv.sql",
+    "public/functions/createpurchasetosale.sql",
+    "public/functions/createrecurringinvoices.sql",
+    "public/functions/createrecurringitems.sql",
+    "public/functions/createtodoitem.sql",
+    "public/functions/createurl.sql",
+    "public/functions/createuser.sql",
+    "public/functions/createwo.sql",
+    "public/functions/createwomaterial.sql",
+    "public/functions/creditmemototal.sql",
+    "public/functions/crmacct.sql",
+    "public/functions/currconcat.sql",
+    "public/functions/currentapmemonumber.sql",
+    "public/functions/currentarmemonumber.sql",
+    "public/functions/currentcashrcptnumber.sql",
+    "public/functions/currentnumber.sql",
+    "public/functions/currgain.sql",
+    "public/functions/currrate.sql",
+    "public/functions/currtobase.sql",
+    "public/functions/currtocurr.sql",
+    "public/functions/currtolocal.sql",
+    "public/functions/custitem.sql",
+    "public/functions/customercanpurchase.sql",
+    "public/functions/defaultlocationname.sql",
+    "public/functions/deleteaccount.sql",
+    "public/functions/deleteaccountingperiod.sql",
+    "public/functions/deleteaccountingyearperiod.sql",
+    "public/functions/deleteaddress.sql",
+    "public/functions/deleteapcheck.sql",
+    "public/functions/deletebankadjustmenttype.sql",
+    "public/functions/deletebankreconciliation.sql",
+    "public/functions/deletebom.sql",
+    "public/functions/deletebomworkset.sql",
+    "public/functions/deletebudget.sql",
+    "public/functions/deletebudgetitems.sql",
+    "public/functions/deletecashrcpt.sql",
+    "public/functions/deletecharacteristic.sql",
+    "public/functions/deletecheck.sql",
+    "public/functions/deleteclasscode.sql",
+    "public/functions/deletecompany.sql",
+    "public/functions/deletecreditmemo.sql",
+    "public/functions/deletecustomer.sql",
+    "public/functions/deletecustomertype.sql",
+    "public/functions/deleteempgrp.sql",
+    "public/functions/deleteexpiredips.sql",
+    "public/functions/deletefile.sql",
+    "public/functions/deleteflgroup.sql",
+    "public/functions/deleteform.sql",
+    "public/functions/deletefreightclass.sql",
+    "public/functions/deleteglseries.sql",
+    "public/functions/deleteincident.sql",
+    "public/functions/deleteinvoice.sql",
+    "public/functions/deleteipsitem.sql",
+    "public/functions/deleteipsprodcat.sql",
+    "public/functions/deleteitem.sql",
+    "public/functions/deleteitemcost.sql",
+    "public/functions/deleteitemsite.sql",
+    "public/functions/deleteitemuom.sql",
+    "public/functions/deleteitemuomconv.sql",
+    "public/functions/deletelocation.sql",
+    "public/functions/deletemetasql.sql",
+    "public/functions/deleteopenrecurringitems.sql",
+    "public/functions/deleteopportunity.sql",
+    "public/functions/deletepackage.sql",
+    "public/functions/deletepo.sql",
+    "public/functions/deletepoitem.sql",
+    "public/functions/deletepr.sql",
+    "public/functions/deleteproductcategory.sql",
+    "public/functions/deleteprofitcenter.sql",
+    "public/functions/deleteproject.sql",
+    "public/functions/deleteprojecttask.sql",
+    "public/functions/deleteqryhead.sql",
+    "public/functions/deletequote.sql",
+    "public/functions/deleterecvfororder.sql",
+    "public/functions/deletesalescategory.sql",
+    "public/functions/deleteshippingcharge.sql",
+    "public/functions/deleteshippingchargetype.sql",
+    "public/functions/deleteshipto.sql",
+    "public/functions/deleteso.sql",
+    "public/functions/deletesoitem.sql",
+    "public/functions/deletestandardjournal.sql",
+    "public/functions/deletestandardjournalgroup.sql",
+    "public/functions/deletesubaccount.sql",
+    "public/functions/deletesubaccounttype.sql",
+    "public/functions/deletetax.sql",
+    "public/functions/deletetaxclass.sql",
+    "public/functions/deletetaxtype.sql",
+    "public/functions/deletetaxzone.sql",
+    "public/functions/deletetodoitem.sql",
+    "public/functions/deleteunusedclasscodes.sql",
+    "public/functions/deleteunusedfreightclasses.sql",
+    "public/functions/deleteunusedproductcategories.sql",
+    "public/functions/deleteuom.sql",
+    "public/functions/deleteuomconv.sql",
+    "public/functions/deleteurl.sql",
+    "public/functions/deleteuserpreference.sql",
+    "public/functions/deletevendoraddr.sql",
+    "public/functions/deletevendortype.sql",
+    "public/functions/deletewo.sql",
+    "public/functions/deletewomaterial.sql",
+    "public/functions/detachccpayfromso.sql",
+    "public/functions/detachcontact.sql",
+    "public/functions/detag.sql",
+    "public/functions/detailednnqoh.sql",
+    "public/functions/detailedqoh.sql",
+    "public/functions/determinediscountdate.sql",
+    "public/functions/determineduedate.sql",
+    "public/functions/disablepackage.sql",
+    "public/functions/distributeitemlocseries.sql",
+    "public/functions/distributetodefault.sql",
+    "public/functions/distributetodefaultitemloc.sql",
+    "public/functions/distributetolocations.sql",
+    "public/functions/distributevoucherline.sql",
+    "public/functions/dopostcosts.sql",
+    "public/functions/doupdatecosts.sql",
+    "public/functions/dropifexists.sql",
+    "public/functions/editccnumber.sql",
+    "public/functions/enablepackage.sql",
+    "public/functions/endoftime.sql",
+    "public/functions/entercount.sql",
+    "public/functions/enterporeceipt.sql",
+    "public/functions/enterporeturn.sql",
+    "public/functions/enterreceipt.sql",
+    "public/functions/expirecreditcard.sql",
+    "public/functions/explodebom.sql",
+    "public/functions/explodekit.sql",
+    "public/functions/explodephantomorder.sql",
+    "public/functions/explodewo.sql",
+    "public/functions/explodewoeffective.sql",
+    "public/functions/fetchapmemonumber.sql",
+    "public/functions/fetcharmemonumber.sql",
+    "public/functions/fetchcashrcptnumber.sql",
+    "public/functions/fetchcmnumber.sql",
+    "public/functions/fetchcrmaccountnumber.sql",
+    "public/functions/fetchdefaultfob.sql",
+    "public/functions/fetchdefaultshipvia.sql",
+    "public/functions/fetchglsequence.sql",
+    "public/functions/fetchincidentnumber.sql",
+    "public/functions/fetchinvcnumber.sql",
+    "public/functions/fetchitemuomconvtypes.sql",
+    "public/functions/fetchjournalnumber.sql",
+    "public/functions/fetchmetricbool.sql",
+    "public/functions/fetchmetrictext.sql",
+    "public/functions/fetchmetricvalue.sql",
+    "public/functions/fetchnextchecknumber.sql",
+    "public/functions/fetchnextnumber.sql",
+    "public/functions/fetchponumber.sql",
+    "public/functions/fetchprefwarehousid.sql",
+    "public/functions/fetchprnumber.sql",
+    "public/functions/fetchqunumber.sql",
+    "public/functions/fetchshipmentnumber.sql",
+    "public/functions/fetchsonumber.sql",
+    "public/functions/fetchtonumber.sql",
+    "public/functions/fetchusrprefbool.sql",
+    "public/functions/fetchvonumber.sql",
+    "public/functions/fetchwonumber.sql",
+    "public/functions/financialreport.sql",
+    "public/functions/findapaccount.sql",
+    "public/functions/findapdiscountaccount.sql",
+    "public/functions/findapprepaidaccount.sql",
+    "public/functions/findaraccount.sql",
+    "public/functions/findardiscountaccount.sql",
+    "public/functions/findcalendarorigin.sql",
+    "public/functions/findcustomerform.sql",
+    "public/functions/finddeferredaccount.sql",
+    "public/functions/findfreightaccount.sql",
+    "public/functions/findperiodend.sql",
+    "public/functions/findperiodstart.sql",
+    "public/functions/findprepaidaccount.sql",
+    "public/functions/findsalesaccnt.sql",
+    "public/functions/findspecialfinancial.sql",
+    "public/functions/first_agg.sql",
+    "public/functions/firstline.sql",
+    "public/functions/fixacl.sql",
+    "public/functions/formatabachecks.sql",
+    "public/functions/formatachchecks.sql",
+    "public/functions/formatachcompanyid.sql",
+    "public/functions/formataddr.sql",
+    "public/functions/formatboolyn.sql",
+    "public/functions/formatbooseq.sql",
+    "public/functions/formatbytea.sql",
+    "public/functions/formatccdashes.sql",
+    "public/functions/formatccnumber.sql",
+    "public/functions/formatcntctname.sql",
+    "public/functions/formatcost.sql",
+    "public/functions/formatcounttagbarcode.sql",
+    "public/functions/formatcreditmemonumber.sql",
+    "public/functions/formatdate.sql",
+    "public/functions/formatdatetime.sql",
+    "public/functions/formatextprice.sql",
+    "public/functions/formatflitemdescrip.sql",
+    "public/functions/formatglaccount.sql",
+    "public/functions/formatglaccountlong.sql",
+    "public/functions/formatindent.sql",
+    "public/functions/formatinterval.sql",
+    "public/functions/formatinvcnumber.sql",
+    "public/functions/formatitemsitebarcode.sql",
+    "public/functions/formatlocationbarcode.sql",
+    "public/functions/formatlocationcontentsbarcode.sql",
+    "public/functions/formatlocationissuebarcode.sql",
+    "public/functions/formatlocationname.sql",
+    "public/functions/formatlotserialnumber.sql",
+    "public/functions/formatmoney.sql",
+    "public/functions/formatnumeric.sql",
+    "public/functions/formatperiodname.sql",
+    "public/functions/formatplonumber.sql",
+    "public/functions/formatprcnt.sql",
+    "public/functions/formatprice.sql",
+    "public/functions/formatpurchprice.sql",
+    "public/functions/formatqty.sql",
+    "public/functions/formatqtyper.sql",
+    "public/functions/formatratio.sql",
+    "public/functions/formatrevnumber.sql",
+    "public/functions/formatsalesprice.sql",
+    "public/functions/formatscrap.sql",
+    "public/functions/formatshipmentnumber.sql",
+    "public/functions/formatsobarcode.sql",
+    "public/functions/formatsoitembarcode.sql",
+    "public/functions/formatsoitemnumber.sql",
+    "public/functions/formatsolinenumber.sql",
+    "public/functions/formatsonumber.sql",
+    "public/functions/formattime.sql",
+    "public/functions/formatuomratio.sql",
+    "public/functions/formatuserbarcode.sql",
+    "public/functions/formatweight.sql",
+    "public/functions/formatwobarcode.sql",
+    "public/functions/formatwonumber.sql",
+    "public/functions/formatwooperseq.sql",
+    "public/functions/forwardupdateaccount.sql",
+    "public/functions/forwardupdateinvbalance.sql",
+    "public/functions/forwardupdateitemsite.sql",
+    "public/functions/forwardupdatetrialbalance.sql",
+    "public/functions/freezeaccountingperiod.sql",
+    "public/functions/freightdetail.sql",
+    "public/functions/freightdetailquote.sql",
+    "public/functions/freightforrecv.sql",
+    "public/functions/getactiverevid.sql",
+    "public/functions/getaddrid.sql",
+    "public/functions/getadjustmenttaxtypeid.sql",
+    "public/functions/getaropenid.sql",
+    "public/functions/getbankaccntid.sql",
+    "public/functions/getbomitemid.sql",
+    "public/functions/getbooitemseqid.sql",
+    "public/functions/getbudgheadid.sql",
+    "public/functions/getcashrcptid.sql",
+    "public/functions/getcharid.sql",
+    "public/functions/getclasscodeid.sql",
+    "public/functions/getcmheadid.sql",
+    "public/functions/getcmnttypeid.sql",
+    "public/functions/getcntctid.sql",
+    "public/functions/getcoheadid.sql",
+    "public/functions/getcoitemid.sql",
+    "public/functions/getcostcatid.sql",
+    "public/functions/getcostelemid.sql",
+    "public/functions/getcrmacctid.sql",
+    "public/functions/getcurrid.sql",
+    "public/functions/getcustnamefrominfo.sql",
+    "public/functions/getcustid.sql",
+    "public/functions/getcusttypeid.sql",
+    "public/functions/getdeptid.sql",
+    "public/functions/getediprofileid.sql",
+    "public/functions/getediprofilename.sql",
+    "public/functions/geteffectivextuser.sql",
+    "public/functions/getempid.sql",
+    "public/functions/getexpcatid.sql",
+    "public/functions/getflcoldata.sql",
+    "public/functions/getflstmthead.sql",
+    "public/functions/getfltrendhead.sql",
+    "public/functions/getfreightclassid.sql",
+    "public/functions/getfreighttaxtypeid.sql",
+    "public/functions/getgainlossaccntid.sql",
+    "public/functions/getglaccntid.sql",
+    "public/functions/getimageid.sql",
+    "public/functions/getincdtcatid.sql",
+    "public/functions/getincdtcrmacctid.sql",
+    "public/functions/getincdtpriorityid.sql",
+    "public/functions/getincdtresolutionid.sql",
+    "public/functions/getincdtseverityid.sql",
+    "public/functions/getincidentid.sql",
+    "public/functions/getinvcheadid.sql",
+    "public/functions/getinvcitemlotserial.sql",
+    "public/functions/getipsheadid.sql",
+    "public/functions/getipsitemid.sql",
+    "public/functions/getipsprodcatid.sql",
+    "public/functions/getitemid.sql",
+    "public/functions/getitemidfromupc.sql",
+    "public/functions/getitemsiteid.sql",
+    "public/functions/getitemsrcid.sql",
+    "public/functions/getitemtaxtype.sql",
+    "public/functions/getlasttrialbalid.sql",
+    "public/functions/getlocationid.sql",
+    "public/functions/getlotserialid.sql",
+    "public/functions/getopheadid.sql",
+    "public/functions/getpacklistcharname.sql",
+    "public/functions/getpacklistcharvalue.sql",
+    "public/functions/getpacklistitemlotserial.sql",
+    "public/functions/getperiodid.sql",
+    "public/functions/getpkgheadid.sql",
+    "public/functions/getplancodeid.sql",
+    "public/functions/getpoheadid.sql",
+    "public/functions/getpoitemid.sql",
+    "public/functions/getprjaccntid.sql",
+    "public/functions/getprjid.sql",
+    "public/functions/getprjtaskid.sql",
+    "public/functions/getprodcatid.sql",
+    "public/functions/getprospectid.sql",
+    "public/functions/getquoteid.sql",
+    "public/functions/getquotelineitemid.sql",
+    "public/functions/getrevid.sql",
+    "public/functions/getrsnid.sql",
+    "public/functions/getsalescatid.sql",
+    "public/functions/getsaleslineitemid.sql",
+    "public/functions/getsalesorderid.sql",
+    "public/functions/getsalesrepid.sql",
+    "public/functions/getsaletypeid.sql",
+    "public/functions/getshiftid.sql",
+    "public/functions/getshipchrgid.sql",
+    "public/functions/getshipformid.sql",
+    "public/functions/getshipheadid.sql",
+    "public/functions/getshiptoid.sql",
+    "public/functions/getshiptonumberfrominfo.sql",
+    "public/functions/getshipviaid.sql",
+    "public/functions/getshipzoneid.sql",
+    "public/functions/getsitetypeid.sql",
+    "public/functions/getsoscheddate.sql",
+    "public/functions/getsostatus.sql",
+    "public/functions/getsubtax.sql",
+    "public/functions/gettaxauthid.sql",
+    "public/functions/gettaxid.sql",
+    "public/functions/gettaxtypeid.sql",
+    "public/functions/gettaxzoneid.sql",
+    "public/functions/gettermsid.sql",
+    "public/functions/getunassignedaccntid.sql",
+    "public/functions/getuomid.sql",
+    "public/functions/getuomtypeid.sql",
+    "public/functions/getusrid.sql",
+    "public/functions/getvendaddrid.sql",
+    "public/functions/getvendid.sql",
+    "public/functions/getvendtypeid.sql",
+    "public/functions/getwarehousid.sql",
+    "public/functions/getwhsezoneid.sql",
+    "public/functions/grantallmodulecmnttypesource.sql",
+    "public/functions/grantallmodulepriv.sql",
+    "public/functions/grantallmoduleprivgroup.sql",
+    "public/functions/grantcmnttypesource.sql",
+    "public/functions/grantgroup.sql",
+    "public/functions/grantpriv.sql",
+    "public/functions/grantprivgroup.sql",
+    "public/functions/hasalarms.sql",
+    "public/functions/hasevents.sql",
+    "public/functions/hasmessages.sql",
+    "public/functions/haspriv.sql",
+    "public/functions/hasprivonobject.sql",
+    "public/functions/implodewo.sql",
+    "public/functions/incdt.sql",
+    "public/functions/indentedbom.sql",
+    "public/functions/indentedwhereused.sql",
+    "public/functions/indentedwo.sql",
+    "public/functions/indentedwomatl.sql",
+    "public/functions/initeffectivextuser.sql",
+    "public/functions/initialdistribution.sql",
+    "public/functions/insertIntoGLSeries.sql",
+    "public/functions/insertccard.sql",
+    "public/functions/insertflgroup.sql",
+    "public/functions/insertgltransaction.sql",
+    "public/functions/insertitemcost.sql",
+    "public/functions/intervaltominutes.sql",
+    "public/functions/invadjustment.sql",
+    "public/functions/invexpense.sql",
+    "public/functions/invhistsense.sql",
+    "public/functions/invoicetotal.sql",
+    "public/functions/invreceipt.sql",
+    "public/functions/invscrap.sql",
+    "public/functions/isdba.sql",
+    "public/functions/ismulticurr.sql",
+    "public/functions/isnumeric.sql",
+    "public/functions/issueallbalancetoshipping.sql",
+    "public/functions/issuelinebalancetoshipping.sql",
+    "public/functions/issues.sql",
+    "public/functions/issuetoshipping.sql",
+    "public/functions/issuewomaterial.sql",
+    "public/functions/issuewomaterialbatch.sql",
+    "public/functions/itemaltcapinvrat.sql",
+    "public/functions/itemaltcapuom.sql",
+    "public/functions/itemcapinvrat.sql",
+    "public/functions/itemcapuom.sql",
+    "public/functions/itemcharprice.sql",
+    "public/functions/itemcharvalue.sql",
+    "public/functions/itemcost.sql",
+    "public/functions/iteminventoryuominuse.sql",
+    "public/functions/iteminvpricerat.sql",
+    "public/functions/itemipsprice.sql",
+    "public/functions/itemlocdistqty.sql",
+    "public/functions/itemprice.sql",
+    "public/functions/itemsellinguom.sql",
+    "public/functions/itemsrcprice.sql",
+    "public/functions/itemuombytype.sql",
+    "public/functions/itemuomfractionalbytype.sql",
+    "public/functions/itemuomfractionalbyuom.sql",
+    "public/functions/itemuomratiobytype.sql",
+    "public/functions/itemuomtouom.sql",
+    "public/functions/itemuomtouomratio.sql",
+    "public/functions/last_agg.sql",
+    "public/functions/login.sql",
+    "public/functions/logout.sql",
+    "public/functions/lowercost.sql",
+    "public/functions/maintainbomworkspace.sql",
+    "public/functions/markapcheckasposted.sql",
+    "public/functions/markapcheckasprinted.sql",
+    "public/functions/markcheckasposted.sql",
+    "public/functions/markcheckasprinted.sql",
+    "public/functions/massexpirebomitem.sql",
+    "public/functions/massreplacebomitem.sql",
+    "public/functions/merge2crmaccts.sql",
+    "public/functions/mergecrmaccts.sql",
+    "public/functions/movebomitemdown.sql",
+    "public/functions/movebomitemup.sql",
+    "public/functions/moveccarddown.sql",
+    "public/functions/moveccardup.sql",
+    "public/functions/moveflgroupdown.sql",
+    "public/functions/moveflgroupup.sql",
+    "public/functions/moveflitemdown.sql",
+    "public/functions/moveflitemup.sql",
+    "public/functions/moveflspecdown.sql",
+    "public/functions/moveflspecup.sql",
+    "public/functions/movescript.sql",
+    "public/functions/moveuiform.sql",
+    "public/functions/moveupdown.sql",
+    "public/functions/nextperiodbyinterval.sql",
+    "public/functions/nextprsubnumber.sql",
+    "public/functions/nextwosubnumber.sql",
+    "public/functions/noneg.sql",
+    "public/functions/nopos.sql",
+    "public/functions/normalizetrialbal.sql",
+    "public/functions/numofdatabaseusers.sql",
+    "public/functions/numofserverusers.sql",
+    "public/functions/openaccountingperiod.sql",
+    "public/functions/openaccountingyearperiod.sql",
+    "public/functions/openapitemsvalue.sql",
+    "public/functions/openaritemsvalue.sql",
+    "public/functions/openrecurringitems.sql",
+    "public/functions/ophead.sql",
+    "public/functions/orderedbypo.sql",
+    "public/functions/orderedbywo.sql",
+    "public/functions/orderhead.sql",
+    "public/functions/orderitem.sql",
+    "public/functions/orderitemdata.sql",
+    "public/functions/packageisenabled.sql",
+    "public/functions/pkgmaybemodified.sql",
+    "public/functions/postapcheck.sql",
+    "public/functions/postapchecks.sql",
+    "public/functions/postapcreditmemoapplication.sql",
+    "public/functions/postapopenitems.sql",
+    "public/functions/postarcreditmemoapplication.sql",
+    "public/functions/postaropenitems.sql",
+    "public/functions/postbankadjustment.sql",
+    "public/functions/postbankreconciliation.sql",
+    "public/functions/postbillingselection.sql",
+    "public/functions/postbillingselectionconsolidated.sql",
+    "public/functions/postbillingselections.sql",
+    "public/functions/postcashreceipt.sql",
+    "public/functions/postcashreceiptdisc.sql",
+    "public/functions/postcccashreceipt.sql",
+    "public/functions/postcccredit.sql",
+    "public/functions/postccvoid.sql",
+    "public/functions/postcheck.sql",
+    "public/functions/postchecks.sql",
+    "public/functions/postcomment.sql",
+    "public/functions/postcost.sql",
+    "public/functions/postcountslip.sql",
+    "public/functions/postcounttag.sql",
+    "public/functions/postcounttaglocation.sql",
+    "public/functions/postcounttags.sql",
+    "public/functions/postcreditmemo.sql",
+    "public/functions/postcreditmemos.sql",
+    "public/functions/postglseries.sql",
+    "public/functions/postglseriesnosumm.sql",
+    "public/functions/postintoinvbalance.sql",
+    "public/functions/postintotrialbalance.sql",
+    "public/functions/postinvhist.sql",
+    "public/functions/postinvoice.sql",
+    "public/functions/postinvoices.sql",
+    "public/functions/postinvtrans.sql",
+    "public/functions/postitemlocseries.sql",
+    "public/functions/postjournals.sql",
+    "public/functions/postmessage.sql",
+    "public/functions/postmisccount.sql",
+    "public/functions/postpogltransactions.sql",
+    "public/functions/postporeceipt.sql",
+    "public/functions/postporeceipts.sql",
+    "public/functions/postporeturncreditmemo.sql",
+    "public/functions/postporeturns.sql",
+    "public/functions/postproduction.sql",
+    "public/functions/postreceipt.sql",
+    "public/functions/postreceipts.sql",
+    "public/functions/postsogltransactions.sql",
+    "public/functions/postsoitemproduction.sql",
+    "public/functions/poststandardjournal.sql",
+    "public/functions/poststandardjournalgroup.sql",
+    "public/functions/postvalueintoinvbalance.sql",
+    "public/functions/postvoucher.sql",
+    "public/functions/postvouchers.sql",
+    "public/functions/primarykeyfields.sql",
+    "public/functions/prj.sql",
+    "public/functions/prjtask.sql",
+    "public/functions/purgecreditmemos.sql",
+    "public/functions/purgecrmacctmerge.sql",
+    "public/functions/purgeinvoicerecord.sql",
+    "public/functions/purgeinvoicerecords.sql",
+    "public/functions/purgepostedcountslips.sql",
+    "public/functions/purgepostedcounttags.sql",
+    "public/functions/purgeshipments.sql",
+    "public/functions/qtyallocated.sql",
+    "public/functions/qtyatshipping.sql",
+    "public/functions/qtyavailable.sql",
+    "public/functions/qtyinshipment.sql",
+    "public/functions/qtylocation.sql",
+    "public/functions/qtyordered.sql",
+    "public/functions/qtypr.sql",
+    "public/functions/qtytoreceive.sql",
+    "public/functions/recallshipment.sql",
+    "public/functions/recallwo.sql",
+    "public/functions/receipts.sql",
+    "public/functions/releaseapmemonumber.sql",
+    "public/functions/releasearmemonumber.sql",
+    "public/functions/releasecashrcptnumber.sql",
+    "public/functions/releasecmnumber.sql",
+    "public/functions/releasecrmaccountnumber.sql",
+    "public/functions/releaseincidentnumber.sql",
+    "public/functions/releaseinvcnumber.sql",
+    "public/functions/releasenumber.sql",
+    "public/functions/releaseponumber.sql",
+    "public/functions/releaseprnumber.sql",
+    "public/functions/releasepurchaseorder.sql",
+    "public/functions/releasequnumber.sql",
+    "public/functions/releaseshipmentnumber.sql",
+    "public/functions/releasesohead.sql",
+    "public/functions/releasesonumber.sql",
+    "public/functions/releaseunusedbillingheader.sql",
+    "public/functions/releasevonumber.sql",
+    "public/functions/releasewo.sql",
+    "public/functions/releasewonumber.sql",
+    "public/functions/relocateinventory.sql",
+    "public/functions/reorderdate.sql",
+    "public/functions/replaceallvoidedapchecks.sql",
+    "public/functions/replaceallvoidedchecks.sql",
+    "public/functions/replacevoidedapcheck.sql",
+    "public/functions/replacevoidedcheck.sql",
+    "public/functions/reprioritizewo.sql",
+    "public/functions/resetdbobjperms.sql",
+    "public/functions/resetlowlevelcode.sql",
+    "public/functions/resetqohbalance.sql",
+    "public/functions/resolvecosaccount.sql",
+    "public/functions/resolvecowaccount.sql",
+    "public/functions/resolvecreditaccount.sql",
+    "public/functions/resolvesalesaccount.sql",
+    "public/functions/restoresaleshistory.sql",
+    "public/functions/returncompleteshipment.sql",
+    "public/functions/returnitemshipments.sql",
+    "public/functions/returnshipmenttransaction.sql",
+    "public/functions/returnwomaterial.sql",
+    "public/functions/returnwomaterialbatch.sql",
+    "public/functions/reversecashreceipt.sql",
+    "public/functions/reversecashreceiptdisc.sql",
+    "public/functions/reverseglseries.sql",
+    "public/functions/revokeallmodulecmnttypesource.sql",
+    "public/functions/revokeallmodulepriv.sql",
+    "public/functions/revokeallmoduleprivgroup.sql",
+    "public/functions/revokecmnttypesource.sql",
+    "public/functions/revokegroup.sql",
+    "public/functions/revokepriv.sql",
+    "public/functions/revokeprivgroup.sql",
+    "public/functions/rollupactualcost.sql",
+    "public/functions/rollupsoracost.sql",
+    "public/functions/rollupstandardcost.sql",
+    "public/functions/roundcost.sql",
+    "public/functions/roundqty.sql",
+    "public/functions/roundsale.sql",
+    "public/functions/roundup.sql",
+    "public/functions/saveaddr.sql",
+    "public/functions/savealarm.sql",
+    "public/functions/savebomhead.sql",
+    "public/functions/savecntct.sql",
+    "public/functions/saveimageass.sql",
+    "public/functions/saveipsitem.sql",
+    "public/functions/saveipsprodcat.sql",
+    "public/functions/saveitemimage.sql",
+    "public/functions/saveitemuomconv.sql",
+    "public/functions/savemetasql.sql",
+    "public/functions/scraps.sql",
+    "public/functions/scrapwomaterial.sql",
+    "public/functions/selectbalanceforbilling.sql",
+    "public/functions/selectdiscountitemsforpayment.sql",
+    "public/functions/selectdueitemsforpayment.sql",
+    "public/functions/selectforbilling.sql",
+    "public/functions/selectpayment.sql",
+    "public/functions/selectuninvoicedshipment.sql",
+    "public/functions/selectuninvoicedshipments.sql",
+    "public/functions/setapjournalnumber.sql",
+    "public/functions/setarjournalnumber.sql",
+    "public/functions/setbudget.sql",
+    "public/functions/setbytea.sql",
+    "public/functions/setccbankaccnt.sql",
+    "public/functions/seteffectivextuser.sql",
+    "public/functions/setgljournalnumber.sql",
+    "public/functions/setmetric.sql",
+    "public/functions/setmetricenc.sql",
+    "public/functions/setnextapmemonumber.sql",
+    "public/functions/setnextarmemonumber.sql",
+    "public/functions/setnextcashrcptnumber.sql",
+    "public/functions/setnextchecknumber.sql",
+    "public/functions/setnextcmnumber.sql",
+    "public/functions/setnextcrmaccountnumber.sql",
+    "public/functions/setnextincidentnumber.sql",
+    "public/functions/setnextinvcnumber.sql",
+    "public/functions/setnextnumber.sql",
+    "public/functions/setnextponumber.sql",
+    "public/functions/setnextprnumber.sql",
+    "public/functions/setnextqunumber.sql",
+    "public/functions/setnextshipmentnumber.sql",
+    "public/functions/setnextsonumber.sql",
+    "public/functions/setnextvcnumber.sql",
+    "public/functions/setnextwonumber.sql",
+    "public/functions/setusercancreateusers.sql",
+    "public/functions/setuserpreference.sql",
+    "public/functions/shipments.sql",
+    "public/functions/shipshipment.sql",
+    "public/functions/singlecharacteristicstostring.sql",
+    "public/functions/singlelevelbom.sql",
+    "public/functions/site.sql",
+    "public/functions/snoozemessage.sql",
+    "public/functions/spellamount.sql",
+    "public/functions/splitreceipt.sql",
+    "public/functions/splitrecurrence.sql",
+    "public/functions/startoftime.sql",
+    "public/functions/stdcost.sql",
+    "public/functions/sufficientinventorytoshipitem.sql",
+    "public/functions/sufficientinventorytoshiporder.sql",
+    "public/functions/summarizedbom.sql",
+    "public/functions/summarizetransactions.sql",
+    "public/functions/summdemand.sql",
+    "public/functions/summprod.sql",
+    "public/functions/summtransa.sql",
+    "public/functions/summtransc.sql",
+    "public/functions/summtransi.sql",
+    "public/functions/summtransr.sql",
+    "public/functions/summtranss.sql",
+    "public/functions/summtranst.sql",
+    "public/functions/taxassignments.sql",
+    "public/functions/thawaccountingperiod.sql",
+    "public/functions/thawitemsite.sql",
+    "public/functions/todoitem.sql",
+    "public/functions/todoitemmove.sql",
+    "public/functions/todoitemmovedown.sql",
+    "public/functions/todoitemmoveup.sql",
+    "public/functions/togglebankreccleared.sql",
+    "public/functions/togglebomitemcost.sql",
+    "public/functions/tonumeric.sql",
+    "public/functions/transitwhs.sql",
+    "public/functions/transtype.sql",
+    "public/functions/trylock.sql",
+    "public/functions/undomerge.sql",
+    "public/functions/uomusedforitem.sql",
+    "public/functions/updateabcclass.sql",
+    "public/functions/updatecharassignment.sql",
+    "public/functions/updatecost.sql",
+    "public/functions/updatecustomprivs.sql",
+    "public/functions/updateitemcost.sql",
+    "public/functions/updateitemsiteleadtime.sql",
+    "public/functions/updatelistprice.sql",
+    "public/functions/updatelowerusercosts.sql",
+    "public/functions/updatelowlevel.sql",
+    "public/functions/updateoutlevel.sql",
+    "public/functions/updatereorderlevel.sql",
+    "public/functions/updateretainedearnings.sql",
+    "public/functions/updatesoracost.sql",
+    "public/functions/updatestdcost.sql",
+    "public/functions/updatetodoitem.sql",
+    "public/functions/usedefaultlocation.sql",
+    "public/functions/usercancreateusers.sql",
+    "public/functions/usercanlogin.sql",
+    "public/functions/userid.sql",
+    "public/functions/validateorderqty.sql",
+    "public/functions/validlocation.sql",
+    "public/functions/valueatshipping.sql",
+    "public/functions/voidapcheck.sql",
+    "public/functions/voidapopenvoucher.sql",
+    "public/functions/voidcheck.sql",
+    "public/functions/voidcreditmemo.sql",
+    "public/functions/voidinvoice.sql",
+    "public/functions/voidpostedapcheck.sql",
+    "public/functions/voidpostedcheck.sql",
+    "public/functions/woeffectivedate.sql",
+    "public/functions/woinvavail.sql",
+    "public/functions/woinvavailmatl.sql",
+    "public/functions/wostarted.sql",
     "public/tables/metasql/accountNumbers-detail.mql",
     "public/tables/metasql/addresses-detail.mql",
     "public/tables/metasql/allocations-detail.mql",
diff --git a/foundation-database/public/functions/acknowledgemessage.sql b/foundation-database/public/functions/acknowledgemessage.sql
new file mode 100644 (file)
index 0000000..fd3cb1a
--- /dev/null
@@ -0,0 +1,19 @@
+
+CREATE OR REPLACE FUNCTION acknowledgeMessage(INTEGER) RETURNS BOOLEAN AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pMsgid ALIAS FOR $1;
+
+BEGIN
+
+  UPDATE msguser
+  SET msguser_viewed=CURRENT_TIMESTAMP
+  WHERE ( (msguser_msg_id=pMsgid)
+   AND (msguser_username=getEffectiveXtUser()) );
+
+  RETURN TRUE;
+
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/actcost.sql b/foundation-database/public/functions/actcost.sql
new file mode 100644 (file)
index 0000000..a4d70e9
--- /dev/null
@@ -0,0 +1,45 @@
+CREATE OR REPLACE FUNCTION actCost(INTEGER) RETURNS NUMERIC AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN actCost($1, NULL, baseCurrId());
+END;
+' LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION actCost(INTEGER, INTEGER) RETURNS NUMERIC AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN actCost($1, $2, baseCurrId());
+END;
+' LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION actCost(INTEGER, INTEGER, INTEGER) RETURNS NUMERIC AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemid ALIAS FOR $1;
+  pBomitemid ALIAS FOR $2;
+  pCurrid ALIAS FOR $3;
+  _cost NUMERIC;
+
+BEGIN
+
+  -- Return actual cost in the given currency at the current conversion rate
+  SELECT SUM(CASE WHEN (bomitemcost_id IS NOT NULL) THEN
+                  ROUND(currToCurr(bomitemcost_curr_id, pCurrid, bomitemcost_actcost, CURRENT_DATE), 6)
+                  ELSE
+                  ROUND(currToCurr(itemcost_curr_id, pCurrid, itemcost_actcost, CURRENT_DATE), 6)
+             END) INTO _cost
+  FROM itemcost
+    LEFT OUTER JOIN bomitemcost ON (bomitemcost_bomitem_id=pBomitemid AND bomitemcost_costelem_id=itemcost_costelem_id)
+  WHERE (itemcost_item_id=pItemid);
+
+  IF (_cost IS NULL) THEN
+    RETURN 0;
+  ELSE
+    RETURN _cost;
+  END IF;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/addrusecount.sql b/foundation-database/public/functions/addrusecount.sql
new file mode 100644 (file)
index 0000000..d96cdd4
--- /dev/null
@@ -0,0 +1,56 @@
+CREATE OR REPLACE FUNCTION addrUseCount(integer) RETURNS integer STABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pAddrId ALIAS FOR $1;
+  _fk RECORD;
+  _r RECORD;
+  _seq INTEGER;
+  _col TEXT;
+  _qry TEXT;
+  _count INTEGER = 0;
+
+BEGIN
+  -- Determine where this address is used by analyzing foreign key linkages
+  -- TO DO: Can this be rationalized with cntctused(int)?
+  FOR _fk IN
+    SELECT pg_namespace.nspname AS schemaname, con.relname AS tablename, conkey AS seq, conrelid AS class_id 
+    FROM pg_constraint, pg_class f, pg_class con, pg_namespace
+    WHERE confrelid=f.oid
+    AND conrelid=con.oid
+    AND f.relname = 'addr'
+    AND con.relnamespace=pg_namespace.oid
+    AND con.relname NOT IN ('pohead') -- exception(s) where address key doesn't actually drive document information
+  LOOP
+    -- Validate
+    IF (ARRAY_UPPER(_fk.seq,1) > 1) THEN
+      RAISE EXCEPTION 'Checks to tables where the address is one of multiple foreign key columns is not supported. Error on Table: %',
+        pg_namespace.nspname || '.' || con.relname;
+    END IF;
+    
+    _seq := _fk.seq[1];
+
+    -- Get the specific column name
+    SELECT attname INTO _col
+    FROM pg_attribute, pg_class
+    WHERE ((attrelid=pg_class.oid)
+    AND (pg_class.oid=_fk.class_id)
+    AND (attnum=_seq));
+
+    -- See if there are dependencies
+    _qry := 'SELECT * 
+            FROM ' || _fk.schemaname || '.' || _fk.tablename || '
+            WHERE ('|| _col || '=' || pAddrId || ');';
+
+    FOR _r IN 
+      EXECUTE _qry
+    LOOP
+      _count := _count + 1;
+    END LOOP;
+         
+  END LOOP;
+
+  RETURN _count;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/addtaxtoglseries.sql b/foundation-database/public/functions/addtaxtoglseries.sql
new file mode 100644 (file)
index 0000000..00f472f
--- /dev/null
@@ -0,0 +1,63 @@
+-- add tax information to a GL Series
+-- return the base currency value of the GL Series records inserted
+--       NULL if there has been an error
+
+CREATE OR REPLACE FUNCTION addTaxToGLSeries(INTEGER, TEXT, TEXT, TEXT, INTEGER, DATE, DATE, TEXT, INTEGER, TEXT) RETURNS NUMERIC AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSequence    ALIAS FOR $1;
+  pSource      ALIAS FOR $2;
+  pDocType     ALIAS FOR $3;
+  pDocNumber   ALIAS FOR $4;
+  pCurrId     ALIAS FOR $5;
+  pExchDate    ALIAS FOR $6;
+  pDistDate    ALIAS FOR $7;
+  pTableName   ALIAS FOR $8;
+  pParentId    ALIAS FOR $9;
+  pNotes       ALIAS FOR $10;
+
+  _count       INTEGER := 0;
+  _baseTax     NUMERIC := 0;
+  _returnVal   NUMERIC := 0;
+  _t           RECORD;
+  _test        INTEGER := 0;
+
+BEGIN
+
+-- This is just a fancy select statement on taxhist.
+-- Because all tax records tables inherit from taxhist,
+-- we can use the same select statement for all.
+-- https://www.postgresql.org/docs/8.1/static/ddl-inherit.html
+-- pTableName in the where clause narrows down the selection
+-- to the correct sub table.
+
+  FOR _t IN SELECT *
+            FROM taxhist JOIN tax ON (tax_id = taxhist_tax_id)
+                         JOIN pg_class ON (pg_class.oid = taxhist.tableoid)
+            WHERE ( (taxhist_parent_id = pParentId)
+              AND   (relname = pTableName) ) LOOP
+
+    _count := _count + 1;
+    _baseTax := currToBase(pCurrId, _t.taxhist_tax, pExchDate);
+    _returnVal := _returnVal + _baseTax;
+    PERFORM insertIntoGLSeries( pSequence, pSource, pDocType, pDocNumber,
+                                _t.tax_sales_accnt_id, _baseTax,
+                                pDistDate, pNotes );
+                                
+    UPDATE taxhist SET 
+      taxhist_docdate=pExchDate,
+      taxhist_distdate=pDistDate,
+      taxhist_curr_id=pCurrId,
+      taxhist_curr_rate=curr_rate
+    FROM curr_rate
+    WHERE ((taxhist_id=_t.taxhist_id)
+      AND (pCurrId=curr_id)
+      AND ( pExchDate BETWEEN curr_effective 
+                          AND curr_expires) );
+
+  END LOOP;
+
+  RETURN _returnVal;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/addtopackinglistbatch.sql b/foundation-database/public/functions/addtopackinglistbatch.sql
new file mode 100644 (file)
index 0000000..67725ec
--- /dev/null
@@ -0,0 +1,90 @@
+CREATE OR REPLACE FUNCTION addToPackingListBatch(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSoheadid    ALIAS FOR $1;
+  returnVal    INTEGER;
+BEGIN
+
+  -- MIN because error codes are negative
+  SELECT MIN(addToPackingListBatch(''SO'', pSoheadid, shiphead_id)) INTO returnVal
+  FROM shiphead
+  WHERE ((shiphead_order_id=pSoheadid)
+    AND  (NOT shiphead_shipped)
+    AND  (shiphead_order_type=''SO''));
+  IF (NOT FOUND OR returnVal IS NULL) THEN
+    returnVal := addToPackingListBatch(''SO'', pSoheadid, NULL);
+  END IF;
+
+  RETURN returnVal;
+END;
+' LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION addToPackingListBatch(INTEGER, INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN addToPackingListBatch(''SO'', $1, $2);
+END;
+' LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION addToPackingListBatch(TEXT, INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pheadtype    ALIAS FOR $1;
+  pheadid      ALIAS FOR $2;
+  returnVal    INTEGER;
+BEGIN
+  -- MIN because error codes are negative
+  SELECT MIN(addToPackingListBatch(pheadtype, pheadid, shiphead_id)) INTO returnVal
+  FROM shiphead
+  WHERE ((shiphead_order_id=pheadid)
+    AND  (NOT shiphead_shipped)
+    AND  (shiphead_order_type=pheadtype));
+
+  IF (NOT FOUND OR returnVal IS NULL) THEN
+    returnVal := addToPackingListBatch(pheadtype, pheadid, NULL);
+  END IF;
+
+  RETURN returnVal;
+END;
+' LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION addToPackingListBatch(TEXT, INTEGER, INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pheadtype    ALIAS FOR $1;
+  pheadid      ALIAS FOR $2;
+  pshipheadid  ALIAS FOR $3;
+  _check INTEGER;
+
+BEGIN
+  SELECT pack_id INTO _check
+  FROM pack
+  WHERE ((pack_head_id=pheadid)
+    AND  ((pack_shiphead_id=pshipheadid) OR 
+         (pshipheadid IS NULL AND pack_shiphead_id IS NULL))
+    AND  (pack_head_type=pheadtype)
+       );
+
+  IF (NOT FOUND) THEN
+    INSERT INTO pack
+    ( pack_head_type, pack_head_id, pack_shiphead_id, pack_printed )
+    VALUES
+    ( pheadtype, pheadid, pshipheadid, FALSE );
+    -- Auto Firm Sales Orders conditionally based on metric
+    IF ( (pheadtype = ''SO'') AND (fetchMetricBool(''FirmSalesOrderPackingList'')) ) THEN
+      UPDATE coitem SET coitem_firm=TRUE
+      WHERE (coitem_cohead_id=pheadid);
+    END IF; 
+  END IF;
+
+  RETURN pheadid;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/adjustinvvalue.sql b/foundation-database/public/functions/adjustinvvalue.sql
new file mode 100644 (file)
index 0000000..69de45c
--- /dev/null
@@ -0,0 +1,61 @@
+CREATE OR REPLACE FUNCTION adjustInvValue(INTEGER, NUMERIC, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid     ALIAS FOR $1;
+  pNewValue       ALIAS FOR $2;
+  pAccountid      ALIAS FOR $3;
+  _delta          NUMERIC;
+  _glreturn       INTEGER;
+  _invhistid      INTEGER;
+  _itemlocSeries  INTEGER;
+
+BEGIN
+
+  SELECT pNewValue - itemsite_value INTO _delta
+  FROM itemsite
+  WHERE (itemsite_id=pItemsiteid)
+  FOR UPDATE;
+
+  IF (NOT FOUND) THEN
+    RETURN -1;
+  END IF;
+
+  SELECT insertGLTransaction('I/M', '', 'Post Value',
+         'Inventory Value Adjustment for ' || item_number,
+         COALESCE (pAccountid, costcat_adjustment_accnt_id),
+         costcat_asset_accnt_id, -1,
+         _delta, CURRENT_DATE) INTO _glreturn
+  FROM itemsite
+   JOIN costcat ON (itemsite_costcat_id=costcat_id)
+   JOIN item ON (itemsite_item_id=item_id)
+  WHERE (itemsite_id=pItemsiteid);
+
+--  Create the AD transaction
+  INSERT INTO invhist
+   ( invhist_itemsite_id,
+     invhist_transdate, invhist_transtype, invhist_invqty,
+     invhist_qoh_before, invhist_qoh_after,
+     invhist_docnumber, invhist_comments,
+     invhist_invuom, invhist_unitcost, invhist_hasdetail,
+     invhist_costmethod, invhist_value_before, invhist_value_after,
+     invhist_series )
+  SELECT itemsite_id,
+         CURRENT_TIMESTAMP, 'AD', 0.0,
+         itemsite_qtyonhand, itemsite_qtyonhand,
+         '', 'Inventory Value Adjustment',
+         uom_name, _delta, FALSE,
+         itemsite_costmethod, itemsite_value, pNewValue,
+         0
+  FROM itemsite, item, uom
+  WHERE ( (itemsite_item_id=item_id)
+   AND (item_inv_uom_id=uom_id)
+   AND (itemsite_id=pItemsiteid) );
+
+  UPDATE itemsite SET itemsite_value=pNewValue
+  WHERE (itemsite_id=pItemsiteid);
+
+  RETURN 0;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/adjustments.sql b/foundation-database/public/functions/adjustments.sql
new file mode 100644 (file)
index 0000000..5336693
--- /dev/null
@@ -0,0 +1,15 @@
+CREATE OR REPLACE FUNCTION adjustments(TEXT) RETURNS BOOLEAN AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pTransType ALIAS FOR $1;
+
+BEGIN
+  IF (pTransType IN (''CC'', ''AD'')) THEN
+    RETURN TRUE;
+  ELSE
+    RETURN FALSE;
+  END IF;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/allocatedforso.sql b/foundation-database/public/functions/allocatedforso.sql
new file mode 100644 (file)
index 0000000..b5dee64
--- /dev/null
@@ -0,0 +1,53 @@
+CREATE OR REPLACE FUNCTION allocatedForSo(INTEGER, INTEGER) RETURNS NUMERIC STABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  pDate ALIAS FOR $2;
+
+BEGIN
+
+  RETURN allocatedForSo(pItemsiteid, startOfTime(), (CURRENT_DATE + pDate));
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION allocatedForSo(INTEGER, DATE) RETURNS NUMERIC STABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  pDate ALIAS FOR $2;
+
+BEGIN
+
+  RETURN allocatedForSo(pItemsiteid, startOfTime(), pDate);
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION allocatedForSo(INTEGER, DATE, DATE) RETURNS NUMERIC STABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  pStartDate ALIAS FOR $2;
+  pEndDate ALIAS FOR $3;
+  _qty NUMERIC;
+
+BEGIN
+
+  SELECT COALESCE(SUM(noNeg(itemuomtouom(itemsite_item_id, coitem_qty_uom_id, NULL, coitem_qtyord - (coitem_qtyshipped + qtyAtShipping(coitem_id)) + coitem_qtyreturned))), 0.0) INTO _qty
+  FROM coitem, itemsite, item
+  WHERE ( (coitem_itemsite_id=itemsite_id)
+    AND (itemsite_item_id=item_id)
+    AND (coitem_status='O')
+    AND (coitem_itemsite_id=pItemsiteid)
+    AND (coitem_scheddate BETWEEN pStartDate AND pEndDate) );
+
+  RETURN _qty;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/allocatedforwo.sql b/foundation-database/public/functions/allocatedforwo.sql
new file mode 100644 (file)
index 0000000..975f006
--- /dev/null
@@ -0,0 +1,79 @@
+CREATE OR REPLACE FUNCTION allocatedForWo(INTEGER, INTEGER) RETURNS NUMERIC STABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  pLookAheadDays ALIAS FOR $2;
+
+BEGIN
+
+  RETURN allocatedForWo(pItemsiteid, startOfTime(), (CURRENT_DATE + pLookaheadDays));
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION allocatedForWo(INTEGER, DATE) RETURNS NUMERIC STABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  pDate ALIAS FOR $2;
+
+BEGIN
+
+  RETURN allocatedForWo(pItemsiteid, startOfTime(), pDate);
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION allocatedForWo(INTEGER, DATE, DATE) RETURNS NUMERIC STABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  pStartDate ALIAS FOR $2;
+  pEndDate ALIAS FOR $3;
+  _itemtype TEXT;
+  _qty NUMERIC;
+
+BEGIN
+
+  SELECT item_type INTO _itemtype
+  FROM itemsite JOIN item ON (item_id=itemsite_item_id)
+  WHERE (itemsite_id=pItemsiteid);
+
+  IF (_itemtype != 'T') THEN
+    SELECT
+      COALESCE(SUM(noNeg(itemuomtouom(itemsite_item_id, womatl_uom_id, NULL, womatl_qtyreq - womatl_qtyiss))), 0.0) INTO _qty
+    FROM womatl JOIN wo ON (wo_id=womatl_wo_id AND wo_status IN ('E','I','R'))
+                JOIN itemsite ON (itemsite_id=womatl_itemsite_id)
+    WHERE (womatl_itemsite_id=pItemsiteid)
+      AND (womatl_duedate BETWEEN pStartDate AND pEndDate);
+  ELSE
+    SELECT
+      COALESCE(SUM(noNeg(itemuomtouom(itemsite_item_id, womatl_uom_id, NULL, womatl_qtyreq))), 0.0)  -
+       (
+               SELECT COALESCE(SUM(invhist_invqty),0) 
+               FROM itemsite, item, wo, womatl
+                       LEFT OUTER JOIN womatlpost ON (womatl_id=womatlpost_womatl_id)
+                       LEFT OUTER JOIN invhist ON ((womatlpost_invhist_id=invhist_id)
+                            AND (invhist_invqty > 0))
+               WHERE ( (womatl_itemsite_id=pItemsiteid)
+               AND (womatl_itemsite_id=itemsite_id)
+               AND (itemsite_item_id=item_id)
+               AND (womatl_duedate BETWEEN pStartDate AND pEndDate) 
+               AND (wo_id=womatl_wo_id)
+               AND (wo_status IN ('E','I','R')) )
+       ) INTO _qty
+    FROM womatl JOIN wo ON (wo_id=womatl_wo_id AND wo_status IN ('E','I','R'))
+                JOIN itemsite ON (itemsite_id=womatl_itemsite_id)
+    WHERE (womatl_itemsite_id=pItemsiteid)
+      AND (womatl_duedate BETWEEN pStartDate AND pEndDate);
+  END IF;
+
+  RETURN COALESCE(_qty,0);
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/alterencrypt.sql b/foundation-database/public/functions/alterencrypt.sql
new file mode 100644 (file)
index 0000000..9d476dd
--- /dev/null
@@ -0,0 +1,121 @@
+CREATE OR REPLACE FUNCTION alterencrypt(text, text)
+  RETURNS integer AS
+'
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pOldKey ALIAS FOR $1;
+  pNewKey ALIAS FOR $2;
+  _cc RECORD;
+  _ccaud RECORD;
+  _metricenc RECORD;
+  num_updated INTEGER;
+
+BEGIN
+
+  num_updated := 0;
+
+-- Update ccard
+
+  FOR _cc IN SELECT ccard_id, 
+             decrypt(setbytea(ccard_name), setbytea(pOldKey), ''bf'') AS ccard_name,
+             decrypt(setbytea(ccard_address1), setbytea(pOldKey), ''bf'') AS ccard_address1,
+             decrypt(setbytea(ccard_address2), setbytea(pOldKey), ''bf'') AS ccard_address2,
+             decrypt(setbytea(ccard_city), setbytea(pOldKey), ''bf'') AS ccard_city,
+             decrypt(setbytea(ccard_state), setbytea(pOldKey), ''bf'') AS ccard_state,
+             decrypt(setbytea(ccard_zip), setbytea(pOldKey), ''bf'') AS ccard_zip,
+             decrypt(setbytea(ccard_country), setbytea(pOldKey), ''bf'') AS ccard_country,
+             decrypt(setbytea(ccard_number), setbytea(pOldKey), ''bf'') AS ccard_number,
+             decrypt(setbytea(ccard_month_expired), setbytea(pOldKey), ''bf'') AS ccard_month_expired,
+             decrypt(setbytea(ccard_year_expired), setbytea(pOldKey), ''bf'') AS ccard_year_expired
+      FROM ccard LOOP
+
+      UPDATE ccard
+             set ccard_name = encrypt(setbytea(_cc.ccard_name), setbytea(pNewKey), ''bf''),
+                 ccard_address1 = encrypt(setbytea(_cc.ccard_address1), setbytea(pNewKey), ''bf''),
+                 ccard_address2 = encrypt(setbytea(_cc.ccard_address2), setbytea(pNewKey), ''bf''),
+                 ccard_city = encrypt(setbytea(_cc.ccard_city), setbytea(pNewKey), ''bf''),
+                 ccard_state = encrypt(setbytea(_cc.ccard_state), setbytea(pNewKey), ''bf''),
+                 ccard_zip = encrypt(setbytea(_cc.ccard_zip), setbytea(pNewKey), ''bf''),
+                 ccard_country = encrypt(setbytea(_cc.ccard_country), setbytea(pNewKey), ''bf''),
+                 ccard_number = encrypt(setbytea(_cc.ccard_number), setbytea(pNewKey), ''bf''),
+                 ccard_month_expired = encrypt(setbytea(_cc.ccard_month_expired), setbytea(pNewKey), ''bf''),
+                 ccard_year_expired = encrypt(setbytea(_cc.ccard_year_expired), setbytea(pNewKey), ''bf'')
+      WHERE ccard_id = _cc.ccard_id;
+
+      num_updated := num_updated + 1;
+
+  END LOOP;
+
+-- Update ccardaud
+
+  FOR _ccaud IN SELECT ccardaud_id, 
+             decrypt(setbytea(ccardaud_ccard_name_old), setbytea(pOldKey), ''bf'') AS ccardaud_ccard_name_old,
+             decrypt(setbytea(ccardaud_ccard_name_new), setbytea(pOldKey), ''bf'') AS ccardaud_ccard_name_new,
+             decrypt(setbytea(ccardaud_ccard_address1_old), setbytea(pOldKey), ''bf'') AS ccardaud_ccard_address1_old,
+             decrypt(setbytea(ccardaud_ccard_address1_new), setbytea(pOldKey), ''bf'') AS ccardaud_ccard_address1_new,
+             decrypt(setbytea(ccardaud_ccard_address2_old), setbytea(pOldKey), ''bf'') AS ccardaud_ccard_address2_old,
+             decrypt(setbytea(ccardaud_ccard_address2_new), setbytea(pOldKey), ''bf'') AS ccardaud_ccard_address2_new,
+             decrypt(setbytea(ccardaud_ccard_city_old), setbytea(pOldKey), ''bf'') AS ccardaud_ccard_city_old,
+             decrypt(setbytea(ccardaud_ccard_city_new), setbytea(pOldKey), ''bf'') AS ccardaud_ccard_city_new,
+             decrypt(setbytea(ccardaud_ccard_state_old), setbytea(pOldKey), ''bf'') AS ccardaud_ccard_state_old,
+             decrypt(setbytea(ccardaud_ccard_state_new), setbytea(pOldKey), ''bf'') AS ccardaud_ccard_state_new,
+             decrypt(setbytea(ccardaud_ccard_zip_old), setbytea(pOldKey), ''bf'') AS ccardaud_ccard_zip_old,
+             decrypt(setbytea(ccardaud_ccard_zip_new), setbytea(pOldKey), ''bf'') AS ccardaud_ccard_zip_new,
+             decrypt(setbytea(ccardaud_ccard_country_old), setbytea(pOldKey), ''bf'') AS ccardaud_ccard_country_old,
+             decrypt(setbytea(ccardaud_ccard_country_new), setbytea(pOldKey), ''bf'') AS ccardaud_ccard_country_new,
+             decrypt(setbytea(ccardaud_ccard_number_old), setbytea(pOldKey), ''bf'') AS ccardaud_ccard_number_old,
+             decrypt(setbytea(ccardaud_ccard_number_new), setbytea(pOldKey), ''bf'') AS ccardaud_ccard_number_new,
+             decrypt(setbytea(ccardaud_ccard_month_expired_old), setbytea(pOldKey), ''bf'') AS ccardaud_ccard_month_expired_old,
+             decrypt(setbytea(ccardaud_ccard_month_expired_new), setbytea(pOldKey), ''bf'') AS ccardaud_ccard_month_expired_new,
+             decrypt(setbytea(ccardaud_ccard_year_expired_old), setbytea(pOldKey), ''bf'') AS ccardaud_ccard_year_expired_old,
+             decrypt(setbytea(ccardaud_ccard_year_expired_new), setbytea(pOldKey), ''bf'') AS ccardaud_ccard_year_expired_new
+      FROM ccardaud LOOP
+
+      UPDATE ccardaud
+             set ccardaud_ccard_name_old = encrypt(setbytea(_ccaud.ccardaud_ccard_name_old), setbytea(pNewKey), ''bf''),
+                 ccardaud_ccard_name_new = encrypt(setbytea(_ccaud.ccardaud_ccard_name_new), setbytea(pNewKey), ''bf''),
+                 ccardaud_ccard_address1_old = encrypt(setbytea(_ccaud.ccardaud_ccard_address1_old), setbytea(pNewKey), ''bf''),
+                 ccardaud_ccard_address1_new = encrypt(setbytea(_ccaud.ccardaud_ccard_address1_new), setbytea(pNewKey), ''bf''),
+                 ccardaud_ccard_address2_old = encrypt(setbytea(_ccaud.ccardaud_ccard_address2_old), setbytea(pNewKey), ''bf''),
+                 ccardaud_ccard_address2_new = encrypt(setbytea(_ccaud.ccardaud_ccard_address2_new), setbytea(pNewKey), ''bf''),
+                 ccardaud_ccard_city_old = encrypt(setbytea(_ccaud.ccardaud_ccard_city_old), setbytea(pNewKey), ''bf''),
+                 ccardaud_ccard_city_new = encrypt(setbytea(_ccaud.ccardaud_ccard_city_new), setbytea(pNewKey), ''bf''),
+                 ccardaud_ccard_state_old = encrypt(setbytea(_ccaud.ccardaud_ccard_state_old), setbytea(pNewKey), ''bf''),
+                 ccardaud_ccard_state_new = encrypt(setbytea(_ccaud.ccardaud_ccard_state_new), setbytea(pNewKey), ''bf''),
+                 ccardaud_ccard_zip_old = encrypt(setbytea(_ccaud.ccardaud_ccard_zip_old), setbytea(pNewKey), ''bf''),
+                 ccardaud_ccard_zip_new = encrypt(setbytea(_ccaud.ccardaud_ccard_zip_new), setbytea(pNewKey), ''bf''),
+                 ccardaud_ccard_country_old = encrypt(setbytea(_ccaud.ccardaud_ccard_country_old), setbytea(pNewKey), ''bf''),
+                 ccardaud_ccard_country_new = encrypt(setbytea(_ccaud.ccardaud_ccard_country_new), setbytea(pNewKey), ''bf''),
+                 ccardaud_ccard_number_old = encrypt(setbytea(_ccaud.ccardaud_ccard_number_old), setbytea(pNewKey), ''bf''),
+                 ccardaud_ccard_number_new = encrypt(setbytea(_ccaud.ccardaud_ccard_number_new), setbytea(pNewKey), ''bf''),
+                 ccardaud_ccard_month_expired_old = encrypt(setbytea(_ccaud.ccardaud_ccard_month_expired_old), setbytea(pNewKey), ''bf''),
+                 ccardaud_ccard_month_expired_new = encrypt(setbytea(_ccaud.ccardaud_ccard_month_expired_new), setbytea(pNewKey), ''bf''),
+                 ccardaud_ccard_year_expired_old = encrypt(setbytea(_ccaud.ccardaud_ccard_year_expired_old), setbytea(pNewKey), ''bf''),
+                 ccardaud_ccard_year_expired_new = encrypt(setbytea(_ccaud.ccardaud_ccard_year_expired_new), setbytea(pNewKey), ''bf'')
+      WHERE ccardaud_id = _ccaud.ccardaud_id;
+
+      num_updated := num_updated + 1;
+
+  END LOOP;
+
+-- Update metricenc
+
+  FOR _metricenc IN SELECT metricenc_id, 
+             decrypt(setbytea(metricenc_value), setbytea(pOldKey), ''bf'') AS metricenc_value
+      FROM metricenc LOOP
+
+      UPDATE metricenc
+             set metricenc_value = encrypt(setbytea(_metricenc.metricenc_value), setbytea(pNewKey), ''bf'')
+      WHERE metricenc_id = _metricenc.metricenc_id;
+
+      num_updated := num_updated + 1;
+
+  END LOOP;
+
+
+  RETURN num_updated;
+
+END;
+'
+  LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/apaging.sql b/foundation-database/public/functions/apaging.sql
new file mode 100644 (file)
index 0000000..5d0028d
--- /dev/null
@@ -0,0 +1,116 @@
+SELECT dropIfExists('FUNCTION', 'apaging(date, boolean)', 'public');
+
+CREATE OR REPLACE FUNCTION apaging(date, boolean) RETURNS SETOF apaging AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pAsOfDate ALIAS FOR $1;
+  pUseDocDate ALIAS FOR $2;
+  _row apaging%ROWTYPE;
+  _x RECORD;
+  _returnVal INTEGER;
+  _asOfDate DATE;
+BEGIN
+
+  _asOfDate := COALESCE(pAsOfDate,current_date);
+
+  FOR _x IN
+        SELECT
+        --report uses currency rate snapshot to convert all amounts to base based on apopen_docdate to ensure the same exchange rate
+
+        --today and greater base:
+        CASE WHEN((apopen_duedate >= DATE(_asOfDate)))
+        THEN (((apopen_amount-apopen_paid+COALESCE(SUM(apapply_target_paid),0)))/apopen_curr_rate *
+        CASE WHEN (apopen_doctype IN ('D', 'V')) THEN 1 ELSE -1 END) ELSE 0 END AS cur_val,
+
+        --0 to 30 base
+        CASE WHEN((apopen_duedate >= DATE(_asOfDate)-30) AND (apopen_duedate < DATE(_asOfDate)))
+        THEN (((apopen_amount-apopen_paid+COALESCE(SUM(apapply_target_paid),0)))/apopen_curr_rate *
+        CASE WHEN (apopen_doctype IN ('D', 'V')) THEN 1 ELSE -1 END) ELSE 0 END AS thirty_val,
+
+        --30-60 base
+        CASE WHEN((apopen_duedate >= DATE(_asOfDate)-60) AND (apopen_duedate < DATE(_asOfDate) - 30 ))
+        THEN (((apopen_amount-apopen_paid+COALESCE(SUM(apapply_target_paid),0)))/apopen_curr_rate *
+        CASE WHEN (apopen_doctype IN ('D', 'V')) THEN 1 ELSE -1 END) ELSE 0 END AS sixty_val,
+
+        --60-90 base
+        CASE WHEN((apopen_duedate >= DATE(_asOfDate)-90) AND (apopen_duedate < DATE(_asOfDate) - 60))
+        THEN (((apopen_amount-apopen_paid+COALESCE(SUM(apapply_target_paid),0)))/apopen_curr_rate *
+        CASE WHEN (apopen_doctype IN ('D', 'V')) THEN 1 ELSE -1 END) ELSE 0 END AS ninety_val,
+
+        --greater than 90 base:
+        CASE WHEN((apopen_duedate > DATE(_asOfDate)-10000) AND (apopen_duedate < DATE(_asOfDate) - 90))
+        THEN (((apopen_amount-apopen_paid + COALESCE(SUM(apapply_target_paid),0)))/apopen_curr_rate *
+        CASE WHEN (apopen_doctype IN ('D', 'V')) THEN 1 ELSE -1 END) ELSE 0 END AS plus_val,
+
+        --total amount base:
+        CASE WHEN((apopen_duedate > DATE(_asOfDate)-10000))
+        THEN (((apopen_amount-apopen_paid+COALESCE(SUM(apapply_target_paid),0)))/apopen_curr_rate *
+        CASE WHEN (apopen_doctype IN ('D', 'V')) THEN 1 ELSE -1 END) ELSE 0 END AS total_val,
+
+        --AP Open Amount base
+        CASE WHEN apopen_doctype IN ('C', 'R') 
+        THEN (apopen_amount * -1) / apopen_curr_rate
+        ELSE apopen_amount / apopen_curr_rate END AS apopen_amount,
+        
+        apopen_docdate,
+        apopen_duedate,
+        apopen_ponumber,
+        apopen_invcnumber,
+        apopen_docnumber,
+        apopen_doctype,
+        vend_id,
+        vend_name,
+        vend_number,
+        vend_vendtype_id,
+        vendtype_code,
+        terms_descrip,
+        determineDiscountDate(terms_id, apopen_docdate) AS discdate,
+        noNeg(apopen_discountable_amount *
+                     CASE WHEN (CURRENT_DATE <= determineDiscountDate(terms_id, apopen_docdate)) THEN terms_discprcnt
+                     ELSE 0.0 END) AS disc_val,
+        terms_discdays AS discdays,
+        (terms_discprcnt * 100.0) AS discprcnt
+
+        FROM vendinfo, vendtype, apopen
+          LEFT OUTER JOIN terms ON (apopen_terms_id=terms_id)
+          LEFT OUTER JOIN apapply ON (((apopen_id=apapply_target_apopen_id)
+                                    OR (apopen_id=apapply_source_apopen_id))
+                                   AND (apapply_postdate >_asOfDate))
+        WHERE ( (apopen_vend_id = vend_id)
+        AND (vend_vendtype_id=vendtype_id)
+        AND (CASE WHEN (pUseDocDate) THEN apopen_docdate ELSE apopen_distdate END <= _asOfDate)
+        AND (COALESCE(apopen_closedate,_asOfDate+1)>_asOfDate) )
+        GROUP BY apopen_id,apopen_docdate,apopen_duedate,apopen_ponumber, apopen_invcnumber, apopen_docnumber,apopen_doctype,apopen_paid,
+                 apopen_curr_id,apopen_amount,vend_id,vend_name,vend_number,vend_vendtype_id,vendtype_code,terms_descrip,
+                 apopen_curr_rate, terms_id, terms_discdays, terms_discprcnt, apopen_discountable_amount
+        ORDER BY vend_number, apopen_duedate
+  LOOP
+        _row.apaging_docdate := _x.apopen_docdate;
+        _row.apaging_duedate := _x.apopen_duedate;
+        _row.apaging_ponumber := _x.apopen_ponumber;
+        _row.apaging_invcnumber := _x.apopen_invcnumber;
+        _row.apaging_docnumber := _x.apopen_docnumber;
+        _row.apaging_doctype := _x.apopen_doctype;
+        _row.apaging_vend_id := _x.vend_id;
+        _row.apaging_vend_number := _x.vend_number;
+        _row.apaging_vend_name := _x.vend_name;
+        _row.apaging_vend_vendtype_id := _x.vend_vendtype_id;
+        _row.apaging_vendtype_code := _x.vendtype_code;
+        _row.apaging_terms_descrip := _x.terms_descrip;
+        _row.apaging_apopen_amount := _x.apopen_amount;
+        _row.apaging_cur_val := _x.cur_val;
+        _row.apaging_thirty_val := _x.thirty_val;
+        _row.apaging_sixty_val := _x.sixty_val;
+        _row.apaging_ninety_val := _x.ninety_val;
+        _row.apaging_plus_val := _x.plus_val;
+        _row.apaging_total_val := _x.total_val;
+        _row.apaging_discdate := _x.discdate;
+        _row.apaging_disc_val := _x.disc_val;
+        _row.apaging_discdays := _x.discdays;
+        _row.apaging_discprcnt := _x.discprcnt;
+        RETURN NEXT _row;
+  END LOOP;
+  RETURN;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/apapplied.sql b/foundation-database/public/functions/apapplied.sql
new file mode 100644 (file)
index 0000000..a797231
--- /dev/null
@@ -0,0 +1,28 @@
+CREATE OR REPLACE FUNCTION apapplied(INTEGER, DATE) RETURNS NUMERIC AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pApopenid ALIAS FOR $1;
+  pDate ALIAS FOR $2;
+  _amount NUMERIC;
+
+BEGIN
+
+  -- Return amount applied to an apopen in base currency as of apapply_postdate
+  SELECT SUM(currtobase(apapply_curr_id,apapply_amount,apapply_postdate)) INTO _amount
+  FROM apapply
+  WHERE (((apapply_target_apopen_id = pApopenid) OR (apapply_source_apopen_id = pApopenid))
+  AND (((apapply_journalnumber=0) AND (apapply_postdate <= pDate))
+  OR EXISTS(SELECT * 
+             FROM gltrans 
+             WHERE ((gltrans_journalnumber=apapply_journalnumber)
+             AND (gltrans_date <= pDate)))));
+
+  IF (_amount IS NULL) THEN
+    RETURN 0;
+  ELSE
+    RETURN _amount;
+  END IF;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/apcheckpending.sql b/foundation-database/public/functions/apcheckpending.sql
new file mode 100644 (file)
index 0000000..9360994
--- /dev/null
@@ -0,0 +1,20 @@
+CREATE OR REPLACE FUNCTION apCheckPending(INTEGER) RETURNS NUMERIC AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pApopenid    ALIAS FOR $1;
+  _qty NUMERIC  := 0.0;
+
+BEGIN
+
+  SELECT SUM(checkitem_amount + checkitem_discount) INTO _qty
+    FROM checkitem JOIN checkhead ON (checkitem_checkhead_id=checkhead_id)
+   WHERE ((checkitem_apopen_id=pApopenid)
+     AND (NOT checkhead_deleted)
+     AND (NOT checkhead_replaced)
+     AND (NOT checkhead_posted));
+
+  RETURN COALESCE(_qty, 0.0);
+
+END;
+$$ LANGUAGE 'plpgsql' STABLE;
diff --git a/foundation-database/public/functions/apcurrgain.sql b/foundation-database/public/functions/apcurrgain.sql
new file mode 100644 (file)
index 0000000..843c709
--- /dev/null
@@ -0,0 +1,43 @@
+-- calculate the change in value caused by exchange rate fluctuations.
+-- we generally care about currency exchange gain/loss when adjusting the G/L,
+-- so this function returns its result in the base currency.
+-- however, we only care about fluctuations in the base value of a foreign
+-- quantity, so this function expects pValue ($3) in the local currency.
+-- negative values = a loss.
+CREATE OR REPLACE FUNCTION apcurrGain(INTEGER, INTEGER, NUMERIC, DATE)
+RETURNS NUMERIC AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pApopenId ALIAS FOR $1;
+  pCurrId ALIAS FOR $2;
+  pValue ALIAS FOR $3;
+  pDate ALIAS FOR $4;
+  _start DATE;
+  _end DATE;
+  _gain NUMERIC;
+  _r RECORD;
+
+BEGIN
+  IF (pApopenId IS NULL OR pValue = 0) THEN
+    RETURN 0;
+  END IF;
+
+  SELECT apopen_docdate, apopen_curr_rate
+    INTO _r
+  FROM apopen
+  WHERE (apopen_id=pApopenId);
+
+  IF (_r.apopen_docdate > pDate) THEN
+    _gain := (currToBase(pCurrId, pValue, pDate) - (pValue / _r.apopen_curr_rate)) * -1;
+  ELSE
+    _gain := (pValue / _r.apopen_curr_rate) - currToBase(pCurrId, pValue, pDate);
+  END IF;
+  
+  IF (_gain IS NULL) THEN
+    RAISE EXCEPTION 'Error processing currency gain/loss.';
+  END IF;
+
+  RETURN _gain;
+END;
+$$ LANGUAGE plpgsql;
diff --git a/foundation-database/public/functions/applyapcreditmemotobalance.sql b/foundation-database/public/functions/applyapcreditmemotobalance.sql
new file mode 100644 (file)
index 0000000..0cb14c2
--- /dev/null
@@ -0,0 +1,126 @@
+CREATE OR REPLACE FUNCTION applyAPCreditMemoToBalance(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pApopenid ALIAS FOR $1;
+  _amount NUMERIC;
+  _curr_id INTEGER;
+  _curr_rate NUMERIC;
+  _docdate DATE;
+  _applyAmount NUMERIC;
+  _r RECORD;
+  _p RECORD;
+
+BEGIN
+
+--  Find the balance to apply
+  SELECT (apopen_amount - apopen_paid - COALESCE(prepared,0.0) - COALESCE(selected,0.0) - COALESCE(SUM(currToCurr(apcreditapply_curr_id,
+                                                  apopen_curr_id,
+                                                  apcreditapply_amount,
+                                                  apopen_docdate)), 0)),
+          apopen_curr_id, apopen_curr_rate, apopen_docdate INTO _amount, _curr_id, _curr_rate, _docdate
+  FROM apopen 
+    LEFT OUTER JOIN apcreditapply ON (apcreditapply_source_apopen_id=apopen_id)
+    LEFT OUTER JOIN (SELECT apopen_id AS selected_apopen_id,
+                       SUM(currToCurr(apselect_curr_id, apopen_curr_id, apselect_amount + apselect_discount, apselect_date)) AS selected
+                     FROM apselect JOIN apopen ON (apselect_apopen_id=apopen_id)
+                     GROUP BY apopen_id) AS sub1 ON (apopen_id=selected_apopen_id)
+    LEFT OUTER JOIN (SELECT apopen_id AS prepared_apopen_id,
+                       SUM(checkitem_amount + checkitem_discount) AS prepared
+                     FROM checkhead 
+                       JOIN checkitem ON (checkitem_checkhead_id=checkhead_id)
+                       JOIN apopen ON (checkitem_apopen_id=apopen_id)
+                     WHERE ((NOT checkhead_posted)
+                       AND  (NOT checkhead_void))
+                     GROUP BY apopen_id) AS sub2 ON (prepared_apopen_id=apopen_id)
+  WHERE (apopen_id=pApopenid)
+  GROUP BY apopen_amount, apopen_paid, apopen_curr_id, apopen_curr_rate, apopen_docdate, prepared, selected;
+
+  IF (_amount < 0) THEN
+    RETURN -1;
+  END IF;
+
+--  Loop through the apopen items in order of due date
+  FOR _r IN SELECT target.apopen_id AS apopenid,
+                   currToCurr(target.apopen_curr_id,source.apopen_curr_id, 
+                     target.apopen_amount - target.apopen_paid - COALESCE(prepared,0.0) - COALESCE(selected,0.0) - COALESCE(applied,0.0),
+                     current_date) AS balance
+           FROM apopen AS source, apopen AS target
+             LEFT OUTER JOIN (SELECT apcreditapply_target_apopen_id AS applied_apopen_id,
+                                     SUM(currToCurr(apcreditapply_curr_id, apopen_curr_id, apcreditapply_amount, apopen_docdate)) AS applied
+                              FROM apcreditapply JOIN apopen ON (apopen_id=apcreditapply_source_apopen_id)
+                              GROUP BY apcreditapply_target_apopen_id) AS sub3
+                              ON (target.apopen_id=applied_apopen_id)
+             LEFT OUTER JOIN (SELECT apopen_id AS selected_apopen_id,
+                                SUM(currToCurr(apselect_curr_id, apopen_curr_id, apselect_amount + apselect_discount, apselect_date)) AS selected
+                                    FROM apselect JOIN apopen ON (apselect_apopen_id=apopen_id)
+                                GROUP BY apopen_id) AS sub1
+                                ON (target.apopen_id=selected_apopen_id)
+             LEFT OUTER JOIN (SELECT apopen_id AS prepared_apopen_id,
+                                SUM(checkitem_amount + checkitem_discount) AS prepared
+                              FROM checkhead 
+                                JOIN checkitem ON (checkitem_checkhead_id=checkhead_id)
+                                JOIN apopen ON (checkitem_apopen_id=apopen_id)
+                              WHERE ((NOT checkhead_posted)
+                               AND  (NOT checkhead_void))
+                              GROUP BY apopen_id) AS sub2 ON (prepared_apopen_id=target.apopen_id)
+            WHERE ( (source.apopen_vend_id=target.apopen_vend_id)
+             AND (target.apopen_doctype IN ('V', 'D'))
+             AND (target.apopen_open)
+             AND (source.apopen_id=pApopenid) )
+            ORDER BY target.apopen_duedate, (target.apopen_amount - target.apopen_paid) LOOP
+
+--  Determine the amount to apply
+    IF (_r.balance <= 0.0) THEN
+      CONTINUE;
+    ELSEIF (_r.balance > _amount) THEN
+      _applyAmount := _amount;
+    ELSE
+      _applyAmount := _r.balance;
+    END IF;
+
+--  Does an apcreditapply record already exist?
+    SELECT apcreditapply_id, 
+              apcreditapply_amount * _curr_rate / 
+                 currRate(apcreditapply_curr_id,_docdate) AS apcreditapply_amount
+      INTO _p
+    FROM apcreditapply
+    WHERE ( (apcreditapply_target_apopen_id=_r.apopenid)
+     AND (apcreditapply_source_apopen_id=pApopenid) );
+
+    IF (FOUND) THEN
+--  The following is depreciated, just skip the record
+--  Recalculate the amount to apply
+--      IF ((_r.balance - _p.apcreditapply_amount) > _amount) THEN
+--        _applyAmount := _amount;
+--      ELSE
+--        _applyAmount := (_r.balance - _p.apcreditapply_amount);
+--      END IF;
+
+--  Update the apcreditapply with the new amount to apply
+--      UPDATE apcreditapply
+--      SET apcreditapply_amount = (apcreditapply_amount + 
+--          _applyAmount *  currRate(apcreditapply_curr_id,_docdate) / _curr_rate)
+--      WHERE (apcreditapply_id=_p.apcreditapply_id);
+
+      CONTINUE;
+    ELSE
+--  Create a new apcreditapply record
+      INSERT INTO apcreditapply
+      ( apcreditapply_source_apopen_id, apcreditapply_target_apopen_id,
+        apcreditapply_amount, apcreditapply_curr_id )
+      VALUES
+      ( pApopenid, _r.apopenid, _applyAmount, _curr_id );
+    END IF;
+
+    _amount := (_amount - _applyAmount);
+    IF (_amount = 0) THEN
+      EXIT;
+    END IF;
+
+  END LOOP;
+
+  RETURN 1;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/applyapcredits.sql b/foundation-database/public/functions/applyapcredits.sql
new file mode 100644 (file)
index 0000000..e35c18e
--- /dev/null
@@ -0,0 +1,38 @@
+CREATE OR REPLACE FUNCTION applyapcredits(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pVendId   ALIAS FOR $1;
+  _result   INTEGER;
+  _apopenid INTEGER;
+  _r        RECORD;
+
+BEGIN
+
+  -- Fetch credit memo(s) for the vendor
+  FOR _r IN SELECT apopen_id, apopen_duedate
+            FROM apopen JOIN vendinfo ON (apopen_vend_id = vend_id)
+            WHERE ((apopen_doctype = 'C')
+               AND (apopen_status = 'O')
+               AND (vend_id = pVendId))
+            ORDER BY apopen_duedate
+  LOOP
+    -- Apply credit memo(s) according to due date
+    SELECT applyapcreditmemotobalance(_r.apopen_id) INTO _result;
+
+    -- Post the credit memo if applied
+    IF (_result = 1) THEN
+      SELECT postapcreditmemoapplication(_r.apopen_id) INTO _apopenid;
+      IF (_apopenid < 0) THEN
+        RETURN -1;
+      END IF;
+    ELSE
+      RETURN -1;
+    END IF;
+
+  END LOOP;
+
+RETURN 1;
+
+END;
+$$ LANGUAGE 'plpgsql' VOLATILE;
diff --git a/foundation-database/public/functions/applyarcreditmemotobalance.sql b/foundation-database/public/functions/applyarcreditmemotobalance.sql
new file mode 100644 (file)
index 0000000..4508c9e
--- /dev/null
@@ -0,0 +1,126 @@
+CREATE OR REPLACE FUNCTION applyARCreditMemoToBalance(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pAropenid ALIAS FOR $1;
+
+BEGIN
+
+  RETURN applyARCreditMemoToBalance(pAropenid, NULL);
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION applyARCreditMemoToBalance(INTEGER, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSourceAropenid ALIAS FOR $1;
+  pTargetAropenid ALIAS FOR $2;
+  _amount NUMERIC;
+  _amountcurrid INTEGER;
+  _applyAmount NUMERIC;
+  _applycurrid  INTEGER;
+  _curr_rate NUMERIC;
+  _r RECORD;
+  _p RECORD;
+
+BEGIN
+
+--  Find the balance to apply
+  SELECT (aropen_amount - COALESCE(SUM(currToCurr(arcreditapply_curr_id,
+                                                  aropen_curr_id,
+                                                  arcreditapply_amount,
+                                                  aropen_docdate)), 0) - aropen_paid - COALESCE(prepared,0.0) - COALESCE(cashapplied,0.0)),
+         aropen_curr_id, aropen_curr_rate INTO _amount, _amountcurrid, _curr_rate
+  FROM aropen LEFT OUTER JOIN arcreditapply ON (arcreditapply_source_aropen_id=aropen_id)
+             LEFT OUTER JOIN (SELECT aropen_id AS prepared_aropen_id,
+                                SUM(checkitem_amount + checkitem_discount) AS prepared
+                               FROM checkhead JOIN checkitem ON (checkitem_checkhead_id=checkhead_id)
+                                              JOIN aropen ON (checkitem_aropen_id=aropen_id)
+                               WHERE ((NOT checkhead_posted)
+                                AND  (NOT checkhead_void))
+                               GROUP BY aropen_id) AS sub1
+                      ON (prepared_aropen_id=aropen_id)
+             LEFT OUTER JOIN (SELECT aropen_id AS cash_aropen_id,
+                                     SUM(cashrcptitem_amount + cashrcptitem_discount) * -1.0 AS cashapplied
+                                FROM cashrcpt JOIN cashrcptitem ON (cashrcptitem_cashrcpt_id=cashrcpt_id)
+                                              JOIN aropen ON (cashrcptitem_aropen_id=aropen_id)
+                               WHERE (NOT cashrcpt_posted) AND (NOT cashrcpt_void)
+                               GROUP BY aropen_id ) AS sub2
+                      ON (cash_aropen_id=aropen_id)
+  WHERE (aropen_id=pSourceAropenid)
+  GROUP BY aropen_amount, aropen_paid, aropen_curr_id, aropen_curr_rate, prepared, cashapplied;
+
+  IF (_amount < 0) THEN
+    RETURN -1;
+  END IF;
+
+--  Loop through the aropen items in order of due date
+  FOR _r IN SELECT target.aropen_id AS aropenid,
+                   currToCurr(target.aropen_curr_id,source.aropen_curr_id,
+                              (target.aropen_amount - target.aropen_paid - calcpendingarapplications(target.aropen_id)),
+                              current_date) AS balance,
+                   target.aropen_curr_id AS curr_id,
+                   target.aropen_docdate AS docdate
+            FROM aropen AS target, aropen AS source
+            WHERE ( (source.aropen_cust_id=target.aropen_cust_id)
+             AND (target.aropen_doctype IN ('D', 'I'))
+             AND (target.aropen_open)
+             AND (source.aropen_id=pSourceAropenid)
+             AND ((pTargetAropenid IS NULL) OR (target.aropen_id=pTargetAropenid)) )
+            ORDER BY target.aropen_duedate, target.aropen_docnumber LOOP
+
+--  Determine the amount to apply
+    IF (_r.balance > _amount) THEN
+      _applyAmount := _amount;
+    ELSE
+      _applyAmount := _r.balance;
+    END IF;
+    _applycurrid := _amountcurrid;
+
+--  Does an arcreditapply record already exist?
+    SELECT arcreditapply_id,
+           arcreditapply_amount,
+           arcreditapply_amount * _curr_rate / 
+                 currRate(arcreditapply_curr_id,_r.docdate) AS
+                      arcreditapply_amount_applycurr INTO _p
+    FROM arcreditapply
+    WHERE ( (arcreditapply_target_aropen_id=_r.aropenid)
+     AND (arcreditapply_source_aropen_id=pSourceAropenid) );
+
+    IF (FOUND) THEN
+--  Offset the amount to apply by the amount already applied
+      _applyAmount := (_applyAmount - _p.arcreditapply_amount_applycurr);
+      IF (_applyAmount < 0) THEN
+        _applyAmount := 0;
+      END IF;
+
+--  Update the arcreditapply with the new amount to apply
+      UPDATE arcreditapply
+      SET arcreditapply_amount = (arcreditapply_amount + 
+          _applyAmount *  currRate(arcreditapply_curr_id,_r.docdate) / _curr_rate)
+      WHERE (arcreditapply_id=_p.arcreditapply_id);
+
+    ELSE
+--  Create a new arcreditapply record
+      INSERT INTO arcreditapply
+      ( arcreditapply_source_aropen_id, arcreditapply_target_aropen_id,
+        arcreditapply_amount, arcreditapply_curr_id )
+      VALUES
+      ( pSourceAropenid, _r.aropenid, _applyAmount, _applycurrid );
+    END IF;
+
+    _amount := _amount - currToCurr(_applycurrid, _amountcurrid, _applyAmount, _r.docdate);
+    IF (_amount = 0) THEN
+      EXIT;
+    END IF;
+
+  END LOOP;
+
+  RETURN 1;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/applycashreceiptlinebalance.sql b/foundation-database/public/functions/applycashreceiptlinebalance.sql
new file mode 100644 (file)
index 0000000..4a07ced
--- /dev/null
@@ -0,0 +1,107 @@
+CREATE OR REPLACE FUNCTION applyCashReceiptLineBalance(INTEGER, INTEGER, NUMERIC, INTEGER) RETURNS NUMERIC AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCashrcptId ALIAS FOR $1;
+  pAropenid ALIAS FOR $2;
+  pAmount ALIAS FOR $3;
+  pCurrId ALIAS FOR $4;
+  _balance NUMERIC;
+  _amount NUMERIC;
+  _applyAmount NUMERIC := 0;
+  _discount NUMERIC := 0;
+  _discprct NUMERIC;
+  _docDate DATE;
+  _r RECORD;
+  _doctype CHAR(1);
+
+BEGIN
+
+--  All calculations performed in currency of Cash Receipt
+
+--  Clear previously applied
+  DELETE FROM cashrcptitem WHERE ((cashrcptitem_cashrcpt_id=pCashrcptId) AND (cashrcptitem_aropen_id=pAropenId));
+
+--  Find the balance to apply
+  SELECT (pAmount - (COALESCE(SUM(cashrcptitem_amount), 0) ) ),
+    COALESCE(cashrcpt_docdate, current_date)
+    INTO _amount, _docDate
+  FROM cashrcpt LEFT OUTER JOIN cashrcptitem ON (cashrcptitem_cashrcpt_id = cashrcpt_id)
+  WHERE (cashrcpt_id=pCashrcptid)
+  GROUP BY cashrcpt_curr_id, cashrcpt_distdate, cashrcpt_docdate;
+
+  SELECT (_amount - COALESCE(SUM(cashrcptmisc_amount), 0)) INTO _amount
+  FROM cashrcptmisc
+  WHERE (cashrcptmisc_cashrcpt_id=pCashrcptid);
+
+  SELECT aropen_doctype INTO _doctype
+  FROM aropen
+  WHERE (aropen_id=pAropenId);
+  
+  RAISE DEBUG 'Amount (%) DocType (%)', _amount, _doctype;
+
+  IF (_amount <= 0 AND _doctype IN ('I','D')) THEN
+    RETURN 0;
+  END IF;
+
+--  Determine Line balance
+  SELECT currToCurr(aropen_curr_id, cashrcpt_curr_id,
+         aropen_amount - aropen_paid, cashrcpt_distdate) -
+         COALESCE((SELECT (SUM(cashrcptitem_amount) + SUM(cashrcptitem_discount))
+                   FROM cashrcptitem, cashrcpt
+                   WHERE ((cashrcpt_id=cashrcptitem_cashrcpt_id)
+                     AND  (NOT cashrcpt_void)
+                     AND  (NOT cashrcpt_posted)
+                     AND  (cashrcpt_id != pCashrcptId)
+                     AND  (cashrcptitem_aropen_id=pAropenId))), 0)
+         INTO _balance
+         FROM aropen, cashrcpt
+           WHERE ((aropen_id=pAropenId)
+           AND (cashrcpt_id=pCashrcptId));
+
+  RAISE DEBUG 'Balance (%)', _balance;
+            
+--  If Invoice or Debit Memo, determine Max Discount as per Terms
+  IF (_doctype IN ('I','D')) THEN
+    SELECT  round(noNeg(_balance * 
+            CASE WHEN (_docDate <= determineDiscountDate(terms_id, aropen_docdate)) THEN COALESCE(terms_discprcnt, 0.0) 
+            ELSE 0.00 END - applied),2),
+            CASE WHEN (_docDate <= determineDiscountDate(terms_id, aropen_docdate)) THEN COALESCE(terms_discprcnt, 0.0) 
+            ELSE 0.00 END INTO _discount, _discprct
+    FROM aropen LEFT OUTER JOIN terms ON (terms_id=aropen_terms_id), 
+         (SELECT COALESCE(SUM(arapply_applied), 0.00) AS applied  
+         FROM arapply, aropen 
+          WHERE ((arapply_target_aropen_id=pAropenId) 
+           AND (arapply_source_aropen_id=pAropenId) 
+           AND  (aropen_discount) )
+             ) AS data 
+    WHERE (aropen_id=pAropenId);
+
+--  Determine the amount to apply
+    IF (_balance <= _amount + _discount) THEN
+      _applyAmount := _balance - _discount;
+    ELSE
+      _discount := round((_amount / (1 - _discprct)) - _amount, 2);
+      _applyAmount := _amount;
+    END IF;
+  ELSIF (_doctype IN ('C', 'R')) THEN
+  -- Handle Credits, discounts don't apply here
+    _applyAmount := _balance * -1;
+  ELSE
+    _applyAmount := _amount;
+  END IF;
+
+  IF (_applyAmount != 0) THEN
+--  Create a new cashrcptitem
+      INSERT INTO cashrcptitem
+      ( cashrcptitem_aropen_id, cashrcptitem_cashrcpt_id,
+        cashrcptitem_amount,cashrcptitem_discount )
+      VALUES
+      ( pAropenid, pCashrcptid, round(_applyAmount, 2), round(_discount, 2) );
+  END IF;
+
+  RETURN abs(_applyAmount);
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/applycashreceipttobalance.sql b/foundation-database/public/functions/applycashreceipttobalance.sql
new file mode 100644 (file)
index 0000000..550736b
--- /dev/null
@@ -0,0 +1,181 @@
+CREATE OR REPLACE FUNCTION applyCashReceiptToBalance(INTEGER, NUMERIC) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCashrcptid ALIAS FOR $1;
+  pAmount ALIAS FOR $2;
+
+BEGIN
+  RETURN applyCashReceiptToBalance(pCashrcptid, pAmount, baseCurrId() );
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION applyCashReceiptToBalance(INTEGER, NUMERIC, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCashrcptid ALIAS FOR $1;
+  pAmount ALIAS FOR $2;
+  pCurrId ALIAS FOR $3;
+
+BEGIN
+
+  RETURN applyCashReceiptToBalance(pCashrcptid, pAmount, pCurrId, false);
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION applyCashReceiptToBalance(INTEGER, NUMERIC, INTEGER, BOOLEAN) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCashrcptid ALIAS FOR $1;
+  pAmount ALIAS FOR $2;
+  pCurrId ALIAS FOR $3;
+  pInclCredits ALIAS FOR $4;
+  _amount NUMERIC;
+  _applied NUMERIC := 0;
+  _applyAmount NUMERIC;
+  _discount NUMERIC;
+  _discprct NUMERIC;
+  _docDate DATE;
+  _r RECORD;
+  _toApply NUMERIC;
+
+BEGIN
+
+--  Apply open credits first if applicable
+  IF (pInclCredits) THEN
+    -- First find total debits unaccounted for by this receipt so we can apply as much credit 
+    -- as possible to clear, but no more
+    SELECT coalesce(noNeg(sum(currToCurr(aropen_curr_id, cashrcpt_curr_id,
+         aropen_amount - aropen_paid, cashrcpt_distdate) -
+         COALESCE((SELECT (SUM(cashrcptitem_amount) + SUM(cashrcptitem_discount))
+                   FROM cashrcptitem, cashrcpt
+                   WHERE ((cashrcpt_id=cashrcptitem_cashrcpt_id)
+                     AND  (NOT cashrcpt_void)
+                     AND  (NOT cashrcpt_posted)
+                     AND  (cashrcpt_id != pCashrcptid)
+                     AND  (cashrcptitem_aropen_id=aropen_id))), 0)) - pAmount),0)
+    INTO _toApply
+    FROM cashrcpt
+      JOIN custinfo ON (cashrcpt_cust_id=cust_id)
+      JOIN aropen ON (cust_id=aropen_cust_id)
+    WHERE ((cashrcpt_id=pCashrcptid)
+      AND (aropen_open)
+      AND (aropen_doctype IN ('I','D')));
+           
+    -- Loop through and apply credits until we account for all remaining debits we can
+    FOR _r IN 
+      SELECT aropen_id
+      FROM cashrcpt
+        JOIN custinfo ON (cashrcpt_cust_id=cust_id)
+        JOIN aropen ON (cust_id=aropen_cust_id)
+      WHERE ((cashrcpt_id=pCashrcptid)
+        AND (aropen_open)
+        AND (aropen_doctype IN ('C','R')))
+      ORDER BY aropen_duedate, aropen_docnumber
+    LOOP
+     EXIT WHEN _toApply <= 0;
+      _toApply := _toApply - applyCashReceiptLineBalance(pCashrcptid, _r.aropen_id, _toApply, pCurrId);
+    END LOOP;
+  END IF;
+
+--  Find the balance to apply
+  SELECT (currToCurr(pCurrId, cashrcpt_curr_id, pAmount, cashrcpt_distdate) -
+              (COALESCE(SUM(cashrcptitem_amount), 0) ) ),
+              COALESCE(cashrcpt_docdate, current_date) 
+              INTO _amount, _docDate
+  FROM cashrcpt LEFT OUTER JOIN cashrcptitem ON (cashrcptitem_cashrcpt_id = cashrcpt_id)
+  WHERE (cashrcpt_id=pCashrcptid)
+  GROUP BY cashrcpt_curr_id, cashrcpt_distdate, cashrcpt_docdate;
+
+  SELECT (_amount - COALESCE(SUM(cashrcptmisc_amount), 0)) INTO _amount
+  FROM cashrcptmisc
+  WHERE (cashrcptmisc_cashrcpt_id=pCashrcptid);
+
+  IF (_amount = 0) THEN
+    RETURN 1;
+  END IF;
+
+--  Loop through the aropen item in order of due date, searching only for
+--  aropen items that are open, for the current customer and have an outstanding balance
+  FOR _r IN SELECT aropen_id,
+               currToCurr(aropen_curr_id, cashrcpt_curr_id,
+               aropen_amount - aropen_paid, cashrcpt_distdate) -
+               COALESCE((SELECT SUM(cashrcptitem_amount) + SUM(cashrcptitem_discount)
+                           FROM cashrcptitem, cashrcpt
+                           WHERE ((cashrcpt_id=cashrcptitem_cashrcpt_id)
+                             AND  (NOT cashrcpt_void)
+                             AND  (NOT cashrcpt_posted)
+                             AND  (cashrcpt_id != pCashrcptId)
+                             AND  (cashrcptitem_aropen_id=aropen_id))), 0) AS balance,
+                   s.cashrcptitem_id AS cashrcptitem_id
+            FROM cashrcpt, aropen LEFT OUTER JOIN
+                 cashrcptitem s ON (s.cashrcptitem_aropen_id=aropen_id AND s.cashrcptitem_cashrcpt_id=pCashrcptId)
+                 LEFT OUTER JOIN terms ON (aropen_terms_id=terms_id),
+                 (SELECT COALESCE(SUM(arapply_applied), 0.00) AS applied  
+                  FROM arapply, aropen 
+                  WHERE ((arapply_target_aropen_id=aropen_id) 
+                    AND (arapply_source_aropen_id=aropen_id) 
+                    AND  (aropen_discount) )
+                 ) AS data
+
+            WHERE ( (aropen_cust_id=cashrcpt_cust_id)
+             AND (aropen_doctype IN ('I', 'D'))
+             AND (aropen_open)
+             AND (cashrcpt_id=pCashrcptid) )
+            ORDER BY aropen_duedate, aropen_amount, balance LOOP
+
+--  Determine Max Discount as per Terms
+    SELECT  round(noNeg(_r.balance * 
+            CASE WHEN (_docDate <= determineDiscountDate(terms_id, aropen_docdate)) THEN terms_discprcnt 
+            ELSE 0.00 END - applied),2),
+            CASE WHEN (_docDate <= determineDiscountDate(terms_id, aropen_docdate)) THEN terms_discprcnt 
+            ELSE 0.00 END INTO _discount, _discprct
+            FROM aropen LEFT OUTER JOIN terms ON (terms_id=aropen_terms_id), 
+                 (SELECT COALESCE(SUM(arapply_applied), 0.00) AS applied  
+                         FROM arapply, aropen 
+                  WHERE ((arapply_target_aropen_id=_r.aropen_id) 
+                  AND (arapply_source_aropen_id=_r.aropen_id) 
+                  AND  (aropen_discount) )
+                 ) AS data 
+            WHERE (aropen_id=_r.aropen_id);
+
+--  Determine the amount to apply
+    IF (_r.balance <= _amount + _discount) THEN
+      _applyAmount := _r.balance - _discount;
+    ELSE
+      _discount := round((_amount / (1 - _discprct)) - _amount, 2);
+      _applyAmount := _amount;
+    END IF;
+
+    IF (_applyAmount > 0) THEN
+--  Does an cashrcptitem already exist?
+      IF (_r.cashrcptitem_id IS NOT NULL) THEN
+--  Update the cashrcptitem with the new amount to apply
+        UPDATE cashrcptitem
+        SET cashrcptitem_amount = round(cashrcptitem_amount + _applyAmount, 2),
+            cashrcptitem_discount = round(_discount, 2)
+        WHERE (cashrcptitem_id=_r.cashrcptitem_id);
+      ELSE
+--  Create a new cashrcptitem
+        INSERT INTO cashrcptitem
+        ( cashrcptitem_aropen_id, cashrcptitem_cashrcpt_id,
+          cashrcptitem_amount, cashrcptitem_discount )
+        VALUES
+        ( _r.aropen_id, pCashrcptid, round(_applyAmount, 2), round(_discount, 2) );
+      END IF;
+
+      _amount := (_amount - _applyAmount);
+      IF (round(_amount, 2) = 0) THEN
+        EXIT;
+      END IF;
+
+    END IF;
+  END LOOP;
+
+  RETURN 1;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/araging.sql b/foundation-database/public/functions/araging.sql
new file mode 100644 (file)
index 0000000..8c759be
--- /dev/null
@@ -0,0 +1,146 @@
+SELECT dropIfExists('FUNCTION', 'araging(date)', 'public');
+CREATE OR REPLACE FUNCTION araging(date, boolean) RETURNS SETOF araging AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pAsOfDate ALIAS FOR $1;
+  pUseDocDate ALIAS FOR $2;
+  _row araging%ROWTYPE;
+
+BEGIN
+
+  FOR _row IN SELECT *
+            FROM araging(pAsOfDate, pUseDocDate, true)
+  LOOP
+    RETURN NEXT _row;
+  END LOOP;
+
+  RETURN;
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION araging(date, boolean, boolean) RETURNS SETOF araging AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pAsOfDate ALIAS FOR $1;
+  pUseDocDate ALIAS FOR $2;
+  pConvBaseCurr ALIAS FOR $3;
+  _row araging%ROWTYPE;
+  _x RECORD;
+  _returnVal INTEGER;
+  _asOfDate DATE;
+BEGIN
+
+  _asOfDate := COALESCE(pAsOfDate,current_date);
+
+  FOR _x IN
+        SELECT
+        aropen_docdate,
+        aropen_duedate,
+        aropen_ponumber,
+        aropen_docnumber,
+        aropen_doctype,
+        cust_id,
+        cust_name,
+        cust_number,
+        cust_custtype_id,
+        custtype_code,
+        terms_descrip,
+
+        --if pConvBaseCurr is true then use currtobase to convert all amounts to base based on aropen_docdate to ensure the same exchange rate
+        --otherwise use currtocurr to convert all amounts to customer's currency based on aropen_docdate to ensure the same exchange rate
+
+        --today and greater:
+        CASE WHEN((aropen_duedate >= DATE(_asOfDate))) THEN balance
+             ELSE 0.0 END AS cur_val,
+
+        --0 to 30
+        CASE WHEN((aropen_duedate >= DATE(_asOfDate)-30) AND (aropen_duedate < DATE(_asOfDate))) THEN balance
+             ELSE 0.0 END AS thirty_val,
+
+        --30-60
+        CASE WHEN((aropen_duedate >= DATE(_asOfDate)-60) AND (aropen_duedate < DATE(_asOfDate) - 30 )) THEN balance
+             ELSE 0.0 END AS sixty_val,
+
+        --60-90
+        CASE WHEN((aropen_duedate >= DATE(_asOfDate)-90) AND (aropen_duedate < DATE(_asOfDate) - 60)) THEN balance
+             ELSE 0.0 END AS ninety_val,
+
+        --greater than 90:
+        CASE WHEN((aropen_duedate > DATE(_asOfDate)-10000) AND (aropen_duedate < DATE(_asOfDate) - 90)) THEN balance
+             ELSE 0.0 END AS plus_val,
+
+        --total amount:
+        CASE WHEN((aropen_duedate > DATE(_asOfDate)-10000)) THEN balance
+             ELSE 0.0 END AS total_val,
+
+        --AR Open Amount base
+        aropen_amount
+
+        FROM (
+          SELECT
+          (((aropen_amount - aropen_paid + COALESCE(SUM(arapply_target_paid),0))) /
+             CASE WHEN (pConvBaseCurr) THEN aropen_curr_rate
+                  ELSE currRate(aropen_curr_id, cust_curr_id, aropen_docdate)
+             END *
+             CASE WHEN (aropen_doctype IN ('C', 'R')) THEN -1.0
+                  ELSE 1.0
+             END) AS balance,
+          ((aropen_amount) /
+             CASE WHEN (pConvBaseCurr) THEN aropen_curr_rate
+                  ELSE currRate(aropen_curr_id, cust_curr_id, aropen_docdate)
+             END *
+             CASE WHEN (aropen_doctype IN ('C', 'R')) THEN -1.0
+                  ELSE 1.0
+             END) AS aropen_amount,
+          aropen_docdate,
+          aropen_duedate,
+          aropen_ponumber,
+          aropen_docnumber,
+          aropen_doctype,
+          cust_id,
+          cust_name,
+          cust_number,
+          cust_custtype_id,
+          custtype_code,
+          COALESCE(arterms.terms_descrip, custterms.terms_descrip, '') AS terms_descrip
+
+          FROM aropen
+            JOIN custinfo ON (cust_id=aropen_cust_id)
+            JOIN custtype ON (custtype_id=cust_custtype_id)
+            LEFT OUTER JOIN terms arterms ON (arterms.terms_id=aropen_terms_id)
+            LEFT OUTER JOIN terms custterms ON (custterms.terms_id=cust_terms_id)
+            LEFT OUTER JOIN arapply ON (((aropen_id=arapply_target_aropen_id)
+                                      OR (aropen_id=arapply_source_aropen_id))
+                                     AND (arapply_distdate>_asOfDate))
+          WHERE ( (CASE WHEN (pUseDocDate) THEN aropen_docdate ELSE aropen_distdate END <= _asOfDate)
+          AND (COALESCE(aropen_closedate,_asOfDate+1)>_asOfDate) )
+          GROUP BY aropen_id,aropen_docdate,aropen_duedate,aropen_ponumber,aropen_docnumber,aropen_doctype,aropen_paid,
+                   aropen_curr_id,aropen_amount,cust_id,cust_name,cust_number,cust_custtype_id,custtype_code,
+                   arterms.terms_descrip,custterms.terms_descrip, aropen_curr_rate, aropen_curr_id, cust_curr_id
+          ORDER BY cust_number, aropen_duedate ) AS data
+  LOOP
+        _row.araging_docdate := _x.aropen_docdate;
+        _row.araging_duedate := _x.aropen_duedate;
+        _row.araging_ponumber := _x.aropen_ponumber;
+        _row.araging_docnumber := _x.aropen_docnumber;
+        _row.araging_doctype := _x.aropen_doctype;
+        _row.araging_cust_id := _x.cust_id;
+        _row.araging_cust_number := _x.cust_number;
+        _row.araging_cust_name := _x.cust_name;
+        _row.araging_cust_custtype_id := _x.cust_custtype_id;
+        _row.araging_custtype_code := _x.custtype_code;
+        _row.araging_terms_descrip := _x.terms_descrip;
+        _row.araging_aropen_amount := _x.aropen_amount;
+        _row.araging_cur_val := _x.cur_val;
+        _row.araging_thirty_val := _x.thirty_val;
+        _row.araging_sixty_val := _x.sixty_val;
+        _row.araging_ninety_val := _x.ninety_val;
+        _row.araging_plus_val := _x.plus_val;
+        _row.araging_total_val := _x.total_val;
+        RETURN NEXT _row;
+  END LOOP;
+  RETURN;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/arapplied.sql b/foundation-database/public/functions/arapplied.sql
new file mode 100644 (file)
index 0000000..525a31e
--- /dev/null
@@ -0,0 +1,28 @@
+CREATE OR REPLACE FUNCTION arapplied(INTEGER, DATE) RETURNS NUMERIC AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pAropenid ALIAS FOR $1;
+  pDate ALIAS FOR $2;
+  _amount NUMERIC;
+
+BEGIN
+
+  -- Return amount applied to an aropen in base currency item as of the parameter date
+  SELECT SUM(currtobase(arapply_curr_id,arapply_applied,pDate)) INTO _amount
+  FROM arapply
+  WHERE (((arapply_target_aropen_id = pAropenid) OR (arapply_source_aropen_id = pAropenid))
+  AND (((arapply_journalnumber=0) AND (arapply_postdate <= pDate))
+  OR EXISTS(SELECT * 
+             FROM gltrans 
+             WHERE ((gltrans_journalnumber=arapply_journalnumber)
+             AND (gltrans_date <= pDate)))));
+
+  IF (_amount IS NULL) THEN
+    RETURN 0;
+  ELSE
+    RETURN _amount;
+  END IF;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/archivesaleshistory.sql b/foundation-database/public/functions/archivesaleshistory.sql
new file mode 100644 (file)
index 0000000..bc76eb9
--- /dev/null
@@ -0,0 +1,136 @@
+CREATE OR REPLACE FUNCTION archiveSalesHistory(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSohistid ALIAS FOR $1;
+
+BEGIN
+
+  INSERT INTO asohist ( asohist_id,
+                        asohist_cust_id,
+                        asohist_itemsite_id,
+                        asohist_shipdate,
+                        asohist_invcdate,
+                        asohist_duedate,
+                        asohist_promisedate,
+                        asohist_ordernumber,
+                        asohist_invcnumber,
+                        asohist_qtyshipped,
+                        asohist_unitprice,
+                        asohist_unitcost,
+                        asohist_billtoname,
+                        asohist_billtoaddress1,
+                        asohist_billtoaddress2,
+                        asohist_billtoaddress3,
+                        asohist_billtocity,
+                        asohist_billtostate,
+                        asohist_billtozip,
+                        asohist_shiptoname,
+                        asohist_shiptoaddress1,
+                        asohist_shiptoaddress2,
+                        asohist_shiptoaddress3,
+                        asohist_shiptocity,
+                        asohist_shiptostate,
+                        asohist_shiptozip,
+                        asohist_shipto_id,
+                        asohist_shipvia,
+                        asohist_salesrep_id,
+                        asohist_misc_type,
+                        asohist_misc_descrip,
+                        asohist_misc_id,
+                        asohist_commission,
+                        asohist_commissionpaid,
+                        asohist_doctype,
+                        asohist_orderdate,
+                        asohist_imported,
+                       asohist_ponumber,
+                        asohist_curr_id,
+                        asohist_taxtype_id,
+                        asohist_taxzone_id )
+  SELECT cohist_id,
+         cohist_cust_id,
+         cohist_itemsite_id,
+         cohist_shipdate,
+         cohist_invcdate,
+         cohist_duedate,
+         cohist_promisedate,
+         cohist_ordernumber,
+         cohist_invcnumber,
+         cohist_qtyshipped,
+         cohist_unitprice,
+         cohist_unitcost,
+         cohist_billtoname,
+         cohist_billtoaddress1,
+         cohist_billtoaddress2,
+         cohist_billtoaddress3,
+         cohist_billtocity,
+         cohist_billtostate,
+         cohist_billtozip,
+         cohist_shiptoname,
+         cohist_shiptoaddress1,
+         cohist_shiptoaddress2,
+         cohist_shiptoaddress3,
+         cohist_shiptocity,
+         cohist_shiptostate,
+         cohist_shiptozip,
+         cohist_shipto_id,
+         cohist_shipvia,
+         cohist_salesrep_id,
+         cohist_misc_type,
+         cohist_misc_descrip,
+         cohist_misc_id,
+         cohist_commission,
+         cohist_commissionpaid,
+         cohist_doctype,
+         cohist_orderdate,
+         cohist_imported,
+         cohist_ponumber,
+        cohist_curr_id,
+         cohist_taxtype_id,
+         cohist_taxzone_id
+  FROM cohist
+  WHERE (cohist_id=pSohistid);
+
+  INSERT INTO asohisttax ( taxhist_id,
+                           taxhist_parent_id,
+                           taxhist_taxtype_id,
+                           taxhist_tax_id,
+                           taxhist_basis,
+                           taxhist_basis_tax_id,
+                           taxhist_sequence,
+                           taxhist_percent,
+                           taxhist_amount,
+                           taxhist_tax,
+                           taxhist_docdate,
+                           taxhist_distdate,
+                           taxhist_curr_id,
+                           taxhist_curr_rate,
+                           taxhist_journalnumber )
+  SELECT taxhist_id,
+         taxhist_parent_id,
+         taxhist_taxtype_id,
+         taxhist_tax_id,
+         taxhist_basis,
+         taxhist_basis_tax_id,
+         taxhist_sequence,
+         taxhist_percent,
+         taxhist_amount,
+         taxhist_tax,
+         taxhist_docdate,
+         taxhist_distdate,
+         taxhist_curr_id,
+         taxhist_curr_rate,
+         taxhist_journalnumber
+  FROM cohisttax
+  WHERE (taxhist_parent_id=pSohistid);
+
+  DELETE FROM cohisttax
+  WHERE (taxhist_parent_id=pSohistid);
+
+  DELETE FROM cohist
+  WHERE (cohist_id=pSohistid);
+
+  RETURN pSohistid;
+
+END;
+$$ LANGUAGE plpgsql;
diff --git a/foundation-database/public/functions/arcurrgain.sql b/foundation-database/public/functions/arcurrgain.sql
new file mode 100644 (file)
index 0000000..071eeb6
--- /dev/null
@@ -0,0 +1,43 @@
+-- calculate the change in value caused by exchange rate fluctuations.
+-- we generally care about currency exchange gain/loss when adjusting the G/L,
+-- so this function returns its result in the base currency.
+-- however, we only care about fluctuations in the base value of a foreign
+-- quantity, so this function expects pValue ($3) in the local currency.
+-- negative values = a loss.
+CREATE OR REPLACE FUNCTION arcurrGain(INTEGER, INTEGER, NUMERIC, DATE)
+RETURNS NUMERIC AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pAropenId ALIAS FOR $1;
+  pCurrId ALIAS FOR $2;
+  pValue ALIAS FOR $3;
+  pDate ALIAS FOR $4;
+  _start DATE;
+  _end DATE;
+  _gain NUMERIC;
+  _r RECORD;
+
+BEGIN
+  IF (pAropenId IS NULL OR pValue = 0) THEN
+    RETURN 0;
+  END IF;
+
+  SELECT aropen_docdate, aropen_curr_id, aropen_curr_rate
+    INTO _r
+  FROM aropen
+  WHERE (aropen_id=pAropenId);
+
+  IF (_r.aropen_docdate > pDate) THEN
+    _gain := (currToBase(pCurrId, pValue, pDate) - currToCurr(pCurrId,_r.aropen_curr_id, pValue, pDate) / _r.aropen_curr_rate) * -1;
+  ELSE
+    _gain := currToCurr(pCurrId,_r.aropen_curr_id, pValue, pDate) / _r.aropen_curr_rate - currToBase(pCurrId, pValue, pDate);
+  END IF;
+
+  IF (_gain IS NULL) THEN
+    RAISE EXCEPTION 'Error processing currency gain/loss.';
+  END IF;
+
+  RETURN _gain;
+END;
+$$ LANGUAGE plpgsql;
diff --git a/foundation-database/public/functions/asofinvbal.sql b/foundation-database/public/functions/asofinvbal.sql
new file mode 100644 (file)
index 0000000..2354753
--- /dev/null
@@ -0,0 +1,110 @@
+CREATE OR REPLACE FUNCTION asofinvbal(INTEGER, DATE) RETURNS SETOF invbal STABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteId ALIAS FOR $1;
+  pAsofDate ALIAS FOR $2;
+  _result invbal%ROWTYPE;
+  _i RECORD;
+  _h RECORD;
+  _r RECORD;
+  _prevCostmethod TEXT := 'A';
+  _prevDate TIMESTAMP WITH TIME ZONE;
+  _runningQty NUMERIC := 0;
+  _runningNn NUMERIC := 0;
+  _runningValue NUMERIC := 0;
+  _runningNnval NUMERIC := 0;
+
+BEGIN
+  /* This is a base function to gather data.  Because it is STABLE it should only need
+  to be calculated once, even though it is likely to be called several times by other
+  functions in parent query to present the various data.
+  */
+  
+  -- First make sure inventory balance is forward updated
+  PERFORM forwardUpdateItemsite(pItemsiteId);
+
+  -- Next find the previous period balace to use as a starting point
+  SELECT invbal.*, period_start, itemsite_costmethod INTO _i
+  FROM invbal 
+    JOIN itemsite ON (invbal_itemsite_id=itemsite_id) 
+    JOIN period ON (invbal_period_id=period_id)
+  WHERE ((invbal_itemsite_id=pItemsiteId)
+    AND  (pAsofDate >= period_start))
+  ORDER BY period_start DESC
+  LIMIT 1;
+
+  _runningQty := _i.invbal_qoh_beginning;
+  _runningNn := _i.invbal_nn_beginning;
+  _runningValue := _i.invbal_value_beginning;
+  _runningNnval := _i.invbal_nnval_beginning;
+  _prevDate := _i.period_start;
+  _prevCostmethod := _i.itemsite_costmethod;
+
+  FOR _r IN 
+    SELECT invhist_id, invhist_created, invhist_invqty, invhist_transtype, invhist_unitcost,
+      invhist_costmethod, itemsite_item_id, invhistSense(invhist_id) AS sense
+    FROM invhist
+      JOIN itemsite ON (itemsite_id=invhist_itemsite_id)
+    WHERE ((invhist_itemsite_id=pItemsiteId)
+    AND (invhist_transdate::date BETWEEN _i.period_start AND pAsofdate))
+    ORDER BY invhist_created, invhist_id
+  LOOP
+    -- Update balances changed by any standard cost update between transactions
+    IF (_prevCostmethod = 'S' AND _runningQty != 0) THEN
+      FOR _h IN
+        SELECT costhist_oldcost, costhist_newcost
+        FROM costhist
+          JOIN item ON (costhist_item_id=item_id)
+          JOIN itemsite ON (itemsite_item_id=item_id)
+        WHERE ((itemsite_id=pItemsiteId)
+          AND (costhist_date BETWEEN _prevDate AND _r.invhist_created)
+          AND (costhist_type IN ('S','D')))
+      LOOP
+        _runningValue := _runningValue + round((_h.costhist_newcost-_h.costhist_oldcost) * _runningQty,2);
+        _runningNnval := _runningNnval + round((_h.costhist_newcost-_h.costhist_oldcost) * _runningNn,2);
+      END LOOP;
+    END IF;
+
+    _prevDate := _r.invhist_created;
+    _prevCostmethod := _r.invhist_costmethod;
+    _runningQty := _runningQty + _r.invhist_invqty * _r.sense;
+    _runningValue := _runningValue + round( _r.invhist_invqty * _r.sense * _r.invhist_unitcost,2);
+    IF (_r.invhist_transtype = 'NN') THEN
+      _runningNn := _runningNn + _r.invhist_invqty * -1;
+      _runningNnval := _runningNnval + round( _r.invhist_invqty * -1 * _r.invhist_unitcost,2);
+    END IF;
+    
+  END LOOP;
+
+  _prevDate := COALESCE(_prevDate, _i.period_start);
+  _prevCostmethod := COALESCE(_r.invhist_costmethod, _i.itemsite_costmethod);
+  
+  IF (_prevCostmethod = 'S' AND _runningQty != 0) THEN
+    FOR _h IN
+      SELECT costhist_oldcost, costhist_newcost
+      FROM costhist
+        JOIN item ON (costhist_item_id=item_id)
+        JOIN itemsite ON (itemsite_item_id=item_id)
+      WHERE ((itemsite_id=pItemsiteId)
+        AND (costhist_date BETWEEN _prevDate AND CAST(pAsofDate + 1 AS TIMESTAMP WITH TIME ZONE))
+        AND (costhist_type IN ('S','D')))
+    LOOP
+      _runningValue := _runningValue + round((_h.costhist_newcost-_h.costhist_oldcost) * _runningQty,2);
+      _runningNnval := _runningNnval + round((_h.costhist_newcost-_h.costhist_oldcost) * _runningNn,2);
+    END LOOP;
+  END IF;
+
+  _result := _i;
+  _result.invbal_qoh_ending := _runningQty;
+  _result.invbal_value_ending := _runningValue;
+  _result.invbal_nn_ending := _runningNn;
+  _result.invbal_nnval_ending := _runningNnval;
+
+  RETURN NEXT _result;
+
+  RETURN;
+  
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/asofinvnn.sql b/foundation-database/public/functions/asofinvnn.sql
new file mode 100644 (file)
index 0000000..b95beec
--- /dev/null
@@ -0,0 +1,17 @@
+CREATE OR REPLACE FUNCTION asofinvnn(INTEGER, DATE) RETURNS NUMERIC STABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteId ALIAS FOR $1;
+  pAsofDate ALIAS FOR $2;
+  _result NUMERIC;
+
+BEGIN
+
+  SELECT invbal_nn_ending INTO _result
+  FROM asofinvbal(pItemsiteId, pAsofDate);
+
+  RETURN COALESCE(_result, 0);
+  
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/asofinvqty.sql b/foundation-database/public/functions/asofinvqty.sql
new file mode 100644 (file)
index 0000000..824d72c
--- /dev/null
@@ -0,0 +1,17 @@
+CREATE OR REPLACE FUNCTION asofinvqty(INTEGER, DATE) RETURNS NUMERIC STABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteId ALIAS FOR $1;
+  pAsofDate ALIAS FOR $2;
+  _result NUMERIC;
+
+BEGIN
+
+  SELECT invbal_qoh_ending INTO _result
+  FROM asofinvbal(pItemsiteId, pAsofDate);
+
+  RETURN COALESCE(_result, 0);
+  
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/attachcontact.sql b/foundation-database/public/functions/attachcontact.sql
new file mode 100644 (file)
index 0000000..8efdfaa
--- /dev/null
@@ -0,0 +1,15 @@
+
+CREATE OR REPLACE FUNCTION attachContact(INTEGER, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pcntctId    ALIAS FOR $1;
+  pcrmacctId  ALIAS FOR $2;
+BEGIN
+  UPDATE cntct SET cntct_crmacct_id = pcrmacctId
+  WHERE cntct_id = pcntctId;
+
+  RETURN 0;
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/attachquotetoopportunity.sql b/foundation-database/public/functions/attachquotetoopportunity.sql
new file mode 100644 (file)
index 0000000..7bf8f4a
--- /dev/null
@@ -0,0 +1,37 @@
+CREATE OR REPLACE FUNCTION attachQuoteToOpportunity(INTEGER, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pQuheadid    ALIAS FOR $1;
+  pOpheadid    ALIAS FOR $2;
+BEGIN
+
+-- Check Quote
+  IF (NOT EXISTS(SELECT quhead_id
+                 FROM quhead
+                 WHERE (quhead_id=pQuheadid))) THEN
+    RETURN -1;
+  END IF;
+
+-- Check Opportunity
+  IF (NOT EXISTS(SELECT ophead_id
+                 FROM ophead
+                 WHERE (ophead_id=pOpheadid))) THEN
+    RETURN -2;
+  END IF;
+
+-- Cannot attach if already attached
+  IF (EXISTS(SELECT quhead_id
+            FROM quhead
+            WHERE ((quhead_id=pQuheadid)
+              AND  (quhead_ophead_id IS NOT NULL)))) THEN
+    RETURN -3;
+  END IF;
+
+  UPDATE quhead SET quhead_ophead_id=pOpheadid
+  WHERE (quhead_id=pQuheadid);
+
+  RETURN 0;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/attachsalesordertoopportunity.sql b/foundation-database/public/functions/attachsalesordertoopportunity.sql
new file mode 100644 (file)
index 0000000..3c90492
--- /dev/null
@@ -0,0 +1,37 @@
+CREATE OR REPLACE FUNCTION attachSalesOrderToOpportunity(INTEGER, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSoheadid    ALIAS FOR $1;
+  pOpheadid    ALIAS FOR $2;
+BEGIN
+
+-- Check Sales Order
+  IF (NOT EXISTS(SELECT cohead_id
+                 FROM cohead
+                 WHERE (cohead_id=pSoheadid))) THEN
+    RETURN -1;
+  END IF;
+
+-- Check Opportunity
+  IF (NOT EXISTS(SELECT ophead_id
+                 FROM ophead
+                 WHERE (ophead_id=pOpheadid))) THEN
+    RETURN -2;
+  END IF;
+
+-- Cannot attach if already attached
+  IF (EXISTS(SELECT cohead_id
+            FROM cohead
+            WHERE ((cohead_id=pSoheadid)
+              AND  (cohead_ophead_id IS NOT NULL)))) THEN
+    RETURN -3;
+  END IF;
+
+  UPDATE cohead SET cohead_ophead_id=pOpheadid
+  WHERE (cohead_id=pSoheadid);
+
+  RETURN 0;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/averagesalesprice.sql b/foundation-database/public/functions/averagesalesprice.sql
new file mode 100644 (file)
index 0000000..260e11e
--- /dev/null
@@ -0,0 +1,28 @@
+CREATE OR REPLACE FUNCTION averageSalesPrice(INTEGER, DATE, DATE) RETURNS NUMERIC AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  pStartDate ALIAS FOR $2;
+  pEndDate ALIAS FOR $3;
+  _p RECORD;
+
+BEGIN
+-- Returns value in base currency
+-- ToDo: is cohist_shipdate the right DATE to use?
+  SELECT SUM(cohist_qtyshipped * currToBase(cohist_curr_id, cohist_unitprice,
+                                            cohist_shipdate)) AS totalsales,
+         SUM(cohist_qtyshipped) AS totalship INTO _p
+  FROM cohist
+  WHERE ( (cohist_itemsite_id=pItemsiteid)
+   AND (cohist_invcdate BETWEEN pStartDate AND pEndDate) );
+
+  IF ( (_p.totalship IS NULL) OR
+       (_p.totalship = 0) ) THEN
+    RETURN 0;
+  ELSE
+    RETURN (_p.totalsales / _p.totalship);
+  END IF;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/avgcost.sql b/foundation-database/public/functions/avgcost.sql
new file mode 100644 (file)
index 0000000..ffd98a0
--- /dev/null
@@ -0,0 +1,19 @@
+CREATE OR REPLACE FUNCTION avgCost(pItemsiteid INTEGER) RETURNS NUMERIC AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _value NUMERIC;
+  _qoh NUMERIC;
+  _qohnn NUMERIC;
+BEGIN
+  SELECT itemsite_value, itemsite_qtyonhand, itemsite_nnqoh
+    INTO _value, _qoh, _qohnn
+    FROM itemsite
+   WHERE(itemsite_id=pItemsiteid);
+  IF (_qoh = 0.0 AND _qohnn = 0.0) THEN
+    RETURN 0.0;
+  END IF;
+  RETURN _value / (_qoh + _qohnn);
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/balanceitemsite.sql b/foundation-database/public/functions/balanceitemsite.sql
new file mode 100644 (file)
index 0000000..17a62cf
--- /dev/null
@@ -0,0 +1,61 @@
+CREATE OR REPLACE FUNCTION balanceItemsite(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  _itemlocseries INTEGER;
+  _balanced NUMERIC;
+  _qoh NUMERIC;
+  _nnQoh NUMERIC;
+
+BEGIN
+
+--  Make sure that that passed Itemsite is MLC or Lot/Serial controlled
+  IF ( ( SELECT (NOT ( (itemsite_loccntrl) OR (itemsite_controlmethod IN ('L', 'S')) ))
+         FROM itemsite
+         WHERE (itemsite_id=pItemsiteid) ) ) THEN
+    RETURN 0;
+  END IF;
+
+  IF ( ( SELECT itemsite_freeze
+           FROM itemsite
+          WHERE(itemsite_id=pItemsiteid) ) ) THEN
+    RETURN -1;
+  END IF;
+
+--  Calculate the Netable portion
+  SELECT COALESCE(SUM(itemloc_qty), 0) INTO _balanced
+  FROM itemloc LEFT OUTER JOIN location ON (itemloc_location_id=location_id)
+  WHERE ( ( (location_id IS NULL) OR (location_netable) )
+   AND (itemloc_itemsite_id=pItemsiteid) );
+
+--  Post an AD Transaction for the Netable portion
+  SELECT invAdjustment( itemsite_id, (_balanced - itemsite_qtyonhand),
+                        'Balance', 'Inventory Balance' ) INTO _itemlocseries
+  FROM itemsite
+  WHERE (itemsite_id=pItemsiteid);
+
+--  Post the invtrans records associated with the itemlocdist records
+  PERFORM postInvhist(itemlocdist_invhist_id)
+     FROM itemlocdist
+    WHERE(itemlocdist_series=_itemlocseries);
+
+--  Kill the resultant distribution records
+  DELETE FROM itemlocdist
+  WHERE (itemlocdist_series=_itemlocseries);
+
+--  Calculate and write the Non-Netable portion directly
+  SELECT COALESCE(SUM(itemloc_qty), 0) INTO _nnQoh
+  FROM itemloc, location
+  WHERE ( (itemloc_location_id=location_id)
+   AND (NOT location_netable)
+   AND (itemloc_itemsite_id=pItemsiteid) );
+
+  UPDATE itemsite
+  SET itemsite_nnqoh = _nnQoh
+  WHERE (itemsite_id=pItemsiteid);
+
+  RETURN 1;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/basecurrid.sql b/foundation-database/public/functions/basecurrid.sql
new file mode 100644 (file)
index 0000000..0fc1c32
--- /dev/null
@@ -0,0 +1,16 @@
+CREATE OR REPLACE FUNCTION baseCurrID ()
+    RETURNS INTEGER IMMUTABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  returnVal INTEGER;
+BEGIN
+  SELECT curr_id INTO returnVal
+    FROM curr_symbol
+   WHERE curr_base = TRUE;
+  IF NOT FOUND THEN
+    RAISE EXCEPTION 'No base currency found';
+  END IF;
+  RETURN returnVal;
+END;
+$$ LANGUAGE plpgsql;
diff --git a/foundation-database/public/functions/bomcontains.sql b/foundation-database/public/functions/bomcontains.sql
new file mode 100644 (file)
index 0000000..570861f
--- /dev/null
@@ -0,0 +1,22 @@
+CREATE OR REPLACE FUNCTION BOMContains(INTEGER, INTEGER) RETURNS BOOLEAN AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pparentitemid        ALIAS FOR $1;
+  pchilditemid ALIAS FOR $2;
+  _bomworksetid        INTEGER;
+  _result      BOOLEAN;
+
+BEGIN
+  _bomworksetid := indentedWhereUsed(pchilditemid);
+  _result := EXISTS(SELECT bomwork_id
+                   FROM bomwork
+                   WHERE ((bomwork_set_id=_bomworksetid)
+                     AND  (bomwork_item_id=pparentitemid) ));
+
+  PERFORM deleteBOMWorkset(_bomworksetid);
+
+  RETURN _result;
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/bomhistsequence.sql b/foundation-database/public/functions/bomhistsequence.sql
new file mode 100644 (file)
index 0000000..4b88e13
--- /dev/null
@@ -0,0 +1,41 @@
+CREATE OR REPLACE FUNCTION bomhistSequence(INTEGER) RETURNS TEXT AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pHistid ALIAS FOR $1;
+  _wid INTEGER;
+  _seqnum TEXT;
+  _bomhist RECORD;
+
+BEGIN
+  _wid := pHistid;
+
+  SELECT bomhist_parent_id AS parent,
+         to_char(bomhist_seqnumber, '00009') AS seq INTO _bomhist
+    FROM bomhist
+   WHERE bomhist_seq_id=_wid;
+
+  IF (FOUND) THEN
+    _seqnum := _bomhist.seq;
+    _wid := _bomhist.parent;
+
+    WHILE (_wid != -1) LOOP
+      SELECT bomhist_parent_id AS parent,
+             to_char(bomhist_seqnumber, '00009') AS seq INTO _bomhist
+      FROM bomhist
+      WHERE bomhist_seq_id=_wid;
+
+      IF (FOUND) THEN
+        _seqnum := _bomhist.seq || '-' || _seqnum;
+        _wid    := _bomhist.parent;
+      ELSE
+        _wid := -1;
+      END IF;
+    END LOOP;
+  ELSE
+    _seqnum := ''::TEXT;
+  END IF;
+
+  RETURN _seqnum;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/bomitem.sql b/foundation-database/public/functions/bomitem.sql
new file mode 100644 (file)
index 0000000..57451ee
--- /dev/null
@@ -0,0 +1,11 @@
+CREATE OR REPLACE FUNCTION bomitem(INTEGER) RETURNS SETOF bomitem AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+  SELECT * FROM bomitem WHERE ((bomitem_parent_item_id=$1) AND (bomitem_rev_id=getActiveRevId('BOM',$1)));
+$$ LANGUAGE 'sql';
+
+CREATE OR REPLACE FUNCTION bomitem(INTEGER,INTEGER) RETURNS SETOF bomitem AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+  SELECT * FROM bomitem WHERE ((bomitem_parent_item_id=$1) AND (bomitem_rev_id=$2));
+$$ LANGUAGE 'sql';
diff --git a/foundation-database/public/functions/bomlevelbyitem.sql b/foundation-database/public/functions/bomlevelbyitem.sql
new file mode 100644 (file)
index 0000000..b32f721
--- /dev/null
@@ -0,0 +1,64 @@
+CREATE OR REPLACE FUNCTION bomLevelByItem(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemid ALIAS FOR $1;
+  _cnt INTEGER;
+  _result INTEGER;
+  _bomitem RECORD;
+
+BEGIN
+  _cnt := 0;
+
+  BEGIN
+  FOR _bomitem IN SELECT bomitem_parent_item_id
+                    FROM bomitem
+                   WHERE ((bomitem_item_id=pItemid)
+                     AND  (bomitem_rev_id=getActiveRevId('BOM',bomitem_parent_item_id))
+                     AND  (CURRENT_DATE BETWEEN bomitem_effective AND (bomitem_expires - 1)))
+  LOOP
+    SELECT bomLevelByItem(_bomitem.bomitem_parent_item_id) + 1 INTO _result;
+    IF (_result > _cnt) THEN
+      _cnt := _result;
+    END IF;
+  END LOOP;
+  EXCEPTION WHEN statement_too_complex THEN
+      RAISE EXCEPTION 'potential recursive BOM found for item_id %', pItemid;
+  END;
+
+  return _cnt;
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION bomLevelByItem(INTEGER,INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemid ALIAS FOR $1;
+  pBomrevid ALIAS FOR $2;
+  _cnt INTEGER;
+  _result INTEGER;
+  _bomitem RECORD;
+
+BEGIN
+  _cnt := 0;
+
+  BEGIN
+  FOR _bomitem IN SELECT bomitem_parent_item_id
+                    FROM bomitem
+                   WHERE ((bomitem_item_id=pItemid)
+                     AND  (bomitem_rev_id=pBomrevid)
+                     AND  (CURRENT_DATE BETWEEN bomitem_effective AND (bomitem_expires - 1)))
+  LOOP
+    SELECT bomLevelByItem(_bomitem.bomitem_parent_item_id, pBomrevid) + 1 INTO _result;
+    IF (_result > _cnt) THEN
+      _cnt := _result;
+    END IF;
+  END LOOP;
+  EXCEPTION WHEN statement_too_complex THEN
+      RAISE EXCEPTION 'potential recursive BOM found for item_id %', pItemid;
+  END;
+
+  return _cnt;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/bomworkeffective.sql b/foundation-database/public/functions/bomworkeffective.sql
new file mode 100644 (file)
index 0000000..6be655f
--- /dev/null
@@ -0,0 +1,29 @@
+CREATE OR REPLACE FUNCTION bomworkEffective( INTEGER, DATE ) RETURNS BOOLEAN AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+    workid ALIAS FOR $1;
+    effdate ALIAS FOR $2;
+    _wid INTEGER;
+    _bomwork RECORD;
+BEGIN
+    _wid := workid;
+    WHILE (_wid != -1) LOOP
+        SELECT bomwork_parent_id AS parent,
+               bomwork_effective AS effective
+          INTO _bomwork
+          FROM bomwork
+         WHERE bomwork_id=_wid;
+
+         IF (FOUND) THEN
+             _wid := _bomwork.parent;
+             IF (_bomwork.effective > effdate) THEN
+                 RETURN FALSE;
+             END IF;
+         ELSE
+             _wid := -1;
+         END IF;
+    END LOOP;
+    RETURN TRUE;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/bomworkexpired.sql b/foundation-database/public/functions/bomworkexpired.sql
new file mode 100644 (file)
index 0000000..1c406ab
--- /dev/null
@@ -0,0 +1,29 @@
+CREATE OR REPLACE FUNCTION bomworkExpired( INTEGER, DATE ) RETURNS BOOLEAN AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+    workid ALIAS FOR $1;
+    expdate ALIAS FOR $2;
+    _wid INTEGER;
+    _bomwork RECORD;
+BEGIN
+    _wid := workid;
+    WHILE (_wid != -1) LOOP
+        SELECT bomwork_parent_id AS parent,
+               bomwork_expires AS expires
+          INTO _bomwork
+          FROM bomwork
+         WHERE bomwork_id=_wid;
+
+         IF (FOUND) THEN
+             _wid := _bomwork.parent;
+             IF (_bomwork.expires <= expdate) THEN
+                 RETURN TRUE;
+             END IF;
+         ELSE
+             _wid := -1;
+         END IF;
+    END LOOP;
+    RETURN FALSE;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/bomworkitemsequence.sql b/foundation-database/public/functions/bomworkitemsequence.sql
new file mode 100644 (file)
index 0000000..1342098
--- /dev/null
@@ -0,0 +1,43 @@
+CREATE OR REPLACE FUNCTION bomworkItemSequence(INTEGER) RETURNS TEXT AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWorkid ALIAS FOR $1;
+  _wid INTEGER;
+  _seqnum TEXT;
+  _bomwork RECORD;
+
+BEGIN
+  _wid := pWorkid;
+
+  SELECT bomwork_parent_id AS parent,
+         item_number AS seq INTO _bomwork
+    FROM bomwork, item
+   WHERE ((bomwork_id=_wid)
+   AND (bomwork_item_id=item_id));
+
+  IF (FOUND) THEN
+    _seqnum := _bomwork.seq;
+    _wid := _bomwork.parent;
+
+    WHILE (_wid != -1) LOOP
+      SELECT bomwork_parent_id AS parent,
+             item_number AS seq INTO _bomwork
+      FROM bomwork, item
+      WHERE ((bomwork_id=_wid)
+      AND (bomwork_item_id=item_id));
+
+      IF (FOUND) THEN
+        _seqnum := _bomwork.seq || '-' || _seqnum;
+        _wid    := _bomwork.parent;
+      ELSE
+        _wid := -1;
+      END IF;
+    END LOOP;
+  ELSE
+    _seqnum := ''::TEXT;
+  END IF;
+
+  RETURN _seqnum;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/bomworksequence.sql b/foundation-database/public/functions/bomworksequence.sql
new file mode 100644 (file)
index 0000000..24f27c5
--- /dev/null
@@ -0,0 +1,41 @@
+CREATE OR REPLACE FUNCTION bomworkSequence(INTEGER) RETURNS TEXT AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWorkid ALIAS FOR $1;
+  _wid INTEGER;
+  _seqnum TEXT;
+  _bomwork RECORD;
+
+BEGIN
+  _wid := pWorkid;
+
+  SELECT bomwork_parent_id AS parent,
+         to_char(bomwork_seqnumber, ''00009'') AS seq INTO _bomwork
+    FROM bomwork
+   WHERE bomwork_id=_wid;
+
+  IF (FOUND) THEN
+    _seqnum := _bomwork.seq;
+    _wid := _bomwork.parent;
+
+    WHILE (_wid != -1) LOOP
+      SELECT bomwork_parent_id AS parent,
+             to_char(bomwork_seqnumber, ''00009'') AS seq INTO _bomwork
+      FROM bomwork
+      WHERE bomwork_id=_wid;
+
+      IF (FOUND) THEN
+        _seqnum := _bomwork.seq || ''-'' || _seqnum;
+        _wid    := _bomwork.parent;
+      ELSE
+        _wid := -1;
+      END IF;
+    END LOOP;
+  ELSE
+    _seqnum := ''''::TEXT;
+  END IF;
+
+  RETURN _seqnum;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/buildinvbal.sql b/foundation-database/public/functions/buildinvbal.sql
new file mode 100644 (file)
index 0000000..655e7f1
--- /dev/null
@@ -0,0 +1,81 @@
+CREATE OR REPLACE FUNCTION buildInvBal(integer) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteId ALIAS FOR $1;
+  _r RECORD;
+  _prevCostmethod TEXT := 'A';
+  _prevDate TIMESTAMP WITH TIME ZONE;
+  _runningQty NUMERIC := 0;
+  _runningNn NUMERIC := 0;
+
+BEGIN
+  -- Validate
+  IF (SELECT (count(invhist_id) > 0)
+      FROM invhist
+      WHERE ((invhist_itemsite_id=pItemsiteId)
+      AND (NOT invhist_posted))) THEN
+
+    SELECT item_number, warehous_code INTO _r
+    FROM itemsite
+      JOIN item ON (item_id=itemsite_item_id)
+      JOIN whsinfo ON (itemsite_warehous_id=warehous_id)
+    WHERE (itemsite_id=pItemsiteId);
+    
+    RAISE EXCEPTION 'Unposted inventory transactions exist for % at % [xtuple: buildInvBal, -1, %, %]',
+                    _r.item_number, _r.warehous_code,
+                    _r.item_number, _r.warehous_code;
+  END IF;
+
+  -- Remove any old records
+  DELETE FROM invbal WHERE invbal_itemsite_id=pItemsiteId;
+
+  FOR _r IN 
+    SELECT invhist.*,
+      itemsite_item_id, invhistSense(invhist_id) AS sense,
+      item_number, warehous_code
+    FROM invhist
+      JOIN itemsite ON (itemsite_id=invhist_itemsite_id)
+      JOIN item ON (itemsite_item_id=item_id)
+      JOIN whsinfo ON (itemsite_warehous_id=warehous_id)
+    WHERE (invhist_itemsite_id=pItemsiteId)
+    ORDER BY invhist_created, invhist_id
+  LOOP
+    RAISE NOTICE 'Calculating balances for Item % at Site % against transaction %, transtype %, sense %, qty %, %', _r.item_number, _r.warehous_code, _r.invhist_id, _r.invhist_transtype, _r.sense, _r.invhist_invqty, _r.invhist_comments;
+    -- Update balances changed by any standard cost update between transactions
+    IF (_prevCostmethod = 'S' AND _runningQty != 0) THEN
+      PERFORM postValueintoInvBalance(pItemsiteid, costhist_date::date, _runningQty, _runningNn, costhist_oldcost, costhist_newcost )
+      FROM costhist
+      WHERE ((costhist_item_id=_r.itemsite_item_id)
+        AND (costhist_date BETWEEN _prevDate AND _r.invhist_created)
+        AND (costhist_type IN ('S','D')));
+    END IF;
+
+    -- Post transaction into inventory balance table
+    PERFORM postIntoInvBalance(_r.invhist_id);
+
+    _prevDate := _r.invhist_created;
+    _prevCostmethod := _r.invhist_costmethod;
+    _runningQty := _runningQty + _r.invhist_invqty * _r.sense;
+    IF (_r.invhist_transtype = 'NN') THEN
+      _runningNn := _runningNn + _r.invhist_invqty * -1;
+    END IF;
+    
+  END LOOP;
+
+  -- Update balances changed by any standard cost since last transaction
+  IF (_prevCostmethod = 'S' AND _runningQty != 0) THEN
+    PERFORM postValueintoInvBalance(pItemsiteid, costhist_date::date, _runningQty, _runningNn, costhist_oldcost, costhist_newcost )
+    FROM costhist
+    WHERE ((costhist_item_id=_r.itemsite_item_id)
+      AND (costhist_date > _prevDate)
+      AND (costhist_type IN ('S','D')));
+  END IF;
+
+  -- Forward update changes through all the balances
+  PERFORM forwardupdateitemsite(pItemsiteId);
+  
+  RETURN 1;
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/buildsearchpath.sql b/foundation-database/public/functions/buildsearchpath.sql
new file mode 100644 (file)
index 0000000..1631996
--- /dev/null
@@ -0,0 +1,49 @@
+CREATE OR REPLACE FUNCTION buildSearchPath() RETURNS TEXT AS $$
+DECLARE
+  _path   TEXT    := '';
+  _schema TEXT;
+  _seq    INTEGER;
+BEGIN
+  -- get the schemas as ordered by the administrator
+  SELECT concatagg(quote_ident(schemaord_name) || ',') INTO _path
+    FROM (SELECT schemaord_name
+            FROM schemaord
+            LEFT OUTER JOIN pkghead ON (schemaord_name=pkghead_name)
+           WHERE (pkghead_id IS NULL
+               OR (pkghead_id IS NOT NULL AND packageisenabled(pkghead_id)))
+           ORDER BY schemaord_order
+         ) AS xtspq;
+
+  -- add others that we think/know we need
+  -- TODO: is there a reason not to include public, api, or packages?
+  FOR _schema, _seq IN
+      SELECT pkghead_name AS schema, 0 AS seq
+        FROM pkghead
+       WHERE packageisenabled(pkghead_id)
+      UNION ALL
+      SELECT 'public', 1
+      UNION ALL
+      SELECT 'api', 2
+      ORDER BY seq, schema
+  LOOP
+    IF (_path !~* (E'(^|\\W)' || _schema || E'(\\W|$)')) THEN
+      _path := _path || ',' || quote_ident(_schema);
+    END IF;
+  END LOOP;
+
+  -- remove extraneous spaces and commas
+  _path = BTRIM(REGEXP_REPLACE(_path, '( ?, ?)+', ',', 'g'),
+                ', ');
+
+  RAISE DEBUG 'buildSearchPath() returning %', _path;
+
+  RETURN _path;
+END;
+$$
+LANGUAGE 'plpgsql';
+
+COMMENT ON FUNCTION buildSearchPath() IS
+'buildSearchPath() examines the schemaord and pkghead tables to build a search
+path string. It ensures that public, api, and all enabled packages are included
+even if they are not listed in the schemaord table.
+It returns the constructed search_path but does not set it.';
diff --git a/foundation-database/public/functions/calcbillingamts.sql b/foundation-database/public/functions/calcbillingamts.sql
new file mode 100644 (file)
index 0000000..76f6a35
--- /dev/null
@@ -0,0 +1,85 @@
+CREATE OR REPLACE FUNCTION calcCobillAmt(INTEGER) RETURNS NUMERIC AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCobillid ALIAS FOR $1;
+  _amount NUMERIC := 0;
+
+BEGIN
+
+  SELECT COALESCE(round((cobill_qty * coitem_qty_invuomratio) *
+                        (coitem_price / coitem_price_invuomratio), 2), 0) INTO _amount
+  FROM cobill JOIN coitem ON (coitem_id=cobill_coitem_id)
+  WHERE (cobill_id=pCobillid);
+
+  RETURN _amount;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION calcCobillTax(INTEGER) RETURNS NUMERIC AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCobillid ALIAS FOR $1;
+  _amount NUMERIC := 0;
+
+BEGIN
+
+  SELECT COALESCE(calculateTax(cobmisc_taxzone_id,
+                               cobill_taxtype_id,
+                               cobmisc_shipdate,
+                               cobmisc_curr_id,
+                               calcCobillAmt(cobill_id)), 0) INTO _amount
+  FROM cobill JOIN coitem ON (coitem_id=cobill_coitem_id)
+              JOIN cobmisc ON (cobmisc_id=cobill_cobmisc_id)
+  WHERE (cobill_id=pCobillid);
+
+  RETURN _amount;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION calcCobmiscAmt(INTEGER) RETURNS NUMERIC AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCobmiscid ALIAS FOR $1;
+  _amount NUMERIC := 0;
+
+BEGIN
+
+  SELECT SUM(COALESCE(calcCobillAmt(cobill_id), 0)) INTO _amount
+  FROM cobmisc JOIN cobill ON (cobmisc_id=cobill_cobmisc_id)
+  WHERE (cobmisc_id=pCobmiscid);
+
+  RETURN _amount;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION calcCobmiscTax(INTEGER) RETURNS NUMERIC AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCobmiscid ALIAS FOR $1;
+  _amount NUMERIC := 0;
+
+BEGIN
+
+  SELECT SUM(
+         COALESCE(calculateTax(cobmisc_taxzone_id,
+                               cobill_taxtype_id,
+                               cobmisc_shipdate,
+                               cobmisc_curr_id,
+                               COALESCE(round((cobill_qty * coitem_qty_invuomratio) * (coitem_price / coitem_price_invuomratio), 2), 0))
+                 , 0)
+            ) INTO _amount
+  FROM cobmisc JOIN cobill ON (cobmisc_id=cobill_cobmisc_id)
+               JOIN coitem ON (coitem_id=cobill_coitem_id)
+  WHERE (cobmisc_id=pCobmiscid);
+
+  RETURN _amount;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/calccashbudget.sql b/foundation-database/public/functions/calccashbudget.sql
new file mode 100644 (file)
index 0000000..1b2bcf1
--- /dev/null
@@ -0,0 +1,82 @@
+CREATE OR REPLACE FUNCTION calccashbudget(integer, integer, character) RETURNS numeric AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pAccntId ALIAS FOR $1;
+  pPeriodId ALIAS FOR $2;
+  pInterval ALIAS FOR $3;
+  _accntType CHAR;
+  _currentBudget NUMERIC;
+  _priorBudget NUMERIC;
+  _result NUMERIC;
+BEGIN
+
+        SELECT accnt_type INTO _accntType
+        FROM accnt
+        WHERE (accnt_id=pAccntId);
+
+        SELECT COALESCE(SUM(budget_amount),0) INTO _currentBudget
+        FROM budget
+        WHERE ((budget_accnt_id=pAccntId)
+        AND (budget_period_id=pPeriodId));
+
+        IF (pInterval='M') THEN
+        SELECT (COALESCE(SUM(budget_amount),0)) INTO _priorBudget
+                FROM budget,
+                (SELECT COALESCE(pp.period_id,-1) AS prior_period_id
+                        FROM period cp, period pp
+                        WHERE ((cp.period_id=pPeriodId)
+                        AND (cp.period_start > pp.period_start))
+                        ORDER BY pp.period_start DESC LIMIT 1) AS data
+                WHERE ((budget_accnt_id=pAccntId)
+                AND (budget_period_id=prior_period_id));
+
+                ELSE IF (pInterval='Q') THEN
+                        SELECT (COALESCE(SUM(budget_amount),0)) INTO _priorBudget
+                        FROM budget,
+                                (SELECT COALESCE(pp.period_id,-1) AS prior_period_id
+                                FROM period cp, period pp
+                                WHERE ((cp.period_id=pPeriodId)
+                                AND (cp.period_start > pp.period_start)
+                                AND (pp.period_quarter=
+                                CASE WHEN cp.period_quarter > 1 THEN
+                                        cp.period_quarter - 1
+                                ELSE 4 END)
+                                AND (pp.period_start >= cp.period_start - interval '1 year'))
+                                ORDER BY pp.period_start DESC LIMIT 1) AS data
+                        WHERE ((budget_accnt_id=pAccntId)
+                        AND (budget_period_id=prior_period_id));
+
+
+                ELSE
+                        SELECT (COALESCE(SUM(budget_amount),0)) INTO _priorBudget
+                        FROM budget,
+                                (SELECT pp.period_id AS prior_period_id
+                        FROM period cp, period pp, yearperiod cy, yearperiod py
+                        WHERE ((cp.period_id=pPeriodId)
+                        AND (cp.period_yearperiod_id=cy.yearperiod_id)
+                        AND (pp.period_yearperiod_id=py.yearperiod_id)
+                        AND (cy.yearperiod_start > py.yearperiod_start))
+                        ORDER BY pp.period_start DESC LIMIT 1) AS data
+                        WHERE ((budget_accnt_id=pAccntId)
+                        AND (budget_period_id=prior_period_id));
+
+                END IF;
+        END IF;
+
+        IF _accntType='A' THEN
+                _result := ((_priorBudget-_currentBudget) * -1 );
+
+        ELSE IF (_accntType IN ('L','Q')) THEN
+                _result := ((_priorBudget-_currentBudget) *-1);
+
+        ELSE RETURN -1;
+        END IF;
+  END IF;
+
+
+  RETURN _result;
+
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/calccreditmemoamts.sql b/foundation-database/public/functions/calccreditmemoamts.sql
new file mode 100644 (file)
index 0000000..c8b643d
--- /dev/null
@@ -0,0 +1,42 @@
+CREATE OR REPLACE FUNCTION calcCmheadAmt(INTEGER) RETURNS NUMERIC AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCmheadid ALIAS FOR $1;
+  _amount NUMERIC := 0;
+
+BEGIN
+
+  SELECT SUM(COALESCE(extprice, 0)) INTO _amount
+  FROM cmhead JOIN creditmemoitem ON (cmhead_id=cmitem_cmhead_id)
+  WHERE (cmhead_id=pCmheadid);
+
+  RETURN _amount;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION calcCmheadTax(INTEGER) RETURNS NUMERIC AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCmheadid ALIAS FOR $1;
+  _headamount NUMERIC := 0;
+  _itemamount NUMERIC := 0;
+  _amount NUMERIC := 0;
+
+BEGIN
+
+  SELECT COALESCE(SUM(taxhist_tax), 0) INTO _headamount
+  FROM cmhead JOIN cmheadtax ON (taxhist_parent_id=cmhead_id)
+  WHERE (cmhead_id=pCmheadid);
+
+  SELECT SUM(COALESCE(tax, 0)) INTO _itemamount
+  FROM cmhead JOIN creditmemoitem ON (cmhead_id=cmitem_cmhead_id)
+  WHERE (cmhead_id=pCmheadid);
+
+  _amount := _headamount + _itemamount;
+  RETURN (_amount * -1.0);
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/calcpendingarapplications.sql b/foundation-database/public/functions/calcpendingarapplications.sql
new file mode 100644 (file)
index 0000000..79c88e4
--- /dev/null
@@ -0,0 +1,35 @@
+CREATE OR REPLACE FUNCTION calcpendingarapplications(INTEGER) RETURNS NUMERIC AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  paropenid     ALIAS FOR $1;
+  _arcreditsum  NUMERIC;
+  _aropencurrid INTEGER;
+  _cashrcptsum  NUMERIC;
+  _sense INTEGER;
+
+BEGIN
+  SELECT aropen_curr_id,
+    (CASE WHEN aropen_doctype IN ('I','D') THEN 1 ELSE -1 END) 
+    INTO _aropencurrid, _sense
+  FROM aropen
+  WHERE (aropen_id=paropenid);
+
+  SELECT SUM(currToCurr(cashrcpt_curr_id, _aropencurrid,
+                        cashrcptitem_amount + cashrcptitem_discount, coalesce(cashrcpt_applydate, cashrcpt_distdate))) * _sense INTO _cashrcptsum
+  FROM cashrcptitem, cashrcpt
+  WHERE ((cashrcptitem_cashrcpt_id=cashrcpt_id)
+    AND  (NOT cashrcpt_posted)
+    AND  (NOT cashrcpt_void)
+    AND  (cashrcptitem_aropen_id=paropenid)
+    );
+
+  SELECT SUM(currToCurr(arcreditapply_curr_id, _aropencurrid,
+                        arcreditapply_amount, CURRENT_DATE)) INTO _arcreditsum
+  FROM arcreditapply
+  WHERE ((arcreditapply_target_aropen_id=paropenid)
+    );
+
+  RETURN round(COALESCE(_cashrcptsum, 0) + COALESCE(_arcreditsum, 0),2);
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/calcquoteamt.sql b/foundation-database/public/functions/calcquoteamt.sql
new file mode 100644 (file)
index 0000000..bf14c70
--- /dev/null
@@ -0,0 +1,34 @@
+DROP FUNCTION IF EXISTS calcquoteamt(integer);
+CREATE OR REPLACE FUNCTION calcQuoteAmt(INTEGER) RETURNS NUMERIC AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pQuheadid ALIAS FOR $1;
+  _subtotal NUMERIC := 0;
+  _tax NUMERIC := 0;
+  _freight NUMERIC := 0;
+  _misc NUMERIC := 0;
+  _amount NUMERIC := 0;
+
+BEGIN
+
+  SELECT COALESCE(SUM(ROUND((quitem_qtyord * quitem_qty_invuomratio) *
+                            (quitem_price / quitem_price_invuomratio), 2)), 0) INTO _subtotal
+  FROM quitem
+  WHERE (quitem_quhead_id=pQuheadid);
+
+  SELECT COALESCE(SUM(tax), 0) INTO _tax
+  FROM ( SELECT ROUND(SUM(taxdetail_tax), 2) AS tax
+         FROM tax JOIN calculateTaxDetailSummary('Q', pQuheadid, 'T') ON (taxdetail_tax_id=tax_id)
+         GROUP BY tax_id ) AS data;
+
+  SELECT COALESCE(quhead_freight, 0), COALESCE(quhead_misc, 0) INTO _freight, _misc
+  FROM quhead
+  WHERE (quhead_id=pQuheadid);
+
+  _amount := _subtotal + _tax + _freight + _misc;
+
+  RETURN _amount;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/calcsalesorderamt.sql b/foundation-database/public/functions/calcsalesorderamt.sql
new file mode 100644 (file)
index 0000000..fb3029e
--- /dev/null
@@ -0,0 +1,35 @@
+DROP FUNCTION IF EXISTS calcsalesorderamt(integer);
+CREATE OR REPLACE FUNCTION calcSalesOrderAmt(INTEGER) RETURNS NUMERIC AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCoheadid ALIAS FOR $1;
+  _subtotal NUMERIC := 0;
+  _tax NUMERIC := 0;
+  _freight NUMERIC := 0;
+  _misc NUMERIC := 0;
+  _amount NUMERIC := 0;
+
+BEGIN
+
+  SELECT COALESCE(SUM(ROUND((coitem_qtyord * coitem_qty_invuomratio) *
+                            (coitem_price / coitem_price_invuomratio), 2)), 0) INTO _subtotal
+  FROM coitem
+  WHERE (coitem_cohead_id=pCoheadid)
+    AND (coitem_status != 'X');
+
+  SELECT COALESCE(SUM(tax), 0) INTO _tax
+  FROM ( SELECT ROUND(SUM(taxdetail_tax), 2) AS tax
+         FROM tax JOIN calculateTaxDetailSummary('S', pCoheadid, 'T') ON (taxdetail_tax_id=tax_id)
+         GROUP BY tax_id ) AS data;
+
+  SELECT COALESCE(cohead_freight, 0), COALESCE(cohead_misc, 0) INTO _freight, _misc
+  FROM cohead
+  WHERE (cohead_id=pCoheadid);
+
+  _amount := _subtotal + _tax + _freight + _misc;
+
+  RETURN _amount;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/calcshipfreight.sql b/foundation-database/public/functions/calcshipfreight.sql
new file mode 100644 (file)
index 0000000..42e1f68
--- /dev/null
@@ -0,0 +1,318 @@
+
+CREATE OR REPLACE FUNCTION calcShipFreight(integer) RETURNS NUMERIC AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pShipheadId ALIAS FOR $1;
+  _result NUMERIC := 0;
+  _order RECORD;
+  _shipment RECORD;
+  _weights RECORD;
+  _price RECORD;
+  _sales RECORD;
+  _freightid INTEGER;
+  _totalprice NUMERIC;
+  _includepkgweight BOOLEAN := FALSE;
+  _freight RECORD;
+  _debug BOOLEAN := false;
+BEGIN
+  --Get shipment
+  SELECT shiphead_order_id, shiphead_order_type, shiphead_freight 
+  INTO _shipment
+  FROM shiphead
+  WHERE (shiphead_id=pShipheadId);
+
+  IF (NOT FOUND) THEN
+    RAISE EXCEPTION 'Shipment not found';
+  END IF;
+
+  IF (_shipment.shiphead_order_type = 'SO') THEN
+  --Sales Orders
+  
+  --Get the order header information
+    SELECT cust_id AS cust_id,
+           custtype_id,
+           custtype_code,
+           shipto_id,
+           shipto_num,
+           cohead_orderdate AS orderdate,
+           cohead_shipvia AS shipvia,
+           shipto_shipzone_id AS shipzone_id,
+           cohead_curr_id AS curr_id,
+           currConcat(cohead_curr_id) AS currAbbr,
+           cohead_calcfreight,
+           cohead_freight
+    INTO _order
+    FROM cohead 
+      JOIN custinfo ON (cust_id=cohead_cust_id)
+      JOIN custtype ON (custtype_id=cust_custtype_id)
+      LEFT OUTER JOIN shiptoinfo ON (shipto_id=cohead_shipto_id)
+    WHERE (cohead_id=_shipment.shiphead_order_id);
+
+    IF (NOT FOUND) THEN
+      RAISE EXCEPTION 'Order not found';
+    END IF;
+
+    IF (_debug) THEN
+      RAISE NOTICE 'cust_id = %', _order.cust_id;
+      RAISE NOTICE 'custtype_id = %', _order.custtype_id;
+      RAISE NOTICE 'shipto_id = %', _order.shipto_id;
+      RAISE NOTICE 'shipto_num = %', _order.shipto_num;
+      RAISE NOTICE 'orderdate = %', _order.orderdate;
+      RAISE NOTICE 'shipvia = %', _order.shipvia;
+      RAISE NOTICE 'shipzone_id = %', _order.shipzone_id;
+      RAISE NOTICE 'curr_id = %', _order.curr_id;
+      RAISE NOTICE 'currAbbr = %', _order.currAbbr;
+      RAISE NOTICE 'calcfreight = %', _order.cohead_calcfreight;
+      RAISE NOTICE 'freight = %', _order.cohead_freight;
+    END IF;
+
+    IF (NOT _order.cohead_calcfreight) THEN
+      SELECT noNeg( _order.cohead_freight -
+                    COALESCE((SELECT SUM(shiphead_freight)
+                              FROM shiphead
+                              WHERE (shiphead_order_id = _shipment.shiphead_order_id)
+                                AND (shiphead_shipped='true')), 0) ) INTO _result;
+      RETURN _result;
+    END IF;
+
+    SELECT fetchMetricBool('IncludePackageWeight') INTO _includepkgweight;
+
+    --Calculate Sales Order freight
+    --Get a list of aggregated weights from sites and
+    --freight classes used on order lines
+    FOR _weights IN
+      SELECT CASE WHEN (_includePkgWeight) THEN
+                        SUM(shipitem_qty * (item_prodweight + item_packweight))
+                  ELSE  SUM(shipitem_qty * item_prodweight)
+             END AS weight,
+             itemsite_warehous_id, item_freightclass_id
+      FROM shiphead
+        JOIN shipitem ON (shipitem_shiphead_id=shiphead_id)
+        JOIN coitem ON (shipitem_orderitem_id=coitem_id)
+        JOIN itemsite ON (itemsite_id=coitem_itemsite_id)
+        JOIN item ON (item_id=itemsite_item_id)
+      WHERE ( (shiphead_id=pShipheadId)
+        AND   (item_freightclass_id IS NOT NULL) )
+      GROUP BY itemsite_warehous_id, item_freightclass_id LOOP
+
+    IF (_debug) THEN
+      RAISE NOTICE '_weights.weight - %', _weights.weight;
+      RAISE NOTICE '_weights.itemsite_warehous_id = %', _weights.itemsite_warehous_id;
+      RAISE NOTICE '_weights.item_freightclass_id = %', _weights.item_freightclass_id;
+    END IF;
+
+    -- First get a sales price if any so we when we find other prices
+    -- we can determine if we want that price or this price.
+    --  Check for a Sale Price
+    SELECT ipsfreight_id,
+           CASE WHEN (ipsfreight_type='F') THEN currToCurr(ipshead_curr_id, _order.curr_id,
+                                                           ipsfreight_price, _order.orderdate)
+                ELSE currToCurr(ipshead_curr_id, _order.curr_id,
+                                (_weights.weight * ipsfreight_price), _order.orderdate)
+           END AS price
+           INTO _sales
+    FROM ipsfreight JOIN ipshead ON (ipshead_id=ipsfreight_ipshead_id)
+                    JOIN sale ON (sale_ipshead_id=ipshead_id)
+    WHERE ( (ipsfreight_qtybreak <= _weights.weight)
+      AND   ((ipsfreight_warehous_id IS NULL) OR (ipsfreight_warehous_id=_weights.itemsite_warehous_id))
+      AND   ((ipsfreight_freightclass_id IS NULL) OR (ipsfreight_freightclass_id=_weights.item_freightclass_id))
+      AND   ((ipsfreight_shipzone_id IS NULL) OR (ipsfreight_shipzone_id=_order.shipzone_id))
+      AND   ((ipsfreight_shipvia IS NULL) OR (ipsfreight_shipvia=_order.shipvia))
+      AND   (CURRENT_DATE BETWEEN sale_startdate AND sale_enddate) )
+    ORDER BY ipsfreight_qtybreak DESC, price ASC
+    LIMIT 1;
+
+    IF (_debug) THEN
+      IF (_sales.price IS NOT NULL) THEN
+        RAISE NOTICE 'Sales Price found, %', _sales.price;
+      END IF;
+    END IF;
+
+    --  Check for a Customer Shipto Price
+    SELECT ipsfreight_id,
+           CASE WHEN (ipsfreight_type='F') THEN currToCurr(ipshead_curr_id, _order.curr_id,
+                                                           ipsfreight_price, _order.orderdate)
+                ELSE currToCurr(ipshead_curr_id, _order.curr_id,
+                                (_weights.weight * ipsfreight_price), _order.orderdate)
+           END AS price
+           INTO _price
+    FROM ipsfreight JOIN ipshead ON (ipshead_id=ipsfreight_ipshead_id)
+                    JOIN ipsass ON (ipsass_ipshead_id=ipshead_id)
+    WHERE ( (ipsfreight_qtybreak <= _weights.weight)
+      AND   ((ipsfreight_warehous_id IS NULL) OR (ipsfreight_warehous_id=_weights.itemsite_warehous_id))
+      AND   ((ipsfreight_freightclass_id IS NULL) OR (ipsfreight_freightclass_id=_weights.item_freightclass_id))
+      AND   ((ipsfreight_shipzone_id IS NULL) OR (ipsfreight_shipzone_id=_order.shipzone_id))
+      AND   ((ipsfreight_shipvia IS NULL) OR (ipsfreight_shipvia=_order.shipvia))
+      AND   (CURRENT_DATE BETWEEN ipshead_effective AND (ipshead_expires - 1))
+      AND   (ipsass_cust_id=_order.cust_id)
+      AND   (ipsass_shipto_id != -1)
+      AND   (ipsass_shipto_id=_order.shipto_id) )
+    ORDER BY ipsfreight_qtybreak DESC, price ASC
+    LIMIT 1;
+
+    IF (_debug) THEN
+      IF (_price.price IS NOT NULL) THEN
+        RAISE NOTICE 'Customer Shipto Price found, %', _price.price;
+      END IF;
+    END IF;
+
+    IF (_price.price IS NULL) THEN
+    --  Check for a Customer Shipto Pattern Price
+      SELECT ipsfreight_id,
+             CASE WHEN (ipsfreight_type='F') THEN currToCurr(ipshead_curr_id, _order.curr_id,
+                                                             ipsfreight_price, _order.orderdate)
+                  ELSE currToCurr(ipshead_curr_id, _order.curr_id,
+                                  (_weights.weight * ipsfreight_price), _order.orderdate)
+             END AS price
+             INTO _price
+      FROM ipsfreight JOIN ipshead ON (ipshead_id=ipsfreight_ipshead_id)
+                      JOIN ipsass ON (ipsass_ipshead_id=ipshead_id)
+      WHERE ( (ipsfreight_qtybreak <= _weights.weight)
+        AND   (CURRENT_DATE BETWEEN ipshead_effective AND (ipshead_expires - 1))
+        AND   (ipsass_cust_id=_order.cust_id)
+        AND   (COALESCE(LENGTH(ipsass_shipto_pattern), 0) > 0)
+        AND   (_order.shipto_num ~ ipsass_shipto_pattern)
+        AND   ((ipsfreight_warehous_id IS NULL) OR (ipsfreight_warehous_id=_weights.itemsite_warehous_id))
+        AND   ((ipsfreight_freightclass_id IS NULL) OR (ipsfreight_freightclass_id=_weights.item_freightclass_id))
+        AND   ((ipsfreight_shipzone_id IS NULL) OR (ipsfreight_shipzone_id=_order.shipzone_id))
+        AND   ((ipsfreight_shipvia IS NULL) OR (ipsfreight_shipvia=_order.shipvia)) )
+      ORDER BY ipsfreight_qtybreak DESC, price ASC
+      LIMIT 1;
+
+      IF (_debug) THEN
+        IF (_price.price IS NOT NULL) THEN
+          RAISE NOTICE 'Customer Shipto Pattern Price found, %', _price.price;
+        END IF;
+      END IF;
+
+    END IF;
+
+    IF (_price.price IS NULL) THEN
+    --  Check for a Customer Price
+      SELECT ipsfreight_id,
+             CASE WHEN (ipsfreight_type='F') THEN currToCurr(ipshead_curr_id, _order.curr_id,
+                                                             ipsfreight_price, _order.orderdate)
+                  ELSE currToCurr(ipshead_curr_id, _order.curr_id,
+                                  (_weights.weight * ipsfreight_price), _order.orderdate)
+             END AS price
+             INTO _price
+      FROM ipsfreight JOIN ipshead ON (ipshead_id=ipsfreight_ipshead_id)
+                      JOIN ipsass ON (ipsass_ipshead_id=ipshead_id)
+      WHERE ( (ipsfreight_qtybreak <= _weights.weight)
+        AND   ((ipsfreight_warehous_id IS NULL) OR (ipsfreight_warehous_id=_weights.itemsite_warehous_id))
+        AND   ((ipsfreight_freightclass_id IS NULL) OR (ipsfreight_freightclass_id=_weights.item_freightclass_id))
+        AND   ((ipsfreight_shipzone_id IS NULL) OR (ipsfreight_shipzone_id=_order.shipzone_id))
+        AND   ((ipsfreight_shipvia IS NULL) OR (ipsfreight_shipvia=_order.shipvia))
+        AND   (CURRENT_DATE BETWEEN ipshead_effective AND (ipshead_expires - 1))
+        AND   (ipsass_cust_id=_order.cust_id)
+        AND   (COALESCE(LENGTH(ipsass_shipto_pattern), 0) = 0) )
+      ORDER BY ipsfreight_qtybreak DESC, price ASC
+      LIMIT 1;
+
+      IF (_debug) THEN
+        IF (_price.price IS NOT NULL) THEN
+          RAISE NOTICE 'Customer Price found, %', _price.price;
+        END IF;
+      END IF;
+
+    END IF;
+
+    IF (_price.price IS NULL) THEN
+    --  Check for a Customer Type Price
+      SELECT ipsfreight_id,
+             CASE WHEN (ipsfreight_type='F') THEN currToCurr(ipshead_curr_id, _order.curr_id,
+                                                             ipsfreight_price, _order.orderdate)
+                  ELSE currToCurr(ipshead_curr_id, _order.curr_id,
+                                  (_weights.weight * ipsfreight_price), _order.orderdate)
+             END AS price
+             INTO _price
+      FROM ipsfreight JOIN ipshead ON (ipshead_id=ipsfreight_ipshead_id)
+                      JOIN ipsass ON (ipsass_ipshead_id=ipshead_id)
+      WHERE ( (ipsfreight_qtybreak <= _weights.weight)
+        AND   ((ipsfreight_warehous_id IS NULL) OR (ipsfreight_warehous_id=_weights.itemsite_warehous_id))
+        AND   ((ipsfreight_freightclass_id IS NULL) OR (ipsfreight_freightclass_id=_weights.item_freightclass_id))
+        AND   ((ipsfreight_shipzone_id IS NULL) OR (ipsfreight_shipzone_id=_order.shipzone_id))
+        AND   ((ipsfreight_shipvia IS NULL) OR (ipsfreight_shipvia=_order.shipvia))
+        AND   (CURRENT_DATE BETWEEN ipshead_effective AND (ipshead_expires - 1))
+        AND   (ipsass_custtype_id=_order.custtype_id) )
+      ORDER BY ipsfreight_qtybreak DESC, price ASC
+      LIMIT 1;
+
+      IF (_debug) THEN
+        IF (_price.price IS NOT NULL) THEN
+          RAISE NOTICE 'Customer Type Price found, %', _price.price;
+        END IF;
+      END IF;
+
+    END IF;
+
+    IF (_price.price IS NULL) THEN
+    --  Check for a Customer Type Pattern Price
+      SELECT ipsfreight_id,
+             CASE WHEN (ipsfreight_type='F') THEN currToCurr(ipshead_curr_id, _order.curr_id,
+                                                             ipsfreight_price, _order.orderdate)
+                  ELSE currToCurr(ipshead_curr_id, _order.curr_id,
+                                  (_weights.weight * ipsfreight_price), _order.orderdate)
+             END AS price
+             INTO _price
+      FROM ipsfreight JOIN ipshead ON (ipshead_id=ipsfreight_ipshead_id)
+                      JOIN ipsass ON (ipsass_ipshead_id=ipshead_id)
+      WHERE ( (ipsfreight_qtybreak <= _weights.weight)
+        AND   ((ipsfreight_warehous_id IS NULL) OR (ipsfreight_warehous_id=_weights.itemsite_warehous_id))
+        AND   ((ipsfreight_freightclass_id IS NULL) OR (ipsfreight_freightclass_id=_weights.item_freightclass_id))
+        AND   ((ipsfreight_shipzone_id IS NULL) OR (ipsfreight_shipzone_id=_order.shipzone_id))
+        AND   ((ipsfreight_shipvia IS NULL) OR (ipsfreight_shipvia=_order.shipvia))
+        AND   (CURRENT_DATE BETWEEN ipshead_effective AND (ipshead_expires - 1))
+        AND   (COALESCE(LENGTH(ipsass_custtype_pattern), 0) > 0)
+        AND   (_order.custtype_code ~ ipsass_custtype_pattern) )
+      ORDER BY ipsfreight_qtybreak DESC, price ASC
+      LIMIT 1;
+
+      IF (_debug) THEN
+        IF (_price.price IS NOT NULL) THEN
+          RAISE NOTICE 'Customer Type Pattern Price found, %', _price.price;
+        END IF;
+      END IF;
+
+    END IF;
+
+    -- Select the lowest price  
+    IF ( (_price.price IS NOT NULL) AND ((_sales.price IS NULL) OR (_price.price < _sales.price)) ) THEN
+      _freightid := _price.ipsfreight_id;
+      _totalprice := _price.price;
+    ELSE
+      IF ( (_sales.price IS NOT NULL) AND ((_price.price IS NULL) OR (_sales.price <= _price.price)) ) THEN
+        _freightid := _sales.ipsfreight_id;
+        _totalprice := _sales.price;
+      END IF;
+    END IF;
+
+    -- Total
+    IF (_freightid IS NOT NULL) THEN
+      _result := _result + _totalprice;
+    END IF;
+
+    END LOOP;
+    RETURN ROUND(_result,2);
+  END IF;
+
+  IF (_shipment.shiphead_order_type = 'TO') THEN
+  --Transfer Orders
+  
+    SELECT noNeg( (SELECT SUM(toitem_freight) + tohead_freight
+                   FROM tohead, toitem
+                   WHERE (toitem_tohead_id=tohead_id)
+                     AND (tohead_id = _shipment.shiphead_order_id)
+                   GROUP BY tohead_freight) -
+                  COALESCE((SELECT SUM(shiphead_freight)
+                            FROM shiphead
+                            WHERE (shiphead_order_id = _shipment.shiphead_order_id)
+                              AND (shiphead_shipped='true')), 0) ) INTO _result;
+    RETURN _result;
+  END IF;
+
+  RETURN _result;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/calctotalslipqty.sql b/foundation-database/public/functions/calctotalslipqty.sql
new file mode 100644 (file)
index 0000000..c248643
--- /dev/null
@@ -0,0 +1,17 @@
+CREATE OR REPLACE FUNCTION calcTotalSlipQty(INTEGER) RETURNS NUMERIC AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pTagid ALIAS FOR $1;
+  _qty NUMERIC := 0;
+
+BEGIN
+
+  SELECT SUM(COALESCE(cntslip_qty, 0.0)) INTO _qty
+  FROM cntslip
+  WHERE (cntslip_cnttag_id=pTagid);
+
+  RETURN _qty;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/calculatefreightdetail.sql b/foundation-database/public/functions/calculatefreightdetail.sql
new file mode 100644 (file)
index 0000000..e58697b
--- /dev/null
@@ -0,0 +1,286 @@
+
+CREATE OR REPLACE FUNCTION calculateFreightDetail(integer,integer,text,integer,integer,text,date,text,integer,character varying,integer,integer,numeric)
+  RETURNS SETOF freightData AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple.
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCustId ALIAS FOR $1;
+  pCustTypeId ALIAS FOR $2;
+  pCustTypeCode ALIAS FOR $3;
+  pShiptoId ALIAS FOR $4;
+  pShipZoneId ALIAS FOR $5;
+  pShiptoNum ALIAS FOR $6;
+  pOrderDate ALIAS FOR $7;
+  pShipVia ALIAS FOR $8;
+  pCurrId ALIAS FOR $9;
+  pCurrAbbr ALIAS FOR $10;
+  pItemSiteWhsId ALIAS FOR $11;
+  pItemFreightclassId ALIAS FOR $12;
+  pWeight ALIAS FOR $13;
+
+  _row freightData%ROWTYPE;
+  _price RECORD;
+  _sales RECORD;
+  _freightid INTEGER;
+  _totalprice NUMERIC;
+  _asof DATE;
+  _debug BOOLEAN := FALSE;
+
+BEGIN
+
+  --Get pricing effectivity metric
+  IF (SELECT fetchMetricText('soPriceEffective') = 'OrderDate') THEN
+    _asof := pOrderDate;
+  ELSE
+    _asof := CURRENT_DATE;
+  END IF;
+
+  _freightid := NULL;
+  _totalprice := 0.0;
+
+  IF (_debug) THEN
+    RAISE NOTICE 'pWeight - %', pWeight;
+    RAISE NOTICE 'pItemSiteWhsId = %', pItemSiteWhsId;
+    RAISE NOTICE 'pItemFreightclassId = %', pItemFreightclassId;
+  END IF;
+
+-- First get a sales price if any so when we find other prices
+-- we can determine if we want that price or this sales price.
+--  Check for a Sale Price
+  SELECT ipsfreight_id,
+    CASE WHEN (ipsfreight_type='F') THEN currToCurr(ipshead_curr_id, pCurrId,
+        ipsfreight_price, pOrderDate)
+      ELSE currToCurr(ipshead_curr_id, pCurrId,
+        (pWeight * ipsfreight_price), pOrderDate)
+    END AS price
+  INTO _sales
+  FROM ipsfreight
+  JOIN ipshead ON (ipshead_id=ipsfreight_ipshead_id)
+  JOIN sale ON (sale_ipshead_id=ipshead_id)
+  WHERE ( (ipsfreight_qtybreak <= pWeight)
+    AND ((ipsfreight_warehous_id IS NULL) OR (ipsfreight_warehous_id=pItemSiteWhsId))
+    AND ((ipsfreight_freightclass_id IS NULL) OR (ipsfreight_freightclass_id=pItemFreightclassId))
+    AND ((ipsfreight_shipzone_id IS NULL) OR (ipsfreight_shipzone_id=pShipZoneId))
+    AND ((ipsfreight_shipvia IS NULL) OR (ipsfreight_shipvia=pShipVia))
+    AND (_asof BETWEEN sale_startdate AND sale_enddate)
+    AND (pCustId IS NOT NULL) )
+  ORDER BY ipsfreight_qtybreak DESC, price ASC
+  LIMIT 1;
+
+  IF (_debug) THEN
+    IF (_sales.price IS NOT NULL) THEN
+      RAISE NOTICE 'Sales Price found, %', _sales.price;
+    END IF;
+  END IF;
+
+--  Check for a Customer Shipto Price
+  SELECT ipsfreight_id,
+    CASE WHEN (ipsfreight_type='F') THEN currToCurr(ipshead_curr_id, pCurrId,
+        ipsfreight_price, pOrderDate)
+      ELSE currToCurr(ipshead_curr_id, pCurrId,
+        (pWeight * ipsfreight_price), pOrderDate)
+    END AS price
+  INTO _price
+  FROM ipsfreight
+  JOIN ipshead ON (ipshead_id=ipsfreight_ipshead_id)
+  JOIN ipsass ON (ipsass_ipshead_id=ipshead_id)
+  WHERE ( (ipsfreight_qtybreak <= pWeight)
+    AND ((ipsfreight_warehous_id IS NULL) OR (ipsfreight_warehous_id=pItemSiteWhsId))
+    AND ((ipsfreight_freightclass_id IS NULL) OR (ipsfreight_freightclass_id=pItemFreightclassId))
+    AND ((ipsfreight_shipzone_id IS NULL) OR (ipsfreight_shipzone_id=pShipZoneId))
+    AND ((ipsfreight_shipvia IS NULL) OR (ipsfreight_shipvia=pShipVia))
+    AND (_asof BETWEEN ipshead_effective AND (ipshead_expires - 1))
+    AND   (ipsass_shipto_id != -1)
+    AND   (ipsass_shipto_id=pShiptoId) )
+  ORDER BY ipsfreight_qtybreak DESC, price ASC
+  LIMIT 1;
+
+  IF (_debug) THEN
+    IF (_price.price IS NOT NULL) THEN
+      RAISE NOTICE 'Customer Shipto Price found, %', _price.price;
+    END IF;
+  END IF;
+
+  IF (_price.price IS NULL) THEN
+--  Check for a Customer Shipto Pattern Price
+  SELECT ipsfreight_id,
+    CASE WHEN (ipsfreight_type='F') THEN currToCurr(ipshead_curr_id, pCurrId,
+        ipsfreight_price, pOrderDate)
+      ELSE currToCurr(ipshead_curr_id, pCurrId,
+        (pWeight * ipsfreight_price), pOrderDate)
+    END AS price
+  INTO _price
+  FROM ipsfreight
+  JOIN ipshead ON (ipshead_id=ipsfreight_ipshead_id)
+  JOIN ipsass ON (ipsass_ipshead_id=ipshead_id)
+  WHERE ( (ipsfreight_qtybreak <= pWeight)
+    AND (_asof BETWEEN ipshead_effective AND (ipshead_expires - 1))
+    AND (ipsass_cust_id=pCustId)
+    AND (COALESCE(LENGTH(ipsass_shipto_pattern), 0) > 0)
+    AND (pShiptoNum ~ ipsass_shipto_pattern)
+    AND ((ipsfreight_warehous_id IS NULL) OR (ipsfreight_warehous_id=pItemSiteWhsId))
+    AND ((ipsfreight_freightclass_id IS NULL) OR (ipsfreight_freightclass_id=pItemFreightclassId))
+    AND ((ipsfreight_shipzone_id IS NULL) OR (ipsfreight_shipzone_id=pShipZoneId))
+    AND ((ipsfreight_shipvia IS NULL) OR (ipsfreight_shipvia=pShipVia)) )
+  ORDER BY ipsfreight_qtybreak DESC, price ASC
+  LIMIT 1;
+
+  IF (_debug) THEN
+    IF (_price.price IS NOT NULL) THEN
+      RAISE NOTICE 'Customer Shipto Pattern Price found, %', _price.price;
+    END IF;
+  END IF;
+
+  END IF;
+
+  IF (_price.price IS NULL) THEN
+--  Check for a Customer Price
+  SELECT ipsfreight_id,
+    CASE WHEN (ipsfreight_type='F') THEN currToCurr(ipshead_curr_id, pCurrId,
+        ipsfreight_price, pOrderDate)
+      ELSE currToCurr(ipshead_curr_id, pCurrId,
+        (pWeight * ipsfreight_price), pOrderDate)
+    END AS price
+  INTO _price
+  FROM ipsfreight
+  JOIN ipshead ON (ipshead_id=ipsfreight_ipshead_id)
+  JOIN ipsass ON (ipsass_ipshead_id=ipshead_id)
+  WHERE ( (ipsfreight_qtybreak <= pWeight)
+    AND((ipsfreight_warehous_id IS NULL) OR (ipsfreight_warehous_id=pItemSiteWhsId))
+    AND ((ipsfreight_freightclass_id IS NULL) OR (ipsfreight_freightclass_id=pItemFreightclassId))
+    AND ((ipsfreight_shipzone_id IS NULL) OR (ipsfreight_shipzone_id=pShipZoneId))
+    AND ((ipsfreight_shipvia IS NULL) OR (ipsfreight_shipvia=pShipVia))
+    AND (_asof BETWEEN ipshead_effective AND (ipshead_expires - 1))
+    AND (ipsass_cust_id=pCustId)
+    AND (COALESCE(LENGTH(ipsass_shipto_pattern), 0) = 0) )
+  ORDER BY ipsfreight_qtybreak DESC, price ASC
+  LIMIT 1;
+
+  IF (_debug) THEN
+    IF (_price.price IS NOT NULL) THEN
+      RAISE NOTICE 'Customer Price found, %', _price.price;
+    END IF;
+  END IF;
+
+  END IF;
+
+  IF (_price.price IS NULL) THEN
+--  Check for a Customer Type Price
+  SELECT ipsfreight_id,
+    CASE WHEN (ipsfreight_type='F') THEN currToCurr(ipshead_curr_id, pCurrId,
+        ipsfreight_price, pOrderDate)
+      ELSE currToCurr(ipshead_curr_id, pCurrId,
+        (pWeight * ipsfreight_price), pOrderDate)
+    END AS price
+  INTO _price
+  FROM ipsfreight JOIN ipshead ON (ipshead_id=ipsfreight_ipshead_id)
+                  JOIN ipsass ON (ipsass_ipshead_id=ipshead_id)
+  WHERE ( (ipsfreight_qtybreak <= pWeight)
+    AND ((ipsfreight_warehous_id IS NULL) OR (ipsfreight_warehous_id=pItemSiteWhsId))
+    AND ((ipsfreight_freightclass_id IS NULL) OR (ipsfreight_freightclass_id=pItemFreightclassId))
+    AND ((ipsfreight_shipzone_id IS NULL) OR (ipsfreight_shipzone_id=pShipZoneId))
+    AND ((ipsfreight_shipvia IS NULL) OR (ipsfreight_shipvia=pShipVia))
+    AND (_asof BETWEEN ipshead_effective AND (ipshead_expires - 1))
+    AND (ipsass_custtype_id=pCustTypeId) )
+  ORDER BY ipsfreight_qtybreak DESC, price ASC
+  LIMIT 1;
+
+  IF (_debug) THEN
+    IF (_price.price IS NOT NULL) THEN
+      RAISE NOTICE 'Customer Type Price found, %', _price.price;
+    END IF;
+  END IF;
+
+  END IF;
+
+  IF (_price.price IS NULL) THEN
+--  Check for a Customer Type Pattern Price
+  SELECT ipsfreight_id,
+    CASE WHEN (ipsfreight_type='F') THEN currToCurr(ipshead_curr_id, pCurrId,
+        ipsfreight_price, pOrderDate)
+      ELSE currToCurr(ipshead_curr_id, pCurrId,
+        (pWeight * ipsfreight_price), pOrderDate)
+    END AS price
+  INTO _price
+  FROM ipsfreight JOIN ipshead ON (ipshead_id=ipsfreight_ipshead_id)
+                  JOIN ipsass ON (ipsass_ipshead_id=ipshead_id)
+  WHERE ( (ipsfreight_qtybreak <= pWeight)
+    AND ((ipsfreight_warehous_id IS NULL) OR (ipsfreight_warehous_id=pItemSiteWhsId))
+    AND ((ipsfreight_freightclass_id IS NULL) OR (ipsfreight_freightclass_id=pItemFreightclassId))
+    AND ((ipsfreight_shipzone_id IS NULL) OR (ipsfreight_shipzone_id=pShipZoneId))
+    AND ((ipsfreight_shipvia IS NULL) OR (ipsfreight_shipvia=pShipVia))
+    AND (_asof BETWEEN ipshead_effective AND (ipshead_expires - 1))
+    AND (COALESCE(LENGTH(ipsass_custtype_pattern), 0) > 0)
+    AND (pCustTypeCode ~ ipsass_custtype_pattern) )
+  ORDER BY ipsfreight_qtybreak DESC, price ASC
+  LIMIT 1;
+
+  IF (_debug) THEN
+    IF (_price.price IS NOT NULL) THEN
+      RAISE NOTICE 'Customer Type Pattern Price found, %', _price.price;
+    END IF;
+  END IF;
+
+  END IF;
+
+  -- Select the lowest price
+  IF ( (_price.price IS NOT NULL) AND ((_sales.price IS NULL) OR (_price.price < _sales.price)) ) THEN
+    _freightid := _price.ipsfreight_id;
+    _totalprice := _price.price;
+  ELSE
+    IF ( (_sales.price IS NOT NULL) AND ((_price.price IS NULL) OR (_sales.price <= _price.price)) ) THEN
+      _freightid := _sales.ipsfreight_id;
+      _totalprice := _sales.price;
+    END IF;
+  END IF;
+
+  IF (_debug) THEN
+    RAISE NOTICE '_freightid = %', _freightid;
+    RAISE NOTICE '_totalprice = %', _totalprice;
+  END IF;
+
+  -- Get information for the selected ipsfreight
+  -- and return
+  IF (_freightid IS NULL) THEN
+    _row.freightdata_schedule := 'N/A';
+    _row.freightdata_from := '';
+    _row.freightdata_to := '';
+    _row.freightdata_shipvia := '';
+    _row.freightdata_freightclass := '';
+    _row.freightdata_weight := 0;
+    _row.freightdata_uom := '';
+    _row.freightdata_price := 0;
+    _row.freightdata_type := '';
+    _row.freightdata_total := 0;
+    _row.freightdata_currency := '';
+    RETURN NEXT _row;
+  ELSE
+    SELECT ipshead_name AS freightdata_schedule,
+      COALESCE(warehous_code, 'Any') AS freightdata_from,
+      COALESCE(shipzone_name, 'Any') AS freightdata_to,
+      COALESCE(ipsfreight_shipvia, 'Any') AS freightdata_shipvia,
+      COALESCE(freightclass_code, 'Any') AS freightdata_freightclass,
+      pWeight AS freightdata_weight,
+      uom_name AS freightdata_uom,
+      currToCurr(ipshead_curr_id, pCurrId, ipsfreight_price, pOrderDate) AS freightdata_price,
+      CASE WHEN (ipsfreight_type='F') THEN 'Flat Rate'
+        ELSE 'Per UOM'
+      END AS freightdata_type,
+      _totalprice AS freightdata_total,
+      pCurrAbbr AS freightdata_currency
+    INTO _row
+    FROM ipsfreight
+      JOIN ipshead ON (ipshead_id=ipsfreight_ipshead_id)
+      LEFT OUTER JOIN uom ON (uom_item_weight)
+      LEFT OUTER JOIN whsinfo ON (warehous_id=ipsfreight_warehous_id)
+      LEFT OUTER JOIN shipzone ON (shipzone_id=ipsfreight_shipzone_id)
+      LEFT OUTER JOIN freightclass ON (freightclass_id=ipsfreight_freightclass_id)
+    WHERE (ipsfreight_id=_freightid);
+
+    RETURN NEXT _row;
+  END IF;
+
+  RETURN;
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/calculatesubtax.sql b/foundation-database/public/functions/calculatesubtax.sql
new file mode 100644 (file)
index 0000000..9dc9681
--- /dev/null
@@ -0,0 +1,83 @@
+
+CREATE OR REPLACE FUNCTION calculatesubtax(integer, date, integer, numeric, integer)
+  RETURNS SETOF taxdetail AS
+$$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pTaxCodeId ALIAS FOR $1;
+  pDate ALIAS FOR $2;
+  pCurrId ALIAS FOR $3;
+  pAmount ALIAS FOR $4;
+  pLevel ALIAS FOR $5;
+  _row taxdetail%ROWTYPE;
+  _rownumber INTEGER := 1;
+  _calc_tax Numeric :=0;
+  _x RECORD;
+  _y RECORD;
+
+BEGIN
+  FOR _x IN 
+
+  SELECT tax_id, tax_code, tax_descrip, tax_basis_tax_id,
+    taxrate_id, taxrate_percent, taxrate_curr_id, taxrate_amount,  
+    taxclass_id, taxclass_code, COALESCE(taxclass_sequence,0) AS taxclass_sequence,
+    curr_id, curr_abbr
+  FROM tax, taxrate, taxclass, curr_symbol
+  WHERE ((tax_id = taxrate_tax_id)
+  AND (tax_taxclass_id = taxclass_id)
+  AND (taxrate_curr_id = curr_id)
+  AND (tax_basis_tax_id = pTaxCodeId)
+  AND (pDate BETWEEN taxrate_effective AND taxrate_expires)
+  AND (taxrate_curr_id = pCurrId))
+  
+  LOOP
+    SELECT 
+      ROUND((_x.taxrate_percent * pAmount + currToCurr(_x.curr_id, pCurrId, _x.taxrate_amount, pDate)), 6) 
+    INTO _calc_tax;
+
+    _row.taxdetail_tax_id = _x.tax_id;
+    _row.taxdetail_tax_code = _x.tax_code;
+    _row.taxdetail_tax_descrip = _x.tax_descrip;
+    _row.taxdetail_tax_basis_tax_id = _x.tax_basis_tax_id ;
+    _row.taxdetail_taxrate_percent = _x.taxrate_percent;
+    _row.taxdetail_taxrate_amount = _x.taxrate_amount;
+    _row.taxdetail_level = pLevel + 1;
+    _row.taxdetail_taxclass_id = _x.taxclass_id ; 
+    _row.taxdetail_taxclass_code = _x.taxclass_code;
+    _row.taxdetail_taxclass_sequence = _x.taxclass_sequence;
+    _row.taxdetail_tax = _calc_tax;
+    _row.taxdetail_curr_id = _x.curr_id;
+    _row.taxdetail_curr_abbr = _x.curr_abbr;
+
+    RETURN NEXT _row;
+    _rownumber := _rownumber + 1;
+
+    FOR _y IN  
+    SELECT * 
+    FROM calculateSubTax( _x.tax_id, pDate, pCurrId, _calc_tax, pLevel + 1)
+    LOOP
+      _row.taxdetail_tax_id = _y.taxdetail_tax_id;
+      _row.taxdetail_tax_code = _y.taxdetail_tax_code;
+      _row.taxdetail_tax_descrip = _y.taxdetail_tax_descrip;
+      _row.taxdetail_tax_basis_tax_id = _y.taxdetail_tax_basis_tax_id ;
+      _row.taxdetail_taxrate_percent = _y.taxdetail_taxrate_percent;
+      _row.taxdetail_taxrate_amount = _y.taxdetail_taxrate_amount;
+      _row.taxdetail_level = _y.taxdetail_level + 1;
+      _row.taxdetail_taxclass_id = _y.taxdetail_taxclass_id ; 
+      _row.taxdetail_taxclass_code = _y.taxdetail_taxclass_code;
+      _row.taxdetail_taxclass_sequence = _y.taxdetail_taxclass_sequence;
+      _row.taxdetail_tax = _y.taxdetail_tax;
+      _row.taxdetail_curr_id = _y.taxdetail_curr_id;
+      _row.taxdetail_curr_abbr = _y.taxdetail_curr_abbr;
+      
+      RETURN NEXT _row;
+      _rownumber := _rownumber + 1;
+
+    END LOOP;
+
+  END LOOP;
+
+END;
+$$
+  LANGUAGE 'plpgsql' VOLATILE;
diff --git a/foundation-database/public/functions/calculatetax.sql b/foundation-database/public/functions/calculatetax.sql
new file mode 100644 (file)
index 0000000..46fc1c4
--- /dev/null
@@ -0,0 +1,24 @@
+CREATE OR REPLACE FUNCTION calculatetax(integer, integer, date, integer, numeric)
+  RETURNS numeric AS
+$BODY$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pTaxZoneId ALIAS FOR  $1;
+  pTaxTypeId ALIAS FOR  $2;
+  pDate ALIAS FOR  $3;
+  pCurrId ALIAS FOR $4;
+  pAmount ALIAS FOR $5;
+  _tottax numeric := 0;  -- total tax
+  
+BEGIN
+
+  SELECT COALESCE(ROUND(SUM(taxdetail_tax),6),0)
+    INTO _tottax 
+  FROM calculateTaxDetail(pTaxZoneId, pTaxTypeId, pDate, pCurrId, pAmount);
+
+  RETURN _tottax;
+  
+END;
+$BODY$
+  LANGUAGE 'plpgsql' VOLATILE;
diff --git a/foundation-database/public/functions/calculatetaxdetail.sql b/foundation-database/public/functions/calculatetaxdetail.sql
new file mode 100644 (file)
index 0000000..85172c9
--- /dev/null
@@ -0,0 +1,144 @@
+
+CREATE OR REPLACE FUNCTION calculatetaxdetail(integer, integer, date, integer, numeric)
+  RETURNS SETOF taxdetail AS
+$BODY$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pTaxZoneId ALIAS FOR  $1;
+  pTaxTypeId ALIAS FOR  $2;
+  pDate ALIAS FOR  $3;
+  pCurrId ALIAS FOR $4;
+  pAmount ALIAS FOR $5;
+  _row taxdetail%ROWTYPE;
+  _x RECORD;
+  _y RECORD;
+  _z RECORD;
+  _currcum numeric := 0;  -- Current cumulative tax
+  _currseq numeric := 0;  -- Current group sequence
+  _prevcum numeric := 0;  -- Previous cumulative tax
+  _tax numeric := 0;      -- Calculated tax amount
+  _taxbasis numeric := 0;  -- Used for calculating sub taxes
+
+BEGIN
+
+  IF ((COALESCE(pTaxTypeId,-1) = -1) OR (COALESCE(pTaxZoneId,-1) = -1)) THEN
+    RETURN;
+  END IF;
+
+  SELECT DISTINCT
+    COALESCE(taxass_taxzone_id, -1) AS taxzone_id,
+    COALESCE(taxass_taxtype_id, -1) AS taxtype_id,
+    taxass_tax_id,
+    CASE
+      WHEN ((taxass_taxzone_id IS NOT NULL) AND (taxass_taxtype_id IS NOT NULL)) THEN
+        0
+      WHEN ((taxass_taxzone_id IS NOT NULL) AND (taxass_taxtype_id IS NULL)) THEN
+        1
+      WHEN ((taxass_taxzone_id IS NULL) AND (taxass_taxtype_id IS NOT NULL)) THEN
+        2
+      ELSE
+        3
+    END AS sequence
+    INTO _x
+  FROM taxass
+  WHERE  ((COALESCE(taxass_taxzone_id, pTaxZoneId, -1) = COALESCE(pTaxZoneId,-1))
+  AND    (COALESCE(taxass_taxtype_id, pTaxTypeId, -1) = COALESCE(pTaxTypeId,-1)))
+  ORDER BY sequence LIMIT 1;
+
+  --Now loop through each tax detail record and return calculated result
+  FOR _y IN
+    SELECT  --the data required by taxdetail type.  Coalesce group sequence to 0 if no class.
+    tax_id
+    ,tax_code
+    ,tax_descrip
+    ,tax_basis_tax_id
+    ,taxrate_percent
+    ,taxrate_amount
+    ,0 as taxdetail_level 
+    ,taxclass_id
+    ,taxclass_code
+    ,COALESCE(taxclass_sequence, 0) AS taxclass_sequence
+    ,0 as taxdetail_tax
+    ,curr_id
+    ,curr_abbr 
+    FROM taxass, taxclass RIGHT OUTER JOIN tax  
+      LEFT OUTER JOIN taxrate ON (taxrate_tax_id=tax_id)
+    ON (tax_taxclass_id=taxclass_id),
+    curr_symbol 
+    WHERE 
+    taxass_tax_id=tax_id
+    AND taxrate_curr_id=curr_id
+    AND COALESCE(taxass_taxzone_id, -1) = _x.taxzone_id
+    AND COALESCE(taxass_taxtype_id, -1) = _x.taxtype_id
+    AND pDate BETWEEN COALESCE(taxrate_effective, startoftime()) AND COALESCE(taxrate_expires, endoftime())
+    ORDER BY COALESCE(taxclass_sequence, 0)
+  LOOP
+    -- If sequence has changed, cache the previous cumulative tax
+    IF (_currseq != _x.sequence) THEN
+      _prevcum := _currcum;
+    END IF;
+
+    -- Calculate the tax amount.  Convert currency for flat rate amounts
+    SELECT 
+    ROUND((_y.taxrate_percent * (pAmount + _prevcum) + currToCurr(_y.curr_id, pCurrId, _y.taxrate_amount, pDate)), 6) 
+    INTO _tax
+    FROM tax JOIN  taxrate ON (tax_id = taxrate_tax_id)
+    WHERE (tax_id=_x.taxass_tax_id)
+    AND (pDate BETWEEN COALESCE(taxrate_effective, startoftime()) AND COALESCE(taxrate_expires, endoftime()));
+
+    --Map fields to _row
+
+    _row.taxdetail_tax_id := _y.tax_id;
+    _row.taxdetail_tax_code := _y.tax_code;
+    _row.taxdetail_tax_descrip := _y.tax_descrip;
+    _row.taxdetail_tax_basis_tax_id := _y.tax_basis_tax_id;
+    _row.taxdetail_taxrate_percent := _y.taxrate_percent;
+    _row.taxdetail_taxrate_amount := _y.taxrate_amount;
+    _row.taxdetail_level := _y.taxdetail_level;
+    _row.taxdetail_taxclass_id := _y.taxclass_id;
+    _row.taxdetail_taxclass_code := _y.taxclass_code;
+    _row.taxdetail_taxclass_sequence := _y.taxclass_sequence;
+    _row.taxdetail_tax := _tax;
+    _row.taxdetail_curr_id := _y.curr_id;
+    _row.taxdetail_curr_abbr := _y.curr_abbr;
+  
+    RETURN NEXT _row;
+
+    -- Increment cumulative balance and sequence number
+    IF(_y.taxclass_sequence <> 0) THEN
+      _currcum := _currcum + _tax;
+    END IF;
+    _currseq := _y.taxclass_sequence;
+
+    -- Loop to Calculate sub taxes
+    FOR _z IN
+    SELECT *
+    FROM calculateSubTax(_y.tax_id,pDate, pCurrId, _tax, 0)
+    LOOP
+     --Mapping of data
+    _row.taxdetail_tax_id := _z.taxdetail_tax_id;
+    _row.taxdetail_tax_code := _z.taxdetail_tax_code;
+    _row.taxdetail_tax_descrip := _z.taxdetail_tax_descrip;
+    _row.taxdetail_tax_basis_tax_id := _z.taxdetail_tax_basis_tax_id;
+    _row.taxdetail_taxrate_percent := _z.taxdetail_taxrate_percent;
+    _row.taxdetail_taxrate_amount := _z.taxdetail_taxrate_amount;
+    _row.taxdetail_level := _z.taxdetail_level;
+    _row.taxdetail_taxclass_id := _z.taxdetail_taxclass_id;
+    _row.taxdetail_taxclass_code := _z.taxdetail_taxclass_code;
+    _row.taxdetail_taxclass_sequence := _z.taxdetail_taxclass_sequence;
+    _row.taxdetail_tax := _z.taxdetail_tax;
+    _row.taxdetail_curr_id := _z.taxdetail_curr_id;
+    _row.taxdetail_curr_abbr := _z.taxdetail_curr_abbr;
+
+     RETURN NEXT _row;
+     --Add to cumulative counter (_curcum)
+     _currcum := _currcum + _z.taxdetail_tax ;
+
+    END LOOP;
+
+   END LOOP;
+END;
+ $BODY$
+  LANGUAGE 'plpgsql' VOLATILE;
diff --git a/foundation-database/public/functions/calculatetaxdetailline.sql b/foundation-database/public/functions/calculatetaxdetailline.sql
new file mode 100644 (file)
index 0000000..334b644
--- /dev/null
@@ -0,0 +1,55 @@
+
+CREATE OR REPLACE FUNCTION calculatetaxdetailline(text, integer) 
+  RETURNS SETOF taxdetail AS
+$BODY$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pOrderType ALIAS FOR $1;
+  pOrderId ALIAS FOR $2;
+  _row taxdetail%ROWTYPE;
+  _qry text;
+  _totaltax numeric;
+  _y RECORD;
+  _table text;
+  
+BEGIN
+   _totaltax=0.0;
+
+   IF pOrderType = 'II' THEN
+     _table := 'invcitemtax';
+   ELSIF pOrderType = 'BI' THEN
+     _table := 'cobilltax';
+   ELSIF pOrderType = 'CI' THEN
+     _table := 'cmitemtax';
+   ELSIF pOrderType = 'VI' THEN
+     _table := 'voitemtax';
+   ELSIF pOrderType = 'TI' THEN
+     _table := 'toitemtax';
+   ELSIF pOrderType = 'AR' THEN
+     _table := 'aropentax';
+   ELSIF pOrderType = 'AP' THEN
+     _table := 'apopentax';
+   END IF;
+     
+   _qry := 'SELECT taxhist_tax_id as tax_id, tax_code, tax_descrip, taxhist_tax, COALESCE(taxhist_sequence,0) AS taxhist_sequence
+            FROM taxhist 
+             JOIN tax ON (taxhist_tax_id=tax_id) 
+             JOIN pg_class ON (pg_class.oid=taxhist.tableoid) 
+            WHERE ( (taxhist_parent_id = ' || pOrderId || ')
+             AND (relname=''' || _table || ''') );';
+    
+   FOR _y IN  EXECUTE _qry
+   LOOP
+     _row.taxdetail_tax_id=_y.tax_id;
+     _row.taxdetail_tax_code = _y.tax_code;
+     _row.taxdetail_tax_descrip = _y.tax_descrip;
+     _row.taxdetail_tax = _y.taxhist_tax;
+     _row.taxdetail_level= 0 ;
+     _row.taxdetail_taxclass_sequence= _y.taxhist_sequence;
+     _totaltax = _totaltax + _y.taxhist_tax;
+     RETURN NEXT _row;
+   END LOOP;
+ END;
+$BODY$
+  LANGUAGE 'plpgsql' VOLATILE;
diff --git a/foundation-database/public/functions/calculatetaxdetailsummary.sql b/foundation-database/public/functions/calculatetaxdetailsummary.sql
new file mode 100644 (file)
index 0000000..610f016
--- /dev/null
@@ -0,0 +1,185 @@
+
+CREATE OR REPLACE FUNCTION calculatetaxdetailsummary(text, integer, text)
+  RETURNS SETOF taxdetail AS
+$BODY$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pOrderType ALIAS FOR $1;
+  pOrderId ALIAS FOR $2;
+  pDisplayType ALIAS FOR $3;
+  _row taxdetail%ROWTYPE;
+  _qry text := '';
+  _qry1 text;
+  _totaltax numeric;
+  _x RECORD;
+  _y RECORD;
+  _table text;
+  
+BEGIN
+ _totaltax=0.0;
+ IF pOrderType IN ('S','Q','RA','PO') THEN
+   
+   IF pOrderType = 'S' THEN
+     _qry := 'SELECT ' || 'COALESCE(cohead_taxzone_id, -1) AS taxzone_id, cohead_orderdate AS order_date,
+                cohead_curr_id AS curr_id, COALESCE(coitem_taxtype_id, -1) AS taxtype_id,
+                ROUND((coitem_qtyord * coitem_qty_invuomratio) * (coitem_price / coitem_price_invuomratio),2) AS amount
+              FROM cohead, coitem
+              WHERE ( (coitem_cohead_id = ' || pOrderId || ')
+               AND (' || 'cohead_id = coitem_cohead_id) 
+               AND ( coitem_status != ''X'') )';
+   ELSEIF  pOrderType = 'Q' THEN
+     _qry := 'SELECT ' || 'COALESCE(quhead_taxzone_id, -1) AS taxzone_id, quhead_quotedate AS order_date,
+                quhead_curr_id AS curr_id, COALESCE(quitem_taxtype_id, -1) AS taxtype_id, 
+                ROUND((quitem_qtyord * quitem_qty_invuomratio) * (quitem_price / quitem_price_invuomratio),2) AS amount
+              FROM quhead, quitem 
+              WHERE ( (quitem_quhead_id = ' || pOrderId || ')
+               AND (quhead_id = quitem_quhead_id) )'; 
+   ELSEIF  pOrderType = 'RA' THEN
+     _qry := 'SELECT ' || 'COALESCE(rahead_taxzone_id, -1) AS taxzone_id, rahead_authdate AS order_date,
+                rahead_curr_id AS curr_id, COALESCE(raitem_taxtype_id, -1) AS taxtype_id, 
+                ROUND((raitem_qtyauthorized * raitem_qty_invuomratio) * (raitem_unitprice / raitem_price_invuomratio),2) AS amount
+              FROM rahead, raitem 
+              WHERE ( (raitem_rahead_id = ' || pOrderId || ')
+               AND (rahead_id = raitem_rahead_id) )';
+   ELSEIF  pOrderType = 'PO' THEN
+     _qry := 'SELECT ' || 'COALESCE(pohead_taxzone_id, -1) AS taxzone_id, pohead_orderdate AS order_date,
+                pohead_curr_id AS curr_id, COALESCE(poitem_taxtype_id, -1) AS taxtype_id, 
+                ROUND(poitem_qty_ordered * poitem_unitprice, 2) AS amount
+              FROM pohead, poitem 
+              WHERE ( (poitem_pohead_id = ' || pOrderId || ')
+               AND (pohead_id = poitem_pohead_id) )'; 
+  END IF;
+
+  FOR _x IN EXECUTE _qry
+  LOOP  
+    _qry1 := 'SELECT * from calculatetaxdetail(' || _x.taxzone_id || ',' || _x.taxtype_id || ',''' || _x.order_date || ''',' || _x.curr_id  || ',' || _x.amount || ')';
+    FOR _y IN  EXECUTE _qry1
+    LOOP
+      _row.taxdetail_tax_id=_y.taxdetail_tax_id;
+      _row.taxdetail_tax_code = _y.taxdetail_tax_code;
+      _row.taxdetail_tax_descrip = _y.taxdetail_tax_descrip;
+      _row.taxdetail_tax = _y.taxdetail_tax;
+      _row.taxdetail_level=_y.taxdetail_level;
+      _row.taxdetail_taxclass_sequence= _y.taxdetail_taxclass_sequence;
+      _totaltax = _totaltax + _y.taxdetail_tax;
+      RETURN NEXT _row;
+    END LOOP;
+  END LOOP;
+
+  IF pDisplayType = 'T' AND pOrderType <> 'PO' THEN
+   IF pOrderType = 'S' THEN 
+    _qry := 'SELECT COALESCE(cohead_taxzone_id, -1) AS taxzone_id, cohead_orderdate AS order_date,
+               cohead_curr_id AS curr_id, cohead_freight AS freight
+             FROM cohead WHERE cohead_id = ' || pOrderId ;
+   ELSEIF  pOrderType = 'Q' THEN 
+    _qry := 'SELECT COALESCE(quhead_taxzone_id, -1) AS taxzone_id, quhead_quotedate AS order_date,
+               quhead_curr_id AS curr_id, COALESCE(quhead_freight,0) AS freight
+             FROM quhead WHERE quhead_id = ' || pOrderId;
+   ELSEIF pOrderType = 'RA' THEN
+    _qry := 'SELECT COALESCE(rahead_taxzone_id, -1) AS taxzone_id, COALESCE(rahead_authdate,CURRENT_DATE) AS order_date,
+               rahead_curr_id AS curr_id, COALESCE(rahead_freight,0) AS freight
+             FROM rahead WHERE rahead_id = ' || pOrderId;
+   END IF;
+
+  FOR _x IN EXECUTE _qry
+  LOOP
+     _qry1 := 'SELECT * from calculatetaxdetail(' || _x.taxzone_id || ', getfreighttaxtypeid(),''' || _x.order_date || ''',' || _x.curr_id  || ',' || _x.freight || ')';
+    FOR _y IN  EXECUTE _qry1
+    LOOP
+       _row.taxdetail_tax_id=_y.taxdetail_tax_id;
+       _row.taxdetail_tax_code = _y.taxdetail_tax_code;
+       _row.taxdetail_tax_descrip = _y.taxdetail_tax_descrip;
+       _row.taxdetail_tax = _y.taxdetail_tax;
+       _row.taxdetail_level=_y.taxdetail_level;
+       _row.taxdetail_taxclass_sequence= _y.taxdetail_taxclass_sequence;
+       _totaltax = _totaltax + _y.taxdetail_tax;
+       RETURN NEXT _row;
+     END LOOP;
+   END LOOP;
+  END IF;
+  
+ ELSEIF pOrderType IN ('I','B','CM', 'VO','TO') THEN
+   IF (pOrderType='I') THEN
+     _table := 'invcheadtax';
+   ELSIF (pOrderType='B') THEN
+     _table := 'cobmisctax';
+   ELSIF (pOrderType='CM') THEN
+     _table := 'cmheadtax';
+   ELSIF (pOrderType='VO') THEN
+     _table := 'voheadtax';
+   ELSIF (pOrderType='TO') THEN
+     _table := 'tohead';
+   END IF;
+   
+   IF pOrderType = 'I' AND (pDisplayType IN ('L','T')) THEN
+     _qry := 'SELECT taxhist_tax_id as tax_id, tax_code, tax_descrip, taxhist_tax, taxhist_sequence
+              FROM invchead, invcitemtax LEFT OUTER JOIN tax ON (taxhist_tax_id=tax_id)
+               LEFT OUTER JOIN invcitem ON (invcitem_id=taxhist_parent_id) 
+              WHERE invcitem_invchead_id = ' || pOrderId || ' 
+               AND invchead_id = invcitem_invchead_id ';
+   ELSIF pOrderType = 'B' AND (pDisplayType IN ('L','T')) THEN
+    _qry := 'SELECT taxhist_tax_id as tax_id, tax_code, tax_descrip, taxhist_tax, taxhist_sequence
+             FROM cobmisc, cobilltax LEFT OUTER JOIN tax ON (taxhist_tax_id=tax_id)
+             LEFT OUTER JOIN cobill ON (cobill_id=taxhist_parent_id)
+             WHERE cobill_cobmisc_id = ' || pOrderId || ' 
+             AND cobmisc_id = cobill_cobmisc_id ';
+   ELSIF pOrderType = 'CM' AND (pDisplayType IN ('L','T')) THEN
+    _qry := 'SELECT taxhist_tax_id as tax_id, tax_code, tax_descrip, taxhist_tax, taxhist_sequence
+             FROM cmhead, cmitemtax LEFT OUTER JOIN tax ON (taxhist_tax_id=tax_id)
+             LEFT OUTER JOIN cmitem ON (cmitem_id=taxhist_parent_id)
+             WHERE cmitem_cmhead_id = ' || pOrderId || ' 
+             AND cmhead_id = cmitem_cmhead_id ';
+   ELSIF pOrderType = 'VO' AND (pDisplayType IN ('L','T')) THEN
+    _qry := 'SELECT taxhist_tax_id as tax_id, tax_code, tax_descrip, taxhist_tax, taxhist_sequence
+             FROM vohead, voitemtax LEFT OUTER JOIN tax ON (taxhist_tax_id=tax_id)
+             LEFT OUTER JOIN voitem ON (voitem_id=taxhist_parent_id)
+             WHERE voitem_vohead_id = ' || pOrderId || ' 
+             AND vohead_id = voitem_vohead_id ';
+   ELSIF pOrderType = 'TO' AND (pDisplayType IN ('L','T')) THEN
+    _qry := 'SELECT taxhist_tax_id as tax_id, tax_code, tax_descrip, taxhist_tax, taxhist_sequence
+             FROM tohead, toitemtax LEFT OUTER JOIN tax ON (taxhist_tax_id=tax_id)
+             LEFT OUTER JOIN toitem ON (toitem_id=taxhist_parent_id)
+             WHERE toitem_tohead_id = ' || pOrderId || ' 
+             AND tohead_id = toitem_tohead_id ';
+   END IF;
+   IF pDisplayType IN ('F','T') AND pOrderType <> 'VO' THEN
+     IF (length(_qry) > 0) THEN
+       _qry := _qry || ' UNION ALL ';
+     END IF;
+     _qry := _qry || 'SELECT taxhist_tax_id as tax_id, tax_code, tax_descrip, taxhist_tax, taxhist_sequence
+              FROM taxhist 
+               JOIN tax ON (taxhist_tax_id=tax_id)
+               JOIN pg_class ON (pg_class.oid=taxhist.tableoid)
+              WHERE ( (taxhist_parent_id = ' || pOrderId || ') 
+               AND (taxhist_taxtype_id=getfreighttaxtypeid())
+               AND (relname=''' || _table || ''') )';
+   END IF;
+   IF pDisplayType IN ('A','T') THEN
+     IF (length(_qry) > 0) THEN
+       _qry := _qry || ' UNION ALL ';
+     END IF;
+     _qry := _qry || 'SELECT taxhist_tax_id as tax_id, tax_code, tax_descrip, taxhist_tax, taxhist_sequence
+              FROM taxhist 
+               JOIN tax ON (taxhist_tax_id=tax_id)
+               JOIN pg_class ON (pg_class.oid=taxhist.tableoid)
+              WHERE ( (taxhist_parent_id = ' || pOrderId || ') 
+               AND (taxhist_taxtype_id=getadjustmenttaxtypeid())
+               AND (relname=''' || _table || ''') )';
+
+   END IF;
+   FOR _y IN  EXECUTE _qry
+   LOOP
+     _row.taxdetail_tax_id=_y.tax_id;
+     _row.taxdetail_tax_code = _y.tax_code;
+     _row.taxdetail_tax_descrip = _y.tax_descrip;
+     _row.taxdetail_tax = _y.taxhist_tax;
+     _row.taxdetail_level= 0 ;
+     _row.taxdetail_taxclass_sequence= COALESCE(_y.taxhist_sequence,0);
+     _totaltax = _totaltax + _y.taxhist_tax;
+     RETURN NEXT _row;
+   END LOOP;
+ END IF;
+ END;
+$BODY$
+  LANGUAGE 'plpgsql' VOLATILE;
diff --git a/foundation-database/public/functions/calculatetaxhist.sql b/foundation-database/public/functions/calculatetaxhist.sql
new file mode 100644 (file)
index 0000000..cb91913
--- /dev/null
@@ -0,0 +1,51 @@
+CREATE OR REPLACE FUNCTION calculatetaxhist(text, integer, integer, integer, date, integer, numeric)
+  RETURNS boolean AS
+$BODY$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pTableName ALIAS FOR $1;
+  pParentId  ALIAS FOR $2;
+  pTaxZoneId ALIAS FOR $3;
+  pTaxTypeId ALIAS FOR $4;
+  pDate      ALIAS FOR $5;
+  pCurrId    ALIAS FOR $6;
+  pAmount    ALIAS FOR $7;
+  _qry TEXT;
+  
+BEGIN
+  IF (pTableName IS NULL) THEN
+    RAISE EXCEPTION 'A table name is required to calculate tax history';
+  ELSEIF (pParentId IS NULL) THEN
+    RAISE EXCEPTION 'A parent ID is required to calculate tax history';
+  ELSEIF (pDate IS NULL) THEN
+    RAISE EXCEPTION 'A date is required to calculate tax history';
+  ELSEIF (pAmount IS NULL) THEN
+     RAISE EXCEPTION 'An amount is required to calculate tax history';
+  END IF;
+
+  -- Build a query that deletes any previous tax history for this document record
+  _qry := 'DELETE FROM ' || pTableName || ' WHERE taxhist_parent_id = ' || pParentId || ' AND taxhist_taxtype_id <> getadjustmenttaxtypeid();';
+  EXECUTE _qry;
+
+  -- Next, build and execute query that inserts new rows.
+  _qry := 'INSERT INTO ' || pTableName || ' (
+             taxhist_parent_id, taxhist_taxtype_id, taxhist_tax_id, taxhist_basis,
+             taxhist_basis_tax_id, taxhist_sequence, taxhist_percent, 
+             taxhist_amount, taxhist_tax, taxhist_docdate)
+           SELECT ' || pParentId || ',';
+  IF (pTaxTypeId IS NULL) THEN
+    _qry := _qry || 'NULL';
+  ELSE
+    _qry := _qry || pTaxTypeId;
+  END If;
+  _qry := _qry || ', taxdetail_tax_id,' || pAmount || ', 
+             taxdetail_tax_basis_tax_id, taxdetail_taxclass_sequence, taxdetail_taxrate_percent,
+             taxdetail_taxrate_amount, taxdetail_tax, ''' || pDate || '''
+           FROM calculatetaxdetail(' || COALESCE(pTaxZoneId,-1) || ',' || COALESCE(pTaxTypeId,-1) ||',''' || pDate || ''',' || pCurrId || ',' || pAmount || ');';
+  EXECUTE _qry;
+
+  RETURN true;
+END;
+$BODY$
+  LANGUAGE 'plpgsql' VOLATILE;
diff --git a/foundation-database/public/functions/calcvoucheramts.sql b/foundation-database/public/functions/calcvoucheramts.sql
new file mode 100644 (file)
index 0000000..5a2e719
--- /dev/null
@@ -0,0 +1,58 @@
+CREATE OR REPLACE FUNCTION calcVoucherFreight(INTEGER) RETURNS NUMERIC AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pVoucherid ALIAS FOR $1;
+  _amount NUMERIC := 0;
+
+BEGIN
+
+  SELECT SUM(COALESCE(voitem_freight, 0)) INTO _amount
+  FROM voitem
+  WHERE (voitem_vohead_id=pVoucherid);
+
+  RETURN _amount;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION calcVoucherTax(INTEGER) RETURNS NUMERIC AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pVoucherid ALIAS FOR $1;
+  _amount NUMERIC := 0;
+
+BEGIN
+
+  SELECT COALESCE(calculateTax(vohead_taxzone_id,
+                               vohead_taxtype_id,
+                               vohead_docdate,
+                               vohead_curr_id,
+                               calcVoucherAmt(vohead_id)), 0) INTO _amount
+  FROM vohead
+  WHERE (vohead_id=pVoucherid);
+
+  RETURN _amount;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION calcVoucherAmt(INTEGER) RETURNS NUMERIC AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pVoucherid ALIAS FOR $1;
+  _amount NUMERIC := 0;
+
+BEGIN
+
+  SELECT SUM(COALESCE(vodist_amount, 0)) INTO _amount
+  FROM vodist
+  WHERE (vodist_vohead_id=pVoucherid);
+
+  RETURN _amount;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/calcwooperstartstub.sql b/foundation-database/public/functions/calcwooperstartstub.sql
new file mode 100644 (file)
index 0000000..71f743b
--- /dev/null
@@ -0,0 +1,19 @@
+
+CREATE OR REPLACE FUNCTION calcWooperStartStub(INTEGER, INTEGER) RETURNS DATE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWoId         ALIAS FOR $1;
+  pBooitemSeqId ALIAS FOR $2;
+  _result       DATE;
+BEGIN
+
+  IF ( SELECT ((metric_value='t') AND packageIsEnabled('xtmfg'))
+         FROM metric
+        WHERE(metric_name='Routings') ) THEN
+    RETURN xtmfg.calcWooperStart(pWoId, pBooitemSeqId);
+  END IF;
+  RETURN null;
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/cancelbillingselection.sql b/foundation-database/public/functions/cancelbillingselection.sql
new file mode 100644 (file)
index 0000000..f00e21e
--- /dev/null
@@ -0,0 +1,24 @@
+CREATE OR REPLACE FUNCTION cancelBillingSelection(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCobmiscid ALIAS FOR $1;
+
+BEGIN
+
+  IF ( ( SELECT cobmisc_posted
+         FROM cobmisc
+         WHERE (cobmisc_id=pCobmiscid) ) ) THEN
+    RETURN -1;
+  END IF;
+
+  DELETE FROM cobill
+  WHERE (cobill_cobmisc_id=pCobmiscid); 
+
+  DELETE FROM cobmisc
+  WHERE (cobmisc_id=pCobmiscid);
+
+  RETURN 1;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/changeaccountingperioddates.sql b/foundation-database/public/functions/changeaccountingperioddates.sql
new file mode 100644 (file)
index 0000000..2590e21
--- /dev/null
@@ -0,0 +1,74 @@
+
+CREATE OR REPLACE FUNCTION changeAccountingPeriodDates(INTEGER, DATE, DATE) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pPeriodid ALIAS FOR $1;
+  pStartDate ALIAS FOR $2;
+  pEndDate ALIAS FOR $3;
+  _check INTEGER;
+  _r RECORD;
+
+BEGIN
+
+--  Check to make sure that the passed period is not closed
+  IF ( ( SELECT period_closed
+         FROM period
+         WHERE (period_id=pPeriodid) ) ) THEN
+    RETURN -1;
+  END IF;
+
+--  Check to make sure that the passed start date does not fall
+--  into another period
+  SELECT period_id INTO _check
+  FROM period
+  WHERE ( (pStartDate BETWEEN period_start AND period_end)
+    AND (period_id <> pPeriodid) )
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -2;
+  END IF;
+
+--  Check to make sure that the passed end date does not fall
+--  into another period
+  SELECT period_id INTO _check
+  FROM period
+  WHERE ( (pEndDate BETWEEN period_start AND period_end)
+    AND (period_id <> pPeriodid) )
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -3;
+  END IF;
+
+--  Check to make sure that the new passed start and end dates do not
+--  orphan a posted G/L Transaction
+  SELECT gltrans_id INTO _check
+  FROM gltrans, period
+  WHERE ( (gltrans_date BETWEEN period_start AND period_end)
+   AND (gltrans_posted)
+   AND (NOT (gltrans_date BETWEEN pStartDate AND pEndDate))
+   AND (period_id=pPeriodid) )
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -4;
+  END IF;
+
+--  Alter the start and end dates of the pass period
+  UPDATE period
+  SET period_start=pStartDate, period_end=pEndDate
+  WHERE (period_id=pPeriodid);
+
+--  Post any unposted G/L Transactions into the period
+  FOR _r IN SELECT DISTINCT gltrans_sequence
+            FROM gltrans
+            WHERE ( (NOT gltrans_posted)
+             AND (gltrans_date BETWEEN pStartDate AND pEndDate) ) LOOP
+    PERFORM postIntoTrialBalance(_r.gltrans_sequence);
+  END LOOP;
+
+--  All done
+  RETURN 1;
+
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/changeaccountingyearperioddates.sql b/foundation-database/public/functions/changeaccountingyearperioddates.sql
new file mode 100644 (file)
index 0000000..123317b
--- /dev/null
@@ -0,0 +1,69 @@
+
+CREATE OR REPLACE FUNCTION changeAccountingYearPeriodDates(INTEGER, DATE, DATE) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pPeriodid ALIAS FOR $1;
+  pStartDate ALIAS FOR $2;
+  pEndDate ALIAS FOR $3;
+  _check INTEGER;
+  _checkBool BOOLEAN;
+  _r RECORD;
+
+BEGIN
+
+--  Check to make sure that the passed yearperiod is not closed
+  IF ( ( SELECT yearperiod_closed
+         FROM yearperiod
+         WHERE (yearperiod_id=pPeriodid) ) ) THEN
+    RETURN -1;
+  END IF;
+
+--  Check to make sure that the passed start date does not fall
+--  into another yearperiod
+  SELECT yearperiod_id INTO _check
+  FROM yearperiod
+  WHERE ( (pStartDate BETWEEN yearperiod_start AND yearperiod_end)
+    AND (yearperiod_id <> pPeriodid) )
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -2;
+  END IF;
+
+--  Check to make sure that the passed end date does not fall
+--  into another yearperiod
+  SELECT yearperiod_id INTO _check
+  FROM yearperiod
+  WHERE ( (pEndDate BETWEEN yearperiod_start AND yearperiod_end)
+    AND (yearperiod_id <> pPeriodid) )
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -3;
+  END IF;
+
+--  Check to make sure that the passed yearperiod is not closed
+  IF ( ( SELECT (count(period_id) > 0)
+         FROM period
+         WHERE ((period_yearperiod_id=pPeriodid)
+          AND (period_start < pStartDate OR period_end > pEndDate)) ) ) THEN
+    RETURN -4;
+  END IF;
+
+--  Make sure that the passed start is prior to the end date
+  SELECT (pStartDate > pEndDate) INTO _checkBool;
+  IF (_checkBool) THEN
+    RETURN -5;
+  END IF;
+
+
+--  Alter the start and end dates of the pass period
+  UPDATE yearperiod
+  SET yearperiod_start=pStartDate, yearperiod_end=pEndDate
+  WHERE (yearperiod_id=pPeriodid);
+
+--  All done
+  RETURN 1;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/changefkeypointers.sql b/foundation-database/public/functions/changefkeypointers.sql
new file mode 100644 (file)
index 0000000..e68c9ea
--- /dev/null
@@ -0,0 +1,82 @@
+CREATE OR REPLACE FUNCTION changeFKeyPointers(TEXT, TEXT, INTEGER, INTEGER, TEXT[], BOOLEAN) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSchema       ALIAS FOR $1;
+  pTable        ALIAS FOR $2;
+  pSourceId     ALIAS FOR $3;
+  pTargetId     ALIAS FOR $4;
+  pIgnore       ALIAS FOR $5;
+  _purge        BOOLEAN := COALESCE($6, FALSE);
+
+  _counter      INTEGER := 0;
+  _count1       INTEGER := 0;
+  _fk           RECORD;
+  _pk           TEXT[];
+
+BEGIN
+  -- for all foreign keys that point to pSchema.pTable
+  FOR _fk IN
+    EXECUTE 'SELECT fkeyns.nspname AS schemaname, fkeytab.relname AS tablename,
+                    conkey, attname, typname
+               FROM pg_constraint
+               JOIN pg_class     basetab ON (confrelid=basetab.oid)
+               JOIN pg_namespace basens  ON (basetab.relnamespace=basens.oid)
+               JOIN pg_class     fkeytab ON (conrelid=fkeytab.oid)
+               JOIN pg_namespace fkeyns  ON (fkeytab.relnamespace=fkeyns.oid)
+               JOIN pg_attribute         ON (attrelid=conrelid AND attnum=conkey[1])
+               JOIN pg_type              ON (atttypid=pg_type.oid)
+              WHERE basetab.relname = ' || quote_literal(pTable)  || '
+                AND basens.nspname  = ' || quote_literal(pSchema) || '
+                AND fkeytab.relname NOT IN (''' || ARRAY_TO_STRING(pIgnore, ''', ''') || ''')'
+  LOOP
+    IF (ARRAY_UPPER(_fk.conkey, 1) > 1) THEN
+      RAISE EXCEPTION 'Cannot change the foreign key in %.% that refers to %.% because the foreign key constraint has multiple columns. [xtuple: changefkeypointers, -1, %.%, %.%]',
+        _fk.schemaname, _fk.tablename, pSchema, pTable,
+        _fk.schemaname, _fk.tablename, pSchema, pTable;
+    END IF;
+    
+    -- optionally make a backup copy of the data
+    IF (NOT _purge) THEN
+      -- determine the primary key column of the fkey table
+      _pk := primaryKeyFields(_fk.schemaname, _fk.tablename);
+      IF (ARRAY_UPPER(_pk, 1) > 1) THEN
+        RAISE EXCEPTION 'Cannot change foreign key references in %.% because it has a composite primary key. Try setting the purge option. [xtuple: changefkeypointers, -4, %.%]',
+                        _fk.schemaname, _fk.tablename, _fk.schemaname, _fk.tablename;
+      END IF;
+
+      -- make the backup copy
+      EXECUTE 'INSERT INTO mrgundo (
+                     mrgundo_schema,      mrgundo_table,
+                     mrgundo_pkey_col,    mrgundo_pkey_id,
+                     mrgundo_col,         mrgundo_value,      mrgundo_type,
+                     mrgundo_base_schema, mrgundo_base_table, mrgundo_base_id
+             ) SELECT ' || quote_literal(_fk.schemaname) || ', '
+                        || quote_literal(_fk.tablename)  || ', '
+                        || quote_literal(_pk[1])         || ', ' 
+                        || _pk[1]                        || ', '
+                        || quote_literal(_fk.attname)    || ', ' 
+                        || _fk.attname                   || ', ' 
+                        || quote_literal(_fk.typname)    || ', '
+                        || quote_literal(pSchema)        || ', '
+                        || quote_literal(pTable)         || ', '
+                        || pTargetId                     || '
+                 FROM ' || _fk.schemaname || '.' || _fk.tablename ||
+              ' WHERE ('|| _fk.attname    || '=' || pSourceId || ');';
+    END IF;
+
+    -- actually change the foreign keys to point to the desired base table record
+    EXECUTE 'UPDATE '  || _fk.schemaname || '.' || _fk.tablename ||
+              ' SET '  || _fk.attname    || '=' || pTargetId ||
+            ' WHERE (' || _fk.attname    || '=' || pSourceId || ');';
+
+    GET DIAGNOSTICS _count1 = ROW_COUNT;
+    _counter := _counter + _count1;
+  END LOOP;
+
+  RETURN _counter;
+END;
+$$ LANGUAGE 'plpgsql';
+
+COMMENT ON FUNCTION changeFKeyPointers(TEXT, TEXT, INTEGER, INTEGER, TEXT[], BOOLEAN) IS
+'Change the data in all tables with foreign key relationships so they point to the pSchema.pTable record with primary key pTargetId instead of the record with primary key pSourceId. Ignore any tables listed in pIgnore. If the final arg is TRUE, make a backup copy of the original data in the mrgundo table.';
diff --git a/foundation-database/public/functions/changepoitemduedate.sql b/foundation-database/public/functions/changepoitemduedate.sql
new file mode 100644 (file)
index 0000000..76965a9
--- /dev/null
@@ -0,0 +1,24 @@
+DROP FUNCTION IF EXISTS changepoitemduedate(integer, date);
+CREATE OR REPLACE FUNCTION changePoitemDueDate(INTEGER, DATE) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pPoitemid ALIAS FOR $1;
+  pDate ALIAS FOR $2;
+
+BEGIN
+
+  IF ( ( SELECT (poitem_status IN (''C''))
+         FROM poitem
+         WHERE (poitem_id=pPoitemid) ) ) THEN
+    RETURN -1;
+  END IF;
+
+  UPDATE poitem
+  SET poitem_duedate=pDate
+  WHERE (poitem_id=pPoitemid);
+
+  RETURN pPoitemid;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/changepoitemqty.sql b/foundation-database/public/functions/changepoitemqty.sql
new file mode 100644 (file)
index 0000000..558af5e
--- /dev/null
@@ -0,0 +1,24 @@
+DROP FUNCTION IF EXISTS changepoitemqty(integer, numeric);
+CREATE OR REPLACE FUNCTION changePoitemQty(INTEGER, NUMERIC) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pPoitemid ALIAS FOR $1;
+  pQty ALIAS FOR $2;
+
+BEGIN
+
+  IF ( ( SELECT (poitem_status IN (''C''))
+         FROM poitem
+         WHERE (poitem_id=pPoitemid) ) ) THEN
+    RETURN -1;
+  END IF;
+
+  UPDATE poitem
+  SET poitem_qty_ordered=pQty
+  WHERE (poitem_id=pPoitemid);
+
+  RETURN pPoitemid;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/changeprdate.sql b/foundation-database/public/functions/changeprdate.sql
new file mode 100644 (file)
index 0000000..b9f92d2
--- /dev/null
@@ -0,0 +1,17 @@
+CREATE OR REPLACE FUNCTION changePrDate(INTEGER, DATE) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pPrid ALIAS FOR $1;
+  pDueDate ALIAS FOR $2;
+
+BEGIN
+
+  UPDATE pr
+  SET pr_duedate=pDueDate
+  WHERE (pr_id=pPrid);
+
+  RETURN 0;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/changeprqty.sql b/foundation-database/public/functions/changeprqty.sql
new file mode 100644 (file)
index 0000000..714dbd6
--- /dev/null
@@ -0,0 +1,17 @@
+CREATE OR REPLACE FUNCTION changePrQty(INTEGER, NUMERIC) RETURNS BOOL AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pPrid ALIAS FOR $1;
+  pQty ALIAS FOR $2;
+
+BEGIN
+
+  UPDATE pr
+  SET pr_qtyreq=pQty
+  WHERE (pr_id=pPrid);
+
+  RETURN TRUE;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/changepseudofkeypointers.sql b/foundation-database/public/functions/changepseudofkeypointers.sql
new file mode 100644 (file)
index 0000000..8090b74
--- /dev/null
@@ -0,0 +1,70 @@
+CREATE OR REPLACE FUNCTION changePseudoFKeyPointers(TEXT, TEXT, TEXT, INTEGER, TEXT, TEXT, INTEGER, TEXT, TEXT, BOOLEAN) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSchema       ALIAS FOR $1;
+  pTable        ALIAS FOR $2;
+  pFkeyCol      ALIAS FOR $3;
+  pSourceId     ALIAS FOR $4;
+  pBaseSchema   ALIAS FOR $5;
+  pBaseTable    ALIAS FOR $6;
+  pTargetId     ALIAS FOR $7;
+  pTypeCol      ALIAS FOR $8;
+  pType         ALIAS FOR $9;
+  _purge        BOOLEAN := COALESCE($10, FALSE);
+
+  _counter      INTEGER := 0;
+  _coltype      TEXT;
+  _pk           TEXT[];
+
+BEGIN
+  IF (NOT _purge) THEN
+    EXECUTE 'SELECT typname
+               FROM pg_type
+               JOIN pg_attribute ON (pg_type.oid=atttypid)
+               JOIN pg_class     ON (attrelid=pg_class.oid)
+               JOIN pg_namespace ON (relnamespace=pg_namespace.oid)
+              WHERE (relname=' || quote_literal(pTable)   || ')
+                AND (nspname=' || quote_literal(pSchema)  || ')
+                AND (attname=' || quote_literal(pFkeyCol) || ')' INTO _coltype;
+
+    _pk := primaryKeyFields(pSchema, pTable);
+    IF (ARRAY_UPPER(_pk, 1) > 1) THEN
+       RAISE EXCEPTION 'Cannot change pseudo-foreign key references in %.% because it has a composite primary key. Try setting the purge option. [xtuple: changepseudofkeypointers, -1, %.%',
+                        pSchema, pTable, pSchema, pTable;
+    END IF;
+
+    EXECUTE 'INSERT INTO mrgundo (
+                     mrgundo_schema,      mrgundo_table,
+                     mrgundo_pkey_col,    mrgundo_pkey_id,
+                     mrgundo_col,         mrgundo_value,      mrgundo_type,
+                     mrgundo_base_schema, mrgundo_base_table, mrgundo_base_id
+           ) SELECT ' || quote_literal(pSchema)     || ', '
+                      || quote_literal(pTable)      || ', '
+                      || quote_literal(_pk[1])      || ', ' 
+                      || quote_ident(_pk[1])        || ', '
+                      || quote_literal(pFkeyCol)    || ', ' 
+                      || quote_ident(pFkeyCol)      || ', ' 
+                      || quote_literal(_coltype)    || ', '
+                      || quote_literal(pBaseSchema) || ', '
+                      || quote_literal(pBaseTable)  || ', '
+                      || pTargetId                  || '
+               FROM '  || quote_ident(pSchema)  || '.' || quote_ident(pTable) || '
+              WHERE (('|| quote_ident(pFkeyCol) || '=' || pSourceId || ')
+                 AND ('|| quote_ident(pTypeCol) || '=' || quote_literal(pType) || '));';
+  END IF;
+
+  -- actually change the foreign keys to point to the desired base table record
+  EXECUTE 'UPDATE '  || quote_ident(pSchema)  || '.' || quote_ident(pTable) ||
+            ' SET '  || quote_ident(pFkeyCol) || '=' || pTargetId ||
+          ' WHERE ((' || quote_ident(pFkeyCol) || '=' || pSourceId || ')
+               AND (' || quote_ident(pTypeCol) || '=' || quote_literal(pType) || '));';
+
+  GET DIAGNOSTICS _counter = ROW_COUNT;
+
+  RETURN _counter;
+END;
+$$ LANGUAGE 'plpgsql';
+
+COMMENT ON FUNCTION changePseudoFKeyPointers(TEXT, TEXT, TEXT, INTEGER, TEXT, TEXT, INTEGER, TEXT, TEXT, BOOLEAN) IS
+'Change the data in pSchema.pTable with a pseudo-foreign key relationship to another (unnamed) table. Make pSchema.pTable point to the record with primary key pTargetId instead of the record with primary key pSourceId. pSchema.pTable cannot have a true foreign key relationship because it holds data that can point to any of several tables. The pType value in the pTypeCol column describes which table the data refer to (e.g. "T" may indicate that the current record refers to a "cntct"). If the final arg is TRUE, make a backup copy of the data in the mrgundo table.';
diff --git a/foundation-database/public/functions/changewodates.sql b/foundation-database/public/functions/changewodates.sql
new file mode 100644 (file)
index 0000000..c17c953
--- /dev/null
@@ -0,0 +1,102 @@
+
+CREATE OR REPLACE FUNCTION changeWoDates(INTEGER, DATE, DATE, BOOLEAN) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE 
+  pWoid ALIAS FOR $1;
+  pStartDate ALIAS FOR $2;
+  pDueDate ALIAS FOR $3;
+  changeChildren ALIAS FOR $4;
+  _p RECORD;
+  returnCode INTEGER;
+  _vtemp NUMERIC;
+
+BEGIN
+
+  SELECT wo_status, wo_startdate, itemsite_warehous_id INTO _p
+  FROM wo
+  Inner Join itemsite on
+      wo_itemsite_id=itemsite_id
+  WHERE (wo_id=pWoid);
+
+  IF (_p.wo_status = 'C') THEN 
+    returnCode := 0;
+
+  ELSIF (_p.wo_status IN ('R','I')) THEN
+    INSERT INTO evntlog (evntlog_evnttime, evntlog_username, evntlog_evnttype_id,
+                         evntlog_ordtype, evntlog_ord_id, evntlog_warehous_id, evntlog_number,
+                         evntlog_olddate, evntlog_newdate)
+    SELECT CURRENT_TIMESTAMP, evntnot_username, evnttype_id,
+           'W', wo_id, itemsite_warehous_id, formatWoNumber(wo_id),
+           wo_duedate, pDueDate
+    FROM evntnot, evnttype, itemsite, item, wo
+    WHERE ( (evntnot_evnttype_id=evnttype_id)
+     AND (evntnot_warehous_id=itemsite_warehous_id)
+     AND (wo_itemsite_id=itemsite_id)
+     AND (itemsite_item_id=item_id)
+     AND (evnttype_name='RWoDueDateRequestChange')
+     AND (wo_id=pWoid) );
+
+     returnCode := 0;
+
+  END IF;
+  
+--  Reschedule operations if routings enabled
+  IF (fetchMetricBool('Routings')) THEN
+
+--    Reschedule wooper
+    IF (fetchMetricBool('UseSiteCalendar')) THEN
+      UPDATE xtmfg.wooper
+      SET wooper_scheduled = calculatenextworkingdate(itemsite_warehous_id,DATE(pStartDate),
+                             CAST(calculateworkdays(itemsite_warehous_id, DATE(wo_startdate), DATE(wooper_scheduled)) as INTEGER))
+      FROM wo JOIN itemsite ON (wo_itemsite_id=itemsite_id)
+      WHERE ( (wooper_wo_id=wo_id)
+        AND   (wo_id=pWoid) );
+    ELSE
+      UPDATE xtmfg.wooper
+      SET wooper_scheduled = (wooper_scheduled::DATE + (pStartDate - wo_startdate))
+      FROM wo
+      WHERE ( (wooper_wo_id=wo_id)
+        AND   (wo_id=pWoid) );
+    END IF;
+
+--    Reschedule any womatl that is linked to wooper items
+--    and is set to be scheduled with the wooper in question
+    UPDATE womatl
+    SET womatl_duedate=wooper_scheduled
+    FROM xtmfg.wooper
+    WHERE ( (womatl_schedatwooper)
+     AND (womatl_wooper_id=wooper_id)
+     AND (womatl_wo_id=pWoid) );
+
+  END IF;
+
+-- Reschedule any womatl that is not linked to wooper items
+  UPDATE womatl
+  SET womatl_duedate=pStartDate
+  WHERE ( (NOT womatl_schedatwooper)
+   AND (womatl_wo_id=pWoid) );
+
+--  Reschedule the W/O
+  UPDATE wo
+  SET wo_startdate=pStartDate,
+      wo_duedate=pDueDate
+  WHERE (wo_id=pWoid);
+
+--  Do the same for the children
+  IF (changeChildren) THEN
+    SELECT MAX(changeWoDates(wo_id, (pStartDate - itemsite_leadtime), pStartDate, TRUE)) INTO returnCode
+    FROM wo, itemsite
+    WHERE ( (wo_itemsite_id=itemsite_id)
+     AND (wo_ordtype='W')
+     AND (wo_ordid=pWoid) );
+  END IF;
+
+  IF (returnCode IS NULL) THEN
+    returnCode := 0;
+  END IF;
+
+  RETURN returnCode;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/changewoproject.sql b/foundation-database/public/functions/changewoproject.sql
new file mode 100644 (file)
index 0000000..6ac5e90
--- /dev/null
@@ -0,0 +1,40 @@
+CREATE OR REPLACE FUNCTION changeWoProject(INTEGER, INTEGER, BOOLEAN) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWoid ALIAS FOR $1;
+  pPrjid ALIAS FOR $2;
+  changeChildren ALIAS FOR $3;
+  woStatus CHAR(1);
+  _result INTEGER;
+
+BEGIN
+
+  SELECT wo_status INTO woStatus
+  FROM wo
+  WHERE (wo_id=pWoid);
+
+  UPDATE wo
+  SET wo_prj_id=pPrjid
+  WHERE (wo_id=pWoid);
+
+  IF (woStatus = ''E'' AND changeChildren) THEN
+    _result := ( SELECT MIN(changeWoProject(wo_id, pPrjid, TRUE))
+                   FROM womatl, wo
+                  WHERE ((womatl_itemsite_id=wo_itemsite_id)
+                    AND (wo_ordtype=''W'')
+                    AND (womatl_wo_id=pWoid)
+                    AND (wo_ordid=pWoid)) );
+
+    UPDATE pr SET pr_prj_id=pPrjid
+      FROM womatl
+     WHERE ((womatl_wo_id=pWoid)
+       AND  (pr_order_type=''W'')
+       AND  (pr_order_id=womatl_id));
+  ELSE
+    _result = 1;
+  END IF;
+
+  RETURN _result;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/changewoqty.sql b/foundation-database/public/functions/changewoqty.sql
new file mode 100644 (file)
index 0000000..376435f
--- /dev/null
@@ -0,0 +1,81 @@
+CREATE OR REPLACE FUNCTION changeWoQty(INTEGER, NUMERIC, BOOLEAN) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWoid ALIAS FOR $1;
+  pQty ALIAS FOR $2;
+  changeChildren ALIAS FOR $3;
+  _r RECORD;
+  _result INTEGER := 1;
+
+BEGIN
+
+  SELECT wo_qtyord, wo_status, item_fractional INTO _r
+  FROM wo JOIN itemsite ON (itemsite_id=wo_itemsite_id)
+          JOIN item ON (item_id=itemsite_item_id)
+  WHERE (wo_id=pWoid);
+
+  IF (_r.wo_qtyord = pQty) THEN
+    RETURN 0;
+  END IF;
+
+  IF (NOT _r.wo_status IN ('O','E','R','I')) THEN
+    RETURN 1;
+  END IF;
+
+  IF (_r.wo_status IN ('R','I')) THEN
+    INSERT INTO evntlog (evntlog_evnttime, evntlog_username, evntlog_evnttype_id,
+                         evntlog_ordtype, evntlog_ord_id, evntlog_warehous_id, evntlog_number,
+                         evntlog_oldvalue, evntlog_newvalue)
+    SELECT CURRENT_TIMESTAMP, evntnot_username, evnttype_id,
+           'W', wo_id, itemsite_warehous_id, formatWoNumber(wo_id),
+           wo_qtyord, pQty
+    FROM evntnot, evnttype, itemsite, item, wo
+    WHERE ( (evntnot_evnttype_id=evnttype_id)
+     AND (evntnot_warehous_id=itemsite_warehous_id)
+     AND (wo_itemsite_id=itemsite_id)
+     AND (itemsite_item_id=item_id)
+     AND (evnttype_name='RWoQtyRequestChange')
+     AND (wo_id=pWoid) );
+
+     _result = 0;
+  END IF;
+
+  UPDATE wo
+  SET wo_qtyord=roundQty(_r.item_fractional, pQty)
+  WHERE (wo_id=pWoid);
+
+  UPDATE womatl
+  SET womatl_qtyreq=(womatl_qtyfxd + wo_qtyord * womatl_qtyper) * (1 + womatl_scrap)
+  FROM wo, itemsite
+  WHERE ((womatl_wo_id=wo_id)
+    AND  (womatl_itemsite_id=itemsite_id)
+    AND  (wo_id=pWoid));
+
+  IF (fetchMetricBool('Routings')) THEN
+
+      UPDATE xtmfg.wooper
+         SET wooper_rntime = CASE WHEN ((booitem_rnqtyper = 0) OR (booitem_invproduomratio = 0)) THEN 0
+                                  WHEN (NOT booitem_rnrpt) THEN 0
+                                  ELSE ( ( booitem_rntime /
+                                           booitem_rnqtyper /
+                                           booitem_invproduomratio ) * wo_qtyord )
+                             END
+        FROM xtmfg.booitem, wo
+       WHERE ((wooper_wo_id=wo_id)
+         AND  (wooper_booitem_id=booitem_id)
+         AND  (wo_id=pWoid));
+  END IF;
+
+  IF (changeChildren) THEN
+    _result := ( SELECT MIN(changeWoQty(wo_id, womatl_qtyreq, TRUE))
+                 FROM womatl, wo
+                 WHERE ((womatl_itemsite_id=wo_itemsite_id)
+                  AND (wo_ordtype='W')
+                  AND (womatl_wo_id=pWoid)
+                  AND (wo_ordid=pWoid)) );
+  END IF;
+
+  RETURN _result;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/characteristicstostring.sql b/foundation-database/public/functions/characteristicstostring.sql
new file mode 100644 (file)
index 0000000..d6852b5
--- /dev/null
@@ -0,0 +1,30 @@
+
+CREATE OR REPLACE FUNCTION characteristicsToString(TEXT, INTEGER, TEXT, TEXT) RETURNS TEXT AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pTargetType ALIAS FOR $1;
+  pTargetId ALIAS FOR $2;
+  pValKeySep ALIAS FOR $3;
+  pPairSep ALIAS FOR $4;
+  _string TEXT := '''';
+  _extra BOOLEAN := false;
+  _r RECORD;
+BEGIN
+  FOR _r IN SELECT char_name, charass_value
+              FROM charass, char
+             WHERE ((charass_char_id=char_id)
+               AND  (charass_target_type=pTargetType)
+               AND  (charass_target_id=pTargetId)) LOOP
+    IF(_extra) THEN
+      _string := _string || pPairSep;
+    END IF;
+    _extra := true;
+
+    _string := _string || _r.char_name || pValKeySep || _r.charass_value;
+  END LOOP;
+
+  RETURN _string;
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/checkcreditmemositeprivs.sql b/foundation-database/public/functions/checkcreditmemositeprivs.sql
new file mode 100644 (file)
index 0000000..9450404
--- /dev/null
@@ -0,0 +1,33 @@
+CREATE OR REPLACE FUNCTION checkCreditMemoSitePrivs(INTEGER) RETURNS BOOLEAN AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCmheadid ALIAS FOR $1;
+  _check    BOOLEAN;
+  _result   INTEGER;
+
+BEGIN
+
+  IF (NOT fetchMetricBool(''MultiWhs'')) THEN
+    RETURN true;
+  END IF;
+
+  IF (NOT fetchUsrPrefBool(''selectedSites'')) THEN
+    RETURN true;
+  END IF;
+
+  SELECT COALESCE(COUNT(*), 0) INTO _result
+    FROM ( SELECT cmitem_id
+             FROM cmitem JOIN itemsite ON (itemsite_id=cmitem_itemsite_id)
+            WHERE ( (cmitem_cmhead_id=pCmheadid)
+              AND   (itemsite_warehous_id NOT IN (SELECT usrsite_warehous_id
+                                                    FROM usrsite
+                                                   WHERE (usrsite_username=getEffectiveXtUser()))) )
+         ) AS data;
+  IF (_result > 0) THEN
+    RETURN false;
+  END IF;
+
+  RETURN true;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/checkdetailformatted.sql b/foundation-database/public/functions/checkdetailformatted.sql
new file mode 100644 (file)
index 0000000..2eb8458
--- /dev/null
@@ -0,0 +1,213 @@
+CREATE OR REPLACE FUNCTION checkDetailFormatted(INTEGER, INTEGER) RETURNS SETOF checkdata
+AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCheckheadid ALIAS FOR $1;
+  pMaxLines ALIAS FOR $2;
+  _row checkdata%ROWTYPE;
+  _checkhead RECORD;
+  _checkdetail RECORD;
+  _rowcount INTEGER := 0;
+  _page INTEGER := 1;
+  _docnumber TEXT := '';
+  _docreference TEXT := '';
+  _docdate TEXT := '';
+  _docamount TEXT := '';
+  _docdiscount TEXT := '';
+  _docnetamount TEXT := '';
+BEGIN
+
+-- Check header information
+  SELECT checkhead_number AS checknumber,
+         INITCAP(spellAmount(checkhead_amount, curr_id)) AS checkwords,
+         formatDate(checkhead_checkdate) AS checkdate,
+         formatMoney(checkhead_amount) AS checkamount,
+         curr_symbol AS checkcurrsymbol,
+         curr_abbr AS checkcurrabbr,
+         curr_name AS checkcurrname,
+         CASE WHEN checkhead_recip_type = 'C' THEN (SELECT cust_name
+                                                      FROM custinfo
+                                                     WHERE cust_id=checkhead_recip_id)
+              WHEN checkhead_recip_type = 'T' THEN (SELECT taxauth_name
+                                                      FROM taxauth
+                                                     WHERE taxauth_id=checkhead_recip_id)
+              WHEN checkhead_recip_type = 'V' THEN
+                                  COALESCE((SELECT vendaddr_name
+                                              FROM vendaddrinfo
+                                             WHERE((UPPER(vendaddr_code)='REMIT')
+                                               AND (vendaddr_vend_id=checkhead_recip_id))),
+                                           (SELECT vend_name
+                                              FROM vendinfo
+                                             WHERE(vend_id=checkhead_recip_id)))
+         END AS checkpayto,
+         formatAddr(CASE WHEN checkhead_recip_type = 'C' THEN
+                                                  (SELECT cntct_addr_id
+                                                   FROM cntct, custinfo
+                                                    WHERE((cust_cntct_id=cntct_id)
+                                                      AND (cust_id=checkhead_recip_id)))
+                         WHEN checkhead_recip_type = 'T' THEN 
+                                                  (SELECT taxauth_addr_id
+                                                     FROM taxauth
+                                                    WHERE(taxauth_id=checkhead_recip_id))
+                         WHEN checkhead_recip_type = 'V' THEN
+                                 COALESCE((SELECT vendaddr_addr_id
+                                             FROM vendaddrinfo
+                                            WHERE((UPPER(vendaddr_code)='REMIT')
+                                              AND (vendaddr_vend_id=checkhead_recip_id))),
+                                          (SELECT vend_addr_id
+                                             FROM vendinfo
+                                            WHERE(vend_id=checkhead_recip_id)))
+                    END) AS checkaddress,
+         checkhead_for AS checkmemo
+    INTO _checkhead
+    FROM checkhead, curr_symbol
+   WHERE((checkhead_curr_id = curr_id)
+     AND (checkhead_id=pCheckheadid) );
+  IF (NOT FOUND) THEN
+    RETURN;
+  END IF;
+
+  _row.checkdata_page := _page;
+  _row.checkdata_checknumber := _checkhead.checknumber;
+  _row.checkdata_checkwords := _checkhead.checkwords;
+  _row.checkdata_checkdate := _checkhead.checkdate;
+  _row.checkdata_checkamount := _checkhead.checkamount;
+  _row.checkdata_checkcurrsymbol := _checkhead.checkcurrsymbol;
+  _row.checkdata_checkcurrabbr := _checkhead.checkcurrabbr;
+  _row.checkdata_checkcurrname := _checkhead.checkcurrname;
+  _row.checkdata_checkpayto := _checkhead.checkpayto;
+  _row.checkdata_checkaddress := _checkhead.checkaddress;
+  _row.checkdata_checkmemo := _checkhead.checkmemo;
+
+-- Check item details
+  FOR _checkdetail IN 
+  SELECT  --VOUCHER-------------
+    1 AS ord,
+    1 AS sequence_value,
+    checkitem_invcnumber,
+    checkitem_ponumber,
+    formatMoney(checkitem_amount) AS docnetamount,
+    'Invoice#: ' || vohead_invcnumber AS docnumber,
+    formatDate(vohead_docdate) AS docdate,
+    vohead_reference AS docreference,
+    'Voucher: ' || checkitem_vouchernumber AS vouchernumber,
+    formatMoney(apopen_amount) AS docamount,
+    formatMoney(checkitem_discount) AS docdiscount
+  FROM checkitem, vohead, apopen
+  WHERE ((checkitem_checkhead_id=pCheckheadid)
+    AND  (checkitem_vouchernumber = vohead_number)
+    AND  (apopen_docnumber = checkitem_vouchernumber)
+    AND  (apopen_doctype = 'V'))
+  
+  UNION
+  
+  SELECT --DEBIT MEMO -------------------------
+    2 AS ord,
+    1 AS sequence_value,
+    checkitem_invcnumber,
+    checkitem_ponumber,
+    formatMoney(checkitem_amount) AS f_amount,
+    'Debit Memo PO#: ' || checkitem_ponumber AS doc_number,
+    ''  AS f_docdate,
+    'Debit Memo: ' || checkitem_vouchernumber AS doc_reference,
+    checkitem_vouchernumber AS vouchernumber,
+    formatMoney(apopen_amount) AS amount,
+    formatMoney(checkitem_discount) AS disc_cred
+  FROM checkitem, apopen
+  WHERE ((checkitem_checkhead_id=pCheckheadid)
+    AND  (checkitem_vouchernumber = apopen_docnumber)
+    AND  (apopen_doctype = 'D'))
+  
+  UNION
+  
+  SELECT --CREDITs--------------------------
+    3 AS ord,
+    1 AS sequence_value,
+    checkitem_invcnumber,
+    checkitem_ponumber,
+    formatMoney(checkitem_amount) AS f_amount,
+    'Invoice#: ' || vohead_invcnumber AS doc_number,
+    formatDate(vohead_docdate) AS f_docdate,
+    'Credit Applied: ' || apapply_source_doctype || ' ' ||
+                          apapply_source_docnumber AS doc_reference,
+    'Voucher ' || checkitem_vouchernumber AS vouchernumber,
+    '' AS amount,
+    formatMoney((apapply_amount)) AS disc_cred
+  FROM checkitem, vohead, apapply
+  WHERE ((checkitem_checkhead_id=pCheckheadid)
+    AND  (checkitem_vouchernumber = vohead_number)
+    AND  (apapply_target_docnumber = checkitem_vouchernumber ))
+  
+  UNION 
+  
+  SELECT --NON-VENDOR-----------------------
+    4 AS ord,
+    1 AS sequence_value,
+    checkitem_invcnumber,
+    checkitem_ponumber,
+    formatMoney(checkitem_amount) AS f_amount,
+    checkitem_invcnumber AS doc_number,
+    formatDate(checkitem_docdate) AS f_docdate,
+    '' AS doc_reference,
+    '' AS vouchernumber,
+    '' AS amount,
+    '' AS disc_cred
+  FROM checkhead LEFT OUTER JOIN
+       checkitem ON (checkitem_checkhead_id=checkhead_id)
+  WHERE ((checkhead_id=pCheckheadid) 
+    AND  (checkhead_recip_type != 'V')) LOOP
+    IF (_rowcount = pMaxLines) THEN
+      _row.checkdata_docnumber := _docnumber;
+      _row.checkdata_docreference := _docreference;
+      _row.checkdata_docdate := _docdate;
+      _row.checkdata_docamount := _docamount;
+      _row.checkdata_docdiscount := _docdiscount;
+      _row.checkdata_docnetamount := _docnetamount;
+      RETURN NEXT _row;
+
+-- update/reset some variables
+      _rowcount = 0;
+      _page := _page + 1;
+      _docnumber := '';
+      _docreference := '';
+      _docdate := '';
+      _docamount := '';
+      _docdiscount := '';
+      _docnetamount := '';
+
+      _row.checkdata_page := _page;
+      _row.checkdata_checknumber := _checkhead.checknumber;
+      _row.checkdata_checkwords := 'VOID VOID PAGE '||_page||' OF CHECK #'||_checkhead.checknumber||' VOID VOID';
+      _row.checkdata_checkdate := 'VOID VOID VOID';
+      _row.checkdata_checkamount := 'VOID VOID VOID';
+      --_row.checkdata_checkcurrsymbol := _checkhead.checkcurrsymbol;
+      --_row.checkdata_checkcurrabbr := _checkhead.checkcurrabbr;
+      --_row.checkdata_checkcurrname := _checkhead.checkcurrname;
+      _row.checkdata_checkpayto := 'VOID VOID VOID';
+      --_row.checkdata_checkaddress := _checkhead.checkaddress;
+      _row.checkdata_checkmemo := 'VOID VOID PAGE '||_page||' OF CHECK #'||_checkhead.checknumber||' VOID VOID';
+    END IF;
+
+    _rowcount := _rowcount + 1;
+    _docnumber := _docnumber || _checkdetail.docnumber || E'\n';
+    _docreference := _docreference || _checkdetail.docreference || E'\n';
+    _docdate := _docdate || _checkdetail.docdate || E'\n';
+    _docamount := _docamount || _checkdetail.docamount || E'\n';
+    _docdiscount := _docdiscount || _checkdetail.docdiscount || E'\n';
+    _docnetamount := _docnetamount || _checkdetail.docnetamount || E'\n';
+  END LOOP;
+
+  _row.checkdata_docnumber := _docnumber;
+  _row.checkdata_docreference := _docreference;
+  _row.checkdata_docdate := _docdate;
+  _row.checkdata_docamount := _docamount;
+  _row.checkdata_docdiscount := _docdiscount;
+  _row.checkdata_docnetamount := _docnetamount;
+
+  RETURN NEXT _row;
+  RETURN;
+END;
+$$
+LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/checkinvoicesiteprivs.sql b/foundation-database/public/functions/checkinvoicesiteprivs.sql
new file mode 100644 (file)
index 0000000..47f1cac
--- /dev/null
@@ -0,0 +1,34 @@
+CREATE OR REPLACE FUNCTION checkInvoiceSitePrivs(INTEGER) RETURNS BOOLEAN STABLE AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pInvcheadid ALIAS FOR $1;
+  _check    BOOLEAN;
+  _result   INTEGER;
+
+BEGIN
+
+  IF (NOT fetchMetricBool(''MultiWhs'')) THEN
+    RETURN true;
+  END IF;
+
+  IF (NOT fetchUsrPrefBool(''selectedSites'')) THEN
+    RETURN true;
+  END IF;
+
+  SELECT COALESCE(COUNT(*), 0) INTO _result
+    FROM ( SELECT invcitem_id
+             FROM invcitem
+            WHERE ( (invcitem_invchead_id=pInvcheadid)
+              AND   (invcitem_warehous_id <> -1)
+              AND   (invcitem_warehous_id NOT IN (SELECT usrsite_warehous_id
+                                                    FROM usrsite
+                                                   WHERE (usrsite_username=getEffectiveXtUser()))) )
+         ) AS data;
+  IF (_result > 0) THEN
+    RETURN false;
+  END IF;
+
+  RETURN true;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/checkpositeprivs.sql b/foundation-database/public/functions/checkpositeprivs.sql
new file mode 100644 (file)
index 0000000..e9a82be
--- /dev/null
@@ -0,0 +1,41 @@
+CREATE OR REPLACE FUNCTION checkPOSitePrivs(INTEGER) RETURNS BOOLEAN STABLE AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pPoheadid ALIAS FOR $1;
+  _check    BOOLEAN;
+  _result   INTEGER;
+
+BEGIN
+
+  IF (NOT fetchMetricBool(''MultiWhs'')) THEN
+    RETURN true;
+  END IF;
+
+  IF (NOT fetchUsrPrefBool(''selectedSites'')) THEN
+    RETURN true;
+  END IF;
+
+  SELECT COALESCE(COUNT(*), 0) INTO _result
+    FROM ( SELECT poitem_id
+             FROM poitem, itemsite
+            WHERE ( (poitem_pohead_id=pPoheadid)
+              AND   (poitem_itemsite_id=itemsite_id)
+              AND   (itemsite_warehous_id NOT IN (SELECT usrsite_warehous_id
+                                                    FROM usrsite
+                                                   WHERE (usrsite_username=getEffectiveXtUser()))) )
+           UNION
+           SELECT pohead_warehous_id
+             FROM pohead
+            WHERE ( (pohead_id=pPoheadid)
+              AND   (pohead_warehous_id NOT IN (SELECT usrsite_warehous_id
+                                                  FROM usrsite
+                                                 WHERE (usrsite_username=getEffectiveXtUser()))) )
+         ) AS data;
+  IF (_result > 0) THEN
+    RETURN false;
+  END IF;
+
+  RETURN true;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/checkprivilege.sql b/foundation-database/public/functions/checkprivilege.sql
new file mode 100644 (file)
index 0000000..2a41620
--- /dev/null
@@ -0,0 +1,30 @@
+CREATE OR REPLACE FUNCTION checkPrivilege(text) RETURNS BOOLEAN STABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pPrivilege ALIAS FOR $1;
+  _result TEXT;
+BEGIN
+  SELECT priv_id INTO _result
+    FROM priv, grppriv, usrgrp
+   WHERE((usrgrp_grp_id=grppriv_grp_id)
+     AND (grppriv_priv_id=priv_id)
+     AND (priv_name=pPrivilege)
+     AND (usrgrp_username=getEffectiveXtUser()));
+  IF (FOUND) THEN
+    RETURN true;
+  END IF;
+
+  SELECT priv_id INTO _result
+  FROM priv, usrpriv
+  WHERE ((priv_id=usrpriv_priv_id)
+  AND (priv_name=pPrivilege)
+  AND (usrpriv_username=getEffectiveXtUser()));
+  
+  IF (FOUND) THEN
+    RETURN true;
+  ELSE
+    RETURN false;
+  END IF;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/checkquotesiteprivs.sql b/foundation-database/public/functions/checkquotesiteprivs.sql
new file mode 100644 (file)
index 0000000..9a4f687
--- /dev/null
@@ -0,0 +1,55 @@
+CREATE OR REPLACE FUNCTION checkQuoteSitePrivs(INTEGER) RETURNS BOOLEAN STABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pQuheadid ALIAS FOR $1;
+
+BEGIN
+
+  RETURN checkQuoteSitePrivs(pQuheadid, NULL);
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION checkQuoteSitePrivs(INTEGER, INTEGER) RETURNS BOOLEAN STABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pQuheadid ALIAS FOR $1;
+  pWarehousid ALIAS FOR $2;
+  _result   INTEGER := 0;
+
+BEGIN
+
+--  RAISE NOTICE 'checkQuoteSitePrivs, pQuheadid = %', pQuheadid;
+--  RAISE NOTICE 'checkQuoteSitePrivs, pWarehousid = %', pWarehousid;
+
+  IF (NOT fetchMetricBool('MultiWhs')) THEN
+    RETURN true;
+  END IF;
+
+  IF ( (NOT fetchUsrPrefBool('selectedSites')) AND (pWarehousid IS NULL) ) THEN
+    RETURN true;
+  END IF;
+
+  IF (pWarehousid IS NULL) THEN
+    SELECT COALESCE(COUNT(*), 0) INTO _result
+    FROM quitem JOIN itemsite ON (itemsite_id=quitem_itemsite_id)
+                JOIN site() ON (warehous_id=itemsite_warehous_id)
+    WHERE (quitem_quhead_id=pQuheadid);
+  ELSE
+    SELECT COALESCE(COUNT(*), 0) INTO _result
+    FROM quitem JOIN itemsite ON (itemsite_id=quitem_itemsite_id)
+                JOIN site() ON (warehous_id=itemsite_warehous_id)
+    WHERE ( (quitem_quhead_id=pQuheadid)
+      AND   (itemsite_warehous_id=pWarehousid) );
+  END IF;
+
+  IF (_result > 0) THEN
+    RETURN true;
+  END IF;
+
+  RETURN false;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/checkrasiteprivs.sql b/foundation-database/public/functions/checkrasiteprivs.sql
new file mode 100644 (file)
index 0000000..7e5a1d9
--- /dev/null
@@ -0,0 +1,42 @@
+CREATE OR REPLACE FUNCTION checkRASitePrivs(INTEGER) RETURNS BOOLEAN STABLE AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pRaheadid ALIAS FOR $1;
+  _check    BOOLEAN;
+  _result   INTEGER;
+
+BEGIN
+
+  IF (NOT fetchMetricBool(''MultiWhs'')) THEN
+    RETURN true;
+  END IF;
+
+  IF (NOT fetchUsrPrefBool(''selectedSites'')) THEN
+    RETURN true;
+  END IF;
+
+  SELECT COALESCE(COUNT(*), 0) INTO _result
+    FROM ( SELECT raitem_id
+             FROM raitem, itemsite
+            WHERE ( (raitem_rahead_id=pRaheadid)
+              AND   (raitem_itemsite_id=itemsite_id)
+              AND   (itemsite_warehous_id NOT IN (SELECT usrsite_warehous_id
+                                                    FROM usrsite
+                                                   WHERE (usrsite_username=getEffectiveXtUser()))) )
+           UNION
+           SELECT raitem_id
+             FROM raitem, itemsite
+            WHERE ( (raitem_rahead_id=pRaheadid)
+              AND   (raitem_coitem_itemsite_id=itemsite_id)
+              AND   (itemsite_warehous_id NOT IN (SELECT usrsite_warehous_id
+                                                  FROM usrsite
+                                                 WHERE (usrsite_username=getEffectiveXtUser()))) )
+         ) AS data;
+  IF (_result > 0) THEN
+    RETURN false;
+  END IF;
+
+  RETURN true;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/checkshipmentsiteprivs.sql b/foundation-database/public/functions/checkshipmentsiteprivs.sql
new file mode 100644 (file)
index 0000000..fbb3e45
--- /dev/null
@@ -0,0 +1,44 @@
+CREATE OR REPLACE FUNCTION checkShipmentSitePrivs(INTEGER) RETURNS BOOLEAN AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pShipheadid ALIAS FOR $1;
+  _check    BOOLEAN;
+  _result   INTEGER;
+
+BEGIN
+
+  IF (NOT fetchMetricBool(''MultiWhs'')) THEN
+    RETURN true;
+  END IF;
+
+  IF (NOT fetchUsrPrefBool(''selectedSites'')) THEN
+    RETURN true;
+  END IF;
+
+  SELECT COALESCE(COUNT(*), 0) INTO _result
+    FROM ( SELECT coitem_id
+             FROM shipitem, coitem, itemsite
+            WHERE ( (shipitem_shiphead_id=pShipheadid)
+              AND   (coitem_id=shipitem_orderitem_id)
+              AND   (coitem_itemsite_id=itemsite_id)
+              AND   (itemsite_warehous_id NOT IN (SELECT usrsite_warehous_id
+                                                    FROM usrsite
+                                                   WHERE (usrsite_username=getEffectiveXtUser()))) )
+           UNION
+           SELECT cohead_warehous_id
+             FROM shipitem, coitem, cohead
+            WHERE ( (shipitem_shiphead_id=pShipheadid)
+              AND   (coitem_id=shipitem_orderitem_id)
+              AND   (cohead_id=coitem_cohead_id)
+              AND   (cohead_warehous_id NOT IN (SELECT usrsite_warehous_id
+                                                  FROM usrsite
+                                                 WHERE (usrsite_username=getEffectiveXtUser()))) )
+         ) AS data;
+  IF (_result > 0) THEN
+    RETURN false;
+  END IF;
+
+  RETURN true;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/checksositeprivs.sql b/foundation-database/public/functions/checksositeprivs.sql
new file mode 100644 (file)
index 0000000..62d767b
--- /dev/null
@@ -0,0 +1,41 @@
+CREATE OR REPLACE FUNCTION checkSOSitePrivs(INTEGER) RETURNS BOOLEAN STABLE AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSoheadid ALIAS FOR $1;
+  _check    BOOLEAN;
+  _result   INTEGER;
+
+BEGIN
+
+  IF (NOT fetchMetricBool(''MultiWhs'')) THEN
+    RETURN true;
+  END IF;
+
+  IF (NOT fetchUsrPrefBool(''selectedSites'')) THEN
+    RETURN true;
+  END IF;
+
+  SELECT COALESCE(COUNT(*), 0) INTO _result
+    FROM ( SELECT coitem_id
+             FROM coitem, itemsite
+            WHERE ( (coitem_cohead_id=pSoheadid)
+              AND   (coitem_itemsite_id=itemsite_id)
+              AND   (itemsite_warehous_id NOT IN (SELECT usrsite_warehous_id
+                                                    FROM usrsite
+                                                   WHERE (usrsite_username=getEffectiveXtUser()))) )
+           UNION
+           SELECT cohead_warehous_id
+             FROM cohead
+            WHERE ( (cohead_id=pSoheadid)
+              AND   (cohead_warehous_id NOT IN (SELECT usrsite_warehous_id
+                                                  FROM usrsite
+                                                 WHERE (usrsite_username=getEffectiveXtUser()))) )
+         ) AS data;
+  IF (_result > 0) THEN
+    RETURN false;
+  END IF;
+
+  RETURN true;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/checkvouchersiteprivs.sql b/foundation-database/public/functions/checkvouchersiteprivs.sql
new file mode 100644 (file)
index 0000000..a844b23
--- /dev/null
@@ -0,0 +1,43 @@
+CREATE OR REPLACE FUNCTION checkVoucherSitePrivs(INTEGER) RETURNS BOOLEAN STABLE AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pVoheadid ALIAS FOR $1;
+  _check    BOOLEAN;
+  _result   INTEGER;
+
+BEGIN
+
+  IF (NOT fetchMetricBool(''MultiWhs'')) THEN
+    RETURN true;
+  END IF;
+
+  IF (NOT fetchUsrPrefBool(''selectedSites'')) THEN
+    RETURN true;
+  END IF;
+
+  SELECT COALESCE(COUNT(*), 0) INTO _result
+    FROM ( SELECT voitem_id
+             FROM voitem, poitem, itemsite
+            WHERE ( (voitem_vohead_id=pVoheadid)
+              AND   (poitem_id=voitem_poitem_id)
+              AND   (poitem_itemsite_id=itemsite_id)
+              AND   (itemsite_warehous_id NOT IN (SELECT usrsite_warehous_id
+                                                    FROM usrsite
+                                                   WHERE (usrsite_username=getEffectiveXtUser()))) )
+           UNION
+           SELECT pohead_warehous_id
+             FROM vohead, pohead
+            WHERE ( (vohead_id=pVoheadid)
+              AND   (pohead_id=vohead_pohead_id)
+              AND   (pohead_warehous_id NOT IN (SELECT usrsite_warehous_id
+                                                  FROM usrsite
+                                                 WHERE (usrsite_username=getEffectiveXtUser()))) )
+         ) AS data;
+  IF (_result > 0) THEN
+    RETURN false;
+  END IF;
+
+  RETURN true;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/clearnumberissue.sql b/foundation-database/public/functions/clearnumberissue.sql
new file mode 100644 (file)
index 0000000..cbd5878
--- /dev/null
@@ -0,0 +1,78 @@
+CREATE OR REPLACE FUNCTION clearNumberIssue(psequence TEXT, pnumber INTEGER) RETURNS BOOLEAN AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  __seqiss     seqiss[];
+  __newiss     seqiss[] := ARRAY[]::seqiss[];
+  _i           INTEGER;
+  _result      BOOLEAN := FALSE;
+  _interval    TEXT := fetchMetricText('NumberIssueResetIntervalDays') || ' day';
+  _number      INTEGER;
+BEGIN
+  -- get the sequence to update
+  SELECT orderseq_seqiss INTO __seqiss
+  FROM orderseq
+  WHERE (orderseq_name=psequence);
+
+  IF (NOT FOUND) THEN
+    RAISE EXCEPTION 'Invalid orderseq_name %', psequence;
+  END IF;
+
+  IF(ARRAY_LENGTH(COALESCE(__seqiss,__newiss),1) IS NULL) THEN
+    RETURN FALSE;
+  END IF;
+
+  -- build a new array sans the number we are releasing
+  FOR _i IN 1..ARRAY_LENGTH(__seqiss,1)
+  LOOP
+    IF((__seqiss[_i]).seqiss_number = pnumber) THEN
+      _result = TRUE;
+    -- don't bother re-adding stale numbers
+    ELSIF (now() - _interval::INTERVAL > (__seqiss[_i]).seqiss_time) THEN
+      IF (_number IS NULL) THEN
+        _number := (__seqiss[_i]).seqiss_number;
+      ELSE 
+        _number := LEAST((__seqiss[_i]).seqiss_number, _number);
+      END IF;
+    ELSE
+      __newiss := __newiss || __seqiss[_i];
+    END IF;
+  END LOOP;
+
+  -- update the order sequence with the result
+  UPDATE orderseq SET
+    orderseq_seqiss = __newiss
+  WHERE (orderseq_name=psequence);
+
+  -- reset to any cleared stale number
+  IF(_number IS NOT NULL) THEN
+    UPDATE orderseq SET
+      orderseq_number = _number
+    WHERE (orderseq_name=psequence);
+  END IF;
+  
+  RETURN _result;
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION clearNumberIssue(psequence TEXT, pnumber TEXT) RETURNS BOOLEAN AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _castpnumber  INTEGER;
+BEGIN
+  -- for now, order numbers in the database are text but usually
+  -- string representations of integers. allow for the occasional non-integer.
+  BEGIN
+    _castpnumber  := CAST(pnumber AS INTEGER);
+  EXCEPTION WHEN cannot_coerce OR
+                 invalid_text_representation
+  THEN
+    RAISE DEBUG 'clearNumberIssue(%, %) received an unexpected pnumber',
+                  psequence, pnumber;
+    RETURN FALSE;
+  END;
+
+  RETURN clearNumberIssue(psequence, _castpnumber);
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/clearpayment.sql b/foundation-database/public/functions/clearpayment.sql
new file mode 100644 (file)
index 0000000..79c16a1
--- /dev/null
@@ -0,0 +1,17 @@
+
+CREATE OR REPLACE FUNCTION clearPayment(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pApselectid ALIAS FOR $1;
+
+BEGIN
+
+  DELETE FROM apselect
+  WHERE (apselect_id=pApselectid);
+
+  RETURN 1;
+
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/closeaccountingperiod.sql b/foundation-database/public/functions/closeaccountingperiod.sql
new file mode 100644 (file)
index 0000000..b2ac699
--- /dev/null
@@ -0,0 +1,139 @@
+
+CREATE OR REPLACE FUNCTION closeAccountingPeriod(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pPeriodid ALIAS FOR $1;
+  _r RECORD;
+  _nextPeriodid INTEGER;
+  _trialbalid INTEGER;
+  _ending NUMERIC;
+  _currYear INTEGER;
+  _nextYear INTEGER;
+BEGIN
+
+--  Bypass error checking is this the the initial period
+  IF ( NOT ( SELECT period_initial
+             FROM period
+             WHERE (period_id=pPeriodid) ) ) THEN
+
+--  Check to make use that the period is not already closed
+    IF ( ( SELECT period_closed
+           FROM period
+           WHERE (period_id=pPeriodid) ) ) THEN
+      RETURN -1;
+    END IF;
+
+--  Make sure that the day before this period belongs to another period
+    SELECT prev.period_id AS periodid, prev.period_closed AS closed INTO _r
+    FROM period AS prev, period AS curr
+    WHERE ( (prev.period_end = (curr.period_start - 1))
+     AND (curr.period_id=pPeriodid) );
+    IF (NOT FOUND) THEN
+      RETURN -2;
+    END IF;
+
+--  Make sure that the previous period is closed
+    IF (NOT _r.closed) THEN
+      RETURN -3;
+    END IF;
+
+  END IF;
+
+--  Make sure that there the next period is defined
+  SELECT next.period_id INTO _nextPeriodid
+  FROM period AS next, period AS curr
+  WHERE ( (next.period_start = (curr.period_end + 1))
+   AND (curr.period_id=pPeriodid) );
+  IF (NOT FOUND) THEN
+    RETURN -4;
+  END IF;
+
+--  Make sure that the user is not trying to prematurely close the Period
+  IF ( ( SELECT (period_end >= CURRENT_DATE)
+         FROM period
+         WHERE (period_id=pPeriodid) ) ) THEN
+    RETURN -5;
+  END IF;
+
+  SELECT yearperiod_id INTO _currYear
+    FROM yearperiod, period
+   WHERE ((period_end BETWEEN yearperiod_start and yearperiod_end)
+     AND  (period_id=pPeriodid));
+  IF (NOT FOUND) THEN
+    _currYear := -1;
+  END IF;
+
+  SELECT yearperiod_id INTO _nextYear
+    FROM yearperiod, period
+   WHERE ((period_end BETWEEN yearperiod_start and yearperiod_end)
+     AND  (period_id=_nextPeriodid));
+  IF (NOT FOUND) THEN
+    RETURN -6;
+  END IF;
+
+--  Walk through the entire COA, calculating the ending balance and pushing
+--  it to the beginning balance for the next period
+  FOR _r IN SELECT accnt_id, accnt_type IN (''E'', ''R'') AS revexp,
+                   trialbal_id, trialbal_beginning,
+                   trialbal_credits, trialbal_debits
+            FROM accnt LEFT OUTER JOIN trialbal ON ( (trialbal_accnt_id=accnt_id) AND (trialbal_period_id=pPeriodid) )
+            ORDER BY accnt_id LOOP
+    IF (_r.trialbal_id IS NULL) THEN
+      _ending = 0;
+
+      INSERT INTO trialbal
+      ( trialbal_period_id, trialbal_accnt_id,
+        trialbal_beginning, trialbal_ending, trialbal_dirty,
+        trialbal_credits, trialbal_debits )
+      VALUES
+      ( pPeriodid, _r.accnt_id,
+        0, 0, FALSE,
+        0, 0 );
+    ELSE
+      _ending = (_r.trialbal_beginning - _r.trialbal_debits + _r.trialbal_credits);
+
+      UPDATE trialbal
+      SET trialbal_ending=_ending,
+          trialbal_dirty = FALSE
+      WHERE (trialbal_id=_r.trialbal_id);
+
+      PERFORM forwardUpdateTrialBalance(_r.trialbal_id);
+    END IF;
+
+    IF (_r.revexp AND _currYear != _nextYear) THEN
+      _ending := 0;
+    END IF;
+
+--  Find the trialbal record for the next period
+    SELECT trialbal_id INTO _trialbalid
+    FROM trialbal
+    WHERE ( (trialbal_period_id=_nextPeriodid)
+     AND (trialbal_accnt_id=_r.accnt_id) );
+    IF (FOUND) THEN
+      UPDATE trialbal
+      SET trialbal_beginning = (_ending + trialbal_yearend),
+          trialbal_ending = (_ending + trialbal_yearend - trialbal_debits + trialbal_credits)
+      WHERE (trialbal_id=_trialbalid);
+    ELSE
+      INSERT INTO trialbal
+      ( trialbal_period_id, trialbal_accnt_id,
+        trialbal_beginning, trialbal_ending, trialbal_dirty,
+        trialbal_credits, trialbal_debits )
+      VALUES(_nextPeriodid, _r.accnt_id,
+             _ending, _ending, TRUE,
+             0, 0 );
+    END IF;
+
+  END LOOP;
+
+--  Set the period_closed flag
+  UPDATE period
+  SET period_closed=TRUE
+  WHERE (period_id=pPeriodid);
+
+  RETURN pPeriodid;
+
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/closeaccountingyearperiod.sql b/foundation-database/public/functions/closeaccountingyearperiod.sql
new file mode 100644 (file)
index 0000000..54ddc8e
--- /dev/null
@@ -0,0 +1,62 @@
+
+CREATE OR REPLACE FUNCTION closeAccountingYearPeriod(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pYearPeriodid ALIAS FOR $1;
+  _result INTEGER;
+BEGIN
+
+--  Check to make sure that the yearperiod is not already closed
+  IF ( ( SELECT yearperiod_closed
+           FROM yearperiod
+          WHERE (yearperiod_id=pYearPeriodid) ) ) THEN
+    RETURN -1;
+  END IF;
+
+  IF ( ( SELECT (count(period_id) > 0)
+           FROM period
+          WHERE ((period_yearperiod_id=pYearPeriodid)
+           AND (NOT period_closed)) ) ) THEN
+    RETURN -10;
+  END IF;
+
+  IF ( ( SELECT (count(yearperiod_id) > 0)
+           FROM yearperiod
+          WHERE ((yearperiod_end< (
+            SELECT yearperiod_end 
+            FROM yearperiod 
+            WHERE (yearperiod_id=pYearPeriodId))
+          )
+           AND (NOT yearperiod_closed)) ) ) THEN
+    RETURN -11;
+  END IF;
+
+--  Should we check for a previous yearperiod existing already ?
+--  If so then we should return -2 if one does not.
+
+--  If we did the previous yearperiod we should check to make sure that
+--  it is also closed. Returning -3 if it is not.
+
+--  Make sure that the user is not trying to prematurely close the YearPeriod
+  IF ( ( SELECT (yearperiod_end >= CURRENT_DATE)
+           FROM yearperiod
+          WHERE (yearperiod_id=pYearPeriodid) ) ) THEN
+    RETURN -5;
+  END IF;
+
+--  Update the year end Retained Earnings
+  SELECT updateRetainedEarnings(pYearPeriodid) INTO _result;
+  IF (_result < 0) THEN
+    RETURN _result;
+  END IF;
+
+  UPDATE yearperiod
+    SET yearperiod_closed = TRUE
+  WHERE yearperiod_id = pYearPeriodid;
+
+  RETURN 0;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/closepo.sql b/foundation-database/public/functions/closepo.sql
new file mode 100644 (file)
index 0000000..91f511c
--- /dev/null
@@ -0,0 +1,21 @@
+CREATE OR REPLACE FUNCTION closePo(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pPoheadid ALIAS FOR $1;
+
+BEGIN
+
+  UPDATE poitem
+  SET poitem_status='C'
+  WHERE (poitem_pohead_id=pPoheadid);
+
+-- _poitemTrigger will close pohead when the last poitem is closed
+--  UPDATE pohead
+--  SET pohead_status='C'
+--  WHERE (pohead_id=pPoheadid);
+
+  RETURN 1;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/closewo.sql b/foundation-database/public/functions/closewo.sql
new file mode 100644 (file)
index 0000000..1afed4f
--- /dev/null
@@ -0,0 +1,120 @@
+CREATE OR REPLACE FUNCTION closeWo(INTEGER, BOOLEAN) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWoid ALIAS FOR $1;
+  pPostMaterialVariances ALIAS FOR $2;
+
+BEGIN
+  
+  RETURN closeWo(pWoid, pPostMaterialVariances, CURRENT_DATE);
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION closeWo(INTEGER, BOOLEAN, DATE) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWoid ALIAS FOR $1;
+  pPostMaterialVariances ALIAS FOR $2;
+  pTransDate ALIAS FOR $3;
+  _woNumber TEXT;
+  _check CHAR;
+  _itemlocSeries INTEGER := 0;
+
+BEGIN
+
+  --If this is item type Job then we cannot close here
+  SELECT itemsite_costmethod INTO _check
+  FROM wo,itemsite
+  WHERE ((wo_id=pWoid)
+  AND (wo_itemsite_id=itemsite_id)
+  AND (itemsite_costmethod = 'J'));
+  IF (FOUND) THEN
+    RAISE EXCEPTION 'Work orders for Job items are closed when all quantities are shipped';
+  END IF;
+
+  SELECT formatWoNumber(pWoid) INTO _woNumber;
+
+-- If there are any tools issued on this job then we cannot close here
+  IF ( SELECT (count(*) > 0)
+       FROM womatl
+       JOIN itemsite ON (womatl_itemsite_id=itemsite_id)
+       JOIN item ON ((itemsite_item_id=item_id) AND (item_type='T'))
+       WHERE ((womatl_wo_id=pWoid)
+         AND  (womatl_qtyiss > 0)) ) THEN
+    RAISE EXCEPTION 'All Tools must be returned before the W/O can be closed';
+  END IF;
+
+--  Distribute any remaining wo_wipvalue to G/L - debit Inventory Cost, credit WIP
+  PERFORM insertGLTransaction( 'W/O', 'WO', _woNumber, ('Manufacturing Inventory Cost Variance for ' || item_number),
+                               getPrjAccntId(wo_prj_id, costcat_wip_accnt_id), 
+                               getPrjAccntId(wo_prj_id, costcat_invcost_accnt_id), -1,
+                               COALESCE(wo_wipvalue, 0), pTransDate )
+  FROM wo, itemsite, item, costcat
+  WHERE ( (wo_itemsite_id=itemsite_id)
+   AND (itemsite_item_id=item_id)
+   AND (itemsite_costcat_id=costcat_id)
+   AND (wo_id=pWoid) );
+
+--  Distribute any remaining wo_brdvalue to G/L - debit Inventory Cost, credit WIP
+  PERFORM insertGLTransaction( 'W/O', 'WO', _woNumber, ('Breeder Inventory Cost Variance for ' || item_number),
+                               getPrjAccntId(wo_prj_id, costcat_wip_accnt_id),
+                               CASE WHEN(itemsite_costmethod='A') THEN costcat_asset_accnt_id
+                                    ELSE getPrjAccntId(wo_prj_id, costcat_invcost_accnt_id)
+                               END,
+                               -1,
+                               COALESCE(wo_brdvalue, 0), pTransDate )
+  FROM wo, itemsite, item, costcat
+  WHERE ( (wo_itemsite_id=itemsite_id)
+   AND (itemsite_item_id=item_id)
+   AND (itemsite_costcat_id=costcat_id)
+   AND (wo_id=pWoid) );
+
+--  Don't bother with posting variances if the qtyrcv is 0 as
+--  they are meaningless.
+  IF ( ( SELECT wo_qtyrcv
+         FROM wo
+         WHERE (wo_id=pWoid) ) > 0 ) THEN
+
+    IF (pPostMaterialVariances) THEN
+--  Post womatl variances
+    INSERT INTO womatlvar ( womatlvar_number, womatlvar_subnumber, womatlvar_posted,
+        womatlvar_parent_itemsite_id, womatlvar_component_itemsite_id,
+        womatlvar_qtyord, womatlvar_qtyrcv,
+        womatlvar_qtyiss, womatlvar_qtyfxd, womatlvar_qtyper,
+        womatlvar_scrap, womatlvar_wipscrap, womatlvar_bomitem_id,
+        womatlvar_notes, womatlvar_ref )
+      SELECT wo_number, wo_subnumber, pTransDate,
+             wo_itemsite_id, womatl_itemsite_id,
+             wo_qtyord, wo_qtyrcv,
+             itemuomtouom(itemsite_item_id, womatl_uom_id, NULL, womatl_qtyiss),
+             itemuomtouom(itemsite_item_id, womatl_uom_id, NULL, womatl_qtyfxd),
+             itemuomtouom(itemsite_item_id, womatl_uom_id, NULL, womatl_qtyper),
+             womatl_scrap,
+             itemuomtouom(itemsite_item_id, womatl_uom_id, NULL, womatl_qtywipscrap),
+             womatl_bomitem_id,
+             womatl_notes, womatl_ref             
+      FROM wo, womatl, itemsite, item
+      WHERE ((womatl_wo_id=wo_id)
+       AND (womatl_itemsite_id=itemsite_id)
+       AND (itemsite_item_id=item_id)
+       AND (item_type <> 'T')      
+       AND (wo_id=pWoid));
+    END IF;
+  END IF;
+
+--  Delete any P/R's created for this W/O
+  PERFORM deletePr('W', womatl_id)
+  FROM womatl
+  WHERE (womatl_wo_id=pWoid);
+
+  UPDATE wo
+  SET wo_wipvalue = 0, wo_brdvalue=0,
+      wo_status='C'
+  WHERE (wo_id=pWoid);
+
+  RETURN 1;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/cntct.sql b/foundation-database/public/functions/cntct.sql
new file mode 100644 (file)
index 0000000..38f80ab
--- /dev/null
@@ -0,0 +1,39 @@
+CREATE OR REPLACE FUNCTION cntct() RETURNS SETOF cntct AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _row cntct%ROWTYPE;
+  _priv TEXT;
+  _grant BOOLEAN;
+
+BEGIN
+  -- This query will give us the most permissive privilege the user has been granted
+  SELECT privilege, granted INTO _priv, _grant
+  FROM privgranted 
+  WHERE privilege IN ('MaintainAllContacts','ViewAllContacts','MaintainPersonalContacts','ViewPersonalContacts')
+  ORDER BY granted DESC, sequence
+  LIMIT 1;
+
+  -- If have an 'All' privilege return all results
+  IF (_priv ~ 'All' AND _grant) THEN
+    FOR _row IN 
+      SELECT * FROM cntct
+    LOOP
+      RETURN NEXT _row;
+    END LOOP;
+  -- Otherwise if have any other grant, must be personal privilege.
+  ELSIF (_grant) THEN
+    FOR _row IN 
+      SELECT * FROM cntct 
+      WHERE cntct_owner_username = getEffectiveXtUser()
+    LOOP
+      RETURN NEXT _row;
+    END LOOP;
+  END IF;
+
+  RETURN;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+COMMENT ON FUNCTION cntct() IS 'A table function that returns Contact results according to privilege settings.';
diff --git a/foundation-database/public/functions/cntctdups.sql b/foundation-database/public/functions/cntctdups.sql
new file mode 100644 (file)
index 0000000..23dcd49
--- /dev/null
@@ -0,0 +1,312 @@
+CREATE OR REPLACE FUNCTION cntctdups(text, boolean, boolean, boolean, boolean, boolean, boolean, boolean, boolean, boolean, boolean, boolean, boolean, boolean, boolean, boolean) RETURNS SETOF cntctdup AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSearchText ALIAS FOR $1;
+  pSearchContactName ALIAS FOR $2;
+  pSearchPhone ALIAS FOR $3;
+  pSearchEmail ALIAS FOR $4;
+  pSearchNumber ALIAS FOR $5;
+  pSearchName ALIAS FOR $6;
+  pShowInactive ALIAS FOR $7;
+  pIgnoreBlanks ALIAS FOR $8;
+  pIndentedDups ALIAS FOR $9;
+  pCheckHnfc ALIAS FOR $10;
+  pCheckFirst ALIAS FOR $11;
+  pCheckMiddle ALIAS FOR $12;
+  pCheckLast ALIAS FOR $13;
+  pCheckSuffix ALIAS FOR $14;
+  pCheckPhone ALIAS FOR $15;
+  pCheckEmail ALIAS FOR $16;
+  _cntct cntctdup%ROWTYPE;
+  _cntctdup cntctdup%ROWTYPE;
+  _rec RECORD;
+  _operator TEXT := '';
+  _clause TEXT;
+  _qry  TEXT := '';
+  _return BOOLEAN := true;
+  _text TEXT;
+  _first BOOLEAN := true;
+
+BEGIN
+  -- Validate
+  IF (pIndentedDups AND NOT pCheckHnfc AND NOT pCheckFirst AND NOT pCheckMiddle AND 
+      NOT pCheckLast AND NOT pCheckSuffix AND NOT pCheckEmail AND NOT pCheckPhone) THEN
+    RETURN;
+  END IF;
+
+  _text = quote_literal(pSearchText);
+
+  IF (pIndentedDups) THEN
+    _qry := 'SELECT 
+       -1 AS cntct_id,
+       -1 AS cntct_crmacct_id,
+       -1 AS cntct_addr_id,';
+    IF (NOT pCheckFirst) THEN
+      _qry := _qry || ''''' AS ';
+    END IF;
+    _qry := _qry || ' cntct_first_name,';
+    IF (NOT pCheckLast) THEN
+      _qry := _qry || ''''' AS ';
+    END IF;
+    _qry := _qry || ' cntct_last_name,';
+    IF (NOT pCheckHnfc) THEN
+      _qry := _qry || ''''' AS ';
+    END IF;
+    _qry := _qry || ' cntct_honorific,';
+    _qry := _qry || ' '''' AS cntct_initials,';
+    _qry := _qry || ' NULL AS cntct_active,';
+    IF (NOT pCheckPhone) THEN
+      _qry := _qry || ''''' AS ';
+    END IF;
+    _qry := _qry || ' cntct_phone,';
+    IF (NOT pCheckPhone) THEN
+      _qry := _qry || ''''' AS ';
+    END IF;
+    _qry := _qry || ' cntct_phone2,';
+    _qry := _qry || ' '''' AS cntct_fax,';
+    IF (NOT pCheckEmail) THEN
+      _qry := _qry || ''''' AS ';
+    END IF;
+    _qry := _qry || ' cntct_email,';
+    _qry := _qry || ' '''' AS cntct_webaddr,';
+    _qry := _qry || ' '''' AS cntct_notes,';
+    _qry := _qry || ' '''' AS cntct_title,';
+    _qry := _qry || ' '''' AS cntct_number,';
+    IF (NOT pCheckMiddle) THEN
+      _qry := _qry || ''''' AS ';
+    END IF;
+    _qry := _qry || ' cntct_middle,';
+    IF (NOT pCheckSuffix) THEN
+      _qry := _qry || ''''' AS ';
+    END IF;
+    _qry := _qry || ' cntct_suffix,';
+    _qry := _qry || ' '''' AS cntct_owner_username,';
+    _qry := _qry || ' '''' AS cntct_name,';
+    _qry := _qry || ' '''' AS crmacct_number, ';
+    _qry := _qry || ' '''' AS crmacct_name, ';
+    _qry := _qry || ' NULL AS addr_id,
+               NULL AS addr_active,
+               '''' AS addr_line1,
+               '''' AS addr_line2,
+               '''' AS addr_line3,
+               '''' AS addr_city,
+               '''' AS addr_state,
+               '''' AS addr_postalcode,
+               '''' AS addr_country,
+               '''' AS addr_notes,
+               '''' AS addr_number,
+               cntctdup_level FROM (';
+  END IF;
+    _clause := 'SELECT 
+               cntct_id,
+               cntct_crmacct_id,
+               cntct_addr_id,
+               UPPER(cntct_first_name) AS cntct_first_name,
+               UPPER(cntct_last_name) AS cntct_last_name,
+               UPPER(cntct_honorific) AS cntct_honorific,
+               cntct_initials,
+               cntct_active,
+               cntct_phone,
+               cntct_phone2,
+               cntct_fax,
+               UPPER(cntct_email) AS cntct_email,
+               cntct_webaddr,
+               cntct_notes,
+               cntct_title,
+               cntct_number,
+               UPPER(cntct_middle) AS cntct_middle,
+               UPPER(cntct_suffix) AS cntct_suffix,
+               cntct_owner_username,
+                cntct_name,
+               crmacct_number, 
+               crmacct_name,
+               addr.*,
+               0 AS cntctdup_level
+             FROM cntct()
+               LEFT OUTER JOIN crmacct ON (cntct_crmacct_id=crmacct_id) 
+               LEFT OUTER JOIN addr ON (cntct_addr_id=addr_id) 
+            WHERE ';
+
+  IF (NOT pIndentedDups) THEN
+    WHILE position('UPPER' in _clause) > 0
+    LOOP
+      _clause := regexp_replace(_clause, 'UPPER', '');
+    END LOOP;
+  END IF;
+
+  _qry := _qry || _clause;
+          
+  IF (NOT pShowInactive) THEN
+    _qry := _qry || ' cntct_active AND ';
+  END IF;
+
+  IF (pIgnoreBlanks) THEN
+    _qry := _qry || ' (COALESCE(LENGTH(cntct_first_name || cntct_last_name),0) > 0) AND ';
+  END IF;
+
+    _qry := _qry || '(false ';
+
+  IF (pSearchNumber) THEN
+    _qry := _qry || ' OR (crmacct_number ~* ' || quote_literal(pSearchText) || ') ';
+  END IF;
+
+  IF (pSearchName) THEN
+    _qry := _qry || ' OR (crmacct_name ~* ' || quote_literal(pSearchText) || ') ';
+  END IF;
+
+  IF (pSearchContactName) THEN
+    _qry := _qry || ' OR (cntct_first_name || '' '' || cntct_last_name ~* ' || quote_literal(pSearchText) || ') ';
+  END IF;
+  
+  IF (pSearchPhone) THEN
+    _qry := _qry || ' OR (cntct_phone || '' '' || cntct_phone2 || '' '' || cntct_fax ~* ' || quote_literal(pSearchText) || ') ';
+  END IF;
+
+  IF (pSearchEmail) THEN
+    _qry := _qry || ' OR (cntct_email ~* ' || quote_literal(pSearchText) || ') ';
+  END IF;
+
+  _qry := _qry || ' ) ';
+  
+  IF (pIndentedDups) THEN
+    _qry := _qry || ') data';
+    _clause := ' GROUP BY cntctdup_level';
+    IF (pCheckHnfc) THEN
+      _clause := _clause || ',cntct_honorific';
+    END IF;
+    IF (pCheckFirst) THEN
+      _clause := _clause || ',cntct_first_name';
+    END IF;
+    IF (pCheckMiddle) THEN
+      _clause := _clause || ',cntct_middle';
+    END IF;
+    IF (pCheckLast) THEN
+      _clause := _clause || ',cntct_last_name';
+    END IF;
+    IF (pCheckSuffix) THEN
+      _clause := _clause || ',cntct_suffix';
+    END IF;
+    IF (pCheckEmail) THEN
+      _clause := _clause || ',cntct_email';
+    END IF;
+    IF (pCheckPhone) THEN
+      _clause := _clause || ',cntct_phone';
+      _clause := _clause || ',cntct_phone2';
+    END IF;
+
+    _qry := _qry || _clause; 
+
+    _clause := ' HAVING(';
+    IF (pCheckHnfc) THEN
+      _clause := _clause || 'OR COUNT(cntct_honorific) > 1 ';
+    END IF;
+    IF (pCheckFirst) THEN
+      _clause := _clause || 'OR COUNT(cntct_first_name) > 1 ';
+    END IF;
+    IF (pCheckMiddle) THEN
+      _clause := _clause || 'OR COUNT(cntct_middle) > 1 ';
+    END IF;
+    IF (pCheckLast) THEN
+      _clause := _clause || 'OR COUNT(cntct_last_name) > 1 ';
+    END IF;
+    IF (pCheckSuffix) THEN
+      _clause := _clause || 'OR COUNT(cntct_suffix) > 1 ';
+    END IF;
+    IF (pCheckEmail) THEN
+      _clause := _clause || 'OR COUNT(cntct_email) > 1 ';
+    END IF;
+    IF (pCheckPhone) THEN
+      _clause := _clause || 'OR (COUNT(cntct_phone) > 1 AND LENGTH(cntct_phone) > 0) ';
+      _clause := _clause || 'OR (COUNT(cntct_phone2) > 1 AND LENGTH(cntct_phone2) > 0) ';
+    END IF;
+    _clause := _clause || ') ';
+    _clause := overlay(_clause placing '' from 9 for 2);
+
+    IF (pCheckHnfc) THEN
+      _clause := _clause || 'AND LENGTH(cntct_honorific) > 0 ';
+    END IF;
+    IF (pCheckFirst) THEN
+      _clause := _clause || 'AND LENGTH(cntct_first_name) > 0  ';
+    END IF;
+    IF (pCheckMiddle) THEN
+      _clause := _clause || 'AND LENGTH(cntct_middle) > 0  ';
+    END IF;
+    IF (pCheckLast) THEN
+      _clause := _clause || 'AND LENGTH(cntct_last_name) > 0  ';
+    END IF;
+    IF (pCheckSuffix) THEN
+      _clause := _clause || 'AND LENGTH(cntct_suffix) > 0  ';
+    END IF;
+    IF (pCheckEmail) THEN
+      _clause := _clause || 'AND LENGTH(cntct_email) > 0  ';
+    END IF;
+    
+    _qry := _qry || _clause;
+  END IF;
+
+  _qry := _qry || ' ORDER BY cntct_last_name, cntct_first_name;'; 
+
+-- raise exception '%',_qry;
+  FOR _cntct IN
+    EXECUTE _qry
+  LOOP
+
+    RETURN NEXT _cntct;
+
+    -- If duplicates, get duplicates
+    IF (pIndentedDups) THEN
+    
+      _qry := 'SELECT                
+                 cntct.*,
+                 crmacct_number, 
+                 crmacct_name,
+                 addr.*,
+                 1 AS cntctdup_level
+               FROM cntct()
+                 LEFT OUTER JOIN crmacct ON (cntct_crmacct_id=crmacct_id) 
+                 LEFT OUTER JOIN addr ON (cntct_addr_id=addr_id)
+               WHERE (true) ';
+
+      IF (pCheckHnfc) THEN
+        _qry := _qry || ' AND (UPPER(cntct_honorific)=' || quote_literal(_cntct.cntct_honorific) || ')';
+      END IF;
+
+      IF (pCheckFirst) THEN
+        _qry := _qry || ' AND (UPPER(cntct_first_name)=' || quote_literal(_cntct.cntct_first_name) || ')';
+      END IF;
+
+      IF (pCheckMiddle) THEN
+        _qry := _qry || ' AND (UPPER(cntct_middle)=' || quote_literal(_cntct.cntct_middle) || ')';
+      END IF;
+
+      IF (pCheckLast) THEN
+        _qry := _qry || ' AND (UPPER(cntct_last_name)=' || quote_literal(_cntct.cntct_last_name) || ')';
+      END IF;
+
+      IF (pCheckSuffix) THEN
+        _qry := _qry || ' AND (UPPER(cntct_suffix)=' ||  quote_literal(_cntct.cntct_suffix) || ')';
+      END IF;
+
+      IF (pCheckPhone) THEN
+        _qry := _qry || ' AND (cntct_phone=' || quote_literal(_cntct.cntct_phone)  || ')';
+      END IF;
+
+      IF (pCheckEmail) THEN
+        _qry := _qry || ' AND (UPPER(cntct_email)=' || quote_literal(_cntct.cntct_email) || ')';
+      END IF;
+
+-- raise exception '%',_qry;
+      FOR _cntctdup IN
+        EXECUTE _qry
+      LOOP
+        RETURN NEXT _cntctdup;
+      END LOOP;
+
+    END IF;
+    
+  END LOOP;
+
+  RETURN;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/cntctmerge.sql b/foundation-database/public/functions/cntctmerge.sql
new file mode 100644 (file)
index 0000000..03ee786
--- /dev/null
@@ -0,0 +1,379 @@
+CREATE OR REPLACE FUNCTION cntctmerge(integer, integer, boolean) RETURNS boolean AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSourceCntctId ALIAS FOR $1;
+  pTargetCntctId ALIAS FOR $2;
+  pPurge ALIAS FOR $3;
+  _fk          RECORD;
+  _pk          RECORD;
+  _sel         RECORD;
+  _seq         INTEGER;
+  _col         TEXT;
+  _pkcol       TEXT;
+  _qry         TEXT;
+  _multi       BOOLEAN;
+
+BEGIN
+  -- Validate
+  IF (pSourceCntctId IS NULL) THEN
+    RAISE EXCEPTION 'Source contact id can not be null';
+  ELSIF (pTargetCntctId IS NULL) THEN
+    RAISE EXCEPTION 'Target contact id can not be null';
+  ELSIF (pPurge IS NULL) THEN
+    RAISE EXCEPTION 'Purge flag can not be null';
+  END IF;
+  
+  -- Determine where this contact is used by analyzing foreign key linkages and update each
+  FOR _fk IN
+    SELECT pg_namespace.nspname AS schemaname, con.relname AS tablename, conkey AS seq, conrelid AS class_id 
+    FROM pg_constraint, pg_class f, pg_class con, pg_namespace
+    WHERE confrelid=f.oid
+    AND conrelid=con.oid
+    AND f.relname = 'cntct'
+    AND con.relnamespace=pg_namespace.oid
+    AND con.relname NOT IN ('cntctsel', 'cntctmrgd', 'mrghist','trgthist')
+  LOOP
+    -- Validate
+    IF (ARRAY_UPPER(_fk.seq,1) > 1) THEN
+      RAISE EXCEPTION 'Updates to tables where the contact is one of multiple foreign key columns is not supported. Error on Table: %',
+        pg_namespace.nspname || '.' || con.relname;
+    END IF;
+    
+    _seq := _fk.seq[1];
+
+    -- Get the specific column name
+    SELECT attname INTO _col
+    FROM pg_attribute, pg_class
+    WHERE ((attrelid=pg_class.oid)
+    AND (pg_class.oid=_fk.class_id)
+    AND (attnum=_seq));
+
+    IF (NOT pPurge) THEN
+    -- Cache what we're going to do so we can restore if need be.
+    -- Start by determining the primary key column for this table.
+      _multi := false;
+      _qry := 'SELECT pg_attribute.attname AS key
+               FROM pg_attribute, pg_class 
+               WHERE pg_class.relnamespace = (
+                 SELECT oid 
+                 FROM pg_namespace 
+                 WHERE pg_namespace.nspname = ''' || _fk.schemaname || ''') 
+                AND  pg_class.oid IN (
+                 SELECT indexrelid 
+                 FROM pg_index 
+                 WHERE indisprimary = true 
+                  AND indrelid IN (
+                    SELECT oid 
+                    FROM pg_class 
+                    WHERE lower(relname) = ''' || _fk.tablename || ''')) 
+                AND pg_attribute.attrelid = pg_class.oid 
+                AND pg_attribute.attisdropped = false 
+               ORDER BY pg_attribute.attnum;';
+
+      FOR _pk IN 
+        EXECUTE _qry
+      LOOP
+        IF (_multi) THEN
+          RAISE EXCEPTION 'Reference tables with composite primary keys not supported.  Try the merge and purge option.';
+        END IF;
+        _pkcol := _pk.key;
+        _multi := true;
+      END LOOP;
+
+      -- Gather and store the history
+      _qry := 'INSERT INTO mrghist 
+               SELECT ' || pSourceCntctId || ', ''' 
+                        || _fk.schemaname || '.' || _fk.tablename || ''', ''' 
+                        || _pkcol || ''', ' 
+                        || _pkcol || ', '''
+                        || _col || ''' 
+               FROM ' || _fk.schemaname || '.' || _fk.tablename || '
+               WHERE (' || _col || '=' || pSourceCntctId || ');';
+                   --           raise exception '%',_qry;
+      EXECUTE _qry;
+      
+    END IF;
+
+    -- Merge references
+    _qry := 'UPDATE ' || _fk.schemaname || '.' || _fk.tablename ||
+            ' SET ' || _col || '=' || pTargetCntctId ||
+            ' WHERE (' || _col || '=' || pSourceCntctId || ');';
+            
+    EXECUTE _qry;
+         
+  END LOOP;
+
+  -- Merge cases with no foreign key
+  IF (NOT pPurge) THEN
+    INSERT INTO mrghist 
+    SELECT pSourceCntctId,
+      'comment',
+      'comment_id', 
+      comment_id,
+      'comment_source_id'
+    FROM comment
+    WHERE ((comment_source_id= pSourceCntctId)
+    AND (comment_source='T'));
+
+    INSERT INTO mrghist 
+    SELECT pSourceCntctId,
+      'docass',
+      'docass_id', 
+      docass_id,
+      'docass_source_id'
+    FROM docass
+    WHERE ((docass_source_id= pSourceCntctId)
+    AND (docass_source_type='T'));
+
+    INSERT INTO mrghist 
+    SELECT pSourceCntctId,
+      'docass',
+      'docass_id', 
+      docass_id,
+      'docass_target_id'
+    FROM docass
+    WHERE ((docass_target_id= pSourceCntctId)
+    AND (docass_target_type='T'));
+
+    INSERT INTO mrghist 
+    SELECT pSourceCntctId,
+      'vendinfo',
+      'vend_id', 
+      vend_id,
+      'vend_cntct1_id'
+    FROM vendinfo
+    WHERE (vend_cntct1_id=pSourceCntctId);
+
+    INSERT INTO mrghist 
+    SELECT pSourceCntctId,
+      'vendinfo',
+      'vend_id', 
+      vend_id,
+      'vend_cntct2_id'
+    FROM vendinfo
+    WHERE (vend_cntct2_id=pSourceCntctId);
+
+    IF (fetchMetricBool('EnableBatchManager') AND packageIsEnabled('xtbatch')) THEN
+      INSERT INTO mrghist 
+      SELECT pSourceCntctId,
+      'xtbatch.emlassc',
+      'emlassc_id', 
+      emlassc_id,
+      'emlassc_assc_id'
+      FROM xtbatch.emlassc
+      WHERE ((emlassc_assc_id= pSourceCntctId)
+      AND (emlassc_type='T'));
+    END IF;
+  END IF;
+
+  UPDATE comment
+  SET comment_source_id = pTargetCntctId
+  WHERE ((comment_source = 'T')
+   AND (comment_source_id = pSourceCntctId));
+
+  UPDATE docass
+  SET docass_source_id = pTargetCntctId
+  WHERE ((docass_source_type = 'T')
+   AND (docass_source_id = pSourceCntctId));
+
+  UPDATE docass
+  SET docass_target_id = pTargetCntctId
+  WHERE ((docass_target_type = 'T')
+   AND (docass_target_id = pSourceCntctId));
+
+  UPDATE vendinfo
+  SET vend_cntct1_id = pTargetCntctId
+  WHERE (vend_cntct1_id = pSourceCntctId);
+
+  UPDATE vendinfo
+  SET vend_cntct2_id = pTargetCntctId
+  WHERE (vend_cntct2_id = pSourceCntctId);
+
+  IF (fetchMetricBool('EnableBatchManager') AND packageIsEnabled('xtbatch')) THEN
+    UPDATE xtbatch.emlassc
+    SET emlassc_assc_id = pTargetCntctId
+    WHERE ((emlassc_type = 'T')
+     AND (emlassc_assc_id = pSourceCntctId));
+  END IF;
+
+  IF (NOT pPurge) THEN
+  -- Record that this has been merged if not already
+    IF (SELECT (COUNT(cntctmrgd_cntct_id) = 0) 
+        FROM cntctmrgd
+        WHERE (cntctmrgd_cntct_id=pSourceCntctId)) THEN
+      INSERT INTO cntctmrgd VALUES (pSourceCntctId,false);
+    END IF;
+  END IF;
+
+ -- Merge field detail to target
+  SELECT * INTO _sel 
+  FROM cntctsel 
+    JOIN cntct ON (cntctsel_cntct_id=cntct_id)
+  WHERE (cntctsel_cntct_id=pSourceCntctId);
+  
+  IF (FOUND) THEN
+    IF (_sel.cntctsel_mrg_crmacct_id) THEN
+      IF (NOT pPurge) THEN
+        INSERT INTO trgthist
+        SELECT pSourceCntctId,pTargetCntctId,'cntct_crmacct_id', cntct_crmacct_id::text || '::integer'
+        FROM cntct
+        WHERE (cntct_id=pTargetCntctId);
+      END IF;
+      UPDATE cntct SET cntct_crmacct_id=_sel.cntct_crmacct_id WHERE (cntct_id=pTargetCntctId);
+    END IF;
+    IF (_sel.cntctsel_mrg_addr_id) THEN
+      IF (NOT pPurge) THEN
+        INSERT INTO trgthist
+        SELECT pSourceCntctId,pTargetCntctId,'cntct_addr_id', cntct_addr_id::text || '::integer'
+        FROM cntct
+        WHERE (cntct_id=pTargetCntctId);
+      END IF;
+      UPDATE cntct SET cntct_addr_id=_sel.cntct_addr_id WHERE (cntct_id=pTargetCntctId);
+    END IF;
+    IF (_sel.cntctsel_mrg_first_name) THEN
+      IF (NOT pPurge) THEN
+        INSERT INTO trgthist
+        SELECT pSourceCntctId,pTargetCntctId,'cntct_first_name', '''' || cntct_first_name || ''''
+        FROM cntct
+        WHERE (cntct_id=pTargetCntctId);
+      END IF;
+      UPDATE cntct SET cntct_first_name=_sel.cntct_first_name WHERE (cntct_id=pTargetCntctId);
+    END IF;
+    IF (_sel.cntctsel_mrg_last_name) THEN
+      IF (NOT pPurge) THEN
+        INSERT INTO trgthist
+        SELECT pSourceCntctId,pTargetCntctId,'cntct_last_name', '''' || cntct_last_name || ''''
+        FROM cntct
+        WHERE (cntct_id=pTargetCntctId);
+      END IF;
+      UPDATE cntct SET cntct_last_name=_sel.cntct_last_name WHERE (cntct_id=pTargetCntctId);
+    END IF;
+    IF (_sel.cntctsel_mrg_honorific) THEN
+      IF (NOT pPurge) THEN
+        INSERT INTO trgthist
+        SELECT pSourceCntctId,pTargetCntctId,'cntct_honorific', '''' || cntct_honorific || ''''
+        FROM cntct
+        WHERE (cntct_id=pTargetCntctId);
+      END IF;
+      UPDATE cntct SET cntct_honorific=_sel.cntct_honorific WHERE (cntct_id=pTargetCntctId);
+    END IF;
+    IF (_sel.cntctsel_mrg_initials) THEN
+      IF (NOT pPurge) THEN
+        INSERT INTO trgthist
+        SELECT pSourceCntctId,pTargetCntctId,'cntct_initials', '''' || cntct_initials || ''''
+        FROM cntct
+        WHERE (cntct_id=pTargetCntctId);
+      END IF;
+      UPDATE cntct SET cntct_initials=_sel.cntct_initials WHERE (cntct_id=pTargetCntctId);
+    END IF;
+    IF (_sel.cntctsel_mrg_phone) THEN
+      IF (NOT pPurge) THEN
+        INSERT INTO trgthist
+        SELECT pSourceCntctId,pTargetCntctId,'cntct_phone', '''' || cntct_phone || ''''
+        FROM cntct
+        WHERE (cntct_id=pTargetCntctId);
+      END IF;
+      UPDATE cntct SET cntct_phone=_sel.cntct_phone WHERE (cntct_id=pTargetCntctId);
+    END IF;
+    IF (_sel.cntctsel_mrg_phone2) THEN
+      IF (NOT pPurge) THEN
+        INSERT INTO trgthist
+        SELECT pSourceCntctId,pTargetCntctId,'cntct_phone2', '''' || cntct_phone2 || ''''
+        FROM cntct
+        WHERE (cntct_id=pTargetCntctId);
+      END IF;
+      UPDATE cntct SET cntct_phone2=_sel.cntct_phone2 WHERE (cntct_id=pTargetCntctId);
+    END IF;
+    IF (_sel.cntctsel_mrg_fax)  THEN
+      IF (NOT pPurge) THEN
+        INSERT INTO trgthist
+        SELECT pSourceCntctId,pTargetCntctId,'cntct_fax', '''' || cntct_fax || ''''
+        FROM cntct
+        WHERE (cntct_id=pTargetCntctId);
+      END IF;
+      UPDATE cntct SET cntct_fax=_sel.cntct_fax WHERE (cntct_id=pTargetCntctId);
+    END IF;
+    IF (_sel.cntctsel_mrg_email)  THEN
+      IF (NOT pPurge) THEN
+        INSERT INTO trgthist
+        SELECT pSourceCntctId,pTargetCntctId,'cntct_email', '''' || cntct_email || ''''
+        FROM cntct
+        WHERE (cntct_id=pTargetCntctId);
+      END IF;
+      UPDATE cntct SET cntct_email=_sel.cntct_email WHERE (cntct_id=pTargetCntctId);
+    END IF;
+    IF (_sel.cntctsel_mrg_webaddr) THEN
+      IF (NOT pPurge) THEN
+        INSERT INTO trgthist
+        SELECT pSourceCntctId,pTargetCntctId,'cntct_webaddr', '''' || cntct_webaddr || ''''
+        FROM cntct
+        WHERE (cntct_id=pTargetCntctId);
+      END IF;
+      UPDATE cntct SET cntct_webaddr=_sel.cntct_webaddr WHERE (cntct_id=pTargetCntctId);
+    END IF;
+    IF (_sel.cntctsel_mrg_notes) THEN
+      IF (NOT pPurge) THEN
+        INSERT INTO trgthist
+        SELECT pSourceCntctId,pTargetCntctId,'cntct_notes', '''' || cntct_notes || ''''
+        FROM cntct
+        WHERE (cntct_id=pTargetCntctId);
+      END IF;
+      UPDATE cntct SET cntct_notes=cntct_notes || '
+
+      ' || _sel.cntct_notes WHERE (cntct_id=pTargetCntctId);
+    END IF;
+    IF (_sel.cntctsel_mrg_title) THEN
+      IF (NOT pPurge) THEN
+        INSERT INTO trgthist
+        SELECT pSourceCntctId,pTargetCntctId,'cntct_title', '''' || cntct_title || ''''
+        FROM cntct
+        WHERE (cntct_id=pTargetCntctId);
+      END IF;
+      UPDATE cntct SET cntct_title=_sel.cntct_title WHERE (cntct_id=pTargetCntctId);
+    END IF;
+    IF (_sel.cntctsel_mrg_middle) THEN
+      IF (NOT pPurge) THEN
+        INSERT INTO trgthist
+        SELECT pSourceCntctId,pTargetCntctId,'cntct_middle', '''' || cntct_middle || ''''
+        FROM cntct
+        WHERE (cntct_id=pTargetCntctId);
+      END IF;
+      UPDATE cntct SET cntct_middle=_sel.cntct_middle WHERE (cntct_id=pTargetCntctId);
+    END IF;
+    IF (_sel.cntctsel_mrg_suffix) THEN
+      IF (NOT pPurge) THEN
+        INSERT INTO trgthist
+        SELECT pSourceCntctId,pTargetCntctId,'cntct_suffix', '''' || cntct_suffix || ''''
+        FROM cntct
+        WHERE (cntct_id=pTargetCntctId);
+      END IF;
+      UPDATE cntct SET cntct_suffix=_sel.cntct_suffix WHERE (cntct_id=pTargetCntctId);
+    END IF;
+    IF (_sel.cntctsel_mrg_owner_username) THEN
+      IF (NOT pPurge) THEN
+        INSERT INTO trgthist
+        SELECT pSourceCntctId,pTargetCntctId,'cntct_owner_username', '''' || cntct_owner_username || ''''
+        FROM cntct
+        WHERE (cntct_id=pTargetCntctId);
+      END IF;
+      UPDATE cntct SET cntct_owner_username=_sel.cntct_owner_username WHERE (cntct_id=pTargetCntctId);
+    END IF;
+  ELSE
+    RAISE EXCEPTION 'Source Contact not Found';
+  END IF;
+
+  -- Disposition source contact
+  IF (pPurge) THEN
+    DELETE FROM cntct WHERE cntct_id = pSourceCntctId;
+  END IF;
+
+  -- Deactivate contact
+  UPDATE cntct SET cntct_active = false WHERE (cntct_id=pSourceCntctId);
+  
+  -- Clean up
+  DELETE FROM cntctsel WHERE (cntctsel_cntct_id=pSourceCntctId);
+
+  RETURN true;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/cntctrestore.sql b/foundation-database/public/functions/cntctrestore.sql
new file mode 100644 (file)
index 0000000..460a020
--- /dev/null
@@ -0,0 +1,53 @@
+CREATE OR REPLACE FUNCTION cntctrestore(integer) RETURNS boolean AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCntctId ALIAS FOR $1;
+  _r RECORD;
+  _qry TEXT;
+
+BEGIN
+  -- Validate
+  SELECT * INTO _r FROM cntctmrgd WHERE (cntctmrgd_cntct_id=pCntctId);
+  IF (NOT FOUND) THEN
+    RETURN false;
+  END IF;
+  
+  -- Gather the list of affected records
+  FOR _r IN
+    SELECT * FROM mrghist
+    WHERE (mrghist_cntct_id=pCntctId)
+  LOOP
+    -- Restore the old references
+    _qry := 'UPDATE ' || _r.mrghist_table ||
+            ' SET ' || _r.mrghist_cntct_col || '=' || pCntctId ||
+            ' WHERE (' || _r.mrghist_pkey_col || '=' || _r.mrghist_pkey_id || ');';
+    
+   EXECUTE _qry;
+         
+  END LOOP;
+
+  -- Gather the list of affected fields
+  FOR _r IN
+    SELECT * FROM trgthist
+    WHERE (trgthist_src_cntct_id=pCntctId)
+  LOOP
+    -- Restore the old values
+    _qry := 'UPDATE cntct
+              SET ' || _r.trgthist_col || '=' || _r.trgthist_value ||
+            ' WHERE (cntct_id=' || _r.trgthist_trgt_cntct_id || ');';
+    
+   EXECUTE _qry;
+         
+  END LOOP;
+
+  -- Clean up
+  UPDATE cntct SET cntct_active=true WHERE (cntct_id=pCntctId);
+  DELETE FROM mrghist WHERE (mrghist_cntct_id=pCntctId);
+  DELETE FROM trgthist WHERE (trgthist_src_cntct_id=pCntctId);
+  DELETE FROM cntctmrgd WHERE (cntctmrgd_cntct_id=pCntctId);
+
+  RETURN true;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/cntctselect.sql b/foundation-database/public/functions/cntctselect.sql
new file mode 100644 (file)
index 0000000..591a569
--- /dev/null
@@ -0,0 +1,22 @@
+CREATE OR REPLACE FUNCTION cntctselect(integer, boolean) RETURNS boolean AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCntctId ALIAS FOR $1;
+  pTarget ALIAS FOR $2;
+
+BEGIN
+  -- If target, delete any other targets
+  IF (pTarget) THEN
+    DELETE FROM cntctsel WHERE cntctsel_target;
+  END IF;
+  
+  -- Delete any previous selection of this contact
+  DELETE FROM cntctsel WHERE cntctsel_cntct_id=pCntctId;
+
+  -- Add this contact in appropriate selection state
+  INSERT INTO cntctsel VALUES (pCntctId,pTarget);
+
+  RETURN true;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/cntctselectcol.sql b/foundation-database/public/functions/cntctselectcol.sql
new file mode 100644 (file)
index 0000000..48f6ac0
--- /dev/null
@@ -0,0 +1,78 @@
+CREATE OR REPLACE FUNCTION cntctselectcol(integer, integer) RETURNS boolean AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCntctId ALIAS FOR $1;
+  pColNumber ALIAS FOR $2;
+
+BEGIN
+
+  IF (pColNumber = 2 OR pColNumber = 3) THEN
+    UPDATE cntctsel SET cntctsel_mrg_crmacct_id=false WHERE (cntctsel_mrg_crmacct_id AND cntctsel_cntct_id != pCntctId);
+    UPDATE cntctsel SET cntctsel_mrg_crmacct_id=true WHERE (cntctsel_cntct_id = pCntctId);
+    RETURN true;
+  ELSIF (pColNumber = 4) THEN
+    UPDATE cntctsel SET cntctsel_mrg_honorific=false WHERE (cntctsel_mrg_honorific AND cntctsel_cntct_id != pCntctId);
+    UPDATE cntctsel SET cntctsel_mrg_honorific=true WHERE (cntctsel_cntct_id = pCntctId);
+    RETURN true;
+  ELSIF (pColNumber = 5) THEN
+    UPDATE cntctsel SET cntctsel_mrg_first_name=false WHERE (cntctsel_mrg_first_name AND cntctsel_cntct_id != pCntctId);
+    UPDATE cntctsel SET cntctsel_mrg_first_name=true WHERE (cntctsel_cntct_id = pCntctId);
+    RETURN true;
+  ELSIF (pColNumber = 6) THEN
+    UPDATE cntctsel SET cntctsel_mrg_middle=false WHERE (cntctsel_mrg_middle AND cntctsel_cntct_id != pCntctId);
+    UPDATE cntctsel SET cntctsel_mrg_middle=true WHERE (cntctsel_cntct_id = pCntctId);
+    RETURN true;
+  ELSIF (pColNumber = 7) THEN
+    UPDATE cntctsel SET cntctsel_mrg_last_name=false WHERE (cntctsel_mrg_last_name AND cntctsel_cntct_id != pCntctId);
+    UPDATE cntctsel SET cntctsel_mrg_last_name=true WHERE (cntctsel_cntct_id = pCntctId);
+    RETURN true;
+  ELSIF (pColNumber = 8) THEN
+    UPDATE cntctsel SET cntctsel_mrg_suffix=false WHERE (cntctsel_mrg_suffix AND cntctsel_cntct_id != pCntctId);
+    UPDATE cntctsel SET cntctsel_mrg_suffix=true WHERE (cntctsel_cntct_id = pCntctId);
+    RETURN true;
+  ELSIF (pColNumber = 9) THEN
+    UPDATE cntctsel SET cntctsel_mrg_initials=false WHERE (cntctsel_mrg_initials AND cntctsel_cntct_id != pCntctId);
+    UPDATE cntctsel SET cntctsel_mrg_initials=true WHERE (cntctsel_cntct_id = pCntctId);
+    RETURN true;
+  ELSIF (pColNumber = 10) THEN
+    UPDATE cntctsel SET cntctsel_mrg_phone=false WHERE (cntctsel_mrg_phone AND cntctsel_cntct_id != pCntctId);
+    UPDATE cntctsel SET cntctsel_mrg_phone=true WHERE (cntctsel_cntct_id = pCntctId);
+    RETURN true;
+  ELSIF (pColNumber = 11) THEN
+    UPDATE cntctsel SET cntctsel_mrg_phone2=false WHERE (cntctsel_mrg_phone2 AND cntctsel_cntct_id != pCntctId);
+    UPDATE cntctsel SET cntctsel_mrg_phone2=true WHERE (cntctsel_cntct_id = pCntctId);
+    RETURN true;
+  ELSIF (pColNumber = 12) THEN
+    UPDATE cntctsel SET cntctsel_mrg_fax=false WHERE (cntctsel_mrg_fax AND cntctsel_cntct_id != pCntctId);
+    UPDATE cntctsel SET cntctsel_mrg_fax=true WHERE (cntctsel_cntct_id = pCntctId);
+    RETURN true;
+  ELSIF (pColNumber = 13) THEN
+    UPDATE cntctsel SET cntctsel_mrg_email=false WHERE (cntctsel_mrg_email AND cntctsel_cntct_id != pCntctId);
+    UPDATE cntctsel SET cntctsel_mrg_email=true WHERE (cntctsel_cntct_id = pCntctId);
+    RETURN true;
+  ELSIF (pColNumber = 14) THEN
+    UPDATE cntctsel SET cntctsel_mrg_webaddr=false WHERE (cntctsel_mrg_webaddr AND cntctsel_cntct_id != pCntctId);
+    UPDATE cntctsel SET cntctsel_mrg_webaddr=true WHERE (cntctsel_cntct_id = pCntctId);
+    RETURN true;
+  ELSIF (pColNumber = 15) THEN
+    UPDATE cntctsel SET cntctsel_mrg_title=false WHERE (cntctsel_mrg_title AND cntctsel_cntct_id != pCntctId);
+    UPDATE cntctsel SET cntctsel_mrg_title=true WHERE (cntctsel_cntct_id = pCntctId);
+    RETURN true;
+  ELSIF (pColNumber = 16) THEN
+    UPDATE cntctsel SET cntctsel_mrg_owner_username=false WHERE (cntctsel_mrg_owner_username AND cntctsel_cntct_id != pCntctId);
+    UPDATE cntctsel SET cntctsel_mrg_owner_username=true WHERE (cntctsel_cntct_id = pCntctId);
+    RETURN true;
+  ELSIF (pColNumber = 17) THEN
+    UPDATE cntctsel SET cntctsel_mrg_notes=false WHERE (cntctsel_mrg_notes AND cntctsel_cntct_id != pCntctId);
+    UPDATE cntctsel SET cntctsel_mrg_notes=true WHERE (cntctsel_cntct_id = pCntctId);
+    RETURN true;
+  ELSIF (pColNumber >= 18) THEN
+    UPDATE cntctsel SET cntctsel_mrg_addr_id=false WHERE (cntctsel_mrg_addr_id AND cntctsel_cntct_id != pCntctId);
+    UPDATE cntctsel SET cntctsel_mrg_addr_id=true WHERE (cntctsel_cntct_id = pCntctId);
+    RETURN true;
+  END IF;
+
+  RETURN false;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/cntctused.sql b/foundation-database/public/functions/cntctused.sql
new file mode 100644 (file)
index 0000000..b0cc98b
--- /dev/null
@@ -0,0 +1,57 @@
+CREATE OR REPLACE FUNCTION cntctused(integer) RETURNS boolean AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCntctId ALIAS FOR $1;
+  _fk RECORD;
+  _r RECORD;
+  _seq INTEGER;
+  _col TEXT;
+  _qry TEXT;
+
+BEGIN
+  -- Determine where this contact is used by analyzing foreign key linkages
+  -- but ignore child tables and those with impermanent relationships
+  FOR _fk IN
+    SELECT pg_namespace.nspname AS schemaname, con.relname AS tablename, conkey AS seq, conrelid AS class_id 
+    FROM pg_constraint, pg_class f, pg_class con, pg_namespace
+    WHERE confrelid=f.oid
+    AND conrelid=con.oid
+    AND f.relname = 'cntct'
+    AND con.relnamespace=pg_namespace.oid
+    AND con.relname NOT IN ('cntctaddr', 'cntctdata', 'cntcteml',
+                            'cohead',    'pohead',    'quhead',   'tohead',
+                            'cntctsel',  'cntctmrgd', 'mrghist',  'trgthist')
+  LOOP
+    -- Validate
+    IF (ARRAY_UPPER(_fk.seq,1) > 1) THEN
+      RAISE EXCEPTION 'Cannot check dependencies when the contact is one of multiple foreign key columns (%.%) [xtuple: fkeycheck, -1, %, %]',
+        _fk.nspname, _fk.relname, _fk.nspname, _fk.relname;
+    END IF;
+    
+    _seq := _fk.seq[1];
+
+    -- Get the specific column name
+    SELECT attname INTO _col
+    FROM pg_attribute, pg_class
+    WHERE ((attrelid=pg_class.oid)
+    AND (pg_class.oid=_fk.class_id)
+    AND (attnum=_seq));
+
+    -- See if there are dependencies
+    _qry := 'SELECT * 
+            FROM ' || _fk.schemaname || '.' || _fk.tablename || '
+            WHERE ('|| _col || '=' || pCntctId || ');';
+
+    FOR _r IN 
+      EXECUTE _qry
+    LOOP
+      RETURN true;
+    END LOOP;
+         
+  END LOOP;
+
+  RETURN false;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/coheadstatecolor.sql b/foundation-database/public/functions/coheadstatecolor.sql
new file mode 100644 (file)
index 0000000..cd7f7a5
--- /dev/null
@@ -0,0 +1,55 @@
+CREATE OR REPLACE FUNCTION coheadstatecolor(INTEGER) RETURNS TEXT AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCoheadId    ALIAS FOR $1;
+  _shipheadid  INTEGER;
+  _result      TEXT := '';
+
+BEGIN
+  
+  IF (pCoheadid IS NULL) THEN
+    RAISE EXCEPTION 'Customer Id is required.';
+  END IF;
+  
+  SELECT 
+    shiphead_id INTO _shipheadid
+  FROM cohead
+    JOIN shiphead ON ((shiphead_order_id=cohead_id)
+                  AND (shiphead_order_type='SO'))
+    JOIN shipitem ON (shiphead_id=shipitem_shiphead_id)
+  WHERE ((cohead_id=pCoheadId)
+    AND (NOT shipitem_invoiced))
+  ORDER BY shiphead_id DESC
+  LIMIT 1;
+
+  IF (FOUND) THEN
+    SELECT 
+      CASE 
+        WHEN ((shiphead_shipped) 
+         AND (COALESCE(shiphead_order_id,0) > 0) 
+         AND (SUM(noNeg(coitem_qtyord - coitem_qtyshipped + coitem_qtyreturned)) <= 0)) THEN 
+           'altemphasis'
+        WHEN ((COALESCE(cobmisc_cohead_id,0) > 0)       
+         AND (SUM(noNeg(coitem_qtyord - coitem_qtyshipped + coitem_qtyreturned)) > 0)) THEN 
+           'error' 
+        WHEN (NOT shiphead_shipped) THEN 
+          'emphasis' 
+       END INTO _result
+    FROM cohead
+      JOIN coitem ON (cohead_id=coitem_cohead_id)
+      JOIN shiphead ON ((shiphead_order_id=cohead_id)
+                    AND (shiphead_order_type='SO'))
+      JOIN shipitem ON (shiphead_id=shipitem_shiphead_id)
+      LEFT OUTER JOIN (SELECT DISTINCT cobmisc_cohead_id FROM cobmisc) AS cobmisc ON (cobmisc_cohead_id=cohead_id) 
+    WHERE (shiphead_id=_shipheadid)
+    GROUP BY shiphead_id,shiphead_shipped,shiphead_order_id,cobmisc_cohead_id
+    ORDER BY shiphead_id DESC;
+  ELSE
+    _result := '';
+  END IF;
+  
+  RETURN _result;
+  
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/concatagg.sql b/foundation-database/public/functions/concatagg.sql
new file mode 100644 (file)
index 0000000..84cf6b4
--- /dev/null
@@ -0,0 +1,19 @@
+DROP AGGREGATE IF EXISTS concatagg(TEXT);
+
+CREATE OR REPLACE FUNCTION concataggSfunc(TEXT, TEXT) RETURNS TEXT AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  prevstate     ALIAS FOR $1;
+  newval        ALIAS FOR $2;
+BEGIN
+  RETURN prevstate || newval;
+END;
+$$
+LANGUAGE 'plpgsql';
+
+CREATE AGGREGATE concatagg (TEXT) (
+  SFUNC = concataggSfunc,
+  STYPE = TEXT,
+  INITCOND = ''
+);
diff --git a/foundation-database/public/functions/consolidatelocations.sql b/foundation-database/public/functions/consolidatelocations.sql
new file mode 100644 (file)
index 0000000..4cda0fa
--- /dev/null
@@ -0,0 +1,33 @@
+CREATE OR REPLACE FUNCTION consolidateLocations(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  _r RECORD;
+
+BEGIN
+
+  UPDATE itemloc
+  SET itemloc_consolflag = TRUE
+  WHERE (itemloc_itemsite_id=pItemsiteid);
+
+  FOR _r IN SELECT itemloc_location_id, SUM(itemloc_qty) AS qty
+            FROM itemloc
+            WHERE (itemloc_itemsite_id=pItemsiteid)
+            GROUP BY itemloc_location_id LOOP
+    INSERT INTO itemloc
+    ( itemloc_itemsite_id, itemloc_location_id,
+      itemloc_expiration, itemloc_qty, itemloc_consolflag )
+    VALUES
+    ( pItemsiteid, _r.itemloc_location_id,
+      endOfTime(), _r.qty, FALSE );
+  END LOOP;
+
+  DELETE FROM itemloc
+  WHERE ( (itemloc_itemsite_id=pItemsiteid)
+   AND (itemloc_consolflag) );
+
+  RETURN 1;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/convertcustomertoprospect.sql b/foundation-database/public/functions/convertcustomertoprospect.sql
new file mode 100644 (file)
index 0000000..a0b7890
--- /dev/null
@@ -0,0 +1,38 @@
+/*  TODO: push this code into the crmacct before trigger?
+          then convert customer -> prospect becomes simply
+          UPDATE crmacct SET crmacct_prospect_id=crmacct_cust_id,
+                             crmacct_cust_id=NULL
+                WHERE crmacct_cust_id=pCustId;
+ */
+CREATE OR REPLACE FUNCTION convertCustomerToProspect(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCustId     ALIAS FOR $1;
+  _c          RECORD;
+BEGIN
+  SELECT * INTO _c
+  FROM custinfo
+  WHERE (cust_id=pCustId);
+
+  INSERT INTO prospect (
+        prospect_id, prospect_active, prospect_number,
+        prospect_name, prospect_cntct_id, prospect_taxzone_id,
+        prospect_salesrep_id, prospect_warehous_id, prospect_comments
+  ) VALUES (
+       _c.cust_id, _c.cust_active, _c.cust_number,
+       _c.cust_name, _c.cust_cntct_id, _c.cust_taxzone_id,
+       CASE WHEN(_c.cust_salesrep_id > 0) THEN _c.cust_salesrep_id
+            ELSE NULL
+       END,
+       CASE WHEN(_c.cust_preferred_warehous_id > 0) THEN _c.cust_preferred_warehous_id
+            ELSE NULL
+       END,
+       _c.cust_comments);
+
+  DELETE FROM custinfo WHERE (cust_id=pCustId);
+
+  RETURN pCustId;
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/convertprospecttocustomer.sql b/foundation-database/public/functions/convertprospecttocustomer.sql
new file mode 100644 (file)
index 0000000..33a1931
--- /dev/null
@@ -0,0 +1,85 @@
+CREATE OR REPLACE FUNCTION convertProspectToCustomer(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN convertProspectToCustomer($1, FALSE);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION convertProspectToCustomer(INTEGER, BOOLEAN) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pProspectId ALIAS FOR $1;
+  pdoquotes   ALIAS FOR $2;
+  _p          RECORD;
+  _q          RECORD;
+
+BEGIN
+  SELECT * INTO _p
+  FROM prospect
+  WHERE (prospect_id=pProspectId);
+
+  IF (EXISTS(SELECT cust_id FROM custinfo WHERE cust_id=pProspectId)) THEN
+    RAISE EXCEPTION '[xtuple: convertProspectToCustomer, -10]';
+  END IF;
+
+  INSERT INTO custinfo (
+        cust_id, cust_active, cust_number,
+        cust_name, cust_cntct_id, cust_taxzone_id,
+        cust_comments, cust_creditstatus,
+        cust_salesrep_id, cust_preferred_warehous_id,
+        cust_terms_id,
+        cust_custtype_id, cust_shipform_id,
+        cust_shipvia, cust_balmethod,
+        cust_ffshipto, cust_backorder,
+        cust_partialship, cust_creditlmt,
+        cust_creditrating, cust_commprcnt,
+        cust_discntprcnt, cust_blanketpos,
+        cust_shipchrg_id, cust_ffbillto,
+        cust_usespos, cust_emaildelivery,
+        cust_autoupdatestatus,cust_autoholdorders,
+        cust_soemaildelivery) 
+  SELECT
+      _p.prospect_id, _p.prospect_active, _p.prospect_number,
+      _p.prospect_name, _p.prospect_cntct_id, _p.prospect_taxzone_id,
+      _p.prospect_comments, 'G',
+      COALESCE(_p.prospect_salesrep_id, salesrep_id),
+      COALESCE(_p.prospect_warehous_id, -1),
+      FetchMetricValue('DefaultTerms'),
+      FetchMetricValue('DefaultCustType'),
+      FetchMetricValue('DefaultShipFormId'),
+      COALESCE(FetchMetricValue('DefaultShipViaId'),-1),
+      FetchMetricText('DefaultBalanceMethod'),
+      FetchMetricBool('DefaultFreeFormShiptos'),
+      FetchMetricBool('DefaultBackOrders'),
+      FetchMetricBool('DefaultPartialShipments'),
+      FetchMetricValue('SOCreditLimit'),
+      FetchMetricText('SOCreditRate'),
+      salesrep_commission,
+      0, false, -1,false,false,false,false,
+      false, false
+  FROM salesrep WHERE (salesrep_id=FetchMetricValue('DefaultSalesRep'));
+
+  DELETE FROM prospect WHERE (prospect_id=pprospectId);
+
+  IF (pdoquotes) THEN
+    BEGIN
+      FOR _q IN SELECT quhead_number, convertQuote(quhead_id) AS err
+                  FROM quhead
+                 WHERE ((COALESCE(quhead_expire, endOfTime()) >= CURRENT_DATE)
+                    AND (quhead_cust_id=pProspectId)) LOOP
+        IF (_q.err < 0) THEN
+          RAISE NOTICE 'Quote % for % didn''t convert to a Sales Order [xtuple: convertQuote, %]',
+                       _q.quhead_number, _p.prospect_number, _q.err;
+        END IF;
+      END LOOP;
+    EXCEPTION WHEN OTHERS THEN
+      RAISE NOTICE 'Ignored errors convering quotes: % %', SQLSTATE, SQLERRM;
+    END;
+  END IF;
+
+  RETURN pProspectId;
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/convertquote.sql b/foundation-database/public/functions/convertquote.sql
new file mode 100644 (file)
index 0000000..d764ef5
--- /dev/null
@@ -0,0 +1,283 @@
+
+CREATE OR REPLACE FUNCTION convertQuote(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pQuheadid ALIAS FOR $1;
+  _soheadid INTEGER;
+  _soitemid INTEGER;
+  _orderid INTEGER;
+  _ordertype CHARACTER(1);
+  _creditstatus        TEXT;
+  _usespos BOOLEAN := false;
+  _blanketpos BOOLEAN := true;
+  _showConvertedQuote BOOLEAN := false;
+  _prospectid  INTEGER;
+  _r RECORD;
+  _soNum TEXT;
+
+BEGIN
+
+-- Check to make sure the quote has not expired
+  IF (SELECT COALESCE(quhead_expire, endOfTime()) < CURRENT_DATE
+        FROM quhead
+       WHERE(quhead_id=pQuheadid)) THEN
+    RETURN -6;
+  END IF;
+
+--  Check to make sure that all of the quote items have a valid itemsite
+  SELECT quitem_id INTO _r
+    FROM quitem LEFT OUTER JOIN itemsite ON (quitem_itemsite_id=itemsite_id)
+   WHERE ((itemsite_id IS NULL)
+     AND  (quitem_quhead_id=pQuheadid));
+  IF (FOUND) THEN
+    INSERT INTO evntlog (evntlog_evnttime, evntlog_username, evntlog_evnttype_id,
+                         evntlog_ordtype, evntlog_ord_id, evntlog_warehous_id, evntlog_number)
+    SELECT CURRENT_TIMESTAMP, evntnot_username, evnttype_id,
+           'Q', quhead_id, quhead_warehous_id, quhead_number
+    FROM evntnot, evnttype, quhead
+    WHERE ( (evntnot_evnttype_id=evnttype_id)
+     AND (evntnot_warehous_id=quhead_warehous_id)
+     AND (evnttype_name='CannotConvertQuote')
+     AND (quhead_id=pQuheadid) );
+
+    RETURN -1;
+  END IF;
+
+  SELECT cust_creditstatus, cust_usespos, cust_blanketpos
+    INTO _creditstatus, _usespos, _blanketpos
+  FROM quhead, custinfo
+  WHERE ((quhead_cust_id=cust_id)
+    AND  (quhead_id=pQuheadid));
+
+  IF (NOT FOUND) THEN
+    SELECT prospect_id INTO _prospectid
+    FROM quhead, prospect
+    WHERE ((quhead_cust_id=prospect_id)
+      AND  (quhead_id=pQuheadid));
+    IF (NOT FOUND) THEN
+      RETURN -2;
+    ELSE
+      RETURN -3;
+    END IF;
+  ELSIF (_creditstatus = 'H' AND NOT checkPrivilege('CreateSOForHoldCustomer')) THEN
+    RETURN -4;
+  ELSIF (_creditstatus = 'W' AND NOT checkPrivilege('CreateSOForWarnCustomer')) THEN
+    RETURN -5;
+  END IF;
+
+  IF ( (_usespos) AND (NOT _blanketpos) ) THEN
+    PERFORM cohead_id
+    FROM quhead JOIN cohead ON ( (cohead_cust_id=quhead_cust_id) AND
+                                 (UPPER(cohead_custponumber)=UPPER(quhead_custponumber)) )
+    WHERE (quhead_id=pQuheadid);
+    IF (FOUND) THEN
+      RAISE EXCEPTION 'Duplicate Customer PO';
+    END IF;
+  END IF;
+  
+  PERFORM quhead_number, cohead_id 
+  FROM quhead, cohead 
+  WHERE quhead_id = pQuheadid
+  AND cohead_number = quhead_number;
+
+  IF (FOUND) THEN
+    SELECT fetchSoNumber() INTO _soNum;
+  ELSE
+    SELECT quhead_number INTO _soNum
+    FROM quhead
+    WHERE quhead_id = pQuheadid;
+  END IF;
+
+  SELECT NEXTVAL('cohead_cohead_id_seq') INTO _soheadid;
+  INSERT INTO cohead
+  ( cohead_id, cohead_number, cohead_cust_id,
+    cohead_orderdate, cohead_packdate,
+    cohead_custponumber, cohead_warehous_id,
+    cohead_billtoname, cohead_billtoaddress1,
+    cohead_billtoaddress2, cohead_billtoaddress3,
+    cohead_billtocity, cohead_billtostate, cohead_billtozipcode,
+    cohead_billtocountry,
+    cohead_shipto_id, cohead_shiptoname, cohead_shiptoaddress1,
+    cohead_shiptoaddress2, cohead_shiptoaddress3,
+    cohead_shiptocity, cohead_shiptostate, cohead_shiptozipcode,
+    cohead_shiptocountry,
+    cohead_salesrep_id, cohead_commission,
+    cohead_terms_id, cohead_shipchrg_id, cohead_shipform_id,
+    cohead_fob, cohead_shipvia,
+    cohead_ordercomments, cohead_shipcomments,
+    cohead_freight, cohead_misc, cohead_misc_accnt_id, cohead_misc_descrip,
+    cohead_holdtype, cohead_wasquote, cohead_quote_number, cohead_prj_id,
+    cohead_curr_id, cohead_taxzone_id, cohead_taxtype_id,
+    cohead_shipto_cntct_id, cohead_shipto_cntct_honorific, cohead_shipto_cntct_first_name,
+    cohead_shipto_cntct_middle, cohead_shipto_cntct_last_name, cohead_shipto_cntct_suffix,
+    cohead_shipto_cntct_phone, cohead_shipto_cntct_title, cohead_shipto_cntct_fax, 
+    cohead_shipto_cntct_email,
+    cohead_billto_cntct_id, cohead_billto_cntct_honorific,
+    cohead_billto_cntct_first_name, cohead_billto_cntct_middle, cohead_billto_cntct_last_name, 
+    cohead_billto_cntct_suffix, cohead_billto_cntct_phone, cohead_billto_cntct_title, 
+    cohead_billto_cntct_fax, cohead_billto_cntct_email, cohead_ophead_id,
+    cohead_calcfreight, cohead_saletype_id, cohead_shipzone_id )
+  SELECT _soheadid, _soNum, quhead_cust_id,
+         CURRENT_DATE, quhead_packdate,
+         quhead_custponumber, quhead_warehous_id,
+         quhead_billtoname, quhead_billtoaddress1,
+         quhead_billtoaddress2, quhead_billtoaddress3,
+         quhead_billtocity, quhead_billtostate, quhead_billtozip,
+         quhead_billtocountry,
+         quhead_shipto_id, quhead_shiptoname, quhead_shiptoaddress1,
+         quhead_shiptoaddress2, quhead_shiptoaddress3,
+         quhead_shiptocity, quhead_shiptostate, quhead_shiptozipcode,
+         quhead_shiptocountry,
+         quhead_salesrep_id, quhead_commission,
+         quhead_terms_id, cust_shipchrg_id, cust_shipform_id,
+         quhead_fob, quhead_shipvia,
+         quhead_ordercomments, quhead_shipcomments,
+         quhead_freight, quhead_misc, quhead_misc_accnt_id, quhead_misc_descrip,
+         'N', TRUE, quhead_number, quhead_prj_id,
+        quhead_curr_id, quhead_taxzone_id, quhead_taxtype_id,
+        quhead_shipto_cntct_id, quhead_shipto_cntct_honorific,
+        quhead_shipto_cntct_first_name, quhead_shipto_cntct_middle, quhead_shipto_cntct_last_name,
+        quhead_shipto_cntct_suffix, quhead_shipto_cntct_phone, quhead_shipto_cntct_title,
+        quhead_shipto_cntct_fax, quhead_shipto_cntct_email, quhead_billto_cntct_id,
+        quhead_billto_cntct_honorific, quhead_billto_cntct_first_name, quhead_billto_cntct_middle,
+        quhead_billto_cntct_last_name, quhead_billto_cntct_suffix, quhead_billto_cntct_phone,
+        quhead_billto_cntct_title, quhead_billto_cntct_fax, quhead_billto_cntct_email, quhead_ophead_id,
+         quhead_calcfreight, quhead_saletype_id, quhead_shipzone_id
+  FROM quhead JOIN custinfo ON (cust_id=quhead_cust_id)
+  WHERE (quhead_id=pQuheadid);
+
+  UPDATE url SET url_source_id = _soheadid,
+                 url_source = 'S'
+  WHERE ((url_source='Q') AND (url_source_id = pQuheadid));
+
+  UPDATE imageass SET imageass_source_id = _soheadid,
+                      imageass_source = 'S'
+  WHERE ((imageass_source='Q') AND (imageass_source_id = pQuheadid));
+
+  UPDATE docass SET docass_source_id = _soheadid,
+                    docass_source_type = 'S'
+  WHERE ((docass_source_type='Q') AND (docass_source_id = pQuheadid));
+
+  -- Copy Comments
+  INSERT INTO comment
+  ( comment_cmnttype_id, comment_source, comment_source_id, comment_date, comment_user, comment_text, comment_public )
+  SELECT comment_cmnttype_id, 'S', _soheadid, comment_date, comment_user, ('Quote-' || comment_text), comment_public
+  FROM comment
+  WHERE ( (comment_source='Q')
+    AND   (comment_source_id=pQuheadid) );
+
+  FOR _r IN SELECT quitem.*,
+                   quhead_number, quhead_prj_id,
+                   itemsite_item_id, itemsite_leadtime,
+                   itemsite_createsopo, itemsite_createsopr,
+                   item_type, COALESCE(quitem_itemsrc_id, itemsrc_id, -1) AS itemsrcid
+            FROM quhead JOIN quitem ON (quitem_quhead_id=quhead_id)
+                        JOIN itemsite ON (itemsite_id=quitem_itemsite_id)
+                        JOIN item ON (item_id=itemsite_item_id)
+                        LEFT OUTER JOIN itemsrc ON ( (itemsrc_item_id=item_id) AND
+                                                     (itemsrc_default) )
+            WHERE (quhead_id=pQuheadid)
+            ORDER BY quitem_linenumber LOOP
+
+    SELECT NEXTVAL('coitem_coitem_id_seq') INTO _soitemid;
+
+    INSERT INTO coitem
+    ( coitem_id, coitem_cohead_id, coitem_linenumber, coitem_itemsite_id,
+      coitem_status, coitem_scheddate, coitem_promdate,
+      coitem_price, coitem_custprice, 
+      coitem_qtyord, coitem_qtyshipped, coitem_qtyreturned,
+      coitem_qty_uom_id, coitem_qty_invuomratio,
+      coitem_price_uom_id, coitem_price_invuomratio,
+      coitem_unitcost, coitem_prcost,
+      coitem_custpn, coitem_memo, coitem_taxtype_id, coitem_order_id )
+    VALUES
+    ( _soitemid, _soheadid, _r.quitem_linenumber, _r.quitem_itemsite_id,
+      'O', _r.quitem_scheddate, _r.quitem_promdate,
+      _r.quitem_price, _r.quitem_custprice,
+      _r.quitem_qtyord, 0, 0,
+      _r.quitem_qty_uom_id, _r.quitem_qty_invuomratio,
+      _r.quitem_price_uom_id, _r.quitem_price_invuomratio,
+      stdcost(_r.itemsite_item_id), _r.quitem_prcost,
+      _r.quitem_custpn, _r.quitem_memo, _r.quitem_taxtype_id, -1 );
+
+    IF (fetchMetricBool('enablextcommissionission')) THEN
+      PERFORM xtcommission.getSalesReps(quhead_cust_id, quhead_shipto_id,
+                                        _r.itemsite_item_id, _r.quitem_price,
+                                        _soitemid, 'SalesItem')
+      FROM quhead
+      WHERE (quhead_id=pQuheadid);
+    END IF;
+
+    INSERT INTO charass
+          (charass_target_type, charass_target_id, charass_char_id, charass_value, charass_default, charass_price)
+    SELECT 'SI', _soitemid, charass_char_id, charass_value, charass_default, charass_price
+      FROM charass
+     WHERE ((charass_target_type='QI')
+       AND  (charass_target_id=_r.quitem_id));
+
+    -- Copy Comments
+    INSERT INTO comment
+    ( comment_cmnttype_id, comment_source, comment_source_id, comment_date, comment_user, comment_text )
+    SELECT comment_cmnttype_id, 'SI', _soitemid, comment_date, comment_user, ('Quote-' || comment_text)
+    FROM comment
+    WHERE ( (comment_source='QI')
+      AND   (comment_source_id=_r.quitem_id) );
+
+    _orderid := -1;
+    _ordertype := '';
+    IF (_r.quitem_createorder) THEN
+
+      IF (_r.item_type IN ('M')) THEN
+        SELECT createWo( CAST(_r.quhead_number AS INTEGER), supply.itemsite_id, 1, (_r.quitem_qtyord * _r.quitem_qty_invuomratio),
+                         _r.itemsite_leadtime, _r.quitem_scheddate, _r.quitem_memo, 'S', _soitemid, _r.quhead_prj_id ) INTO _orderId
+        FROM itemsite sold, itemsite supply
+        WHERE ((sold.itemsite_item_id=supply.itemsite_item_id)
+         AND (supply.itemsite_warehous_id=_r.quitem_order_warehous_id)
+         AND (sold.itemsite_id=_r.quitem_itemsite_id) );
+        _orderType := 'W';
+
+        INSERT INTO charass
+              (charass_target_type, charass_target_id, charass_char_id, charass_value)
+        SELECT 'W', _orderId, charass_char_id, charass_value
+          FROM charass
+         WHERE ((charass_target_type='QI')
+           AND  (charass_target_id=_r.quitem_id));
+
+      ELSIF ( (_r.item_type IN ('P', 'O')) AND (_r.itemsite_createsopr) ) THEN
+        SELECT createPr( CAST(_r.quhead_number AS INTEGER), _r.quitem_itemsite_id, (_r.quitem_qtyord * _r.quitem_qty_invuomratio),
+                         _r.quitem_scheddate, '', 'S', _soitemid ) INTO _orderId;
+        _orderType := 'R';
+        UPDATE pr SET pr_prj_id=_r.quhead_prj_id WHERE pr_id=_orderId;
+      ELSIF ( (_r.item_type IN ('P', 'O')) AND (_r.itemsite_createsopo) ) THEN
+        IF (_r.quitem_prcost=0) THEN
+          SELECT createPurchaseToSale(_soitemid, _r.itemsrcid, _r.quitem_dropship) INTO _orderId;
+        ELSE
+          SELECT createPurchaseToSale(_soitemid, _r.itemsrcid, _r.quitem_dropship, _r.quitem_prcost) INTO _orderId;
+        END IF;
+        _orderType := 'P';
+      END IF;
+
+      UPDATE coitem SET coitem_order_type=_ordertype, coitem_order_id=_orderid
+      WHERE (coitem_id=_soitemid);
+
+    END IF;
+
+  END LOOP;
+
+  SELECT metric_value INTO _showConvertedQuote
+  FROM metric WHERE metric_name = 'ShowQuotesAfterSO';
+
+  IF (_showConvertedQuote) THEN
+    UPDATE quhead
+    SET quhead_status= 'C'
+    WHERE (quhead_id = pQuheadid);
+  ELSE
+  PERFORM deleteQuote(pQuheadid);
+  END IF;
+
+  RETURN _soheadid;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/convertquotetoinvoice.sql b/foundation-database/public/functions/convertquotetoinvoice.sql
new file mode 100644 (file)
index 0000000..30193b2
--- /dev/null
@@ -0,0 +1,320 @@
+
+CREATE OR REPLACE FUNCTION convertQuoteToInvoice(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pQuheadid ALIAS FOR $1;
+  _iheadid INTEGER;
+  _iitemid INTEGER;
+  _orderid INTEGER;
+  _ordertype CHARACTER(1);
+  _creditstatus        TEXT;
+  _usespos BOOLEAN := false;
+  _blanketpos BOOLEAN := true;
+  _showConvertedQuote BOOLEAN := false;
+  _prospectid  INTEGER;
+  _r RECORD;
+  _inNum TEXT;
+
+BEGIN
+
+-- Check to make sure the quote has not expired
+  IF (SELECT COALESCE(quhead_expire, endOfTime()) < CURRENT_DATE
+        FROM quhead
+       WHERE(quhead_id=pQuheadid)) THEN
+    RETURN -6;
+  END IF;
+
+--  Check to make sure that all of the quote items have a valid itemsite
+  SELECT quitem_id INTO _r
+    FROM quitem LEFT OUTER JOIN itemsite ON (quitem_itemsite_id=itemsite_id)
+   WHERE ((itemsite_id IS NULL)
+     AND  (quitem_quhead_id=pQuheadid));
+  IF (FOUND) THEN
+    INSERT INTO evntlog (evntlog_evnttime, evntlog_username, evntlog_evnttype_id,
+                         evntlog_ordtype, evntlog_ord_id, evntlog_warehous_id, evntlog_number)
+    SELECT CURRENT_TIMESTAMP, evntnot_username, evnttype_id,
+           'Q', quhead_id, quhead_warehous_id, quhead_number
+    FROM evntnot, evnttype, quhead
+    WHERE ( (evntnot_evnttype_id=evnttype_id)
+     AND (evntnot_warehous_id=quhead_warehous_id)
+     AND (evnttype_name='CannotConvertQuote')
+     AND (quhead_id=pQuheadid) );
+
+    RETURN -1;
+  END IF;
+
+-- Get Credit Stat, Uses POs and Blanket POs
+
+  SELECT cust_creditstatus, cust_usespos, cust_blanketpos
+    INTO _creditstatus, _usespos, _blanketpos
+  FROM quhead, custinfo
+  WHERE ((quhead_cust_id=cust_id)
+    AND  (quhead_id=pQuheadid));
+
+-- Check to see if customer or prospect
+
+  IF (NOT FOUND) THEN
+    SELECT prospect_id INTO _prospectid
+    FROM quhead, prospect
+    WHERE ((quhead_cust_id=prospect_id)
+      AND  (quhead_id=pQuheadid));
+    IF (NOT FOUND) THEN
+      RETURN -2;
+    ELSE
+      RETURN -3;
+    END IF;
+  ELSIF (_creditstatus = 'H' AND NOT hasPriv('CreateSOForHoldCustomer')) THEN
+    RETURN -4;
+  ELSIF (_creditstatus = 'W' AND NOT hasPriv('CreateSOForWarnCustomer')) THEN
+    RETURN -5;
+  END IF;
+
+-- PO/blanket PO checks
+
+  IF ( (_usespos) AND (NOT _blanketpos) ) THEN
+    PERFORM invchead_id
+    FROM quhead JOIN invchead ON ( (invchead_cust_id=quhead_cust_id) AND
+                                 (UPPER(invchead_custponumber)=UPPER(quhead_custponumber)) )
+    WHERE (quhead_id=pQuheadid);
+    IF (FOUND) THEN
+      RAISE EXCEPTION 'Duplicate Customer PO';
+    END IF;
+  END IF;
+
+--Check to see if an invoice exists with the quote number
+  
+  PERFORM quhead_number, invchead_id 
+  FROM quhead, invchead 
+  WHERE quhead_id = pQuheadid
+  AND invchead_invcnumber = quhead_number;
+
+-- If it does then get a new Invoice number otherwise use the quote number as the invoice number
+
+  IF (FOUND) THEN
+    SELECT fetchinvcnumber() INTO _inNum;
+  ELSE
+    SELECT quhead_number INTO _inNum
+    FROM quhead
+    WHERE quhead_id = pQuheadid;
+  END IF;
+
+--Insert quote info into invoice tables
+
+  SELECT NEXTVAL('invchead_invchead_id_seq') INTO _iheadid;
+  INSERT INTO invchead
+  ( invchead_ordernumber, invchead_shipdate, invchead_recurring,
+    invchead_id, invchead_invcnumber, invchead_cust_id,
+    invchead_orderdate, invchead_ponumber, 
+    invchead_billto_name, invchead_billto_address1,
+    invchead_billto_address2, invchead_billto_address3,
+    invchead_billto_city, invchead_billto_state, invchead_billto_zipcode, invchead_billto_country,
+    invchead_shipto_id, invchead_shipto_name, invchead_shipto_address1,
+    invchead_shipto_address2, invchead_shipto_address3,
+    invchead_shipto_city, invchead_shipto_state, invchead_shipto_zipcode, invchead_shipto_country, 
+    invchead_salesrep_id, invchead_commission,
+    invchead_terms_id, invchead_shipchrg_id, invchead_fob, invchead_shipvia,
+    invchead_notes, invchead_freight, 
+    invchead_misc_amount, invchead_misc_accnt_id, invchead_misc_descrip,
+    invchead_prj_id, invchead_curr_id, invchead_taxzone_id,
+    invchead_posted, invchead_printed, invchead_invcdate,
+    invchead_saletype_id, invchead_shipzone_id
+    --invchead_taxtype_id,
+    --invchead_shipto_cntct_id, invchead_shipto_cntct_honorific, invchead_shipto_cntct_first_name,
+    --invchead_shipto_cntct_middle, invchead_shipto_cntct_last_name, invchead_shipto_cntct_suffix,
+    --invchead_shipto_cntct_phone, invchead_shipto_cntct_title, invchead_shipto_cntct_fax, 
+    --invchead_shipto_cntct_email,
+    --invchead_billto_cntct_id, invchead_billto_cntct_honorific,
+    --invchead_billto_cntct_first_name, invchead_billto_cntct_middle, invchead_billto_cntct_last_name, 
+    --invchead_billto_cntct_suffix, invchead_billto_cntct_phone, invchead_billto_cntct_title, 
+    --invchead_billto_cntct_fax, invchead_billto_cntct_email, 
+    --invchead_ophead_id,
+    --invchead_calcfreight 
+    )
+  SELECT quhead_number, quhead_packdate, 'f',
+         _iheadid, _inNum, quhead_cust_id,
+         CURRENT_DATE, quhead_custponumber, 
+         quhead_billtoname, quhead_billtoaddress1,
+         quhead_billtoaddress2, quhead_billtoaddress3,
+         quhead_billtocity, quhead_billtostate, quhead_billtozip, quhead_billtocountry,
+         quhead_shipto_id, quhead_shiptoname, quhead_shiptoaddress1,
+         quhead_shiptoaddress2, quhead_shiptoaddress3,
+         quhead_shiptocity, quhead_shiptostate, quhead_shiptozipcode, quhead_shiptocountry,
+         quhead_salesrep_id, quhead_commission,
+         quhead_terms_id, cust_shipchrg_id, quhead_fob, quhead_shipvia,
+         quhead_ordercomments,  quhead_freight,
+         quhead_misc, quhead_misc_accnt_id, quhead_misc_descrip,
+         quhead_prj_id, quhead_curr_id, quhead_taxzone_id,
+         'f','f',current_date,
+         quhead_saletype_id, quhead_shipzone_id
+         --quhead_shipto_cntct_id, quhead_shipto_cntct_honorific,
+        --quhead_shipto_cntct_first_name, quhead_shipto_cntct_middle, quhead_shipto_cntct_last_name,
+        --quhead_shipto_cntct_suffix, quhead_shipto_cntct_phone, quhead_shipto_cntct_title,
+        --quhead_shipto_cntct_fax, quhead_shipto_cntct_email, quhead_billto_cntct_id,
+        --quhead_billto_cntct_honorific, quhead_billto_cntct_first_name, quhead_billto_cntct_middle,
+        --quhead_billto_cntct_last_name, quhead_billto_cntct_suffix, quhead_billto_cntct_phone,
+        --quhead_billto_cntct_title, quhead_billto_cntct_fax, quhead_billto_cntct_email, quhead_ophead_id,
+         --quhead_calcfreight
+  FROM quhead JOIN custinfo ON (cust_id=quhead_cust_id)
+  WHERE (quhead_id=pQuheadid);
+
+-- Attachments on Invoice not supported but leaving this in for future use:
+/*
+  UPDATE url SET url_source_id = _iheadid,
+                 url_source = 'I'
+  WHERE ((url_source='Q') AND (url_source_id = pQuheadid));
+
+  UPDATE imageass SET imageass_source_id = _iheadid,
+                      imageass_source = 'I'
+  WHERE ((imageass_source='Q') AND (imageass_source_id = pQuheadid));
+
+  UPDATE docass SET docass_source_id = _iheadid,
+                    docass_source_type = 'I'
+  WHERE ((docass_source_type='Q') AND (docass_source_id = pQuheadid));
+*/
+
+
+-- Comments not supported on Invoice but leaving this in for future use:
+
+/*  
+  INSERT INTO comment
+  ( comment_cmnttype_id, comment_source, comment_source_id, comment_date, comment_user, comment_text, comment_public )
+  SELECT comment_cmnttype_id, 'I', _iheadid, comment_date, comment_user, ('Quote-' || comment_text), comment_public
+  FROM comment
+  WHERE ( (comment_source='Q')
+    AND   (comment_source_id=pQuheadid) );
+*/
+
+  FOR _r IN SELECT quitem.*,
+                   quhead_number, quhead_prj_id,
+                   itemsite_item_id, itemsite_leadtime,
+                   itemsite_createsopo, itemsite_createsopr,
+                   item_type, COALESCE(quitem_itemsrc_id, itemsrc_id, -1) AS itemsrcid
+            FROM quhead JOIN quitem ON (quitem_quhead_id=quhead_id)
+                        JOIN itemsite ON (itemsite_id=quitem_itemsite_id)
+                        JOIN item ON (item_id=itemsite_item_id)
+                        LEFT OUTER JOIN itemsrc ON ( (itemsrc_item_id=item_id) AND
+                                                     (itemsrc_default) )
+            WHERE (quhead_id=pQuheadid) LOOP
+
+    SELECT NEXTVAL('invcitem_invcitem_id_seq') INTO _iitemid;
+
+    INSERT INTO invcitem
+    ( invcitem_id, invcitem_invchead_id, invcitem_linenumber, 
+      invcitem_item_id,
+      invcitem_warehous_id,
+      --invcitem_status, 
+      --invcitem_scheddate, invcitem_promdate,
+      invcitem_price, invcitem_custprice, 
+      invcitem_ordered, invcitem_billed,
+      invcitem_qty_uom_id, invcitem_qty_invuomratio,
+      invcitem_price_uom_id, invcitem_price_invuomratio,
+      invcitem_custpn, invcitem_notes, invcitem_taxtype_id )
+    VALUES
+    ( _iitemid, _iheadid, _r.quitem_linenumber, 
+      (SELECT itemsite_item_id FROM itemsite WHERE itemsite_id = _r.quitem_itemsite_id),
+      (SELECT itemsite_warehous_id FROM itemsite WHERE itemsite_id = _r.quitem_itemsite_id),
+      --'O', 
+      --_r.quitem_scheddate, _r.quitem_promdate,
+      _r.quitem_price, _r.quitem_custprice,
+      _r.quitem_qtyord, _r.quitem_qtyord,
+      _r.quitem_qty_uom_id, _r.quitem_qty_invuomratio,
+      _r.quitem_price_uom_id, _r.quitem_price_invuomratio,
+      _r.quitem_custpn, _r.quitem_memo, _r.quitem_taxtype_id );
+
+    IF (fetchMetricBool('enablextcommissionission')) THEN
+      PERFORM xtcommission.getSalesReps(quhead_cust_id, quhead_shipto_id,
+                                        _r.itemsite_item_id, _r.quitem_price,
+                                        _iitemid, 'InvoiceItem')
+      FROM quhead
+      WHERE (quhead_id=pQuheadid);
+    END IF;
+
+-- Chracteristics not supported on Invoice but leaving in for future use:
+
+/*
+    INSERT INTO charass
+          (charass_target_type, charass_target_id, charass_char_id, charass_value, charass_default, charass_price)
+    SELECT 'SI', _iitemid, charass_char_id, charass_value, charass_default, charass_price
+      FROM charass
+     WHERE ((charass_target_type='QI')
+       AND  (charass_target_id=_r.quitem_id));
+*/
+
+
+-- Comments not supported but leaving in for future use
+
+/*
+    INSERT INTO comment
+    ( comment_cmnttype_id, comment_source, comment_source_id, comment_date, comment_user, comment_text )
+    SELECT comment_cmnttype_id, 'SI', _iitemid, comment_date, comment_user, ('Quote-' || comment_text)
+    FROM comment
+    WHERE ( (comment_source='QI')
+      AND   (comment_source_id=_r.quitem_id) );
+*/
+
+    _orderid := -1;
+    _ordertype := '';
+    IF (_r.quitem_createorder) THEN
+
+      IF (_r.item_type IN ('M')) THEN
+        SELECT createWo( CAST(_r.quhead_number AS INTEGER), supply.itemsite_id, 1, (_r.quitem_qtyord * _r.quitem_qty_invuomratio),
+                         _r.itemsite_leadtime, _r.quitem_scheddate, _r.quitem_memo, 'Q', _iitemid, _r.quhead_prj_id ) INTO _orderId
+        FROM itemsite sold, itemsite supply
+        WHERE ((sold.itemsite_item_id=supply.itemsite_item_id)
+         AND (supply.itemsite_warehous_id=_r.quitem_order_warehous_id)
+         AND (sold.itemsite_id=_r.quitem_itemsite_id) );
+        _orderType := 'W';
+
+        INSERT INTO charass
+              (charass_target_type, charass_target_id, charass_char_id, charass_value)
+        SELECT 'W', _orderId, charass_char_id, charass_value
+          FROM charass
+         WHERE ((charass_target_type='QI')
+           AND  (charass_target_id=_r.quitem_id));
+
+      ELSIF ( (_r.item_type IN ('P', 'O')) AND (_r.itemsite_createsopr) ) THEN
+        SELECT createPr( CAST(_r.quhead_number AS INTEGER), _r.quitem_itemsite_id, (_r.quitem_qtyord * _r.quitem_qty_invuomratio),
+                         _r.quitem_scheddate, '', 'S', _iitemid ) INTO _orderId;
+        _orderType := 'R';
+        UPDATE pr SET pr_prj_id=_r.quhead_prj_id WHERE pr_id=_orderId;
+      ELSIF ( (_r.item_type IN ('P', 'O')) AND (_r.itemsite_createsopo) ) THEN
+        IF (_r.quitem_prcost=0) THEN
+-- For now quote to invoice/dropship will not be supported but with the creation of a createPurchaseToQuote() version of createPurchaseToSale()
+-- it can be
+--          SELECT createPurchaseToSale(_iitemid, _r.itemsrcid, _r.quitem_dropship) INTO _orderId;
+            RAISE EXCEPTION 'Quote contains one or more dropship items that may not be converted from a Quote to an Invoice';
+        ELSE
+-- For now quote to invoice/dropship will not be supported but with the creation of a createPurchaseToQuote() version of createPurchaseToSale()
+-- it can be
+--          SELECT createPurchaseToSale(_iitemid, _r.itemsrcid, _r.quitem_dropship, _r.quitem_prcost) INTO _orderId;
+            RAISE EXCEPTION 'Quote contains one or more dropship items that may not be converted from a Quote to an Invoice';
+        END IF;
+        _orderType := 'P';
+      END IF;
+
+--      UPDATE invcitem SET invcitem_order_type=_ordertype, invcitem_order_id=_orderid
+--      WHERE (invcitem_id=_iitemid);
+
+    END IF;
+
+  END LOOP;
+
+  SELECT metric_value INTO _showConvertedQuote
+  FROM metric WHERE metric_name = 'ShowQuotesAfterSO';
+
+  IF (_showConvertedQuote) THEN
+    UPDATE quhead
+    SET quhead_status= 'C'
+    WHERE (quhead_id = pQuheadid);
+  ELSE
+     PERFORM deleteQuote(pQuheadid);
+  END IF;
+
+  RETURN _iheadid;
+
+END;
+$$
+  LANGUAGE plpgsql VOLATILE
+  COST 100;
+
diff --git a/foundation-database/public/functions/copybom.sql b/foundation-database/public/functions/copybom.sql
new file mode 100644 (file)
index 0000000..4c00775
--- /dev/null
@@ -0,0 +1,140 @@
+CREATE OR REPLACE FUNCTION copyBOM(pSItemid       INTEGER,
+                                   pTItemid       INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _result INTEGER;
+
+BEGIN
+
+  SELECT copyBOM (pSItemid, PTItemid, FALSE) into _result;
+
+  RETURN _result;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION copyBOM(pSItemid       INTEGER,
+                                   pTItemid       INTEGER,
+                                   pCopyUsedAt    BOOLEAN) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _bh RECORD;
+  _bi RECORD;
+  _bomheadid INTEGER;
+  _bomitemid INTEGER;
+  _bomworksetid INTEGER;
+  _temp INTEGER;
+  _schedatwooper BOOLEAN;
+  _booitemseqid INTEGER;
+
+BEGIN
+
+--  Cache source bomhead
+  SELECT * INTO _bh
+  FROM bomhead
+  WHERE ((bomhead_item_id=pSItemid)
+    AND  (bomhead_rev_id=getActiveRevID('BOM', pSItemid)));
+
+  IF (NOT FOUND) THEN
+    RETURN -1;
+  END IF;
+
+--  Make sure that source bomitems exist
+  SELECT bomitem_id INTO _bomitemid
+  FROM bomitem
+  WHERE ((bomitem_parent_item_id=_bh.bomhead_item_id)
+    AND  (bomitem_rev_id=_bh.bomhead_rev_id))
+  LIMIT 1;
+
+  IF (NOT FOUND) THEN
+    RETURN -2;
+  END IF;
+
+--  Make sure that target bomitems do not exist
+  SELECT bomitem_id INTO _bomitemid
+  FROM bomitem
+  WHERE ((bomitem_parent_item_id=pTItemid)
+    AND  (bomitem_rev_id= -1))
+  LIMIT 1;
+
+  IF (FOUND) THEN
+    RETURN -3;
+  END IF;
+
+--  Make sure that the parent is not used in the component at some level
+  IF ( SELECT (item_type IN ('M', 'F'))
+       FROM item
+       WHERE (item_id=pSItemid) ) THEN
+    SELECT indentedWhereUsed(pTItemid) INTO _bomworksetid;
+    SELECT bomwork_id INTO _temp
+    FROM bomwork
+    WHERE ( (bomwork_set_id=_bomworksetid)
+     AND (bomwork_item_id=pSItemid) )
+    LIMIT 1;
+    IF (FOUND) THEN
+      PERFORM deleteBOMWorkset(_bomworksetid);
+      RETURN -4;
+    END IF;
+    PERFORM deleteBOMWorkset(_bomworksetid);
+  END IF;
+
+--  Check for existing target bomhead
+  SELECT bomhead_id INTO _bomheadid
+  FROM bomhead
+  WHERE ((bomhead_item_id=pTItemid)
+    AND  (bomhead_rev_id= -1));
+
+  IF (NOT FOUND) THEN
+    INSERT INTO bomhead
+    ( bomhead_item_id, bomhead_serial, bomhead_docnum,
+      bomhead_batchsize, bomhead_requiredqtyper )
+    VALUES
+    ( pTItemid, _bh.bomhead_serial, _bh.bomhead_docnum,
+      _bh.bomhead_batchsize, _bh.bomhead_requiredqtyper );
+  END IF;
+
+  FOR _bi IN SELECT bomitem.*
+             FROM bomitem(pSItemid) 
+             WHERE (bomitem_expires>CURRENT_DATE) LOOP
+
+    SELECT NEXTVAL('bomitem_bomitem_id_seq') INTO _bomitemid;
+
+    IF (pCopyUsedAt) THEN
+      _schedatwooper := _bi.bomitem_schedatwooper;
+      _booitemseqid := _bi.bomitem_booitem_seq_id;
+    ELSE
+      _schedatwooper := FALSE;
+      _booitemseqid := -1;
+    END IF;
+
+    INSERT INTO bomitem
+    ( bomitem_id, bomitem_parent_item_id, bomitem_seqnumber, bomitem_item_id,
+      bomitem_uom_id, bomitem_qtyfxd, bomitem_qtyper, bomitem_scrap, bomitem_schedatwooper,
+      bomitem_booitem_seq_id,
+      bomitem_effective, bomitem_expires, bomitem_ecn,
+      bomitem_createwo, bomitem_issuemethod, bomitem_moddate, bomitem_subtype,
+      bomitem_notes, bomitem_ref )
+    VALUES
+    ( _bomitemid, pTItemid, _bi.bomitem_seqnumber, _bi.bomitem_item_id,
+      _bi.bomitem_uom_id, _bi.bomitem_qtyfxd, _bi.bomitem_qtyper, _bi.bomitem_scrap, _schedatwooper,
+      _booitemseqid,
+      CURRENT_DATE, _bi.bomitem_expires, _bi.bomitem_ecn,
+      _bi.bomitem_createwo, _bi.bomitem_issuemethod, CURRENT_DATE, _bi.bomitem_subtype,
+      _bi.bomitem_notes, _bi.bomitem_ref );
+
+    INSERT INTO bomitemsub
+    ( bomitemsub_bomitem_id, bomitemsub_item_id,
+      bomitemsub_uomratio, bomitemsub_rank )
+    SELECT _bomitemid, bomitemsub_item_id,
+           bomitemsub_uomratio, bomitemsub_rank
+    FROM bomitemsub
+    WHERE (bomitemsub_bomitem_id=_bi.bomitem_id);
+
+  END LOOP;
+
+  RETURN pTItemid;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/copybudget.sql b/foundation-database/public/functions/copybudget.sql
new file mode 100644 (file)
index 0000000..21988aa
--- /dev/null
@@ -0,0 +1,39 @@
+
+CREATE OR REPLACE FUNCTION copyBudget(INTEGER, TEXT, TEXT, INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pBudgheadid ALIAS FOR $1;
+  pName ALIAS FOR $2;
+  pDescrip ALIAS FOR $3;
+  pInterval ALIAS FOR $4;
+  _budgheadid INTEGER;
+  _periodid INTEGER;
+  _result INTEGER;
+
+BEGIN
+  SELECT 1 INTO _result
+    FROM budgitem
+   WHERE ((budgitem_budghead_id=pBudgheadid)
+     AND  (nextPeriodByInterval(budgitem_period_id, pInterval)=-1))
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -1;
+  END IF;
+
+  SELECT nextval(''budghead_budghead_id_seq'') INTO _budgheadid;
+  INSERT INTO budghead
+        (budghead_id, budghead_name, budghead_descrip)
+  VALUES(_budgheadid, pName, pDescrip);
+
+  INSERT INTO budgitem (budgitem_budghead_id, budgitem_period_id,
+                        budgitem_accnt_id, budgitem_amount)
+  SELECT _budgheadid, nextPeriodByInterval(budgitem_period_id, pInterval),
+         budgitem_accnt_id, budgitem_amount
+    FROM budgitem
+   WHERE (budgitem_budghead_id=pBudgheadid);
+
+  RETURN _budgheadid;
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/copycmd.sql b/foundation-database/public/functions/copycmd.sql
new file mode 100644 (file)
index 0000000..67bf6c8
--- /dev/null
@@ -0,0 +1,24 @@
+CREATE OR REPLACE FUNCTION copycmd(INTEGER, TEXT, TEXT) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCmdId       ALIAS FOR $1;
+  pModule      ALIAS FOR $2;
+  pTitle       ALIAS FOR $3;
+  _cmdId       INTEGER;
+BEGIN
+    SELECT nextval(''cmd_cmd_id_seq'') INTO _cmdId;
+
+    INSERT INTO cmd 
+      SELECT _cmdId, pModule, pTitle, cmd_descrip, cmd_privname, cmd_executable
+      FROM cmd
+      WHERE (cmd_id=pCmdId);
+
+    INSERT INTO cmdarg (cmdarg_cmd_id, cmdarg_order, cmdarg_arg)
+      SELECT _cmdId, cmdarg_order, cmdarg_arg
+      FROM cmdarg
+      WHERE (cmdarg_cmd_id=pCmdId);
+
+    RETURN 1;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/copycontract.sql b/foundation-database/public/functions/copycontract.sql
new file mode 100644 (file)
index 0000000..ffafc62
--- /dev/null
@@ -0,0 +1,109 @@
+CREATE OR REPLACE FUNCTION copyContract(pContrctid INTEGER,
+                                        pNumber TEXT,
+                                        pEffective DATE,
+                                        pExpires DATE) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _contrctid INTEGER;
+  _itemsrcid INTEGER;
+  _r RECORD;
+
+BEGIN
+
+  INSERT INTO contrct
+  ( contrct_number,
+    contrct_vend_id,
+    contrct_descrip,
+    contrct_effective,
+    contrct_expires,
+    contrct_note )
+  SELECT
+    pNumber,
+    contrct_vend_id,
+    contrct_descrip,
+    pEffective,
+    pExpires,
+    contrct_note
+  FROM contrct
+  WHERE (contrct_id=pContrctid)
+  RETURNING contrct_id INTO _contrctid;
+
+  FOR _r IN
+  SELECT * FROM itemsrc WHERE (itemsrc_contrct_id=pContrctid)
+  LOOP
+  INSERT INTO itemsrc
+    ( itemsrc_item_id,
+      itemsrc_vend_id,
+      itemsrc_vend_item_number,
+      itemsrc_vend_item_descrip,
+      itemsrc_comments,
+      itemsrc_vend_uom,
+      itemsrc_invvendoruomratio,
+      itemsrc_minordqty,
+      itemsrc_multordqty,
+      itemsrc_leadtime,
+      itemsrc_ranking,
+      itemsrc_active,
+      itemsrc_manuf_name,
+      itemsrc_manuf_item_number,
+      itemsrc_manuf_item_descrip,
+      itemsrc_default,
+      itemsrc_upccode,
+      itemsrc_effective,
+      itemsrc_expires,
+      itemsrc_contrct_id )
+    VALUES
+    ( _r.itemsrc_item_id,
+      _r.itemsrc_vend_id,
+      _r.itemsrc_vend_item_number,
+      _r.itemsrc_vend_item_descrip,
+      _r.itemsrc_comments,
+      _r.itemsrc_vend_uom,
+      _r.itemsrc_invvendoruomratio,
+      _r.itemsrc_minordqty,
+      _r.itemsrc_multordqty,
+      _r.itemsrc_leadtime,
+      _r.itemsrc_ranking,
+      _r.itemsrc_active,
+      _r.itemsrc_manuf_name,
+      _r.itemsrc_manuf_item_number,
+      _r.itemsrc_manuf_item_descrip,
+      _r.itemsrc_default,
+      _r.itemsrc_upccode,
+      pEffective,
+      pExpires,
+      _contrctid )
+    RETURNING itemsrc_id INTO _itemsrcid;
+
+  INSERT INTO itemsrcp
+    ( itemsrcp_itemsrc_id,
+      itemsrcp_qtybreak,
+      itemsrcp_price,
+      itemsrcp_updated,
+      itemsrcp_curr_id,
+      itemsrcp_dropship,
+      itemsrcp_warehous_id,
+      itemsrcp_type,
+      itemsrcp_discntprcnt,
+      itemsrcp_fixedamtdiscount )
+    SELECT
+      _itemsrcid,
+      itemsrcp_qtybreak,
+      itemsrcp_price,
+      CURRENT_DATE,
+      itemsrcp_curr_id,
+      itemsrcp_dropship,
+      itemsrcp_warehous_id,
+      itemsrcp_type,
+      itemsrcp_discntprcnt,
+      itemsrcp_fixedamtdiscount
+    FROM itemsrcp
+    WHERE (itemsrcp_itemsrc_id=_r.itemsrc_id);
+
+  END LOOP;
+
+  RETURN _contrctid;
+
+END;
+$$ LANGUAGE plpgsql;
diff --git a/foundation-database/public/functions/copyfinancialgroup.sql b/foundation-database/public/functions/copyfinancialgroup.sql
new file mode 100644 (file)
index 0000000..197fb30
--- /dev/null
@@ -0,0 +1,87 @@
+CREATE OR REPLACE FUNCTION copyFinancialGroup(INTEGER, INTEGER, INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSourceGroup ALIAS FOR $1;
+  pFlheadid ALIAS FOR $2;
+  pParentFlgrpid ALIAS FOR $3;
+
+  _flgrpid INTEGER;
+
+BEGIN
+
+  SELECT nextval(''flgrp_flgrp_id_seq'') INTO _flgrpid;
+
+-- Copy the group item
+  INSERT INTO flgrp
+         (flgrp_id, flgrp_flhead_id, flgrp_flgrp_id,
+          flgrp_order, flgrp_name, flgrp_descrip,
+          flgrp_subtotal, flgrp_summarize, flgrp_subtract,
+          flgrp_showstart, flgrp_showend,
+          flgrp_showdelta, flgrp_showbudget, flgrp_showdiff, flgrp_showcustom,
+          flgrp_showstartprcnt, flgrp_showendprcnt,
+          flgrp_showdeltaprcnt, flgrp_showbudgetprcnt, flgrp_showdiffprcnt, flgrp_showcustomprcnt,
+          flgrp_usealtsubtotal, flgrp_altsubtotal,flgrp_prcnt_flgrp_id)
+  SELECT _flgrpid, pFlheadid, pParentFlgrpid,
+         flgrp_order, flgrp_name, flgrp_descrip,
+         flgrp_subtotal, flgrp_summarize, flgrp_subtract,
+         flgrp_showstart, flgrp_showend,
+         flgrp_showdelta, flgrp_showbudget, flgrp_showdiff, flgrp_showcustom,
+         flgrp_showstartprcnt, flgrp_showendprcnt,
+         flgrp_showdeltaprcnt, flgrp_showbudgetprcnt, flgrp_showdiffprcnt, flgrp_showcustomprcnt,
+         flgrp_usealtsubtotal, flgrp_altsubtotal,flgrp_prcnt_flgrp_id
+    FROM flgrp
+   WHERE (flgrp_id=pSourceGroup);
+
+-- Store temporary cross ref info
+   
+   EXECUTE '' INSERT INTO tmp_flgrpxref'' || getEffectiveXtUser() || '' (flgrpxref_oldid,flgrpxref_newid) VALUES ('' || pSourceGroup || '','' || _flgrpid || '');'';
+
+-- Copy any children flitems
+  INSERT INTO flitem
+         (flitem_flhead_id, flitem_flgrp_id,
+          flitem_order, flitem_accnt_id, flitem_showstart,
+          flitem_showend, flitem_showdelta, flitem_showbudget, flitem_showdiff, flitem_showcustom,
+          flitem_subtract, flitem_showstartprcnt,
+          flitem_showendprcnt, flitem_showdeltaprcnt,
+          flitem_showbudgetprcnt, flitem_showdiffprcnt, flitem_showcustomprcnt,
+          flitem_custom_source, flitem_company, flitem_profit, flitem_number,
+          flitem_sub, flitem_type, flitem_subaccnttype_code, flitem_prcnt_flgrp_id)
+  SELECT pFlheadid, _flgrpid,
+         flitem_order, flitem_accnt_id, flitem_showstart,
+         flitem_showend, flitem_showdelta, flitem_showbudget, flitem_showdiff, flitem_showcustom,
+         flitem_subtract, flitem_showstartprcnt,
+         flitem_showendprcnt, flitem_showdeltaprcnt,
+         flitem_showbudgetprcnt, flitem_showdiffprcnt, flitem_showcustomprcnt,
+         flitem_custom_source, flitem_company, flitem_profit, flitem_number,
+          flitem_sub, flitem_type, flitem_subaccnttype_code, flitem_prcnt_flgrp_id
+    FROM flitem
+   WHERE (flitem_flgrp_id=pSourceGroup);
+
+-- Copy any children flspecs
+  INSERT INTO flspec
+         (flspec_flhead_id, flspec_flgrp_id,
+          flspec_order, flspec_name, flspec_type, flspec_showstart,
+          flspec_showend, flspec_showdelta, flspec_showbudget, flspec_showdiff, flspec_showcustom,
+          flspec_subtract, flspec_showstartprcnt,
+          flspec_showendprcnt, flspec_showdeltaprcnt,
+          flspec_showbudgetprcnt, flspec_showdiffprcnt, flspec_showcustomprcnt,
+          flspec_custom_source, flspec_prcnt_flgrp_id)
+  SELECT pFlheadid, _flgrpid,
+         flspec_order, flspec_name, flspec_type, flspec_showstart,
+         flspec_showend, flspec_showdelta, flspec_showbudget, flspec_showdiff, flspec_showcustom,
+         flspec_subtract, flspec_showstartprcnt,
+         flspec_showendprcnt, flspec_showdeltaprcnt,
+         flspec_showbudgetprcnt, flspec_showdiffprcnt, flspec_showcustomprcnt,
+         flspec_custom_source, flspec_prcnt_flgrp_id
+    FROM flspec
+   WHERE (flspec_flgrp_id=pSourceGroup);
+
+-- Copy the groups
+  PERFORM copyFinancialGroup(flgrp_id, pFlheadid, _flgrpid)
+     FROM flgrp
+    WHERE (flgrp_flgrp_id=pSourceGroup);
+
+  RETURN _flgrpid;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/copyglseries.sql b/foundation-database/public/functions/copyglseries.sql
new file mode 100644 (file)
index 0000000..0c7d757
--- /dev/null
@@ -0,0 +1,33 @@
+
+CREATE OR REPLACE FUNCTION copyGLSeries(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSequence ALIAS FOR $1;
+  _sequence INTEGER := fetchGLSequence();
+  _journal INTEGER;    
+
+BEGIN
+
+  SELECT gltrans_journalnumber INTO _journal
+  FROM gltrans
+  WHERE ( gltrans_sequence=pSequence )
+  LIMIT 1;
+
+  IF (FOUND) THEN
+    INSERT INTO glseries
+    ( glseries_sequence, glseries_source, glseries_doctype, glseries_docnumber,
+      glseries_notes, glseries_accnt_id, glseries_amount, glseries_distdate )
+    SELECT _sequence, gltrans_source, gltrans_doctype, gltrans_docnumber,
+           gltrans_notes, gltrans_accnt_id,
+           gltrans_amount, gltrans_date
+    FROM gltrans
+    WHERE ( gltrans_sequence=pSequence );
+  ELSE
+    RAISE EXCEPTION 'g/l transaction sequence not found';
+  END IF;
+
+  RETURN _sequence;
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/copyincdt.sql b/foundation-database/public/functions/copyincdt.sql
new file mode 100644 (file)
index 0000000..01e0a37
--- /dev/null
@@ -0,0 +1,69 @@
+CREATE OR REPLACE FUNCTION copyIncdt(INTEGER, TIMESTAMP WITH TIME ZONE) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pparentid   ALIAS FOR $1;
+  ptimestamp  TIMESTAMP WITH TIME ZONE := COALESCE($2, CURRENT_TIMESTAMP);
+
+  _alarmid    INTEGER;
+  _incdtid    INTEGER;
+  _todoitemid INTEGER;
+
+BEGIN
+  INSERT INTO incdt(incdt_number,          incdt_crmacct_id,
+                    incdt_cntct_id,        incdt_summary,
+                    incdt_descrip,         incdt_item_id,
+                    incdt_timestamp,       incdt_incdtcat_id,
+                    incdt_incdtseverity_id,incdt_incdtpriority_id,
+                    incdt_owner_username,  incdt_recurring_incdt_id
+           ) SELECT fetchIncidentNumber(), incdt_crmacct_id,
+                    incdt_cntct_id,        incdt_summary,
+                    incdt_descrip,         incdt_item_id,
+                    ptimestamp,            incdt_incdtcat_id,
+                    incdt_incdtseverity_id,incdt_incdtpriority_id,
+                    incdt_owner_username,  incdt_recurring_incdt_id
+               FROM incdt
+              WHERE (incdt_id=pparentid)
+  RETURNING incdt_id INTO _incdtid;
+
+  IF (_incdtid IS NULL) THEN
+    RETURN -10;
+  END IF;
+
+  SELECT MIN(copyTodoitem(todoitem_id, CAST(ptimestamp AS DATE), _incdtid))
+            INTO _todoitemid
+    FROM todoitem
+   WHERE (todoitem_incdt_id=pparentid);
+
+  IF (_todoitemid < 0) THEN
+    RETURN _todoitemid;
+  END IF;
+
+  SELECT saveAlarm(NULL, NULL, CAST(ptimestamp AS DATE),
+                   CAST(alarm_time - DATE_TRUNC('day',alarm_time) AS TIME),
+                   alarm_time_offset,
+                   alarm_time_qualifier,
+                   alarm_event_recipient  IS NOT NULL, alarm_event_recipient,
+                   alarm_email_recipient  IS NOT NULL, alarm_email_recipient,
+                   alarm_sysmsg_recipient IS NOT NULL, alarm_sysmsg_recipient,
+                   'INCDT', _incdtid, 'CHANGEONE')
+    INTO _alarmid
+    FROM alarm
+   WHERE ((alarm_source='INCDT')
+      AND (alarm_source_id=pparentid));
+
+   IF (_alarmid < 0) THEN
+     RETURN _alarmid;
+   END IF;
+
+   INSERT INTO docass (docass_source_id, docass_source_type,
+                       docass_target_id, docass_target_type, docass_purpose
+              ) SELECT _incdtid,       'INCDT',
+                       docass_target_id, docass_target_type, docass_purpose
+                  FROM docass
+                 WHERE ((docass_source_id=pparentid)
+                    AND (docass_source_type='INCDT'));
+
+  RETURN _incdtid;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/copyinvoice.sql b/foundation-database/public/functions/copyinvoice.sql
new file mode 100644 (file)
index 0000000..a875421
--- /dev/null
@@ -0,0 +1,113 @@
+CREATE OR REPLACE FUNCTION copyInvoice(INTEGER, DATE) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pInvcheadid ALIAS FOR $1;
+  _invcheadid INTEGER;
+  _invcnumber TEXT;
+  _invcdate DATE := COALESCE($2, CURRENT_DATE);
+  _i RECORD;
+  _l RECORD;
+  _invcitemid INTEGER;
+
+BEGIN
+  SELECT *
+    INTO _i
+    FROM invchead
+   WHERE(invchead_id=pInvcheadid);
+  IF(NOT FOUND) THEN
+    RETURN -1;
+  END IF;
+
+  _invcnumber := fetchInvcNumber();
+  _invcheadid := nextval('invchead_invchead_id_seq');
+
+  INSERT INTO invchead
+        (invchead_id,
+         invchead_cust_id, invchead_shipto_id,
+         invchead_ordernumber, invchead_orderdate,
+         invchead_posted, invchead_printed,
+         invchead_invcnumber, invchead_invcdate, invchead_shipdate,
+         invchead_ponumber, invchead_shipvia,
+         invchead_fob, invchead_billto_name,
+         invchead_billto_address1, invchead_billto_address2,
+         invchead_billto_address3, invchead_billto_city,
+         invchead_billto_state, invchead_billto_zipcode,
+         invchead_billto_phone, invchead_shipto_name,
+         invchead_shipto_address1, invchead_shipto_address2,
+         invchead_shipto_address3, invchead_shipto_city,
+         invchead_shipto_state, invchead_shipto_zipcode,
+         invchead_shipto_phone, invchead_salesrep_id,
+         invchead_commission,
+         invchead_terms_id, invchead_freight,
+         invchead_misc_amount,
+         invchead_misc_descrip, invchead_misc_accnt_id,
+         invchead_payment, invchead_paymentref,
+         invchead_notes,
+         invchead_billto_country, invchead_shipto_country,
+         invchead_prj_id, invchead_curr_id,
+         invchead_taxzone_id,
+         invchead_recurring_invchead_id,
+         invchead_saletype_id, invchead_shipzone_id)
+  VALUES(_invcheadid,
+         _i.invchead_cust_id, _i.invchead_shipto_id,
+         _i.invchead_ordernumber, _i.invchead_orderdate,
+         false, false,
+         _invcnumber, _invcdate, _i.invchead_shipdate,
+         _i.invchead_ponumber, _i.invchead_shipvia,
+         _i.invchead_fob, _i.invchead_billto_name,
+         _i.invchead_billto_address1, _i.invchead_billto_address2,
+         _i.invchead_billto_address3, _i.invchead_billto_city,
+         _i.invchead_billto_state, _i.invchead_billto_zipcode,
+         _i.invchead_billto_phone, _i.invchead_shipto_name,
+         _i.invchead_shipto_address1, _i.invchead_shipto_address2,
+         _i.invchead_shipto_address3, _i.invchead_shipto_city,
+         _i.invchead_shipto_state, _i.invchead_shipto_zipcode,
+         _i.invchead_shipto_phone, _i.invchead_salesrep_id,
+         _i.invchead_commission,
+         _i.invchead_terms_id, _i.invchead_freight,
+         _i.invchead_misc_amount,
+         _i.invchead_misc_descrip, _i.invchead_misc_accnt_id,
+         _i.invchead_payment, _i.invchead_paymentref,
+         _i.invchead_notes,
+         _i.invchead_billto_country, _i.invchead_shipto_country,
+         _i.invchead_prj_id, _i.invchead_curr_id,
+         _i.invchead_taxzone_id,
+         _i.invchead_recurring_invchead_id,
+         _i.invchead_saletype_id, _i.invchead_shipzone_id);
+
+  FOR _l IN SELECT *
+            FROM invcitem
+            WHERE (invcitem_invchead_id=pInvcheadid) LOOP
+    SELECT NEXTVAL('invcitem_invcitem_id_seq') INTO _invcitemid;
+
+    INSERT INTO invcitem
+        (invcitem_id, invcitem_invchead_id,
+         invcitem_linenumber, invcitem_item_id,
+         invcitem_warehous_id, invcitem_custpn,
+         invcitem_number, invcitem_descrip,
+         invcitem_ordered, invcitem_billed,
+         invcitem_custprice, invcitem_price,
+         invcitem_notes, invcitem_salescat_id,
+         invcitem_taxtype_id,
+         invcitem_qty_uom_id, invcitem_qty_invuomratio,
+         invcitem_price_uom_id, invcitem_price_invuomratio,
+         invcitem_coitem_id)
+    VALUES
+        (_invcitemid, _invcheadid,
+         _l.invcitem_linenumber, _l.invcitem_item_id,
+         _l.invcitem_warehous_id, _l.invcitem_custpn,
+         _l.invcitem_number, _l.invcitem_descrip,
+         _l.invcitem_ordered, _l.invcitem_billed,
+         _l.invcitem_custprice, _l.invcitem_price,
+         _l.invcitem_notes, _l.invcitem_salescat_id,
+         _l.invcitem_taxtype_id,
+         _l.invcitem_qty_uom_id, _l.invcitem_qty_invuomratio,
+         _l.invcitem_price_uom_id, _l.invcitem_price_invuomratio,
+         _l.invcitem_coitem_id);
+
+  END LOOP;
+
+  RETURN _invcheadid;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/copyitem.sql b/foundation-database/public/functions/copyitem.sql
new file mode 100644 (file)
index 0000000..d6288a8
--- /dev/null
@@ -0,0 +1,154 @@
+CREATE OR REPLACE FUNCTION copyItem(INTEGER, TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSItemid ALIAS FOR $1;
+  pTItemNumber ALIAS FOR $2;
+  _itemid INTEGER;
+  _r RECORD;
+  _id INTEGER;
+
+BEGIN
+
+  SELECT NEXTVAL('item_item_id_seq') INTO _itemid;
+  INSERT INTO item
+  ( item_id, item_number, item_descrip1, item_descrip2,
+    item_classcode_id, item_type,
+    item_active, item_picklist, item_sold, item_fractional,
+    item_maxcost, item_prodweight, item_packweight,
+    item_prodcat_id,item_exclusive, item_listprice, item_listcost,
+    item_config, item_comments, item_extdescrip,
+    item_upccode, item_inv_uom_id, item_price_uom_id )
+  SELECT _itemid, pTItemNumber, item_descrip1, item_descrip2,
+         item_classcode_id, item_type,
+         item_active, item_picklist, item_sold, item_fractional,
+         item_maxcost, item_prodweight, item_packweight,
+         item_prodcat_id, item_exclusive, item_listprice, item_listcost,
+         item_config, item_comments, item_extdescrip,
+         item_upccode, item_inv_uom_id, item_price_uom_id
+  FROM item
+  WHERE (item_id=pSItemid);
+
+  INSERT INTO imageass
+  (imageass_source_id, imageass_source, imageass_image_id, imageass_purpose)
+  SELECT _itemid, 'I', imageass_image_id, imageass_purpose
+  FROM imageass
+  WHERE ((imageass_source_id=pSItemid)
+  AND (imageass_source='I'));
+  
+  INSERT INTO url
+  (url_source_id, url_source, url_title, url_url)
+  SELECT _itemid, 'I', url_title, url_url
+  FROM url
+  WHERE ((url_source_id=pSItemid)
+  AND (url_source='I'));
+
+  INSERT INTO itemtax
+        (itemtax_item_id, itemtax_taxzone_id, itemtax_taxtype_id)
+  SELECT _itemid, itemtax_taxzone_id, itemtax_taxtype_id
+    FROM itemtax
+   WHERE(itemtax_item_id=pSItemid);
+
+  INSERT INTO charass
+  ( charass_target_type, charass_target_id,
+    charass_char_id, charass_value )
+  SELECT 'I', _itemid, charass_char_id, charass_value
+  FROM charass
+  WHERE ( (charass_target_type='I')
+   AND (charass_target_id=pSItemid) );
+
+  FOR _r IN SELECT itemuomconv_id,
+                   itemuomconv_from_uom_id,
+                   itemuomconv_from_value,
+                   itemuomconv_to_uom_id,
+                   itemuomconv_to_value,
+                   itemuomconv_fractional
+              FROM itemuomconv
+             WHERE(itemuomconv_item_id=pSItemid) LOOP
+    SELECT nextval('itemuomconv_itemuomconv_id_seq') INTO _id;
+    INSERT INTO itemuomconv
+          (itemuomconv_id, itemuomconv_item_id,
+           itemuomconv_from_uom_id, itemuomconv_from_value,
+           itemuomconv_to_uom_id, itemuomconv_to_value,
+           itemuomconv_fractional)
+    VALUES(_id, _itemid,
+           _r.itemuomconv_from_uom_id, _r.itemuomconv_from_value,
+           _r.itemuomconv_to_uom_id, _r.itemuomconv_to_value,
+           _r.itemuomconv_fractional);
+
+    INSERT INTO itemuom
+          (itemuom_itemuomconv_id, itemuom_uomtype_id)
+    SELECT _id, itemuom_uomtype_id
+      FROM itemuom
+     WHERE(itemuom_itemuomconv_id=_r.itemuomconv_id);
+  END LOOP;
+
+  RETURN _itemid;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION copyItem(INTEGER, TEXT, BOOLEAN, BOOLEAN, BOOLEAN) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSItemid ALIAS FOR $1;
+  pTItemNumber ALIAS FOR $2;
+  pCopyBOM ALIAS FOR $3;
+  pCopyBOO ALIAS FOR $4;        -- deprecated - xtmfg-specific
+  pCopyCosts ALIAS FOR $5;
+BEGIN
+  RAISE NOTICE 'copyItem(INTEGER, TEXT, BOOLEAN, BOOLEAN, BOOLEAN) has been deprecated.  Use copyItem(INTEGER, TEXT) or copyItem(INTEGER, TEXT, BOOLEAN, BOOLEAN) or a package-specific version instead.';
+  RETURN copyItem(pSItemid, pTItemNumber, pCopyBOM, pCopyCosts);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION copyItem(INTEGER, TEXT, BOOLEAN, BOOLEAN) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSItemid      ALIAS FOR $1;
+  pTItemNumber  ALIAS FOR $2;
+  pCopyBOM      ALIAS FOR $3;
+  pCopyCosts    ALIAS FOR $4;
+
+  _itemid       INTEGER;
+BEGIN
+
+  _itemid := copyItem(pSItemid, pTItemNumber);
+
+  IF (pCopyBOM) THEN
+    PERFORM copyBOM(pSItemid, _itemid, FALSE);
+  END IF;
+
+  IF (pCopyCosts) THEN
+    INSERT INTO itemcost
+    ( itemcost_item_id, itemcost_costelem_id, itemcost_lowlevel,
+      itemcost_stdcost, itemcost_posted,
+      itemcost_actcost, itemcost_curr_id, itemcost_updated )
+    SELECT _itemid, itemcost_costelem_id, itemcost_lowlevel,
+      itemcost_stdcost, CURRENT_DATE,
+      itemcost_actcost, itemcost_curr_id, CURRENT_DATE
+    FROM itemcost
+    WHERE (itemcost_item_id=pSItemid);
+  END IF;
+
+  RETURN _itemid;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION copyItem(INTEGER, TEXT, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSItemid ALIAS FOR $1;
+  pTItemNumber ALIAS FOR $2;
+  pCopyBOM ALIAS FOR $3;
+  pCopyBOO ALIAS FOR $4;        -- deprecated - xtmfg-specific
+  pCopyCosts ALIAS FOR $5;
+  pCopyUsedAt ALIAS FOR $6;     -- deprecated - xtmfg-specific
+BEGIN
+  RETURN copyItem(pSItemid, pTItemNumber, pCopyBOM, pCopyCosts);
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/copyitemsite.sql b/foundation-database/public/functions/copyitemsite.sql
new file mode 100644 (file)
index 0000000..906df6f
--- /dev/null
@@ -0,0 +1,144 @@
+DROP FUNCTION IF EXISTS copyitemsite(integer, integer);
+CREATE OR REPLACE FUNCTION copyItemSite(INTEGER, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pitemsiteid  ALIAS FOR $1;
+  pdestwhsid   ALIAS FOR $2;
+  _destwhs     whsinfo%ROWTYPE;
+  _new         itemsite%ROWTYPE;
+
+BEGIN
+  -- make a copy of the old itemsite
+  SELECT * INTO _new
+  FROM itemsite
+  WHERE (itemsite_id=pitemsiteid);
+  IF (NOT FOUND) THEN
+    RETURN -1;
+  END IF;
+
+  -- if there is no dest warehouse then perhaps the user is manually copying it
+  IF (pdestwhsid IS NOT NULL) THEN
+    SELECT * INTO _destwhs
+    FROM whsinfo
+    WHERE (warehous_id=pdestwhsid);
+    IF (NOT FOUND) THEN
+      RETURN -2;
+    END IF;
+  END IF;
+
+  IF (NOT checkPrivilege('MaintainItemSites')) THEN
+    RETURN -3;
+  END IF;
+
+  SELECT itemsite_id INTO _new.itemsite_id
+  FROM itemsite
+  WHERE ((itemsite_item_id=_new.itemsite_item_id)
+    AND  (itemsite_warehous_id=pdestwhsid OR
+         (itemsite_warehous_id IS NULL AND pdestwhsid IS NULL)));
+  IF (FOUND) THEN
+    RETURN _new.itemsite_id;
+  END IF;
+
+  -- now override the things we know have to change
+  _new.itemsite_id             := NEXTVAL('itemsite_itemsite_id_seq');
+  _new.itemsite_warehous_id    := pdestwhsid;
+  _new.itemsite_qtyonhand      := 0;
+  _new.itemsite_value           := 0;
+  _new.itemsite_datelastcount  := NULL;
+  _new.itemsite_datelastused   := NULL;
+  _new.itemsite_nnqoh          := 0;
+  _new.itemsite_location_id    := -1;
+
+  IF (_destwhs.warehous_transit) THEN
+    _new.itemsite_reorderlevel := 0;
+    _new.itemsite_ordertoqty   := 0;
+    _new.itemsite_soldranking  := NULL;
+    _new.itemsite_posupply     := FALSE;
+    _new.itemsite_wosupply     := FALSE;
+    _new.itemsite_loccntrl     := FALSE;
+    _new.itemsite_safetystock  := 0;
+    _new.itemsite_minordqty    := 0;
+    _new.itemsite_multordqty   := 0;
+    _new.itemsite_leadtime     := 0;
+    _new.itemsite_controlmethod        := 'R';
+    IF(_new.itemsite_costmethod='N') THEN
+      _new.itemsite_costmethod := 'S';
+    END IF;
+    _new.itemsite_active       := TRUE;
+    -- ? _new.itemsite_plancode_id     := -1;
+    -- ? _new.itemsite_costcat_id      := -1;
+    _new.itemsite_eventfence   := 1;
+    _new.itemsite_sold         := FALSE;
+    _new.itemsite_stocked      := FALSE;
+    _new.itemsite_location_id  := -1;
+    _new.itemsite_useparams    := FALSE;
+    _new.itemsite_useparamsmanual := FALSE;
+    _new.itemsite_createpr     := FALSE;
+    _new.itemsite_location     := NULL;
+    _new.itemsite_location_comments := NULL;
+    _new.itemsite_notes                := 'Transit Warehouse';
+    _new.itemsite_nnqoh                := 0;
+    _new.itemsite_createwo     := FALSE;
+    _new.itemsite_costcat_id   := _destwhs.warehous_costcat_id;
+  END IF;
+
+  INSERT INTO itemsite (
+    itemsite_id,                       itemsite_item_id,
+    itemsite_warehous_id,              itemsite_qtyonhand,
+    itemsite_costmethod,                itemsite_value,
+    itemsite_reorderlevel,             itemsite_ordertoqty,
+    itemsite_cyclecountfreq,           itemsite_datelastcount,
+    itemsite_datelastused,
+    itemsite_posupply,                 itemsite_wosupply,
+    itemsite_loccntrl,
+    itemsite_safetystock,              itemsite_minordqty,
+    itemsite_multordqty,               itemsite_leadtime,
+    itemsite_abcclass,                 itemsite_issuemethod,
+    itemsite_controlmethod,            itemsite_active,
+    itemsite_plancode_id,              itemsite_costcat_id,
+    itemsite_eventfence,               itemsite_sold,
+    itemsite_stocked,                  itemsite_freeze,
+    itemsite_location_id,
+    itemsite_useparams,                        itemsite_useparamsmanual,
+    itemsite_soldranking,              itemsite_createpr,
+    itemsite_location,                 itemsite_location_comments,
+    itemsite_notes,                    itemsite_perishable,
+    itemsite_nnqoh,                    itemsite_autoabcclass,
+    itemsite_ordergroup,               itemsite_disallowblankwip,
+    itemsite_maxordqty,                        itemsite_mps_timefence,
+    itemsite_createwo,                 itemsite_warrpurc,
+    itemsite_autoreg,
+    itemsite_planning_type,             itemsite_supply_itemsite_id
+  ) VALUES (
+    _new.itemsite_id,                  _new.itemsite_item_id,
+    _new.itemsite_warehous_id,         _new.itemsite_qtyonhand,
+    _new.itemsite_costmethod,           _new.itemsite_value,
+    _new.itemsite_reorderlevel,                _new.itemsite_ordertoqty,
+    _new.itemsite_cyclecountfreq,      _new.itemsite_datelastcount,
+    _new.itemsite_datelastused,
+    _new.itemsite_posupply,            _new.itemsite_wosupply,
+    _new.itemsite_loccntrl,
+    _new.itemsite_safetystock,         _new.itemsite_minordqty,
+    _new.itemsite_multordqty,          _new.itemsite_leadtime,
+    _new.itemsite_abcclass,            _new.itemsite_issuemethod,
+    _new.itemsite_controlmethod,       _new.itemsite_active,
+    _new.itemsite_plancode_id,         _new.itemsite_costcat_id,
+    _new.itemsite_eventfence,          _new.itemsite_sold,
+    _new.itemsite_stocked,             _new.itemsite_freeze,
+    _new.itemsite_location_id,
+    _new.itemsite_useparams,           _new.itemsite_useparamsmanual,
+    _new.itemsite_soldranking,         _new.itemsite_createpr,
+    _new.itemsite_location,            _new.itemsite_location_comments,
+    _new.itemsite_notes,               _new.itemsite_perishable,
+    _new.itemsite_nnqoh,               _new.itemsite_autoabcclass,
+    _new.itemsite_ordergroup,          _new.itemsite_disallowblankwip,
+    _new.itemsite_maxordqty,           _new.itemsite_mps_timefence,
+    _new.itemsite_createwo,            _new.itemsite_warrpurc,
+    _new.itemsite_autoreg,
+    _new.itemsite_planning_type,        _new.itemsite_supply_itemsite_id
+    );
+
+  RETURN _new.itemsite_id;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/copylocale.sql b/foundation-database/public/functions/copylocale.sql
new file mode 100644 (file)
index 0000000..f89dfa4
--- /dev/null
@@ -0,0 +1,100 @@
+
+CREATE OR REPLACE FUNCTION copyLocale(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pLocaleid ALIAS FOR $1;
+  _localecode TEXT;
+  _localeid INTEGER;
+
+BEGIN
+
+  SELECT locale_code INTO _localecode
+  FROM locale
+  WHERE (locale_id=pLocaleid);
+
+  IF (NOT FOUND) THEN
+    RAISE EXCEPTION 'Attempt to copy a non-existent locale-id.';
+  END IF;
+
+  IF (EXISTS(SELECT locale_id
+             FROM locale
+             WHERE (locale_code = (_localecode || '-COPY')))) THEN
+    RAISE EXCEPTION 'Attempt to copy a Locale Code that already exists.';
+  END IF;
+
+  SELECT NEXTVAL('locale_locale_id_seq') INTO _localeid;
+
+  INSERT INTO locale
+        (locale_id, locale_code, locale_descrip,
+         locale_lang_file,
+         locale_dateformat,
+         locale_currformat,
+         locale_qtyformat,
+         locale_comments,
+         locale_qtyperformat,
+         locale_salespriceformat,
+         locale_extpriceformat,
+         locale_timeformat,
+         locale_timestampformat,
+         local_costformat,
+         locale_costformat,
+         locale_purchpriceformat,
+         locale_uomratioformat,
+         locale_intervalformat,
+         locale_lang_id,
+         locale_country_id,
+         locale_error_color,
+         locale_warning_color,
+         locale_emphasis_color,
+         locale_altemphasis_color,
+         locale_expired_color,
+         locale_future_color,
+         locale_curr_scale,
+         locale_salesprice_scale,
+         locale_purchprice_scale,
+         locale_extprice_scale,
+         locale_cost_scale,
+         locale_qty_scale,
+         locale_qtyper_scale,
+         locale_uomratio_scale)
+  SELECT _localeid, locale_code || '-COPY', '',
+         locale_lang_file,
+         locale_dateformat,
+         locale_currformat,
+         locale_qtyformat,
+         locale_comments,
+         locale_qtyperformat,
+         locale_salespriceformat,
+         locale_extpriceformat,
+         locale_timeformat,
+         locale_timestampformat,
+         local_costformat,
+         locale_costformat,
+         locale_purchpriceformat,
+         locale_uomratioformat,
+         locale_intervalformat,
+         locale_lang_id,
+         locale_country_id,
+         locale_error_color,
+         locale_warning_color,
+         locale_emphasis_color,
+         locale_altemphasis_color,
+         locale_expired_color,
+         locale_future_color,
+         locale_curr_scale,
+         locale_salesprice_scale,
+         locale_purchprice_scale,
+         locale_extprice_scale,
+         locale_cost_scale,
+         locale_qty_scale,
+         locale_qtyper_scale,
+         locale_uomratio_scale
+    FROM locale
+   WHERE(locale_id=pLocaleid);
+
+  RETURN _localeid;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/copypo.sql b/foundation-database/public/functions/copypo.sql
new file mode 100644 (file)
index 0000000..fe59506
--- /dev/null
@@ -0,0 +1,212 @@
+CREATE OR REPLACE FUNCTION copyPO(INTEGER, INTEGER, DATE, BOOLEAN) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSrcid               ALIAS FOR $1;
+  pVendid              ALIAS FOR $2;
+  pOrderdate           ALIAS FOR $3;
+  pRecheckVendinfo     ALIAS FOR $4;
+
+  _tgtid               INTEGER;
+  _orderdate           DATE;
+  _head                        RECORD;
+  _itemsrc             RECORD;
+  _lineitem            RECORD;
+  _qty                 NUMERIC;
+  _unitprice           NUMERIC;
+  _uomratio            NUMERIC;
+  _vend_restrictpurch  BOOLEAN;
+
+BEGIN
+  SELECT * INTO _head FROM pohead WHERE pohead_id = pSrcid;
+  IF (NOT FOUND) THEN
+    RETURN -1;
+  END IF;
+  IF (_head.pohead_vend_id != pVendid) THEN
+    RETURN -2;         -- not supported now but should be in the future
+  END IF;              -- when enabled, set pRecheckVendinfo if vendors don't match
+
+  IF (pOrderdate IS NULL) THEN
+    _orderdate := CURRENT_DATE;
+  ELSE
+    _orderdate := pOrderdate;
+  END IF;
+
+  INSERT INTO pohead (pohead_status, pohead_number,
+                     pohead_orderdate, pohead_vend_id,
+                     pohead_fob, pohead_shipvia,
+                     pohead_freight, pohead_printed,
+                     pohead_terms_id, pohead_warehous_id,
+                     pohead_vendaddr_id, pohead_agent_username,
+                     pohead_curr_id, pohead_saved,
+                      pohead_taxtype_id, pohead_taxzone_id,
+                      pohead_dropship, pohead_vend_cntct_id,
+                      pohead_vend_cntct_honorific, pohead_vend_cntct_first_name,
+                      pohead_vend_cntct_middle, pohead_vend_cntct_last_name,
+                      pohead_vend_cntct_suffix, pohead_vend_cntct_phone,
+                      pohead_vend_cntct_title, pohead_vend_cntct_fax,
+                      pohead_vend_cntct_email, pohead_vendaddress1,
+                      pohead_vendaddress2, pohead_vendaddress3,
+                      pohead_vendcity, pohead_vendstate,
+                      pohead_vendzipcode, pohead_vendcountry,
+                      pohead_shipto_cntct_id,
+                      pohead_shipto_cntct_honorific, pohead_shipto_cntct_first_name,
+                      pohead_shipto_cntct_middle, pohead_shipto_cntct_last_name,
+                      pohead_shipto_cntct_suffix, pohead_shipto_cntct_phone,
+                      pohead_shipto_cntct_title, pohead_shipto_cntct_fax,
+                      pohead_shipto_cntct_email, pohead_shiptoaddress_id,
+                      pohead_shiptoaddress1,
+                      pohead_shiptoaddress2, pohead_shiptoaddress3,
+                      pohead_shiptocity, pohead_shiptostate,
+                      pohead_shiptozipcode, pohead_shiptocountry
+             ) VALUES (
+                     'U', fetchPoNumber(),
+                     _orderdate, _head.pohead_vend_id,
+                     _head.pohead_fob, _head.pohead_shipvia,
+                     _head.pohead_freight, false,
+                     _head.pohead_terms_id, _head.pohead_warehous_id,
+                     _head.pohead_vendaddr_id, _head.pohead_agent_username,
+                     _head.pohead_curr_id, true,
+                      _head.pohead_taxtype_id, _head.pohead_taxzone_id,
+                      false, _head.pohead_vend_cntct_id,
+                      _head.pohead_vend_cntct_honorific, _head.pohead_vend_cntct_first_name,
+                      _head.pohead_vend_cntct_middle, _head.pohead_vend_cntct_last_name,
+                      _head.pohead_vend_cntct_suffix, _head.pohead_vend_cntct_phone,
+                      _head.pohead_vend_cntct_title, _head.pohead_vend_cntct_fax,
+                      _head.pohead_vend_cntct_email, _head.pohead_vendaddress1,
+                      _head.pohead_vendaddress2, _head.pohead_vendaddress3,
+                      _head.pohead_vendcity, _head.pohead_vendstate,
+                      _head.pohead_vendzipcode, _head.pohead_vendcountry,
+                      _head.pohead_shipto_cntct_id,
+                      _head.pohead_shipto_cntct_honorific, _head.pohead_shipto_cntct_first_name,
+                      _head.pohead_shipto_cntct_middle, _head.pohead_shipto_cntct_last_name,
+                      _head.pohead_shipto_cntct_suffix, _head.pohead_shipto_cntct_phone,
+                      _head.pohead_shipto_cntct_title, _head.pohead_shipto_cntct_fax,
+                      _head.pohead_shipto_cntct_email, _head.pohead_shiptoaddress_id,
+                      _head.pohead_shiptoaddress1,
+                      _head.pohead_shiptoaddress2, _head.pohead_shiptoaddress3,
+                      _head.pohead_shiptocity, _head.pohead_shiptostate,
+                      _head.pohead_shiptozipcode, _head.pohead_shiptocountry);
+
+  _tgtid := CURRVAL('pohead_pohead_id_seq');
+
+  IF (pRecheckVendinfo) THEN
+    SELECT vend_restrictpurch INTO _vend_restrictpurch
+      FROM vendinfo WHERE (vend_id = pVendid);
+
+    FOR _lineitem IN SELECT *
+                 FROM poitem 
+                 WHERE (poitem_pohead_id = pSrcid) LOOP
+
+      SELECT * INTO _itemsrc
+      FROM itemsrc, itemsite
+      WHERE (itemsrc_active
+        AND  (itemsrc_id = _lineitem.poitem_itemsrc_id)
+       AND  (itemsite_id = _lineitem.poitem_itemsite_id));
+      IF (NOT FOUND AND _vend_restrictpurch) THEN
+       RETURN -3;
+      END IF;
+
+      -- handle changes to the uom ratio and consequent qty changes
+      _uomratio := COALESCE(_itemsrc.itemsrc_invvendoruomratio, _lineitem.poitem_invvenduomratio);
+      IF (_itemsrc.itemsrc_invvendoruomratio IS NULL
+         OR _itemsrc.itemsrc_invvendoruomratio != _lineitem.poitem_invvenduomratio) THEN
+       _qty := _lineitem.poitem_qty_ordered;
+
+      ELSE
+       _qty := _lineitem.poitem_qty_ordered * _lineitem.poitem_invvenduomratio /
+                                              _itemsrc.itemsrc_invvendoruomratio;
+       IF (_itemsrc.itemsrc_minordqty IS NOT NULL) THEN
+         IF (_qty < _itemsrc.itemsrc_minordqty) THEN
+           _qty := _itemsrc.itemsrc_minordqty;
+         ELSIF (_itemsrc.itemsrc_multordqty > 0
+                  AND _qty % _itemsrc.itemsrc_multordqty > 0) THEN
+           _qty = _qty % _itemsrc.itemsrc_multordqty + _itemsrc.itemsrc_multordqty;
+         END IF;
+       END IF;
+      END IF;
+
+      IF (_itemsrc.itemsrc_id IS NULL) THEN
+       _unitprice = _lineitem.poitem_unitprice;
+      ELSE
+        SELECT itemsrcPrice(_itemsrc.itemsrc_id, _head.pohead_warehous_id, _head.pohead_dropship,
+                            _lineitem.poitem_qty_ordered, _head.pohead_curr_id, CURRENT_DATE) INTO _unitprice;
+       IF (_unitprice IS NULL) THEN
+         RETURN -4;
+       END IF;
+      END IF;
+
+      INSERT INTO poitem (poitem_status, poitem_pohead_id, poitem_linenumber,
+                         poitem_duedate,
+                         poitem_itemsite_id,
+                         poitem_vend_item_descrip,
+                         poitem_vend_uom,
+                         poitem_invvenduomratio,
+                         poitem_qty_ordered, poitem_unitprice,
+                         poitem_vend_item_number,
+                         poitem_comments, poitem_expcat_id,
+                         poitem_itemsrc_id,
+                         poitem_freight,
+                         poitem_stdcost,
+                         poitem_manuf_name,
+                         poitem_manuf_item_number,
+                         poitem_manuf_item_descrip,
+                          poitem_taxtype_id
+                   ) VALUES (
+                         'U', _tgtid, _lineitem.poitem_linenumber,
+                         _orderdate + COALESCE(_itemsrc.itemsrc_leadtime, 0),
+                         _lineitem.poitem_itemsite_id,
+                         COALESCE(_itemsrc.itemsrc_vend_item_descrip,
+                                  _lineitem.poitem_vend_item_descrip),
+                         COALESCE(_itemsrc.itemsrc_vend_uom, _lineitem.poitem_vend_uom),
+                         COALESCE(_itemsrc.itemsrc_invvendoruomratio,
+                                  _lineitem.poitem_invvenduomratio),
+                         _qty, _unitprice,
+                         COALESCE(_itemsrc.itemsrc_vend_item_number,
+                                  _lineitem.poitem_vend_item_number),
+                         _lineitem.poitem_comments, _lineitem.poitem_expcat_id,
+                         COALESCE(_itemsrc.itemsrc_id, -1),
+                         _lineitem.poitem_freight,
+                         stdcost(_itemsrc.itemsite_item_id),
+                         COALESCE(_itemsrc.itemsrc_manuf_name,
+                                  _lineitem.poitem_manuf_name),
+                         COALESCE(_itemsrc.itemsrc_manuf_item_number,
+                                  _lineitem.poitem_manuf_item_number),
+                         COALESCE(_itemsrc.itemsrc_manuf_item_descrip,
+                                  _lineitem.poitem_manuf_item_descrip),
+                          _lineitem.poitem_taxtype_id);
+
+    END LOOP;
+  ELSE
+    INSERT INTO poitem (poitem_status, poitem_pohead_id, poitem_linenumber,
+                       poitem_duedate, poitem_itemsite_id,
+                       poitem_vend_item_descrip, poitem_vend_uom,
+                       poitem_invvenduomratio, poitem_qty_ordered,
+                       poitem_unitprice,
+                       poitem_vend_item_number, poitem_comments,
+                       poitem_expcat_id, poitem_itemsrc_id, poitem_freight,
+                       poitem_stdcost, poitem_manuf_name, 
+                       poitem_manuf_item_number, poitem_manuf_item_descrip,
+                        poitem_taxtype_id
+               ) SELECT 'U', _tgtid, poitem_linenumber,
+                       _orderdate + COALESCE(itemsrc_leadtime, 0), poitem_itemsite_id,
+                       poitem_vend_item_descrip, poitem_vend_uom,
+                       poitem_invvenduomratio, poitem_qty_ordered,
+                       poitem_unitprice,
+                       poitem_vend_item_number, poitem_comments,
+                       poitem_expcat_id, poitem_itemsrc_id, poitem_freight,
+                       stdcost(itemsite_item_id), poitem_manuf_name,
+                       poitem_manuf_item_number, poitem_manuf_item_descrip,
+                        poitem_taxtype_id
+                 FROM poitem
+                   LEFT OUTER JOIN itemsrc ON (itemsrc_id=poitem_itemsrc_id)
+                   LEFT OUTER JOIN itemsite ON (itemsite_id=poitem_itemsite_id)
+                 WHERE (poitem_pohead_id = pSrcid);
+  END IF;
+
+  -- Todo: recalculate tax?
+
+  RETURN _tgtid;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/copypricingschedule.sql b/foundation-database/public/functions/copypricingschedule.sql
new file mode 100644 (file)
index 0000000..93010a2
--- /dev/null
@@ -0,0 +1,76 @@
+
+CREATE OR REPLACE FUNCTION copypricingschedule(pIpsheadId INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _ipsheadid INTEGER;
+  _ipsitemid INTEGER;
+  _ipsfreightid INTEGER;
+  _x RECORD;
+
+BEGIN
+
+  _ipsheadid := nextval('ipshead_ipshead_id_seq');
+  INSERT INTO ipshead 
+  ( ipshead_id, ipshead_name, ipshead_descrip,
+    ipshead_effective, ipshead_expires, 
+    ipshead_curr_id, ipshead_updated ) 
+  SELECT _ipsheadid, orig.ipshead_name || (SELECT CAST((COUNT(cnt.ipshead_id)+1) AS text)
+                                           FROM ipshead cnt
+                                           WHERE (SUBSTRING(cnt.ipshead_name FROM 0 FOR char_length(orig.ipshead_name)+1) = orig.ipshead_name)),
+        orig.ipshead_descrip, orig.ipshead_effective, orig.ipshead_expires, 
+        orig.ipshead_curr_id, CURRENT_DATE
+  FROM ipshead orig
+  WHERE (orig.ipshead_id=pIpsheadId);
+
+  FOR _x IN
+    SELECT ipsitem_id FROM ipsiteminfo WHERE (ipsitem_ipshead_id=pIpsheadid)
+  LOOP 
+      INSERT INTO ipsiteminfo 
+          (ipsitem_ipshead_id, ipsitem_item_id, ipsitem_prodcat_id,
+           ipsitem_qtybreak, ipsitem_price,
+           ipsitem_qty_uom_id, ipsitem_price_uom_id,
+           ipsitem_discntprcnt, ipsitem_fixedamtdiscount,
+           ipsitem_type, ipsitem_warehous_id) 
+      SELECT _ipsheadid, ipsitem_item_id, ipsitem_prodcat_id,
+           ipsitem_qtybreak, ipsitem_price,
+           ipsitem_qty_uom_id, ipsitem_price_uom_id,
+           ipsitem_discntprcnt, ipsitem_fixedamtdiscount,
+           ipsitem_type, ipsitem_warehous_id
+      FROM ipsiteminfo 
+      WHERE (ipsitem_id=_x.ipsitem_id)
+      RETURNING ipsitem_id INTO _ipsitemid; 
+
+      INSERT INTO ipsitemchar
+        ( ipsitemchar_ipsitem_id, ipsitemchar_char_id,
+          ipsitemchar_value, ipsitemchar_price)
+      SELECT  _ipsitemid, ipsitemchar_char_id,
+          ipsitemchar_value, ipsitemchar_price
+      FROM ipsitemchar
+      WHERE (ipsitemchar_ipsitem_id=_x.ipsitem_id);
+  END LOOP;
+
+  FOR _x IN
+    SELECT ipsfreight_id FROM ipsfreight WHERE (ipsfreight_ipshead_id=pIpsheadid)
+  LOOP 
+      _ipsfreightid := nextval('ipsfreight_ipsfreight_id_seq');
+      INSERT INTO ipsfreight
+          (ipsfreight_id, ipsfreight_ipshead_id, 
+           ipsfreight_qtybreak, ipsfreight_price,
+           ipsfreight_type, ipsfreight_warehous_id,
+           ipsfreight_shipzone_id,ipsfreight_freightclass_id,
+           ipsfreight_shipvia) 
+      SELECT _ipsfreightid, _ipsheadid, ipsfreight_qtybreak, 
+           ipsfreight_price,ipsfreight_type, 
+           ipsfreight_warehous_id,ipsfreight_shipzone_id,
+           ipsfreight_freightclass_id,ipsfreight_shipvia
+      FROM ipsfreight
+      WHERE (ipsfreight_id=_x.ipsfreight_id); 
+
+  END LOOP;
+
+  RETURN _ipsheadid;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/copyprj.sql b/foundation-database/public/functions/copyprj.sql
new file mode 100644 (file)
index 0000000..fbd4076
--- /dev/null
@@ -0,0 +1,84 @@
+CREATE OR REPLACE FUNCTION copyPrj(INTEGER, DATE) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pparentid   ALIAS FOR $1;
+  _counter    INTEGER;
+  _duedate    DATE := COALESCE($2, CURRENT_DATE);
+  _alarmid    INTEGER;
+  _i          INTEGER;
+  _newnumber  TEXT;
+  _p          RECORD;
+  _prjid      INTEGER;
+  _testnumber TEXT;
+
+BEGIN
+  RAISE DEBUG 'copyPrj(%, %) entered', pparentid, _duedate;
+
+  SELECT * INTO _p
+  FROM prj
+  WHERE (prj_id=pparentid);
+
+  -- new number = old number up to but not including -, followed by _duedate
+  -- e.g. REPAIR-FRIDGE becomes REPAIR-2010-05-15
+  --  but REPAIR_FRIDGE becomes REPAIR_FRIDGE-2010-05-15
+  IF (_p.prj_recurring_prj_id IS NULL) THEN
+    _newnumber := _p.prj_number;
+  ELSE
+    _newnumber := SUBSTRING(_p.prj_number FROM '[^-]*');
+    IF (_newnumber IS NULL) THEN
+      _newnumber := _p.prj_number;
+    END IF;
+  END IF;
+  _newnumber := _newnumber || '-' || to_char(_duedate, 'YYYY-MM-DD');
+  
+  RAISE DEBUG 'copyPrj checking if _newnumber % exists', _newnumber;
+  SELECT MAX(prj_number) INTO _testnumber
+    FROM prj
+   WHERE (prj_number ~ ('^' || _newnumber));
+  IF (_testnumber = _newnumber) THEN
+    _newnumber := _newnumber || '-001';
+  ELSIF (_testnumber IS NOT NULL) THEN
+    _counter := CAST(SUBSTRING(_testnumber FROM '...$') AS INTEGER);
+    _counter := _counter + 1;
+    _newnumber := REGEXP_REPLACE(_testnumber, '...$', to_char(_counter, 'FM009'));
+  END IF;
+  RAISE DEBUG 'copyPrj _newnumber is now %', _newnumber;
+
+  INSERT INTO prj(
+            prj_number,     prj_name,           prj_descrip,
+            prj_status,     prj_so,             prj_wo,
+            prj_po,         prj_owner_username,
+            prj_due_date,   prj_username,       prj_recurring_prj_id
+  ) SELECT  _newnumber,     _p.prj_name,        _p.prj_descrip,
+            'P',            _p.prj_so,          _p.prj_wo,
+            _p.prj_po,      _p.prj_owner_username,
+            _duedate,       _p.prj_username,    _p. prj_recurring_prj_id
+      FROM prj
+     WHERE (prj_id=pparentid)
+  RETURNING prj_id INTO _prjid;
+
+  IF (_prjid IS NULL) THEN
+    RETURN -1;
+  END IF;
+
+  SELECT saveAlarm(NULL, NULL, _duedate,
+                   CAST(alarm_time - DATE_TRUNC('day',alarm_time) AS TIME),
+                   alarm_time_offset,
+                   alarm_time_qualifier,
+                   alarm_event_recipient  IS NOT NULL, alarm_event_recipient,
+                   alarm_email_recipient  IS NOT NULL, alarm_email_recipient,
+                   alarm_sysmsg_recipient IS NOT NULL, alarm_sysmsg_recipient,
+                   'J', _prjid, 'CHANGEONE')
+    INTO _alarmid
+    FROM alarm
+   WHERE ((alarm_source='J')
+      AND (alarm_source_id=pparentid));
+
+   IF (_alarmid < 0) THEN
+     RETURN _alarmid;
+   END IF;
+
+  RETURN _prjid;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/copyproject.sql b/foundation-database/public/functions/copyproject.sql
new file mode 100644 (file)
index 0000000..14c52f9
--- /dev/null
@@ -0,0 +1,80 @@
+
+CREATE OR REPLACE FUNCTION copyProject(INTEGER, TEXT, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pPrjId ALIAS FOR $1;
+  pPrjNumber ALIAS FOR $2;
+  pDueDateOffset ALIAS FOR $3;
+  _prjid INTEGER;
+
+BEGIN
+
+  IF (COALESCE(pPrjNumber, '') = '') THEN
+    RETURN -1;
+  END IF;
+
+  IF (EXISTS(SELECT prj_id FROM prj WHERE UPPER(prj_number)=UPPER(pPrjNumber))) THEN
+    RETURN -2;
+  END IF;
+
+  IF (NOT EXISTS(SELECT prj_id FROM prj WHERE prj_id=pPrjId)) THEN
+    RETURN -3;
+  END IF;
+
+  SELECT NEXTVAL('prj_prj_id_seq') INTO _prjid;
+
+  INSERT INTO prj
+  ( prj_id, prj_number, prj_name, 
+    prj_descrip, prj_status,
+    prj_so, prj_wo, prj_po,
+    prj_owner_username, prj_start_date,
+    prj_due_date, prj_assigned_date, prj_completed_date,
+    prj_username, prj_recurring_prj_id,
+    prj_crmacct_id, prj_cntct_id )
+  SELECT _prjid, UPPER(pPrjNumber), prj_name,
+         prj_descrip, 'P',
+         prj_so, prj_wo, prj_po,
+         prj_owner_username, NULL,
+         (prj_due_date + COALESCE(pDueDateOffset, 0)),
+         CASE WHEN (prj_username IS NULL) THEN NULL ELSE CURRENT_DATE END, NULL,
+         prj_username, prj_recurring_prj_id,
+         prj_crmacct_id, prj_cntct_id
+  FROM prj
+  WHERE (prj_id=pPrjId);
+
+  INSERT INTO prjtask
+  ( prjtask_number, prjtask_name, prjtask_descrip,
+    prjtask_prj_id, prjtask_anyuser, prjtask_status,
+    prjtask_hours_budget, prjtask_hours_actual,
+    prjtask_exp_budget, prjtask_exp_actual,
+    prjtask_owner_username, prjtask_start_date,
+    prjtask_due_date, prjtask_assigned_date,
+    prjtask_completed_date, prjtask_username )
+  SELECT prjtask_number, prjtask_name, prjtask_descrip,
+         _prjid, prjtask_anyuser, 'P',
+         prjtask_hours_budget, 0.0,
+         prjtask_exp_budget, 0.0,
+         prjtask_owner_username, NULL,
+         (prjtask_due_date + COALESCE(pDueDateOffset, 0)),
+         CASE WHEN (prjtask_username IS NULL) THEN NULL ELSE CURRENT_DATE END,
+         NULL, prjtask_username
+  FROM prjtask
+  WHERE (prjtask_prj_id=pPrjId);
+
+  INSERT INTO docass
+  ( docass_source_id, docass_source_type,
+    docass_target_id, docass_target_type,
+    docass_purpose )
+  SELECT _prjid, docass_source_type,
+         docass_target_id, docass_target_type,
+         docass_purpose
+  FROM docass
+  WHERE ((docass_source_id=pPrjId)
+    AND  (docass_source_type='J'));
+
+  RETURN _prjid;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/copyquote.sql b/foundation-database/public/functions/copyquote.sql
new file mode 100644 (file)
index 0000000..5fda08a
--- /dev/null
@@ -0,0 +1,96 @@
+
+CREATE OR REPLACE FUNCTION copyQuote(INTEGER, DATE) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pQuheadid ALIAS FOR $1;
+  pSchedDate ALIAS FOR $2;
+  _quheadid INTEGER;
+  _qunumber TEXT;
+
+BEGIN
+
+  SELECT NEXTVAL('quhead_quhead_id_seq') INTO _quheadid;
+  IF (fetchMetricText('QUNumberGeneration') = 'S') THEN
+    SELECT fetchSoNumber() INTO _qunumber;
+  ELSE
+    SELECT fetchQuNumber() INTO _qunumber;
+  END IF;
+
+  INSERT INTO quhead
+  ( quhead_id, quhead_number, quhead_cust_id, quhead_prj_id,
+    quhead_quotedate, quhead_packdate, quhead_fob,
+    quhead_warehous_id, quhead_terms_id, quhead_salesrep_id,
+    quhead_custponumber, quhead_shipvia,
+    quhead_shipto_id, quhead_shiptoname, quhead_shiptoaddress1, quhead_shiptoaddress2, quhead_shiptoaddress3,
+    quhead_shiptocity, quhead_shiptostate, quhead_shiptozipcode, quhead_shiptophone, quhead_shiptocountry,
+    quhead_billtoname, quhead_billtoaddress1, quhead_billtoaddress2, quhead_billtoaddress3,
+    quhead_billtocity, quhead_billtostate, quhead_billtozip,
+    quhead_misc_accnt_id, quhead_misc_descrip, quhead_misc, quhead_freight, quhead_commission,
+    quhead_ordercomments, quhead_shipcomments,
+    quhead_imported, quhead_curr_id, quhead_taxzone_id, quhead_taxtype_id, quhead_ophead_id, quhead_status,
+    quhead_shipto_cntct_id, quhead_shipto_cntct_honorific, quhead_shipto_cntct_first_name, quhead_shipto_cntct_middle,
+    quhead_shipto_cntct_last_name, quhead_shipto_cntct_suffix, quhead_shipto_cntct_phone, quhead_shipto_cntct_title,
+    quhead_shipto_cntct_fax, quhead_shipto_cntct_email, quhead_billto_cntct_id, quhead_billto_cntct_honorific,
+    quhead_billto_cntct_first_name, quhead_billto_cntct_middle, quhead_billto_cntct_last_name, quhead_billto_cntct_suffix,
+    quhead_billto_cntct_phone, quhead_billto_cntct_title, quhead_billto_cntct_fax, quhead_billto_cntct_email )
+  SELECT _quheadid, _qunumber, quhead_cust_id, quhead_prj_id,
+         CURRENT_DATE, COALESCE(pSchedDate, quhead_packdate), quhead_fob,
+         quhead_warehous_id, quhead_terms_id, quhead_salesrep_id,
+         quhead_custponumber, quhead_shipvia,
+         quhead_shipto_id, quhead_shiptoname, quhead_shiptoaddress1, quhead_shiptoaddress2, quhead_shiptoaddress3,
+         quhead_shiptocity, quhead_shiptostate, quhead_shiptozipcode, quhead_shiptophone, quhead_shiptocountry,
+         quhead_billtoname, quhead_billtoaddress1, quhead_billtoaddress2, quhead_billtoaddress3,
+         quhead_billtocity, quhead_billtostate, quhead_billtozip,
+         quhead_misc_accnt_id, quhead_misc_descrip, quhead_misc, quhead_freight, quhead_commission,
+         quhead_ordercomments, quhead_shipcomments,
+         FALSE, quhead_curr_id, quhead_taxzone_id, quhead_taxtype_id, quhead_ophead_id, 'O',
+         quhead_shipto_cntct_id, quhead_shipto_cntct_honorific, quhead_shipto_cntct_first_name, quhead_shipto_cntct_middle,
+         quhead_shipto_cntct_last_name, quhead_shipto_cntct_suffix, quhead_shipto_cntct_phone, quhead_shipto_cntct_title,
+         quhead_shipto_cntct_fax, quhead_shipto_cntct_email, quhead_billto_cntct_id, quhead_billto_cntct_honorific,
+         quhead_billto_cntct_first_name, quhead_billto_cntct_middle, quhead_billto_cntct_last_name, quhead_billto_cntct_suffix,
+         quhead_billto_cntct_phone, quhead_billto_cntct_title, quhead_billto_cntct_fax, quhead_billto_cntct_email
+  FROM quhead
+  WHERE (quhead_id=pQuheadid);
+
+  INSERT INTO quitem
+  ( quitem_quhead_id, quitem_linenumber, quitem_itemsite_id,
+    quitem_scheddate, quitem_promdate, quitem_qtyord,
+    quitem_price, quitem_custprice, quitem_unitcost,
+    quitem_qty_uom_id, quitem_price_uom_id,
+    quitem_qty_invuomratio, quitem_price_invuomratio,
+    quitem_memo, quitem_custpn, quitem_imported, quitem_taxtype_id,
+    quitem_createorder, quitem_order_warehous_id, quitem_item_id, quitem_prcost,
+    quitem_dropship, quitem_itemsrc_id, quitem_pricemode )
+  SELECT _quheadid, quitem_linenumber, quitem_itemsite_id,
+         COALESCE(pSchedDate, quitem_scheddate),
+         quitem_promdate,
+         quitem_qtyord,
+         quitem_price, quitem_custprice, stdCost(itemsite_item_id),
+         quitem_qty_uom_id, quitem_price_uom_id,
+         quitem_qty_invuomratio, quitem_price_invuomratio,
+         quitem_memo, quitem_custpn, FALSE, quitem_taxtype_id,
+         quitem_createorder, quitem_order_warehous_id, quitem_item_id, quitem_prcost,
+         quitem_dropship, quitem_itemsrc_id, quitem_pricemode
+  FROM quitem, itemsite
+  WHERE ( (quitem_itemsite_id=itemsite_id)
+   AND (quitem_quhead_id=pQuheadid));
+
+  INSERT INTO charass
+        (charass_target_type, charass_target_id,
+         charass_char_id, charass_value)
+  SELECT charass_target_type, b.quitem_id,
+         charass_char_id, charass_value
+    FROM quitem a, charass, quitem b
+   WHERE ((charass_target_type='SI')
+     AND  (charass_target_id=a.quitem_id)
+     AND  (a.quitem_quhead_id=pQuheadid)
+     AND  (b.quitem_quhead_id=_quheadid)
+     AND  (a.quitem_linenumber=b.quitem_linenumber)
+     );
+
+  RETURN _quheadid;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/copyso.sql b/foundation-database/public/functions/copyso.sql
new file mode 100644 (file)
index 0000000..19f9644
--- /dev/null
@@ -0,0 +1,274 @@
+CREATE OR REPLACE FUNCTION copySo(pSoheadid INTEGER,
+                                  pSchedDate DATE) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _soheadid INTEGER;
+  _soitemid INTEGER;
+  _soitem RECORD;
+
+BEGIN
+
+  SELECT NEXTVAL('cohead_cohead_id_seq') INTO _soheadid;
+
+  INSERT INTO cohead
+  ( cohead_id,
+    cohead_number,
+    cohead_cust_id,
+    cohead_custponumber,
+    cohead_type,
+    cohead_orderdate,
+    cohead_warehous_id,
+    cohead_shipto_id,
+    cohead_shiptoname,
+    cohead_shiptoaddress1,
+    cohead_shiptoaddress2,
+    cohead_shiptoaddress3,
+    cohead_shiptoaddress4,
+    cohead_shiptoaddress5,
+    cohead_salesrep_id,
+    cohead_terms_id,
+    cohead_fob,
+    cohead_shipvia,
+    cohead_shiptocity,
+    cohead_shiptostate,
+    cohead_shiptozipcode,
+    cohead_freight,
+    cohead_misc,
+    cohead_imported,
+    cohead_ordercomments,
+    cohead_shipcomments,
+    cohead_shiptophone,
+    cohead_shipchrg_id,
+    cohead_shipform_id,
+    cohead_billtoname,
+    cohead_billtoaddress1,
+    cohead_billtoaddress2,
+    cohead_billtoaddress3,
+    cohead_billtocity,
+    cohead_billtostate,
+    cohead_billtozipcode,
+    cohead_misc_accnt_id,
+    cohead_misc_descrip,
+    cohead_commission,
+    cohead_miscdate,
+    cohead_holdtype,
+    cohead_packdate,
+    cohead_prj_id,
+    cohead_wasquote,
+    cohead_lastupdated,
+    cohead_shipcomplete,
+    cohead_created,
+    cohead_creator,
+    cohead_quote_number,
+    cohead_billtocountry,
+    cohead_shiptocountry,
+    cohead_curr_id,
+    cohead_calcfreight,
+    cohead_shipto_cntct_id,
+    cohead_shipto_cntct_honorific,
+    cohead_shipto_cntct_first_name,
+    cohead_shipto_cntct_middle,
+    cohead_shipto_cntct_last_name,
+    cohead_shipto_cntct_suffix,
+    cohead_shipto_cntct_phone,
+    cohead_shipto_cntct_title,
+    cohead_shipto_cntct_fax,
+    cohead_shipto_cntct_email,
+    cohead_billto_cntct_id,
+    cohead_billto_cntct_honorific,
+    cohead_billto_cntct_first_name,
+    cohead_billto_cntct_middle,
+    cohead_billto_cntct_last_name,
+    cohead_billto_cntct_suffix,
+    cohead_billto_cntct_phone,
+    cohead_billto_cntct_title,
+    cohead_billto_cntct_fax,
+    cohead_billto_cntct_email,
+    cohead_taxzone_id,
+    cohead_taxtype_id,
+    cohead_ophead_id,
+    cohead_status,
+    cohead_saletype_id,
+    cohead_shipzone_id )
+  SELECT
+    _soheadid,
+    fetchSoNumber(),
+    cohead_cust_id,
+    cohead_custponumber,
+    cohead_type,
+    CURRENT_DATE,
+    cohead_warehous_id,
+    cohead_shipto_id,
+    cohead_shiptoname,
+    cohead_shiptoaddress1,
+    cohead_shiptoaddress2,
+    cohead_shiptoaddress3,
+    cohead_shiptoaddress4,
+    cohead_shiptoaddress5,
+    cohead_salesrep_id,
+    cohead_terms_id,
+    cohead_fob,
+    cohead_shipvia,
+    cohead_shiptocity,
+    cohead_shiptostate,
+    cohead_shiptozipcode,
+    cohead_freight,
+    cohead_misc,
+    FALSE,
+    cohead_ordercomments,
+    cohead_shipcomments,
+    cohead_shiptophone,
+    cohead_shipchrg_id,
+    cohead_shipform_id,
+    cohead_billtoname,
+    cohead_billtoaddress1,
+    cohead_billtoaddress2,
+    cohead_billtoaddress3,
+    cohead_billtocity,
+    cohead_billtostate,
+    cohead_billtozipcode,
+    cohead_misc_accnt_id,
+    cohead_misc_descrip,
+    cohead_commission,
+    cohead_miscdate,
+    cohead_holdtype,
+    COALESCE(pSchedDate, cohead_packdate),
+    cohead_prj_id,
+    FALSE,
+    cohead_lastupdated,
+    cohead_shipcomplete,
+    NULL,
+    getEffectiveXtUser(),
+    NULL,
+    cohead_billtocountry,
+    cohead_shiptocountry,
+    cohead_curr_id,
+    cohead_calcfreight,
+    cohead_shipto_cntct_id,
+    cohead_shipto_cntct_honorific,
+    cohead_shipto_cntct_first_name,
+    cohead_shipto_cntct_middle,
+    cohead_shipto_cntct_last_name,
+    cohead_shipto_cntct_suffix,
+    cohead_shipto_cntct_phone,
+    cohead_shipto_cntct_title,
+    cohead_shipto_cntct_fax,
+    cohead_shipto_cntct_email,
+    cohead_billto_cntct_id,
+    cohead_billto_cntct_honorific,
+    cohead_billto_cntct_first_name,
+    cohead_billto_cntct_middle,
+    cohead_billto_cntct_last_name,
+    cohead_billto_cntct_suffix,
+    cohead_billto_cntct_phone,
+    cohead_billto_cntct_title,
+    cohead_billto_cntct_fax,
+    cohead_billto_cntct_email,
+    cohead_taxzone_id,
+    cohead_taxtype_id,
+    cohead_ophead_id,
+    cohead_status,
+    cohead_saletype_id,
+    cohead_shipzone_id
+  FROM cohead
+  WHERE (cohead_id=pSoheadid);
+
+  FOR _soitem IN
+    SELECT *
+    FROM coitem JOIN itemsite ON (itemsite_id=coitem_itemsite_id)
+    WHERE ( (coitem_cohead_id=pSoheadid)
+      AND   (coitem_status <> 'X')
+      AND   (coitem_subnumber = 0) ) LOOP
+
+    SELECT NEXTVAL('coitem_coitem_id_seq') INTO _soitemid;
+
+    -- insert characteristics first so they can be copied to associated supply order
+    INSERT INTO charass
+          (charass_target_type, charass_target_id,
+           charass_char_id, charass_value)
+    SELECT charass_target_type, _soitemid,
+           charass_char_id, charass_value
+      FROM charass
+     WHERE ((charass_target_type='SI')
+       AND  (charass_target_id=_soitem.coitem_id));
+
+    INSERT INTO coitem
+    ( coitem_id,
+      coitem_cohead_id,
+      coitem_linenumber,
+      coitem_itemsite_id,
+      coitem_status,
+      coitem_scheddate,
+      coitem_promdate,
+      coitem_qtyord,
+      coitem_unitcost,
+      coitem_price,
+      coitem_custprice,
+      coitem_qtyshipped,
+      coitem_order_id,
+      coitem_memo,
+      coitem_imported,
+      coitem_qtyreturned,
+      coitem_closedate,
+      coitem_custpn,
+      coitem_order_type,
+      coitem_close_username,
+--      coitem_lastupdated,
+      coitem_substitute_item_id,
+      coitem_created,
+      coitem_creator,
+      coitem_prcost,
+      coitem_qty_uom_id,
+      coitem_qty_invuomratio,
+      coitem_price_uom_id,
+      coitem_price_invuomratio,
+      coitem_warranty,
+      coitem_cos_accnt_id,
+      coitem_qtyreserved,
+      coitem_subnumber,
+      coitem_firm,
+      coitem_taxtype_id )
+    VALUES
+    ( _soitemid,
+      _soheadid,
+      _soitem.coitem_linenumber,
+      _soitem.coitem_itemsite_id,
+      'O',
+      COALESCE(pSchedDate, _soitem.coitem_scheddate),
+      _soitem.coitem_promdate,
+      _soitem.coitem_qtyord,
+      stdCost(_soitem.itemsite_item_id),
+      _soitem.coitem_price,
+      _soitem.coitem_custprice,
+      0.0,
+      -1,
+      _soitem.coitem_memo,
+      FALSE,
+      0.0,
+      NULL,
+      _soitem.coitem_custpn,
+      _soitem.coitem_order_type,
+      NULL,
+--      NULL,
+      _soitem.coitem_substitute_item_id,
+      NULL,
+      getEffectiveXtUser(),
+      _soitem.coitem_prcost,
+      _soitem.coitem_qty_uom_id,
+      _soitem.coitem_qty_invuomratio,
+      _soitem.coitem_price_uom_id,
+      _soitem.coitem_price_invuomratio,
+      _soitem.coitem_warranty,
+      _soitem.coitem_cos_accnt_id,
+      0.0,
+      _soitem.coitem_subnumber,
+      _soitem.coitem_firm,
+      _soitem.coitem_taxtype_id );
+
+  END LOOP;
+
+  RETURN _soheadid;
+
+END;
+$$ LANGUAGE plpgsql;
diff --git a/foundation-database/public/functions/copytodoitem.sql b/foundation-database/public/functions/copytodoitem.sql
new file mode 100644 (file)
index 0000000..0ab82c1
--- /dev/null
@@ -0,0 +1,58 @@
+CREATE OR REPLACE FUNCTION copyTodoitem(INTEGER, DATE, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pparentid   ALIAS FOR $1;
+  _duedate    DATE := COALESCE($2, CURRENT_DATE);
+  pincdtid    ALIAS FOR $3;
+  _alarmid    INTEGER;
+  _todoitemid INTEGER;
+
+BEGIN
+  INSERT INTO todoitem(
+            todoitem_name,      todoitem_description,
+            todoitem_incdt_id,
+            todoitem_creator_username,                  todoitem_status,
+            todoitem_active,    todoitem_due_date,
+            todoitem_assigned_date,
+            todoitem_seq,       todoitem_notes,         todoitem_crmacct_id,
+            todoitem_ophead_id, todoitem_owner_username,todoitem_priority_id,
+            todoitem_username,  todoitem_recurring_todoitem_id
+  ) SELECT  todoitem_name,      todoitem_description,
+            CASE WHEN pincdtid IS NULL THEN todoitem_incdt_id ELSE pincdtid END,
+            getEffectiveXtUser(),                               'N',
+            TRUE,               _duedate,
+            CASE WHEN (todoitem_username IS NOT NULL) THEN CURRENT_DATE
+                 ELSE NULL
+            END,
+            todoitem_seq,       todoitem_notes,         todoitem_crmacct_id,
+            todoitem_ophead_id, todoitem_owner_username,todoitem_priority_id,
+            todoitem_username,  todoitem_recurring_todoitem_id
+      FROM todoitem
+     WHERE (todoitem_id=pparentid)
+  RETURNING todoitem_id INTO _todoitemid;
+
+  IF (_todoitemid IS NULL) THEN
+    RETURN -10;
+  END IF;
+
+  SELECT saveAlarm(NULL, NULL, _duedate,
+                   CAST(alarm_time - DATE_TRUNC('day',alarm_time) AS TIME),
+                   alarm_time_offset,
+                   alarm_time_qualifier,
+                   alarm_event, alarm_event_recipient,
+                   alarm_email, alarm_email_recipient,
+                   alarm_sysmsg, alarm_sysmsg_recipient,
+                   'TODO', _todoitemid, 'CHANGEONE')
+    INTO _alarmid
+    FROM alarm
+   WHERE ((alarm_source='TODO')
+      AND (alarm_source_id=pparentid));
+
+   IF (_alarmid < 0) THEN
+     RETURN _alarmid;
+   END IF;
+
+  RETURN _todoitemid;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/copyvoucher.sql b/foundation-database/public/functions/copyvoucher.sql
new file mode 100644 (file)
index 0000000..76b3560
--- /dev/null
@@ -0,0 +1,75 @@
+CREATE OR REPLACE FUNCTION copyVoucher(INTEGER, DATE) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pVoheadid ALIAS FOR $1;
+  _voheadid INTEGER;
+  _vonumber TEXT;
+  _vodate DATE := COALESCE($2, CURRENT_DATE);
+  _i RECORD;
+  _l RECORD;
+  _vodistid INTEGER;
+
+BEGIN
+  SELECT *
+    INTO _i
+    FROM vohead
+   WHERE(vohead_id=pVoheadid);
+  IF(NOT FOUND) THEN
+    RETURN -1;
+  END IF;
+
+  _vonumber := fetchVoNumber();
+  _voheadid := nextval('vohead_vohead_id_seq');
+
+  INSERT INTO vohead
+        (vohead_id,
+         vohead_number, vohead_pohead_id,
+         vohead_posted, vohead_duedate,
+         vohead_invcnumber, vohead_amount,
+         vohead_docdate, vohead_1099,
+         vohead_distdate, vohead_reference,
+         vohead_terms_id, vohead_vend_id,
+         vohead_curr_id, vohead_adjtaxtype_id,
+         vohead_freighttaxtype_id, vohead_gldistdate,
+         vohead_misc, vohead_taxzone_id,
+         vohead_taxtype_id, vohead_notes,
+         vohead_recurring_vohead_id )
+  VALUES(_voheadid,
+         _vonumber, _i.vohead_pohead_id,
+         false, determineDueDate(_i.vohead_terms_id, _vodate),
+         _i.vohead_invcnumber, _i.vohead_amount,
+         _vodate, _i.vohead_1099,
+         _vodate, _i.vohead_reference,
+         _i.vohead_terms_id, _i.vohead_vend_id,
+         _i.vohead_curr_id, _i.vohead_adjtaxtype_id,
+         _i.vohead_freighttaxtype_id, _vodate,
+         _i.vohead_misc, _i.vohead_taxzone_id,
+         _i.vohead_taxtype_id, _i.vohead_notes,
+         _i.vohead_recurring_vohead_id);
+
+  FOR _l IN SELECT *
+            FROM vodist
+            WHERE (vodist_vohead_id=pVoheadid) LOOP
+    SELECT NEXTVAL('vodist_vodist_id_seq') INTO _vodistid;
+
+    INSERT INTO vodist
+        (vodist_id, vodist_poitem_id,
+         vodist_vohead_id, vodist_costelem_id,
+         vodist_accnt_id, vodist_amount,
+         vodist_qty, vodist_expcat_id,
+         vodist_tax_id, vodist_discountable,
+         vodist_notes)
+    VALUES
+        (_vodistid, _l.vodist_poitem_id,
+         _voheadid, _l.vodist_costelem_id,
+         _l.vodist_accnt_id, _l.vodist_amount,
+         _l.vodist_qty, _l.vodist_expcat_id,
+         _l.vodist_tax_id, _l.vodist_discountable,
+         _l.vodist_notes);
+
+  END LOOP;
+
+  RETURN _voheadid;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/correctporeceipt.sql b/foundation-database/public/functions/correctporeceipt.sql
new file mode 100644 (file)
index 0000000..88d41cb
--- /dev/null
@@ -0,0 +1,21 @@
+CREATE OR REPLACE FUNCTION correctPoReceipt(INTEGER, NUMERIC, NUMERIC, INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pPorecvid ALIAS FOR $1;
+  pQty ALIAS FOR $2;
+  pFreight ALIAS FOR $3;
+  pItemlocSeries ALIAS FOR $4;
+
+BEGIN
+  RETURN correctReceipt(''PO'', $1, $2, $3, $4, NULL, NULL);
+END;
+' LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION correctPoReceipt(INTEGER, NUMERIC, NUMERIC, INTEGER, INTEGER, DATE) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN correctReceipt(''PO'', $1, $2, $3, $4, $5, $6);
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/correctproduction.sql b/foundation-database/public/functions/correctproduction.sql
new file mode 100644 (file)
index 0000000..ab4bce2
--- /dev/null
@@ -0,0 +1,186 @@
+CREATE OR REPLACE FUNCTION correctProduction(INTEGER, NUMERIC, BOOLEAN, BOOLEAN) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RAISE NOTICE 'correctProduction(INTEGER, NUMERIC, BOOLEAN, BOOLEAN) has been deprecated. Use corrrectProduction(INTEGER, NUMERIC, BOOLEAN, INTEGER) or a package-specific version instead.';
+  RETURN  correctProduction($1, $2, $3, 0, now());
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION correctProduction(INTEGER, NUMERIC, BOOLEAN, BOOLEAN, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RAISE NOTICE 'correctProduction(INTEGER, NUMERIC, BOOLEAN, BOOLEAN, INTEGER) has been deprecated. Use corrrectProduction(INTEGER, NUMERIC, BOOLEAN, INTEGER) or a package-specific version instead.';
+  RETURN correctProduction($1, $2, $3, $5, now());
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION correctProduction(INTEGER, NUMERIC, BOOLEAN, INTEGER, TIMESTAMP WITH TIME ZONE) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWoid          ALIAS FOR $1;
+  pQty           ALIAS FOR $2;
+  pBackflush     ALIAS FOR $3;
+  pItemlocSeries ALIAS FOR $4;
+  pGlDistTS      ALIAS FOR $5;
+BEGIN
+  RETURN correctProduction($1, $2, $3, $4, $5, NULL);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION correctProduction(INTEGER, NUMERIC, BOOLEAN, INTEGER, TIMESTAMP WITH TIME ZONE, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWoid          ALIAS FOR $1;
+  pQty           ALIAS FOR $2;
+  pBackflush     ALIAS FOR $3;
+  pItemlocSeries ALIAS FOR $4;
+  pGlDistTS      ALIAS FOR $5;
+  pInvhistId     ALIAS FOR $6;
+  _invhistid        INTEGER;
+  _itemlocSeries    INTEGER;
+  _r                RECORD;
+  _parentWIPAccntid INTEGER;
+  _parentQty        NUMERIC;
+  _qty              NUMERIC;
+  _wipPost          NUMERIC;
+  _sense            TEXT;
+  _status           TEXT;
+  _type             TEXT;
+  _qtyfxd           NUMERIC := 0;
+
+BEGIN
+
+  -- Qty is positive for Assembly W/O
+  -- Qty is negative for Disassembly W/O
+  IF (pQty = 0) THEN
+    RETURN pItemlocseries;
+  ELSIF (pQty > 0) THEN
+    _sense := 'from';
+  ELSE
+    _sense := 'to';
+  END IF;
+
+  SELECT item_type, roundQty(item_fractional, pQty), wo_status
+    INTO _type, _parentQty, _status
+    FROM wo JOIN itemsite ON (itemsite_id=wo_itemsite_id)
+            JOIN item ON (item_id=itemsite_item_id)
+   WHERE (wo_id=pWoid);
+  
+  IF (_status != 'I') THEN
+    RETURN -1;
+  END IF;
+
+  IF (_type = 'J') THEN
+    RETURN -2;
+  END IF;
+
+  IF (pItemlocSeries = 0) THEN
+    SELECT NEXTVAL('itemloc_series_seq') INTO _itemlocSeries;
+  ELSE
+    _itemlocSeries := pItemlocSeries;
+  END IF;
+
+  --  Calculate the WIP to correct 
+  SELECT CASE WHEN (wo_cosmethod = 'D') THEN wo_postedvalue
+              ELSE  round(((wo_postedvalue - wo_wipvalue) / wo_qtyrcv * _parentQty), 2)
+         END INTO _wipPost
+  FROM wo
+  WHERE (wo_id=pWoid);
+
+  --  Post the inventory transaction
+  SELECT postInvTrans( itemsite_id, 'RM', (_parentQty * -1.0),
+                       'W/O', 'WO', formatwonumber(pWoid), '',
+                       ('Correct Receive Inventory ' || item_number || ' ' || _sense || ' Manufacturing'),
+                       costcat_asset_accnt_id, getPrjAccntId(wo_prj_id, costcat_wip_accnt_id), _itemlocSeries, pGlDistTS,
+                       (_wipPost * -1.0), -- only used when cost is average
+                       pInvhistId) INTO _invhistid
+  FROM wo JOIN itemsite ON (itemsite_id=wo_itemsite_id)
+          JOIN item ON (item_id=itemsite_item_id)
+          JOIN costcat ON (costcat_id=itemsite_costcat_id)
+  WHERE (wo_id=pWoid);
+
+  --  Decrease this W/O's qty. received and increase its WIP value
+  UPDATE wo
+  SET wo_qtyrcv = (wo_qtyrcv - _parentQty),
+      wo_wipvalue = (wo_wipvalue + (CASE WHEN(itemsite_costmethod IN ('A','J'))
+                                              THEN _wipPost
+                                         WHEN(itemsite_costmethod='S')
+                                              THEN stdcost(itemsite_item_id) * _parentQty
+                                         ELSE 0.0 END))
+  FROM itemsite
+  WHERE ( (wo_itemsite_id=itemsite_id)
+   AND (wo_id=pWoid) );
+
+  IF (pBackflush) THEN
+    FOR _r IN SELECT item_id, item_fractional,
+                      itemsite_id, itemsite_warehous_id,
+                      itemsite_controlmethod, itemsite_loccntrl,
+                      itemsite_costmethod, 
+                      wo_qtyrcv, wo_prj_id,
+                      womatl_id, womatl_qtyfxd, womatl_qtyper,
+                      womatl_scrap, womatl_issuemethod, womatl_uom_id
+               FROM wo JOIN womatl ON (womatl_wo_id=wo_id AND womatl_issuemethod='L')
+                       JOIN itemsite ON (itemsite_id=womatl_itemsite_id)
+                       JOIN item ON (item_id=itemsite_item_id)
+               WHERE (wo_id=pWoid) LOOP
+
+      --  Cache the qty to be issued
+      -- If going back to beginning, unissue fixed qty as well
+      IF (_r.wo_qtyrcv - _parentQty > 0) THEN
+        _qtyfxd := 0;
+      ELSE
+        _qtyfxd := _r.womatl_qtyfxd;
+      END IF;
+      
+      _qty = roundQty(_r.item_fractional, (_qtyfxd + _parentQty * _r.womatl_qtyper) * (1 + _r.womatl_scrap));
+
+      IF (_qty > 0) THEN
+        SELECT returnWoMaterial(_r.womatl_id, _qty, _itemlocSeries, pGlDistTS) INTO _itemlocSeries;
+      END IF;
+
+    END LOOP;
+
+       --  BEGIN ROB Decrease this W/O's WIP value for custom costing
+         UPDATE wo
+         SET wo_wipvalue = (wo_wipvalue - (itemcost_stdcost * _parentQty)) 
+       FROM costelem, itemcost, costcat, itemsite, item
+       WHERE 
+         ((wo_id=pWoid) AND
+         (wo_itemsite_id=itemsite_id) AND
+         (itemsite_item_id=item_id) AND
+         (costelem_id = itemcost_costelem_id) AND
+         (itemcost_item_id = itemsite_item_id) AND
+         (itemsite_costcat_id = costcat_id) AND
+         (costelem_exp_accnt_id) IS NOT NULL  AND 
+         (costelem_sys = false));
+
+       --  ROB Distribute to G/L - create Cost Variance, debit WIP
+         PERFORM insertGLTransaction( 'W/O', 'WO', formatwonumber(pWoid),
+                                      ('Correct Post Other Cost ' || item_number || ' ' || _sense || ' Manufacturing'),
+                                      getPrjAccntId(wo_prj_id, costelem_exp_accnt_id), 
+                                      getPrjAccntId(wo_prj_id, costcat_wip_accnt_id), _invhistid,
+                                      ((itemcost_stdcost * _parentQty)* -1),
+                                      CURRENT_DATE )
+       FROM wo, costelem, itemcost, costcat, itemsite, item
+       WHERE 
+         ((wo_id=pWoid) AND
+         (wo_itemsite_id=itemsite_id) AND
+         (itemsite_item_id=item_id) AND
+         (costelem_id = itemcost_costelem_id) AND
+         (itemcost_item_id = itemsite_item_id) AND
+         (itemsite_costcat_id = costcat_id) AND
+         (costelem_exp_accnt_id) IS NOT NULL  AND 
+         (costelem_sys = false));
+       --End ROB
+
+  END IF;
+
+  RETURN _itemlocSeries;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/correctreceipt.sql b/foundation-database/public/functions/correctreceipt.sql
new file mode 100644 (file)
index 0000000..99ae8c4
--- /dev/null
@@ -0,0 +1,279 @@
+CREATE OR REPLACE FUNCTION correctReceipt(INTEGER, NUMERIC, NUMERIC, INTEGER, INTEGER, DATE) RETURNS INTEGER AS $$
+BEGIN
+  RETURN correctReceipt($1, $2, $3, $4, $5, $6, NULL);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION correctReceipt(INTEGER, NUMERIC, NUMERIC, INTEGER, INTEGER, DATE, NUMERIC) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  precvid              ALIAS FOR $1;
+  pQty                 ALIAS FOR $2;
+  pFreight             ALIAS FOR $3;
+  _itemlocSeries       INTEGER := COALESCE($4, 0);
+  _currid              INTEGER := $5;
+  pEffective           ALIAS FOR $6;
+  pRecvCost            ALIAS FOR $7;
+  _freight             NUMERIC;
+  _qty                 NUMERIC;
+  _invhistid           INTEGER;
+  _o                   RECORD;
+  _r                   RECORD;
+  _recvcost            NUMERIC;
+  _tmp        INTEGER;
+  _pricevar             NUMERIC := 0.00;
+  _journalNumber INTEGER := fetchJournalNumber('GL-MISC');
+
+BEGIN
+  SELECT recv_qty, recv_date::DATE AS recv_date, recv_freight_curr_id,
+        recv_orderitem_id,
+        round(currToCurr(recv_freight_curr_id,
+                         COALESCE(_currid, recv_freight_curr_id),
+         recv_freight, recv_date::DATE),2) AS recv_freight,
+         recv_posted, recv_order_type,
+         COALESCE(itemsite_id, -1) AS itemsiteid,
+        itemsite_item_id, itemsite_costmethod, itemsite_controlmethod,
+        (recv_splitfrom_id IS NOT NULL
+        OR (SELECT (count(*) > 0) 
+            FROM recv
+            WHERE (recv_splitfrom_id=recv_id))) AS split INTO _r
+  FROM recv LEFT OUTER JOIN itemsite ON (recv_itemsite_id=itemsite_id)
+  WHERE (recv_id=precvid);
+
+  IF (NOT FOUND) THEN
+    RETURN _itemlocSeries;
+  END IF;
+
+  IF (NOT _r.recv_order_type IN ('PO', 'RA', 'TO')) THEN
+    RETURN -11;
+  END IF;
+
+  IF (_r.split) THEN
+    RETURN -12;
+  END IF;
+
+  SELECT currToBase(orderitem_unitcost_curr_id, orderitem_unitcost,
+                   _r.recv_date::DATE) AS unitprice_base,
+        orderhead_number, orderitem_linenumber,
+        orderhead_curr_id AS freight_curr_id,
+        orderitem_orderhead_type,
+        orderitem_qty_invuomratio INTO _o
+  FROM orderhead, orderitem
+  WHERE ((orderhead_id=orderitem_orderhead_id)
+    AND  (orderhead_type=orderitem_orderhead_type)
+    AND  (orderitem_id=_r.recv_orderitem_id)
+    AND  (orderitem_orderhead_type=_r.recv_order_type));
+
+  IF (NOT FOUND) THEN
+    RETURN _itemlocSeries;
+  END IF;
+
+  -- Default to _o.orderitem_unitcost if recv_purchcost is not supplied
+  -- Note: this should never happen, a value is always supplied
+  if (pRecvCost IS NULL) THEN
+    _recvcost := _o.orderitem_unitcost;
+  ELSE
+    -- Note: if the receipt has already been posted, pRecvCost will always 
+    --       equal the original recv_purchcost (cannot be modified in GUI)
+    _recvcost := pRecvCost; 
+  END IF;
+
+  IF (_r.recv_posted) THEN
+    _qty := (pQty - _r.recv_qty);
+    IF (_qty <> 0) THEN
+      IF (_r.itemsiteid = -1) THEN
+        PERFORM insertGLTransaction( _journalNumber,'S/R',
+                                     _r.recv_order_type,
+                                     _o.orderhead_number,
+                                     'Receive Non-Inventory from ' || _r.recv_order_type,
+                                     expcat_liability_accnt_id,
+                                     getPrjAccntId(poitem_prj_id, expcat_exp_accnt_id),
+                                     -1,
+                                     ROUND(_o.unitprice_base * _qty, 2),
+                                     pEffective )
+        FROM poitem, expcat
+        WHERE ((poitem_expcat_id=expcat_id)
+          AND  (poitem_id=_r.recv_orderitem_id)
+          AND  (_o.orderitem_orderhead_type='PO'));
+
+        UPDATE recv
+        SET recv_qty=pQty,
+            recv_value=(recv_value + ROUND(_o.unitprice_base * _qty, 2)),
+            recv_date = pEffective
+        WHERE (recv_id=precvid);
+      ELSEIF (_r.itemsite_controlmethod = 'N') THEN
+        PERFORM insertGLTransaction( _journalNumber,'S/R',
+                                     _r.recv_order_type,
+                                     _o.orderhead_number,
+                                     'Receive Non-Controlled Inventory from ' || _r.recv_order_type,
+                                     costcat_liability_accnt_id,
+                                     getPrjAccntId(poitem_prj_id, costcat_exp_accnt_id),
+                                     -1,
+                                     ROUND(_o.unitprice_base * _qty, 2),
+                                     pEffective )
+        FROM poitem, itemsite, costcat
+        WHERE ((poitem_itemsite_id=itemsite_id)
+          AND  (itemsite_costcat_id=costcat_id)
+          AND  (poitem_id=_r.recv_orderitem_id)
+          AND  (_o.orderitem_orderhead_type='PO'));
+
+        UPDATE recv
+        SET recv_qty=pQty,
+            recv_value=(recv_value + ROUND(_o.unitprice_base * _qty, 2)),
+            recv_date = pEffective
+        WHERE (recv_id=precvid);
+      ELSE
+        IF (_itemlocSeries = 0 OR _itemlocSeries IS NULL) THEN
+          _itemlocSeries := NEXTVAL('itemloc_series_seq');
+        END IF;
+
+  SELECT postInvTrans( itemsite_id, 'RP',
+                            (_qty * _o.orderitem_qty_invuomratio),
+                            'S/R', _r.recv_order_type,
+                            _o.orderhead_number::TEXT || '-' || _o.orderitem_linenumber::TEXT, '',
+                            'Receive Inventory from ' || _r.recv_order_type,
+                            costcat_asset_accnt_id,
+                            costcat_liability_accnt_id,
+                            _itemlocSeries, pEffective,
+           ROUND(_recvcost * _qty, 2) -- alway passing since it is ignored if not average costed item
+                           ) INTO _tmp
+       FROM itemsite, costcat
+       WHERE ((itemsite_costcat_id=costcat_id)
+    AND  (itemsite_id=_r.itemsiteid) );
+
+        IF(_r.itemsite_costmethod='A') THEN
+         UPDATE recv
+            SET recv_qty=pQty,
+                recv_value=(recv_value + _recvcost * _qty * _o.orderitem_qty_invuomratio),
+                 recv_date = pEffective
+          WHERE(recv_id=precvid);
+        ELSE
+         UPDATE recv
+            SET recv_qty=pQty,
+                recv_value=(recv_value + stdcost(_r.itemsite_item_id) * _qty * _o.orderitem_qty_invuomratio),
+                 recv_date = pEffective
+          WHERE(recv_id=precvid);
+        END IF;
+    END IF;
+
+      IF (_r.recv_order_type = 'PO') THEN
+       UPDATE poitem
+       SET poitem_qty_received=(poitem_qty_received + _qty)
+       WHERE (poitem_id=_r.recv_orderitem_id);
+      ELSIF (_r.recv_order_type = 'RA' AND fetchMetricBool('EnableReturnAuth')) THEN
+       UPDATE raitem
+       SET raitem_qtyreceived=(raitem_qtyreceived + _qty)
+       WHERE (raitem_id=_r.recv_orderitem_id);
+      ELSIF (_r.recv_order_type = 'TO' AND fetchMetricBool('MultiWhs')) THEN
+       UPDATE toitem
+       SET toitem_qty_received=(toitem_qty_received + _qty)
+       WHERE (toitem_id=_r.recv_orderitem_id);
+      END IF;
+
+    END IF;
+
+       IF (fetchMetricBool('RecordPPVonReceipt')) THEN -- If the 'Purchase Price Variance on Receipt' option is true
+         _invhistid := _tmp;
+         -- Find the difference in the purchase price value expected from the P/O and the value of the transaction
+         SELECT (((currToBase(pohead_curr_id,
+         COALESCE(recv_purchcost, poitem_unitprice),
+         recv_date::DATE)) * _qty) - (invhist_value_after - invhist_value_before)) INTO _pricevar
+         FROM invhist, recv, pohead, poitem
+         WHERE ((recv_orderitem_id=poitem_id)
+           AND  (poitem_pohead_id=pohead_id)
+           AND  (recv_id=precvid)
+           AND  (invhist_id = _invhistid));
+
+         -- If difference exists then
+         IF (_pricevar <> 0.00) THEN
+           -- Record an additional GL Transaction for the purchase price variance
+           SELECT insertGLTransaction( _journalNumber,
+                'S/R', _r.recv_order_type, _o.orderhead_number,
+                                       'Purchase price variance adjusted for P/O ' || _o.orderhead_number || ' for item ' || _o.orderitem_linenumber::TEXT,
+                                       costcat_liability_accnt_id,
+                                       getPrjAccntId(poitem_prj_id, costcat_purchprice_accnt_id), -1,
+                                       _pricevar,
+                                       pEffective, false ) INTO _tmp
+           FROM itemsite, costcat, poitem, recv
+           WHERE ((itemsite_costcat_id=costcat_id)
+              AND (recv_id=precvid)
+              AND (recv_orderitem_id=poitem_id)
+              AND (itemsite_id=recv_itemsite_id) );
+           IF (NOT FOUND) THEN
+             RAISE EXCEPTION 'Could not insert G/L transaction: no cost category found for itemsite_id %',
+             _r.itemsite_id;
+           ELSIF (_tmp < 0 AND _tmp != -3) THEN -- error but not 0-value transaction
+             RETURN _tmp;
+           ELSE
+             -- Posting to trial balance is deferred to prevent locking
+             INSERT INTO itemlocpost ( itemlocpost_glseq, itemlocpost_itemlocseries)
+             VALUES ( _tmp, _itemlocSeries );
+           END IF;
+         END IF;
+       END IF;
+
+    _freight := (pFreight - _r.recv_freight);
+    IF (_freight <> 0) THEN
+
+      IF (_r.itemsiteid = -1) THEN
+  PERFORM insertGLTransaction( _journalNumber,'S/R', _r.recv_order_type,
+                                    _o.orderhead_number,
+                                   'Receive Non-Inventory Freight from ' || _r.recv_order_type,
+             expcat_liability_accnt_id, getPrjAccntId(poitem_prj_id, expcat_freight_accnt_id), -1,
+                                     ROUND(currToBase(_currid, _freight,
+                                                   pEffective), 2),
+                                    pEffective )
+       FROM poitem, expcat
+       WHERE ((poitem_expcat_id=expcat_id)
+         AND  (poitem_id=_r.recv_orderitem_id)
+         AND  (_r.recv_order_type='PO'));
+      ELSE
+  PERFORM insertGLTransaction(_journalNumber,'S/R', _r.recv_order_type,
+                                   _o.orderhead_number, 
+                                   'Receive Non-Inventory Freight from ' ||
+                                                           _r.recv_order_type,
+                                  costcat_liability_accnt_id,
+                                  costcat_freight_accnt_id, -1,
+                                  round(currToBase(_currid, _freight,
+                                                   pEffective), 2),
+                                  pEffective )
+       FROM itemsite, costcat
+       WHERE ( (itemsite_costcat_id=costcat_id)
+         AND   (itemsite_id=_r.itemsiteid) );
+      END IF;
+
+      IF (_r.recv_order_type = 'PO') THEN
+       UPDATE poitem
+       SET poitem_freight_received=(poitem_freight_received +
+                                  currToCurr(_currid, _o.freight_curr_id,
+                                             _freight, pEffective))
+       WHERE (poitem_id=_r.recv_orderitem_id);
+
+      -- raitem does not track freight
+
+      ELSEIF (_r.recv_order_type = 'TO' AND fetchMetricBool('MultiWhs')) THEN
+       UPDATE toitem
+       SET toitem_freight_received=(toitem_freight_received +
+                                  currToCurr(_currid, _o.freight_curr_id,
+                                             _freight, pEffective))
+       WHERE (toitem_id=_r.recv_orderitem_id);
+      END IF;
+
+      UPDATE recv
+      SET recv_freight=currToCurr(_currid, recv_freight_curr_id, pFreight,
+                                 pEffective),
+         recv_date = pEffective
+      WHERE (recv_id=precvid);
+    END IF;
+
+  ELSE
+
+-- Receipt not posted yet
+    UPDATE recv SET recv_qty=pQty, recv_freight=pFreight, recv_purchcost=_recvcost WHERE recv_id=precvid;
+  END IF;
+
+RETURN _itemlocSeries;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/cosbycustomervalue.sql b/foundation-database/public/functions/cosbycustomervalue.sql
new file mode 100644 (file)
index 0000000..f3886f2
--- /dev/null
@@ -0,0 +1,30 @@
+CREATE OR REPLACE FUNCTION cosByCustomerValue(INTEGER, INTEGER) RETURNS NUMERIC AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCustid ALIAS FOR $1;
+  pPeriodid ALIAS FOR $2;
+  _value NUMERIC;
+  _startDate DATE;
+  _endDate DATE;
+
+BEGIN
+
+  _startDate := findPeriodStart(pPeriodid);
+  _endDate := findPeriodEnd(pPeriodid);
+
+-- Returns value in base currency
+-- ToDo: is cohist_shipdate the right DATE to use?
+  SELECT SUM(cohist_qtyshipped * currToBase(cohist_curr_id, cohist_unitcost, cohist_shipdate)) INTO _value
+  FROM cohist
+  WHERE ( (cohist_cust_id=pCustid)
+   AND (cohist_invcdate BETWEEN _startDate AND _endDate) );
+
+  IF (_value IS NULL) THEN
+    _value := 0;
+  END IF;
+
+  RETURN _value;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/costsbycustomerbyitemsite.sql b/foundation-database/public/functions/costsbycustomerbyitemsite.sql
new file mode 100644 (file)
index 0000000..8f45905
--- /dev/null
@@ -0,0 +1,32 @@
+CREATE OR REPLACE FUNCTION costsByCustomerByItemSite(INTEGER, INTEGER, INTEGER) RETURNS NUMERIC AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCustid ALIAS FOR $1;
+  pItemsiteid ALIAS FOR $2;
+  pPeriodid ALIAS FOR $3;
+  _value NUMERIC;
+  _startDate DATE;
+  _endDate DATE;
+
+BEGIN
+
+  _startDate := findPeriodStart(pPeriodid);
+  _endDate := findPeriodEnd(pPeriodid);
+
+-- Returns value in base currency
+-- ToDo: is cohist_shipdate the right DATE to use?
+  SELECT SUM(cohist_qtyshipped * currToBase(cohist_curr_id, cohist_unitcost, cohist_shipdate)) INTO _value
+  FROM cohist
+  WHERE ( (cohist_itemsite_id<>pItemsiteid)
+   AND (cohist_cust_id=pCustid)
+   AND (cohist_invcdate BETWEEN _startDate AND _endDate) );
+
+  IF (_value IS NULL) THEN
+    _value := 0;
+  END IF;
+
+  RETURN _value;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/costsbycustomervalue.sql b/foundation-database/public/functions/costsbycustomervalue.sql
new file mode 100644 (file)
index 0000000..44f283a
--- /dev/null
@@ -0,0 +1,103 @@
+CREATE OR REPLACE FUNCTION costsByCustomerValue(INTEGER, INTEGER) RETURNS NUMERIC AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCustid ALIAS FOR $1;
+  pPeriodid ALIAS FOR $2;
+  _value NUMERIC;
+  _startDate DATE;
+  _endDate DATE;
+
+BEGIN
+
+  _startDate := findPeriodStart(pPeriodid);
+  _endDate := findPeriodEnd(pPeriodid);
+
+-- Returns value in base currency
+-- ToDo: is cohist_shipdate the right DATE to use?
+  SELECT SUM(cohist_qtyshipped * currToBase(cohist_curr_id, cohist_unitcost, cohist_shipdate)) INTO _value
+  FROM cohist
+  WHERE ( (cohist_itemsite_id<>-1)
+   AND (cohist_cust_id=pCustid)
+   AND (cohist_invcdate BETWEEN _startDate AND _endDate) );
+
+  IF (_value IS NULL) THEN
+    _value := 0;
+  END IF;
+
+  RETURN _value;
+
+END;
+' LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION costsByCustomerValue(INTEGER, INTEGER, INTEGER) RETURNS NUMERIC AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCustid ALIAS FOR $1;
+  pPeriodid ALIAS FOR $2;
+  pProdcatid ALIAS FOR $3;
+  _value NUMERIC;
+  _startDate DATE;
+  _endDate DATE;
+
+BEGIN
+
+  _startDate := findPeriodStart(pPeriodid);
+  _endDate := findPeriodEnd(pPeriodid);
+
+-- Returns value in base currency
+-- ToDo: is cohist_shipdate the right DATE to use?
+  SELECT SUM(cohist_qtyshipped * currToBase(cohist_curr_id, cohist_unitcost, cohist_shipdate)) INTO _value
+  FROM cohist, itemsite, item
+  WHERE ( (cohist_cust_id=pCustid)
+   AND (cohist_itemsite_id=itemsite_id)
+   AND (itemsite_item_id=item_id)
+   AND (item_prodcat_id=pProdcatid)
+   AND (cohist_invcdate BETWEEN _startDate AND _endDate) );
+
+  IF (_value IS NULL) THEN
+    _value := 0;
+  END IF;
+
+  RETURN _value;
+
+END;
+' LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION costsByCustomerValue(INTEGER, INTEGER, TEXT) RETURNS NUMERIC AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCustid ALIAS FOR $1;
+  pPeriodid ALIAS FOR $2;
+  pProdcat ALIAS FOR $3;
+  _value NUMERIC;
+  _startDate DATE;
+  _endDate DATE;
+
+BEGIN
+
+  _startDate := findPeriodStart(pPeriodid);
+  _endDate := findPeriodEnd(pPeriodid);
+
+-- Returns value in base currency
+-- ToDo: is cohist_shipdate the right date to use?
+  SELECT SUM(cohist_qtyshipped * currToBase(cohist_curr_id, cohist_unitcost, cohist_shipdate)) INTO _value
+  FROM cohist, itemsite, item, prodcat
+  WHERE ( (cohist_cust_id=pCustid)
+   AND (cohist_itemsite_id=itemsite_id)
+   AND (itemsite_item_id=item_id)
+   AND (item_prodcat_id=prodcat_id)
+   AND (prodcat_code ~ pProdcat)
+   AND (cohist_invcdate BETWEEN _startDate AND _endDate) );
+
+  IF (_value IS NULL) THEN
+    _value := 0;
+  END IF;
+
+  RETURN _value;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/createaccountingperiod.sql b/foundation-database/public/functions/createaccountingperiod.sql
new file mode 100644 (file)
index 0000000..e17b5d9
--- /dev/null
@@ -0,0 +1,98 @@
+
+CREATE OR REPLACE FUNCTION createAccountingPeriod(DATE, DATE) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pStartDate ALIAS FOR $1;
+  pEndDate ALIAS FOR $2;
+
+BEGIN
+
+  RETURN createAccountingPeriod(pStartDate, pEndDate, NULL, NULL);
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION createaccountingperiod(DATE, DATE, INTEGER, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pStartDate ALIAS FOR $1;
+  pEndDate ALIAS FOR $2;
+  pYearPeriodId ALIAS FOR $3;
+  pQuarter ALIAS FOR $4;
+  _periodid INTEGER;
+  _check INTEGER;
+  _r RECORD;
+  _initial BOOLEAN;
+  _number INTEGER;
+
+BEGIN
+
+--  Make that the passed start date doesn't fall into any existing period
+  SELECT period_id INTO _check
+  FROM period
+  WHERE (pStartDate BETWEEN period_start AND period_end);
+  IF (FOUND) THEN
+    RETURN -1;
+  END IF;
+
+--  Make that the passed end date doesn't fall into any existing period
+  SELECT period_id INTO _check
+  FROM period
+  WHERE (pEndDate BETWEEN period_start AND period_end);
+  IF (FOUND) THEN
+    RETURN -2;
+  END IF;
+
+--  Make that the passed start and end dates don't enclose an existing period
+  SELECT period_id INTO _check
+  FROM period
+  WHERE ( (period_start >= pStartDate)
+   AND (period_end <= pEndDate) );
+  IF (FOUND) THEN
+    RETURN -3;
+  END IF;
+
+-- Make sure period is inside fiscal year
+  SELECT yearperiod_id INTO _check
+  FROM yearperiod
+  WHERE ((yearperiod_id=pYearPeriodId)
+  AND (pStartDate>=yearperiod_start)
+  AND (pEndDate<=yearperiod_end));
+  IF NOT (FOUND) THEN
+    RETURN -4;
+  END IF;
+
+--  Determine if this is the initial accounting period
+  SELECT CASE WHEN(count(*) > 0) THEN FALSE
+              ELSE TRUE
+         END INTO _initial
+  FROM period;
+
+-- Determine the next number
+  SELECT COALESCE(MAX(period_number),0) + 1 INTO _number
+  FROM period
+  WHERE (period_yearperiod_id=pYearPeriodId);
+
+--  Create the new accounting period
+  SELECT NEXTVAL('period_period_id_seq') INTO _periodid;
+  INSERT INTO period
+  ( period_id, period_start, period_end, period_closed, period_freeze, 
+    period_initial, period_number, period_yearperiod_id, period_quarter )
+  VALUES
+  ( _periodid, pStartDate, pEndDate, FALSE, FALSE, _initial, _number, pYearPeriodId, pQuarter );
+
+--  Post any unposted G/L Transactions into the new period
+  FOR _r IN SELECT DISTINCT gltrans_sequence
+            FROM gltrans
+            WHERE ( (NOT gltrans_posted)
+             AND (gltrans_date BETWEEN pStartDate AND pEndDate) ) LOOP
+    PERFORM postIntoTrialBalance(_r.gltrans_sequence);
+  END LOOP;
+
+  RETURN _periodid;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/createaccountingyearperiod.sql b/foundation-database/public/functions/createaccountingyearperiod.sql
new file mode 100644 (file)
index 0000000..0869201
--- /dev/null
@@ -0,0 +1,64 @@
+
+CREATE OR REPLACE FUNCTION createAccountingYearPeriod(DATE, DATE) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pStartDate ALIAS FOR $1;
+  pEndDate ALIAS FOR $2;
+  _yearperiodid INTEGER;
+  _check INTEGER;
+  _checkBool BOOLEAN;
+  _r RECORD;
+  _initial BOOLEAN;
+
+BEGIN
+
+--  Make that the passed start date doesn''t fall into any existing yearperiod
+  SELECT yearperiod_id INTO _check
+  FROM yearperiod
+  WHERE (pStartDate BETWEEN yearperiod_start AND yearperiod_end);
+  IF (FOUND) THEN
+    RETURN -1;
+  END IF;
+
+--  Make that the passed end date doesn''t fall into any existing yearperiod
+  SELECT yearperiod_id INTO _check
+  FROM yearperiod
+  WHERE (pEndDate BETWEEN yearperiod_start AND yearperiod_end);
+  IF (FOUND) THEN
+    RETURN -2;
+  END IF;
+
+--  Make that the passed start and end dates don''t enclose an existing yearperiod
+  SELECT yearperiod_id INTO _check
+  FROM yearperiod
+  WHERE ( (yearperiod_start >= pStartDate)
+   AND (yearperiod_end <= pEndDate) );
+  IF (FOUND) THEN
+    RETURN -3;
+  END IF;
+
+--  Make sure that the passed start is prior to the end date
+  SELECT (pStartDate > pEndDate) INTO _checkBool;
+  IF (_checkBool) THEN
+    RETURN -5;
+  END IF;
+
+--  Determine if this is the initial accounting yearperiod
+  SELECT CASE WHEN(count(*) > 0) THEN FALSE
+              ELSE TRUE
+         END INTO _initial
+  FROM yearperiod;
+
+--  Create the new accounting yearperiod
+  SELECT NEXTVAL(''yearperiod_yearperiod_id_seq'') INTO _yearperiodid;
+  INSERT INTO yearperiod
+  ( yearperiod_id, yearperiod_start, yearperiod_end, yearperiod_closed )
+  VALUES
+  ( _yearperiodid, pStartDate, pEndDate, FALSE );
+
+  RETURN _yearperiodid;
+
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/createapchecks.sql b/foundation-database/public/functions/createapchecks.sql
new file mode 100644 (file)
index 0000000..5d26caa
--- /dev/null
@@ -0,0 +1,8 @@
+CREATE OR REPLACE FUNCTION createAPChecks(INTEGER, DATE) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RAISE NOTICE 'createAPChecks() is deprecated - use createChecks() instead';
+  RETURN createChecks($1, $2);
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/createapcreditmemo.sql b/foundation-database/public/functions/createapcreditmemo.sql
new file mode 100644 (file)
index 0000000..1e691a3
--- /dev/null
@@ -0,0 +1,270 @@
+CREATE OR REPLACE FUNCTION createAPCreditMemo(INTEGER, TEXT, TEXT, DATE, NUMERIC, TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pVendid ALIAS FOR $1;
+  pDocNumber ALIAS FOR $2;
+  pPoNumber ALIAS FOR $3;
+  pDocDate ALIAS FOR $4;
+  pAmount ALIAS FOR $5;
+  pNotes ALIAS FOR $6;
+  _result INTEGER;
+
+BEGIN
+
+  SELECT createAPCreditMemo( pVendid, fetchJournalNumber('AP-MISC'),
+                             pDocNumber, pPoNumber, pDocDate, pAmount, pNotes, -1, pDocDate, -1, baseCurrId() ) INTO _result;
+
+  RETURN _result;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION createAPCreditMemo(INTEGER, TEXT, TEXT, DATE, NUMERIC, TEXT, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pVendid ALIAS FOR $1;
+  pDocNumber ALIAS FOR $2;
+  pPoNumber ALIAS FOR $3;
+  pDocDate ALIAS FOR $4;
+  pAmount ALIAS FOR $5;
+  pNotes ALIAS FOR $6;
+  pAccntid ALIAS FOR $7;
+  _result INTEGER;
+
+BEGIN
+
+  SELECT createAPCreditMemo( pVendid, fetchJournalNumber('AP-MISC'),
+                             pDocNumber, pPoNumber, pDocDate, pAmount, pNotes, pAccntid, pDocDate, -1, baseCurrId() ) INTO _result;
+
+  RETURN _result;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION createAPCreditMemo(INTEGER, TEXT, TEXT, DATE, NUMERIC, TEXT, INTEGER, DATE, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pVendid ALIAS FOR $1;
+  pDocNumber ALIAS FOR $2;
+  pPoNumber ALIAS FOR $3;
+  pDocDate ALIAS FOR $4;
+  pAmount ALIAS FOR $5;
+  pNotes ALIAS FOR $6;
+  pAccntid ALIAS FOR $7;
+  pDueDate ALIAS FOR $8;
+  pTermsid ALIAS FOR $9;
+  _result INTEGER;
+
+BEGIN
+
+  SELECT createAPCreditMemo( pVendid, fetchJournalNumber('AP-MISC'),
+                             pDocNumber, pPoNumber, pDocDate, pAmount, pNotes, pAccntid, pDueDate, pTermsid, baseCurrId() ) INTO _result;
+
+  RETURN _result;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION createAPCreditMemo(INTEGER, INTEGER, TEXT, TEXT, DATE, NUMERIC, TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pVendid ALIAS FOR $1;
+  pJournalNumber ALIAS FOR $2;
+  pDocNumber ALIAS FOR $3;
+  pPoNumber ALIAS FOR $4;
+  pDocDate ALIAS FOR $5;
+  pAmount ALIAS FOR $6;
+  pNotes ALIAS FOR $7;
+
+BEGIN
+  RETURN createAPCreditMemo(pVendid, pJournalNumber, pDocNumber, pPoNumber, pDocDate, pAmount, pNotes, -1, pDocDate, -1, baseCurrId() );
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION createAPCreditMemo(INTEGER, INTEGER, TEXT, TEXT, DATE, NUMERIC, TEXT, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pVendid ALIAS FOR $1;
+  pJournalNumber ALIAS FOR $2;
+  pDocNumber ALIAS FOR $3;
+  pPoNumber ALIAS FOR $4;
+  pDocDate ALIAS FOR $5;
+  pAmount ALIAS FOR $6;
+  pNotes ALIAS FOR $7;
+  pAccntid ALIAS FOR $8;
+BEGIN
+  RETURN createAPCreditMemo( pVendid, pJournalNumber,
+                             pDocNumber, pPoNumber, pDocDate, pAmount, pNotes, pAccntid, pDocDate, -1, baseCurrId() );
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION createAPCreditMemo(INTEGER, INTEGER, TEXT, TEXT, DATE, NUMERIC, TEXT, INTEGER, DATE, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pVendid ALIAS FOR $1;
+  pJournalNumber ALIAS FOR $2;
+  pDocNumber ALIAS FOR $3;
+  pPoNumber ALIAS FOR $4;
+  pDocDate ALIAS FOR $5;
+  pAmount ALIAS FOR $6;
+  pNotes ALIAS FOR $7;
+  pAccntid ALIAS FOR $8;
+  pDueDate ALIAS FOR $9;
+  pTermsid ALIAS FOR $10;
+BEGIN
+  RETURN createAPCreditMemo( pVendid, pJournalNumber, pDocNumber, pPoNumber, pDocDate, pAmount, pNotes, pAccntid, pDueDate, pTermsid, baseCurrId() );
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION createAPCreditMemo(INTEGER, INTEGER, TEXT, TEXT, DATE, NUMERIC, TEXT, INTEGER, DATE, INTEGER, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pVendid ALIAS FOR $1;
+  pJournalNumber ALIAS FOR $2;
+  pDocNumber ALIAS FOR $3;
+  pPoNumber ALIAS FOR $4;
+  pDocDate ALIAS FOR $5;
+  pAmount ALIAS FOR $6;
+  pNotes ALIAS FOR $7;
+  pAccntid ALIAS FOR $8;
+  pDueDate ALIAS FOR $9;
+  pTermsid ALIAS FOR $10;
+  pCurrId ALIAS FOR $11;
+BEGIN
+  RETURN createAPCreditMemo( NULL, pVendid, pJournalNumber, pDocNumber, pPoNumber, pDocDate, pAmount, pNotes, pAccntid, pDueDate, pTermsid, pCurrId );
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION createAPCreditMemo(INTEGER, INTEGER, INTEGER, TEXT, TEXT, DATE, NUMERIC, TEXT, INTEGER, DATE, INTEGER, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pId ALIAS FOR $1;
+  pVendid ALIAS FOR $2;
+  pJournalNumber ALIAS FOR $3;
+  pDocNumber ALIAS FOR $4;
+  pPoNumber ALIAS FOR $5;
+  pDocDate ALIAS FOR $6;
+  pAmount ALIAS FOR $7;
+  pNotes ALIAS FOR $8;
+  pAccntid ALIAS FOR $9;
+  pDueDate ALIAS FOR $10;
+  pTermsid ALIAS FOR $11;
+  pCurrId ALIAS FOR $12;
+  _vendName TEXT;
+  _apAccntid INTEGER;
+  _prepaidAccntid INTEGER;
+  _accntid INTEGER;
+  _glSequence INTEGER;
+  _journalNumber INTEGER;
+  _apopenid INTEGER;
+  _baseAmount NUMERIC;
+  _taxBaseValue NUMERIC;
+  _test INTEGER;
+
+BEGIN
+
+  _apopenid := pId;
+
+  SELECT findAPAccount(pVendid) INTO _apAccntid;
+  SELECT findAPPrepaidAccount(pVendid) INTO _prepaidAccntid;
+
+  SELECT vend_name INTO _vendName
+  FROM vendinfo
+  WHERE (vend_id=pVendid);
+  
+  _accntid := pAccntid;
+
+  PERFORM accnt_id
+     FROM accnt
+    WHERE (accnt_id=_accntid);
+  IF (FOUND) THEN
+    _prepaidAccntid := _accntid;
+  ELSE
+    _accntid := -1;
+  END IF;
+
+  IF(pJournalNumber IS NULL) THEN
+    SELECT fetchJournalNumber('AP-MISC') INTO _journalNumber;
+  ELSE
+    _journalNumber := pJournalNumber;
+  END IF;
+
+  SELECT fetchGLSequence() INTO _glSequence;
+
+  IF (_apopenid IS NOT NULL) THEN
+    UPDATE apopen SET
+      apopen_username=getEffectiveXtUser(), apopen_journalnumber=_journalNumber,
+      apopen_vend_id=pVendid, apopen_docnumber=pDocNumber,
+      apopen_doctype='C', apopen_ponumber=pPoNumber,
+      apopen_docdate=pDocDate, apopen_duedate=pDueDate,
+      apopen_distdate=pDocDate, apopen_terms_id=pTermsid,
+      apopen_amount=pAmount, apopen_paid=0,
+      apopen_open=(pAmount <> 0), apopen_notes=pNotes,
+      apopen_accnt_id=_accntid, apopen_curr_id=pCurrId,
+      apopen_closedate=CASE WHEN (pAmount = 0) THEN pDocdate END
+    WHERE apopen_id = _apopenid;
+  ELSE
+    SELECT NEXTVAL('apopen_apopen_id_seq') INTO _apopenid;
+    INSERT INTO apopen
+    ( apopen_id, apopen_username, apopen_journalnumber,
+      apopen_vend_id, apopen_docnumber, apopen_doctype, apopen_ponumber,
+      apopen_docdate, apopen_duedate, apopen_distdate, apopen_terms_id,
+      apopen_amount, apopen_paid, apopen_open, apopen_notes, apopen_accnt_id, apopen_curr_id,
+      apopen_closedate )
+    VALUES
+    ( _apopenid, getEffectiveXtUser(), _journalNumber,
+      pVendid, pDocNumber, 'C', pPoNumber,
+      pDocDate, pDueDate, pDocDate, pTermsid,
+      pAmount, 0, (pAmount <> 0), pNotes, _accntid, pCurrId,
+      CASE WHEN (pAmount = 0) THEN pDocDate END );
+  END IF;
+
+  _baseAmount := round(currToBase(pCurrId, pAmount, pDocDate), 2);
+
+  -- Debit the A/P account for the full amount
+  SELECT insertIntoGLSeries ( _glSequence, 'A/P', 'CM',
+                              pDocNumber, _apAccntid,
+                              (_baseAmount * -1),
+                              pDocDate, (_vendName || ' ' || pNotes) ) INTO _test;
+
+  -- Credit the Tax account for the tax amount
+  _taxBaseValue := addTaxToGLSeries(_glSequence,
+                                     'A/P', 'CM', pDocNumber,
+                                     pCurrId, pDocDate, pDocDate,
+                                      'apopentax', _apopenid,
+                                      _vendName);
+
+  UPDATE apopentax SET taxhist_journalnumber = _journalNumber
+  WHERE taxhist_parent_id=_apopenid;
+
+  -- Credit the Prepaid account for the basis amount
+  SELECT insertIntoGLSeries ( _glSequence, 'A/P', 'CM',
+                              pDocNumber, _prepaidAccntid,
+                              (_baseAmount - _taxBaseValue),
+                              pDocDate, (_vendName || ' ' || pNotes) ) INTO _test;
+
+  --  Commit the GLSeries;
+  SELECT postGLSeries(_glSequence, _journalNumber) INTO _test;
+  IF (_test < 0) THEN
+    DELETE FROM apopen WHERE (apopen_id=_apopenid);
+    PERFORM deleteGLSeries(_glSequence);
+    RAISE EXCEPTION 'postGLSeries commit failed with %', _test;
+  END IF;
+
+  RETURN pJournalNumber;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/createapcreditmemoapplication.sql b/foundation-database/public/functions/createapcreditmemoapplication.sql
new file mode 100644 (file)
index 0000000..26e0db6
--- /dev/null
@@ -0,0 +1,54 @@
+CREATE OR REPLACE FUNCTION createAPCreditMemoApplication(pSourceApopenId INTEGER,
+                                                         pTargetApopenId INTEGER,
+                                                         pAmount NUMERIC,
+                                                         pCurrId INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _apCreditApplyId     INTEGER;
+
+BEGIN
+  IF (pAmount > (SELECT currToCurr(apopen_curr_id, pCurrId, ROUND(apopen_amount - apopen_paid, 2), apopen_docdate)
+                 FROM apopen
+                 WHERE (apopen_id=pTargetApopenId))) THEN
+    RETURN -1;
+  END IF;
+
+  IF (pAmount > (SELECT ROUND((apopen_amount - apopen_paid) - 
+                      COALESCE(SUM(currToCurr(apcreditapply_curr_id,
+                                               apopen_curr_id, 
+                                               apcreditapply_amount, 
+                                               apopen_docdate)), 0), 2)
+             FROM apopen LEFT OUTER JOIN apcreditapply 
+               ON ((apcreditapply_source_apopen_id=apopen_id) 
+              AND (apcreditapply_target_apopen_id<>pTargetApopenId)) 
+             WHERE (apopen_id=pSourceApopenId) 
+             GROUP BY apopen_amount, apopen_paid)) THEN
+      RETURN -2;
+    END IF;
+
+  SELECT apcreditapply_id INTO _apCreditApplyId
+    FROM apcreditapply
+   WHERE ((apcreditapply_source_apopen_id=pSourceApopenId)
+     AND  (apcreditapply_target_apopen_id=pTargetApopenId));
+
+  IF (FOUND) THEN
+    UPDATE apcreditapply SET apcreditapply_amount=pAmount,
+                            apcreditapply_curr_id=pCurrId
+    WHERE (apcreditapply_id=_apCreditApplyId);
+  ELSE
+    INSERT INTO apcreditapply (
+      apcreditapply_source_apopen_id,
+      apcreditapply_target_apopen_id,
+      apcreditapply_amount, apcreditapply_curr_id
+    ) VALUES (
+      pSourceApopenId,
+      pTargetApopenId,
+      pAmount, pCurrId)
+    RETURNING apcreditapply_id INTO _apCreditApplyId;
+  END IF;
+
+  RETURN _apCreditApplyId;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/createapdebitmemo.sql b/foundation-database/public/functions/createapdebitmemo.sql
new file mode 100644 (file)
index 0000000..a6860a2
--- /dev/null
@@ -0,0 +1,276 @@
+CREATE OR REPLACE FUNCTION createAPDebitMemo(INTEGER, TEXT, TEXT, DATE, NUMERIC, TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pVendid ALIAS FOR $1;
+  pDocNumber ALIAS FOR $2;
+  pPoNumber ALIAS FOR $3;
+  pDocDate ALIAS FOR $4;
+  pAmount ALIAS FOR $5;
+  pNotes ALIAS FOR $6;
+  _result INTEGER;
+
+BEGIN
+
+  SELECT createAPDebitMemo( pVendid, fetchJournalNumber('AP-MISC'),
+                            pDocNumber, pPoNumber, pDocDate, pAmount, pNotes, -1, pDocDate, -1, baseCurrId() ) INTO _result;
+
+  RETURN _result;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION createAPDebitMemo(INTEGER, TEXT, TEXT, DATE, NUMERIC, TEXT, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pVendid ALIAS FOR $1;
+  pDocNumber ALIAS FOR $2;
+  pPoNumber ALIAS FOR $3;
+  pDocDate ALIAS FOR $4;
+  pAmount ALIAS FOR $5;
+  pNotes ALIAS FOR $6;
+  pAccntid ALIAS FOR $7;
+  _result INTEGER;
+
+BEGIN
+
+  SELECT createAPDebitMemo( pVendid, fetchJournalNumber('AP-MISC'),
+                            pDocNumber, pPoNumber, pDocDate, pAmount, pNotes, pAccntid, pDocDate, -1, baseCurrId() ) INTO _result;
+
+  RETURN _result;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION createAPDebitMemo(INTEGER, TEXT, TEXT, DATE, NUMERIC, TEXT, INTEGER, DATE, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pVendid ALIAS FOR $1;
+  pDocNumber ALIAS FOR $2;
+  pPoNumber ALIAS FOR $3;
+  pDocDate ALIAS FOR $4;
+  pAmount ALIAS FOR $5;
+  pNotes ALIAS FOR $6;
+  pAccntid ALIAS FOR $7;
+  pDueDate ALIAS FOR $8;
+  pTermsid ALIAS FOR $9;
+  _result INTEGER;
+
+BEGIN
+
+  SELECT createAPDebitMemo( pVendid, fetchJournalNumber('AP-MISC'),
+                            pDocNumber, pPoNumber, pDocDate, pAmount, pNotes, pAccntid, pDueDate, pTermsid, baseCurrId() ) INTO _result;
+
+  RETURN _result;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION createAPDebitMemo(INTEGER, INTEGER, TEXT, TEXT, DATE, NUMERIC, TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pVendid ALIAS FOR $1;
+  pJournalNumber ALIAS FOR $2;
+  pDocNumber ALIAS FOR $3;
+  pPoNumber ALIAS FOR $4;
+  pDocDate ALIAS FOR $5;
+  pAmount ALIAS FOR $6;
+  pNotes ALIAS FOR $7;
+
+BEGIN
+
+  RETURN createAPDebitMemo(pVendid, pJournalNumber, pDocNumber, pPoNumber, pDocDate, pAmount, pNotes, -1, pDocDate, -1, baseCurrId() );
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION createAPDebitMemo(INTEGER, INTEGER, TEXT, TEXT, DATE, NUMERIC, TEXT, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pVendid ALIAS FOR $1;
+  pJournalNumber ALIAS FOR $2;
+  pDocNumber ALIAS FOR $3;
+  pPoNumber ALIAS FOR $4;
+  pDocDate ALIAS FOR $5;
+  pAmount ALIAS FOR $6;
+  pNotes ALIAS FOR $7;
+  pAccntid ALIAS FOR $8;
+BEGIN
+  RETURN createAPDebitMemo( pVendid, pJournalNumber,
+                            pDocNumber, pPoNumber, pDocDate, pAmount, pNotes, pAccntid, pDocDate, -1, baseCurrId() );
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION createAPDebitMemo(INTEGER, INTEGER, TEXT, TEXT, DATE, NUMERIC, TEXT, INTEGER, DATE, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pVendid ALIAS FOR $1;
+  pJournalNumber ALIAS FOR $2;
+  pDocNumber ALIAS FOR $3;
+  pPoNumber ALIAS FOR $4;
+  pDocDate ALIAS FOR $5;
+  pAmount ALIAS FOR $6;
+  pNotes ALIAS FOR $7;
+  pAccntid ALIAS FOR $8;
+  pDueDate ALIAS FOR $9;
+  pTermsid ALIAS FOR $10;
+
+BEGIN
+  RETURN createAPDebitMemo(pVendid, pJournalNumber, pDocNumber, pPoNumber, pDocDate, pAmount, pNotes, pAccntid, pDueDate, pTermsid, baseCurrId() );
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION createAPDebitMemo(INTEGER, INTEGER, TEXT, TEXT, DATE, NUMERIC, TEXT, INTEGER, DATE, INTEGER, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pVendid ALIAS FOR $1;
+  pJournalNumber ALIAS FOR $2;
+  pDocNumber ALIAS FOR $3;
+  pPoNumber ALIAS FOR $4;
+  pDocDate ALIAS FOR $5;
+  pAmount ALIAS FOR $6;
+  pNotes ALIAS FOR $7;
+  pAccntid ALIAS FOR $8;
+  pDueDate ALIAS FOR $9;
+  pTermsid ALIAS FOR $10;
+  pCurrId ALIAS FOR $11;
+
+BEGIN
+  RETURN createAPDebitMemo(NULL, pVendid, pJournalNumber, pDocNumber, pPoNumber, pDocDate, pAmount, pNotes, pAccntid, pDueDate, pTermsid, pCurrId );
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION createAPDebitMemo(INTEGER, INTEGER, INTEGER, TEXT, TEXT, DATE, NUMERIC, TEXT, INTEGER, DATE, INTEGER, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pId ALIAS FOR $1;
+  pVendid ALIAS FOR $2;
+  pJournalNumber ALIAS FOR $3;
+  pDocNumber ALIAS FOR $4;
+  pPoNumber ALIAS FOR $5;
+  pDocDate ALIAS FOR $6;
+  pAmount ALIAS FOR $7;
+  pNotes ALIAS FOR $8;
+  pAccntid ALIAS FOR $9;
+  pDueDate ALIAS FOR $10;
+  pTermsid ALIAS FOR $11;
+  pCurrId ALIAS FOR $12;
+  _vendName TEXT;
+  _apAccntid INTEGER;
+  _prepaidAccntid INTEGER;
+  _accntid INTEGER;
+  _glSequence INTEGER;
+  _journalNumber INTEGER;
+  _apopenid INTEGER;
+  _baseAmount NUMERIC;
+  _taxBaseValue NUMERIC;
+  _test INTEGER;
+
+BEGIN
+
+  _apopenid := pId;
+
+  SELECT findAPAccount(pVendid) INTO _apAccntid;
+  SELECT findAPPrepaidAccount(pVendid) INTO _prepaidAccntid;
+
+  SELECT vend_name INTO _vendName
+  FROM vendinfo
+  WHERE (vend_id=pVendid);
+
+  _accntid := pAccntid;
+
+  PERFORM accnt_id
+     FROM accnt
+    WHERE (accnt_id=_accntid);
+  IF (FOUND) THEN
+    _prepaidAccntid := _accntid;
+  ELSE
+    _accntid := -1;
+  END IF;
+
+  IF(pJournalNumber IS NULL) THEN
+    SELECT fetchJournalNumber('AP-MISC') INTO _journalNumber;
+  ELSE
+    _journalNumber := pJournalNumber;
+  END IF;
+
+  SELECT fetchGLSequence() INTO _glSequence;
+
+  IF (_apopenid IS NOT NULL) THEN
+    UPDATE apopen SET
+      apopen_username=getEffectiveXtUser(), apopen_journalnumber=_journalNumber,
+      apopen_vend_id=pVendid, apopen_docnumber=pDocNumber,
+      apopen_doctype='D', apopen_ponumber=pPoNumber,
+      apopen_docdate=pDocDate, apopen_duedate=pDueDate,
+      apopen_distdate=pDocDate, apopen_terms_id=pTermsid,
+      apopen_amount=pAmount, apopen_paid=0,
+      apopen_open=(pAmount <> 0), apopen_notes=pNotes,
+      apopen_accnt_id=_accntid, apopen_curr_id=pCurrId,
+      apopen_closedate=CASE WHEN (pAmount = 0) THEN pDocdate END
+    WHERE apopen_id = _apopenid;
+  ELSE
+    SELECT NEXTVAL('apopen_apopen_id_seq') INTO _apopenid;
+    INSERT INTO apopen
+    ( apopen_id, apopen_username, apopen_journalnumber,
+      apopen_vend_id, apopen_docnumber, apopen_doctype, apopen_ponumber,
+      apopen_docdate, apopen_duedate, apopen_distdate, apopen_terms_id,
+      apopen_amount, apopen_paid, apopen_discountable_amount, apopen_open, apopen_notes, apopen_accnt_id, apopen_curr_id,
+      apopen_closedate )
+    VALUES
+    ( _apopenid, getEffectiveXtUser(), _journalNumber,
+      pVendid, pDocNumber, 'D', pPoNumber,
+      pDocDate, pDueDate, pDocDate, pTermsid,
+      pAmount, 0, 0, (pAmount <> 0), pNotes, _accntid, pCurrId,
+      CASE WHEN (pAmount = 0) THEN pDocDate END );
+  END IF;
+
+  _baseAmount := round(currToBase(pCurrId, pAmount, pDocDate), 2);
+
+  -- Credit the A/P account for the full amount
+  SELECT insertIntoGLSeries ( _glSequence, 'A/P', 'DM',
+                              pDocNumber, _apAccntid,
+                              _baseAmount,
+                              pDocDate, (_vendName || ' ' || pNotes) ) INTO _test;
+
+  -- Debit the Tax account for the tax amount
+  _taxBaseValue := addTaxToGLSeries(_glSequence,
+                                     'A/P', 'DM', pDocNumber,
+                                     pCurrId, pDocDate, pDocDate,
+                                      'apopentax', _apopenid,
+                                      _vendName);
+
+  UPDATE apopentax SET taxhist_journalnumber = _journalNumber
+  WHERE taxhist_parent_id=_apopenid;
+
+  -- Debit the Prepaid account for the basis amount
+  -- Note, the taxBaseValue is negative so it is added
+  SELECT insertIntoGLSeries ( _glSequence, 'A/P', 'DM',
+                              pDocNumber, _prepaidAccntid,
+                              (_baseAmount + _taxBaseValue) * -1,
+                              pDocDate, (_vendName || ' ' || pNotes) ) INTO _test;
+
+  --  Commit the GLSeries;
+  SELECT postGLSeries(_glSequence, _journalNumber) INTO _test;
+  IF (_test < 0) THEN
+    DELETE FROM apopen WHERE (apopen_id=_apopenid);
+    PERFORM deleteGLSeries(_glSequence);
+    RAISE EXCEPTION 'postGLSeries commit failed with %', _test;
+  END IF;
+
+  RETURN _apopenid;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/createapdiscount.sql b/foundation-database/public/functions/createapdiscount.sql
new file mode 100644 (file)
index 0000000..d1dc6d5
--- /dev/null
@@ -0,0 +1,104 @@
+CREATE OR REPLACE FUNCTION createAPDiscount(INTEGER, NUMERIC) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pApopenid ALIAS FOR $1;
+  pAmount ALIAS FOR $2;
+  _result INTEGER;
+  
+BEGIN
+
+  SELECT createAPDiscount(pApopenid, fetchJournalNumber('AP-MISC'), pAmount) INTO _result;
+
+  RETURN _result;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION createAPDiscount(INTEGER, INTEGER, NUMERIC) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pApopenid ALIAS FOR $1;
+  pJournalNumber ALIAS FOR $2;
+  pAmount ALIAS FOR $3;
+  _ap RECORD;
+  _sequence INTEGER;
+  _apopenid INTEGER;
+  _apcreditapplyid INTEGER;
+  _result INTEGER;
+  _crAccnt INTEGER;
+  _dbAccnt INTEGER;
+  _reference    TEXT;
+  _discountDateAmt NUMERIC;
+
+BEGIN
+
+  SELECT NEXTVAL('apopen_apopen_id_seq') INTO _apopenid;
+
+  SELECT * INTO _ap
+  FROM apopen
+  WHERE (apopen_id = pApopenid);
+  IF (NOT FOUND) THEN
+    RETURN -1;
+  END IF;
+
+  _crAccnt := findAPDiscountAccount(_ap.apopen_vend_id);
+  _dbAccnt := findAPAccount(_ap.apopen_vend_id);
+  _reference := ('Discount for ' || _ap.apopen_doctype || ' ' || _ap.apopen_docnumber);
+
+  SELECT fetchGLSequence() INTO _sequence;
+
+  _discountDateAmt = round(pAmount / _ap.apopen_curr_rate, 2);
+  PERFORM insertIntoGLSeries( _sequence, 'A/P', 'DS', _ap.apopen_docnumber,
+                              _dbAccnt,
+                              _discountDateAmt * -1,
+                              CURRENT_DATE,
+                              _reference);
+  PERFORM insertIntoGLSeries( _sequence, 'A/P', 'DS', _ap.apopen_docnumber,
+                              _crAccnt,
+                              _discountDateAmt,
+                              CURRENT_DATE,
+                              _reference);
+
+  PERFORM postGLSeries(_sequence, pJournalNumber);
+
+  INSERT INTO apopen
+  ( apopen_id, apopen_username, apopen_journalnumber,
+    apopen_vend_id, apopen_docnumber, apopen_doctype, apopen_ponumber,
+    apopen_docdate, apopen_duedate, apopen_distdate, apopen_terms_id, apopen_curr_id,
+    apopen_amount, apopen_paid, apopen_open, apopen_notes, apopen_discount, apopen_curr_rate )
+  SELECT _apopenid, getEffectiveXtUser(), pJournalNumber,
+         apopen_vend_id, apopen_docnumber, 'C', apopen_ponumber,
+         CURRENT_DATE, CURRENT_DATE, CURRENT_DATE, -1, apopen_curr_id,
+         pAmount, 0, TRUE, _reference, TRUE, apopen_curr_rate
+    FROM apopen
+   WHERE (apopen_id=pApopenid);
+
+  SELECT apcreditapply_id INTO _apcreditapplyid
+    FROM apcreditapply
+   WHERE ( (apcreditapply_source_apopen_id=_apopenid)
+     AND   (apcreditapply_target_apopen_id=pApopenid) );
+  IF (FOUND) THEN
+    UPDATE apcreditapply
+       SET apcreditapply_amount=pAmount
+     WHERE (apcreditapply_id=_apcreditapplyid);
+  ELSE
+    SELECT nextval('apcreditapply_apcreditapply_id_seq') INTO _apcreditapplyid;
+    INSERT INTO apcreditapply
+           ( apcreditapply_id, apcreditapply_source_apopen_id,
+             apcreditapply_target_apopen_id, apcreditapply_amount,
+             apcreditapply_curr_id )
+    VALUES ( _apcreditapplyid, _apopenid, pApopenid, pAmount, _ap.apopen_curr_id );
+  END IF;
+
+  SELECT postAPCreditMemoApplication(_apopenid) INTO _result;
+
+  IF (_result < 0) THEN
+    RETURN _result;
+  END IF;
+
+  RETURN pJournalNumber;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/createarcashdeposit.sql b/foundation-database/public/functions/createarcashdeposit.sql
new file mode 100644 (file)
index 0000000..ff7d7c7
--- /dev/null
@@ -0,0 +1,64 @@
+
+CREATE OR REPLACE FUNCTION createARCashDeposit(INTEGER, TEXT, TEXT, DATE, NUMERIC, TEXT, INTEGER, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCustid ALIAS FOR $1;
+  pDocNumber ALIAS FOR $2;
+  pOrderNumber ALIAS FOR $3;
+  pDocDate ALIAS FOR $4;
+  pAmount ALIAS FOR $5;
+  pNotes ALIAS FOR $6;
+  pJournalNumber ALIAS FOR $7;
+  pCurrId ALIAS FOR $8;
+  _prepaidaccntid INTEGER;
+  _deferredaccntid INTEGER;
+  _glSequence INTEGER;
+  _aropenid INTEGER;
+
+BEGIN
+
+  IF (pAmount <= 0) THEN
+    RETURN 0;
+  END IF;
+
+  _prepaidaccntid := findPrepaidAccount(pCustid);
+  IF (_prepaidaccntid = -1) THEN
+    RAISE EXCEPTION 'There was an error creating the Customer Deposit GL Transactions. No Prepaid Account is assigned.';
+  END IF;
+
+  _deferredaccntid := findDeferredAccount(pCustid);
+  IF (_deferredaccntid = -1) THEN
+    RAISE EXCEPTION 'There was an error creating the Customer Deposit GL Transactions. No Deferred Account is assigned.';
+  END IF;
+
+  SELECT NEXTVAL('aropen_aropen_id_seq') INTO _aropenid;
+
+  SELECT insertGLTransaction( pJournalNumber, 'A/R', 'CD',
+                              pDocNumber, pNotes, _deferredaccntid, _prepaidaccntid,
+                              _aropenid,
+                              round(currToBase(pCurrId, pAmount, pDocDate), 2),
+                              pDocDate) INTO _glSequence;
+
+  INSERT INTO aropen
+  ( aropen_id, aropen_username, aropen_journalnumber,
+    aropen_cust_id, aropen_docnumber, aropen_doctype, aropen_ordernumber,
+    aropen_docdate, aropen_duedate, aropen_distdate, aropen_terms_id, aropen_salesrep_id,
+    aropen_amount, aropen_paid, aropen_commission_due, aropen_commission_paid,
+    aropen_applyto, aropen_ponumber, aropen_cobmisc_id,
+    aropen_open, aropen_notes, aropen_rsncode_id,
+    aropen_salescat_id, aropen_accnt_id, aropen_curr_id )
+  VALUES
+  ( _aropenid, getEffectiveXtUser(), pJournalNumber,
+    pCustid, pDocNumber, 'R', pOrderNumber,
+    pDocDate, pDocDate, pDocDate, -1, NULL,
+    round(pAmount, 2), 0, 0.0, FALSE,
+    '', '', -1,
+    TRUE, pNotes, -1,
+    -1, -1, pCurrId );
+
+  RETURN _aropenid;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/createarcreditmemo.sql b/foundation-database/public/functions/createarcreditmemo.sql
new file mode 100644 (file)
index 0000000..d426198
--- /dev/null
@@ -0,0 +1,185 @@
+CREATE OR REPLACE FUNCTION createARCreditMemo(pId            INTEGER,
+                                              pCustid        INTEGER,
+                                              pDocNumber     TEXT,
+                                              pOrderNumber   TEXT,
+                                              pDocDate       DATE,
+                                              pAmount        NUMERIC,
+                                              pNotes         TEXT,
+                                              pRsncodeid     INTEGER,
+                                              pSalescatid    INTEGER,
+                                              pAccntid       INTEGER,
+                                              pDueDate       DATE,
+                                              pTermsid       INTEGER,
+                                              pSalesrepid    INTEGER,
+                                              pCommissiondue NUMERIC = 0,
+                                              pJournalNumber INTEGER = NULL,
+                                              pCurrId        INTEGER = baseCurrId(),
+                                              pArAccntid     INTEGER = NULL,
+                                              pCoCcpayId     INTEGER = NULL) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _accntid        INTEGER;
+  _arAccntid      INTEGER;
+  _aropenid       INTEGER;
+  _cohistid       INTEGER;
+  _custName       TEXT;
+  _duedate        DATE    := COALESCE(pDueDate, pDocDate);
+  _glSequence     INTEGER;
+  _journalNumber  INTEGER;
+  _prepaidAccntid INTEGER;
+  _salescatid     INTEGER;
+  _taxBaseValue   NUMERIC;
+  _test           INTEGER;
+  _tmp            INTEGER;
+
+BEGIN
+
+  _aropenid := pId;
+
+  IF (pAmount <= 0) THEN
+    RETURN 0;
+  END IF;
+
+  _arAccntid := COALESCE(pARAccntid, findARAccount(pCustid));
+  _prepaidAccntid := findPrepaidAccount(pCustid);
+
+  _accntid := pAccntid;
+  _salescatid := pSalescatid;
+
+  SELECT cust_name INTO _custName
+  FROM custinfo
+  WHERE (cust_id=pCustid);
+
+  IF EXISTS(SELECT 1 FROM accnt WHERE (accnt_id=_accntid)) THEN
+    _prepaidAccntid := _accntid;
+  ELSE
+    _accntid := -1;
+  END IF;
+
+  SELECT accnt_id INTO _tmp
+    FROM salescat, accnt
+   WHERE ((salescat_prepaid_accnt_id=accnt_id)
+     AND  (salescat_id=_salescatid));
+  IF (FOUND) THEN
+    _accntid := -1;
+    _prepaidAccntid := _tmp;
+  ELSE
+    _salescatid = -1;
+  END IF;
+
+  IF(pJournalNumber IS NULL) THEN
+    SELECT fetchJournalNumber('AR-MISC') INTO _journalNumber;
+  ELSE
+    _journalNumber := pJournalNumber;
+  END IF;
+
+  _glSequence := fetchGLSequence();
+
+  -- CreatelUpdate aropen for full amount
+  IF (_aropenid IS NOT NULL) THEN
+    UPDATE aropen SET
+      aropen_username=getEffectiveXtUser(), aropen_journalnumber=_journalNumber,
+      aropen_cust_id=pCustid, aropen_docnumber=pDocNumber, aropen_doctype='C',
+      aropen_ordernumber=pOrderNumber,aropen_docdate=pDocDate, aropen_duedate=_duedate,
+      aropen_distdate=pDocDate, aropen_terms_id=pTermsid,
+      aropen_salesrep_id=pSalesrepid, aropen_amount=round(pAmount, 2), aropen_paid=0,
+      aropen_commission_due=pCommissiondue, aropen_commission_paid=FALSE,
+      aropen_applyto='', aropen_ponumber='', aropen_cobmisc_id=-1,
+      aropen_open=TRUE, aropen_notes=pNotes, aropen_rsncode_id=pRsncodeid,
+      aropen_salescat_id=_salescatid, aropen_accnt_id=_accntid, aropen_curr_id=pCurrId
+    WHERE aropen_id = pId;
+  ELSE
+    SELECT NEXTVAL('aropen_aropen_id_seq') INTO _aropenid;
+    INSERT INTO aropen
+    ( aropen_id, aropen_username, aropen_journalnumber,
+      aropen_cust_id, aropen_docnumber, aropen_doctype, aropen_ordernumber,
+      aropen_docdate, aropen_duedate, aropen_distdate, aropen_terms_id, aropen_salesrep_id,
+      aropen_amount, aropen_paid, aropen_commission_due, aropen_commission_paid,
+      aropen_applyto, aropen_ponumber, aropen_cobmisc_id,
+      aropen_open, aropen_notes, aropen_rsncode_id,
+      aropen_salescat_id, aropen_accnt_id, aropen_curr_id )
+    VALUES
+    ( _aropenid, getEffectiveXtUser(), _journalNumber,
+      pCustid, pDocNumber, 'C', pOrderNumber,
+      pDocDate, _duedate, pDocDate, pTermsid, pSalesrepid,
+      round(pAmount, 2), 0, pCommissiondue, FALSE,
+      '', '', -1,
+      TRUE, pNotes, pRsncodeid,
+      _salescatid, _accntid, pCurrId );
+  END IF;
+
+  -- Credit the A/R account for the full amount
+  SELECT insertIntoGLSeries ( _glSequence, 'A/R', 'CM',
+                              pDocNumber, _arAccntid,
+                              round(currToBase(pCurrId, pAmount, pDocDate), 2),
+                              pDocDate, (_custName || ' ' || pNotes)) INTO _test;
+
+  -- Debit the Tax account for the tax amount
+  _taxBaseValue := addTaxToGLSeries(_glSequence,
+                                     'A/R', 'CM', pDocNumber,
+                                     pCurrId, pDocDate, pDocDate,
+                                      'aropentax', _aropenid,
+                                      (_custName || ' ' || pNotes));
+
+  UPDATE aropentax SET taxhist_journalnumber = _journalNumber
+  WHERE taxhist_parent_id=_aropenid;
+
+  -- Debit the Prepaid account for the basis amount
+  -- Note, _taxBaseValue is negative so it is added to pAmount
+  SELECT insertIntoGLSeries ( _glSequence, 'A/R', 'CM',
+                              pDocNumber, _prepaidAccntid,
+                              round(currToBase(pCurrId, pAmount * -1, pDocDate) + _taxBaseValue * -1, 2),
+                              pDocDate, (_custName || ' ' || pNotes)) INTO _test;
+
+  --  Commit the GLSeries;
+  SELECT postGLSeries(_glSequence, _journalNumber) INTO _test;
+  IF (_test < 0) THEN
+    DELETE FROM aropen WHERE (aropen_id=_aropenid);
+    PERFORM deleteGLSeries(_glSequence);
+    RAISE EXCEPTION 'postGLSeries commit failed with %', _test;
+  END IF;
+
+  --  Record Sales History
+  INSERT INTO cohist
+  ( cohist_cust_id,
+   cohist_itemsite_id, cohist_shipto_id,
+    cohist_misc_type, cohist_misc_descrip,
+    cohist_shipdate, cohist_shipvia,
+    cohist_ordernumber, cohist_ponumber, cohist_orderdate,
+    cohist_doctype, cohist_invcnumber, cohist_invcdate,
+    cohist_qtyshipped, cohist_unitprice, cohist_unitcost,
+    cohist_salesrep_id,
+    cohist_commission, cohist_commissionpaid,
+    cohist_curr_id, cohist_sequence, cohist_cohead_ccpay_id)
+  VALUES
+  (CASE WHEN pCustid < 0 THEN NULL ELSE pCustid END,
+   -1, -1,
+    'M', 'A/R Misc Credit Memo',
+    pDocDate, '',
+    pOrderNumber, '', pDocDate,
+    'C', pDocNumber, pDocDate,
+    1, (pAmount - _taxBaseValue) * -1, 0,
+    CASE WHEN pSalesrepid < 0 THEN NULL ELSE pSalesrepid END,
+    (pCommissiondue * -1.0), FALSE,
+    pCurrId, _glSequence, pCoCcpayId)
+  RETURNING cohist_id INTO _cohistid;
+
+  INSERT INTO cohisttax
+  ( taxhist_parent_id, taxhist_taxtype_id, taxhist_tax_id,
+    taxhist_basis, taxhist_basis_tax_id, taxhist_sequence,
+    taxhist_percent, taxhist_amount, taxhist_tax,
+    taxhist_docdate, taxhist_distdate, taxhist_curr_id, taxhist_curr_rate,
+    taxhist_journalnumber )
+  SELECT _cohistid, taxhist_taxtype_id, taxhist_tax_id,
+         taxhist_basis, taxhist_basis_tax_id, taxhist_sequence,
+         taxhist_percent, taxhist_amount, taxhist_tax,
+         taxhist_docdate, taxhist_distdate, taxhist_curr_id, taxhist_curr_rate,
+         taxhist_journalnumber
+  FROM aropentax
+  WHERE (taxhist_parent_id=_aropenid);
+
+  RETURN _aropenid;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/createardebitmemo.sql b/foundation-database/public/functions/createardebitmemo.sql
new file mode 100644 (file)
index 0000000..6b6680b
--- /dev/null
@@ -0,0 +1,181 @@
+
+CREATE OR REPLACE FUNCTION createARDebitMemo(INTEGER, INTEGER, INTEGER, TEXT, TEXT, DATE, NUMERIC, TEXT, INTEGER, INTEGER, INTEGER, DATE, INTEGER, INTEGER, NUMERIC, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pId                  ALIAS FOR $1;
+  pCustid              ALIAS FOR $2;
+  pJournalNumber       ALIAS FOR $3;
+  pDocNumber           ALIAS FOR $4;
+  pOrderNumber         ALIAS FOR $5;
+  pDocDate             ALIAS FOR $6;
+  pAmount              ALIAS FOR $7;
+  pNotes               ALIAS FOR $8;
+  pRsncodeid           ALIAS FOR $9;
+  pSalescatid          ALIAS FOR $10;
+  pAccntid             ALIAS FOR $11;
+  pDueDate             ALIAS FOR $12;
+  pTermsid             ALIAS FOR $13;
+  pSalesrepid          ALIAS FOR $14;
+  pCommissiondue       ALIAS FOR $15;
+  pCurrId              ALIAS FOR $16;
+  _custName TEXT;
+  _journalNumber INTEGER;
+  _arAccntid INTEGER;
+  _prepaidAccntid INTEGER;
+  _salescatid INTEGER;
+  _accntid INTEGER;
+  _glSequence INTEGER;
+  _aropenid INTEGER;
+  _cohistid INTEGER;
+  _tmp INTEGER;
+  _test INTEGER;
+  _taxBaseValue NUMERIC;
+
+BEGIN
+  _aropenid=pId;
+  
+  IF (pAmount <= 0) THEN
+    RETURN 0;
+  END IF;
+
+  SELECT findARAccount(pCustid) INTO _arAccntid;
+  SELECT findPrepaidAccount(pCustid) INTO _prepaidAccntid;
+
+  _accntid := pAccntid;
+  _salescatid := pSalescatid;
+
+  SELECT cust_name INTO _custName
+  FROM custinfo
+  WHERE (cust_id=pCustid);
+  
+  PERFORM accnt_id
+     FROM accnt
+    WHERE (accnt_id=_accntid);
+  IF (FOUND) THEN
+    _prepaidAccntid := _accntid;
+  ELSE
+    _accntid := -1;
+  END IF;
+
+  SELECT accnt_id INTO _tmp
+    FROM salescat, accnt
+   WHERE ((salescat_prepaid_accnt_id=accnt_id)
+     AND  (salescat_id=_salescatid));
+  IF (FOUND) THEN
+    _accntid := -1;
+    _prepaidAccntid := _tmp;
+  ELSE
+    _salescatid = -1;
+  END IF;
+
+  IF (pJournalNumber IS NULL) THEN
+    _journalNumber := fetchJournalNumber('AR-MISC');
+  ELSE
+    _journalNumber := pJournalNumber;
+  END IF;
+
+  SELECT fetchGLSequence() INTO _glSequence;
+
+  -- CreatelUpdate aropen for full amount
+  IF (_aropenid IS NOT NULL) THEN
+    UPDATE aropen SET
+      aropen_username=getEffectiveXtUser(), aropen_journalnumber=_journalNumber,
+      aropen_cust_id=pCustid, aropen_docnumber=pDocNumber, aropen_doctype='D', 
+      aropen_ordernumber=pOrderNumber,aropen_docdate=pDocDate, aropen_duedate=pDueDate, 
+      aropen_distdate=pDocDate, aropen_terms_id=pTermsid, 
+      aropen_salesrep_id=pSalesrepid, aropen_amount=round(pAmount, 2), aropen_paid=0, 
+      aropen_commission_due=pCommissiondue, aropen_commission_paid=FALSE,
+      aropen_applyto='', aropen_ponumber='', aropen_cobmisc_id=-1,
+      aropen_open=TRUE, aropen_notes=pNotes, aropen_rsncode_id=pRsncodeid,
+      aropen_salescat_id=_salescatid, aropen_accnt_id=_accntid, aropen_curr_id=pCurrId
+    WHERE aropen_id = pId;
+  ELSE
+    SELECT NEXTVAL('aropen_aropen_id_seq') INTO _aropenid;
+    INSERT INTO aropen
+    ( aropen_id, aropen_username, aropen_journalnumber,
+      aropen_cust_id, aropen_docnumber, aropen_doctype, aropen_ordernumber,
+      aropen_docdate, aropen_duedate, aropen_distdate, aropen_terms_id, aropen_salesrep_id,
+      aropen_amount, aropen_paid, aropen_commission_due, aropen_commission_paid,
+      aropen_applyto, aropen_ponumber, aropen_cobmisc_id,
+      aropen_open, aropen_notes, aropen_rsncode_id,
+      aropen_salescat_id, aropen_accnt_id, aropen_curr_id )
+    VALUES
+    ( _aropenid, getEffectiveXtUser(), _journalNumber,
+      pCustid, pDocNumber, 'D', pOrderNumber,
+      pDocDate, pDueDate, pDocDate, pTermsid, pSalesrepid,
+      round(pAmount, 2), 0, pCommissiondue, FALSE,
+      '', '', -1,
+      TRUE, pNotes, pRsncodeid,
+      _salescatid, _accntid, pCurrId );
+  END IF;
+
+  -- Debit the A/R account for the full amount
+  SELECT insertIntoGLSeries ( _glSequence, 'A/R', 'DM',
+                              pDocNumber, _arAccntid,
+                              round(currToBase(pCurrId, pAmount, pDocDate) * -1, 2),
+                              pDocDate, (_custName || ' ' || pNotes)) INTO _test;
+
+  -- Credit the Tax account for the tax amount
+  _taxBaseValue := addTaxToGLSeries(_glSequence,
+                                     'A/R', 'DM', pDocNumber,
+                                     pCurrId, pDocDate, pDocDate,
+                                      'aropentax', _aropenid,
+                                      (_custName || ' ' || pNotes));
+
+  UPDATE aropentax SET taxhist_journalnumber = _journalNumber
+  WHERE taxhist_parent_id=_aropenid;
+
+  -- Credit the Prepaid account for the basis amount
+  SELECT insertIntoGLSeries ( _glSequence, 'A/R', 'DM',
+                              pDocNumber, _prepaidAccntid,
+                              round(currToBase(pCurrId, (pAmount), pDocDate), 2) - _taxBaseValue,
+                              pDocDate, (_custName || ' ' || pNotes)) INTO _test;
+
+  --  Commit the GLSeries;
+  SELECT postGLSeries(_glSequence, _journalNumber) INTO _test;
+  IF (_test < 0) THEN
+    DELETE FROM aropen WHERE (aropen_id=_aropenid);
+    PERFORM deleteGLSeries(_glSequence);
+    RAISE EXCEPTION 'postGLSeries commit failed with %', _test;
+  END IF;
+
+  --  Record Sales History
+  SELECT nextval('cohist_cohist_id_seq') INTO _cohistid;
+  INSERT INTO cohist
+  ( cohist_id, cohist_cust_id, cohist_itemsite_id, cohist_shipto_id,
+    cohist_misc_type, cohist_misc_descrip,
+    cohist_shipdate, cohist_shipvia,
+    cohist_ordernumber, cohist_ponumber, cohist_orderdate,
+    cohist_doctype, cohist_invcnumber, cohist_invcdate,
+    cohist_qtyshipped, cohist_unitprice, cohist_unitcost,
+    cohist_salesrep_id, cohist_commission, cohist_commissionpaid,
+    cohist_curr_id, cohist_sequence )
+  VALUES
+  ( _cohistid, pCustid, -1, -1,
+    'M', 'A/R Misc Debit Memo',
+    pDocDate, '',
+    '', '', pDocDate,
+    'D', pDocNumber, pDocDate,
+    1, (pAmount - _taxBaseValue), 0,
+    pSalesrepid, pCommissiondue, FALSE,
+    pCurrId, _glSequence );
+  INSERT INTO cohisttax
+  ( taxhist_parent_id, taxhist_taxtype_id, taxhist_tax_id,
+    taxhist_basis, taxhist_basis_tax_id, taxhist_sequence,
+    taxhist_percent, taxhist_amount, taxhist_tax,
+    taxhist_docdate, taxhist_distdate, taxhist_curr_id, taxhist_curr_rate,
+    taxhist_journalnumber )
+  SELECT _cohistid, taxhist_taxtype_id, taxhist_tax_id,
+         taxhist_basis, taxhist_basis_tax_id, taxhist_sequence,
+         taxhist_percent, taxhist_amount, taxhist_tax,
+         taxhist_docdate, taxhist_distdate, taxhist_curr_id, taxhist_curr_rate,
+         taxhist_journalnumber
+  FROM aropentax
+  WHERE (taxhist_parent_id=_aropenid);
+
+  RETURN _aropenid;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/createbillingheader.sql b/foundation-database/public/functions/createbillingheader.sql
new file mode 100644 (file)
index 0000000..4c3eddd
--- /dev/null
@@ -0,0 +1,159 @@
+CREATE OR REPLACE FUNCTION createbillingheader(integer)
+  RETURNS integer AS
+$BODY$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSoheadid            ALIAS FOR $1;
+  _cobmiscid           INTEGER;
+  _cohead              cohead%ROWTYPE;
+  _miscApplied          NUMERIC := 0.0;
+  _freight             NUMERIC;
+  _freighttypeid        INTEGER;
+  _invcDate            DATE;
+  _schedDate           DATE;
+  _shipDate            DATE;
+  _shipVia             TEXT;
+  _tax                 NUMERIC;
+
+BEGIN
+
+  --  Fetch cohead
+  SELECT * INTO _cohead
+  FROM cohead
+  WHERE (cohead_id=pSoheadid);
+
+  --  Check for an existing cobmisc
+  SELECT cobmisc_id INTO _cobmiscid
+  FROM cobmisc
+  WHERE ( (NOT cobmisc_posted)
+   AND (cobmisc_cohead_id=pSoheadid) );
+
+  IF (FOUND) THEN
+  --  Find a Shipping-Entered freight charge
+    SELECT SUM(currToCurr(shiphead_freight_curr_id, _cohead.cohead_curr_id,
+                          shiphead_freight, CURRENT_DATE)) INTO _freight
+    FROM (
+    SELECT shiphead_id, shiphead_freight_curr_id, shiphead_freight
+    FROM shiphead JOIN shipitem ON (shipitem_shiphead_id=shiphead_id AND NOT shipitem_invoiced)
+    WHERE ((shiphead_order_type='SO')
+      AND  (shiphead_order_id=pSoheadid))
+    GROUP BY shiphead_id, shiphead_freight_curr_id, shiphead_freight) AS data;
+
+    IF (_freight IS NOT NULL) THEN
+      UPDATE cobmisc SET cobmisc_freight = _freight
+      WHERE (cobmisc_id=_cobmiscid);
+    END IF;
+
+    RETURN _cobmiscid;
+  END IF;
+
+  --  Find misc charges that have already been applied for the S/O
+  SELECT COALESCE(SUM(cobmisc_misc), 0.0) INTO _miscApplied
+  FROM cobmisc
+  WHERE (cobmisc_cohead_id=pSoheadid);
+
+  SELECT NEXTVAL('cobmisc_cobmisc_id_seq') INTO _cobmiscid;
+
+  --  Check for a valid shipdate
+  SELECT MIN(shiphead_shipdate) INTO _shipDate
+  FROM shiphead, shipitem
+  WHERE ( (shipitem_shiphead_id=shiphead_id)
+   AND (NOT shipitem_invoiced)
+   AND (shiphead_shipped)
+   AND (shiphead_order_type='SO')
+   AND (shiphead_order_id=pSoheadid) );
+
+  --  Schema shouldn't allow, but we'll try for now
+  IF (_shipDate IS NULL) THEN
+    SELECT MAX(shipitem_shipdate) INTO _shipDate
+    FROM shipitem, shiphead
+    WHERE ( (shipitem_shiphead_id=shiphead_id)
+     AND (shiphead_order_type='SO')
+     AND (shiphead_order_id=pSoheadid) );
+
+    --  How about a transaction date
+    IF (_shipDate IS NULL) THEN
+      SELECT COALESCE(MAX(shipitem_transdate), CURRENT_DATE) INTO _shipDate
+      FROM shipitem, shiphead
+      WHERE ((shipitem_shiphead_id=shiphead_id)
+        AND  (shiphead_order_type='SO')
+        AND  (shiphead_order_id=pSoheadid) );
+    END IF;
+  END IF;
+
+  --  Get the earliest schedule date for this order.
+  SELECT MIN(coitem_scheddate) INTO _schedDate
+    FROM coitem
+   WHERE ((coitem_status <> 'X') AND (coitem_cohead_id=pSoheadid));
+
+  IF (_schedDate IS NULL) THEN
+    _schedDate := _shipDate;
+  END IF;
+
+  --  Find a Shipping-Entered freight charge
+  SELECT SUM(currToCurr(shiphead_freight_curr_id, _cohead.cohead_curr_id,
+                        shiphead_freight, CURRENT_DATE)), shiphead_shipvia
+         INTO _freight, _shipVia
+  FROM (
+  SELECT shiphead_id, shiphead_freight_curr_id, shiphead_freight, shiphead_shipvia
+  FROM shiphead JOIN shipitem ON (shipitem_shiphead_id=shiphead_id AND NOT shipitem_invoiced)
+  WHERE ((shiphead_order_type='SO')
+    AND  (shiphead_order_id=pSoheadid))
+  GROUP BY shiphead_id, shiphead_freight_curr_id, shiphead_freight, shiphead_shipvia) AS data
+  GROUP BY shiphead_shipvia;
+
+  --  Nope, use the cohead freight charge
+  IF (_freight IS NULL) THEN
+    _freight      := _cohead.cohead_freight;
+  END IF;
+
+  --  Finally, look for a Shipping-Entered Ship Via
+  SELECT shiphead_shipvia INTO _shipVia
+  FROM shiphead, shipitem
+  WHERE ( (shipitem_shiphead_id=shiphead_id)
+   AND (NOT shipitem_invoiced)
+   AND (shiphead_order_type='SO')
+   AND (shiphead_order_id=pSoheadid) )
+  LIMIT 1;
+  IF (NOT FOUND) THEN
+    _shipVia := _cohead.cohead_shipvia;
+  END IF;
+
+  --Determine any tax
+
+  SELECT 
+  getFreightTaxTypeId() INTO _freighttypeid;
+  SELECT SUM(COALESCE(taxdetail_tax, 0.00)) INTO _tax
+  FROM calculatetaxdetail(_cohead.cohead_taxzone_id, _freighttypeid, _cohead.cohead_orderdate,_cohead.cohead_curr_id, _freight);
+
+  --  Determine if we are using the _shipDate or _schedDate or current_date for the _invcDate
+  IF( fetchMetricText('InvoiceDateSource')='scheddate') THEN
+    _invcDate := _schedDate;
+  ELSIF( fetchMetricText('InvoiceDateSource')='shipdate') THEN
+    _invcDate := _shipDate;
+  ELSE
+    _invcDate := current_date;
+  END IF;
+
+   INSERT INTO cobmisc (
+       cobmisc_id, cobmisc_cohead_id, cobmisc_shipvia, cobmisc_freight, cobmisc_misc, cobmisc_payment 
+       ,cobmisc_notes,cobmisc_shipdate ,cobmisc_invcdate,cobmisc_posted ,cobmisc_misc_accnt_id 
+       ,cobmisc_misc_descrip,cobmisc_closeorder,cobmisc_curr_id
+       ,cobmisc_taxtype_id,cobmisc_taxzone_id
+       )
+       SELECT
+       _cobmiscid,_cohead.cohead_id,_shipVia,_freight,
+        CASE WHEN (_cohead.cohead_misc - _miscApplied = 0.0) THEN 0.0
+             ELSE (_cohead.cohead_misc - _miscApplied) END,0,
+        _cohead.cohead_ordercomments,_shipDate,_invcDate,FALSE,_cohead.cohead_misc_accnt_id,
+        _cohead.cohead_misc_descrip,NOT(cust_backorder),_cohead.cohead_curr_id,
+       _cohead.cohead_taxtype_id,_cohead.cohead_taxzone_id
+       FROM custinfo
+       WHERE (cust_id=_cohead.cohead_cust_id);
+
+  RETURN _cobmiscid;
+
+END;
+$BODY$
+  LANGUAGE 'plpgsql' VOLATILE;
diff --git a/foundation-database/public/functions/createbomitem.sql b/foundation-database/public/functions/createbomitem.sql
new file mode 100644 (file)
index 0000000..0c0a68d
--- /dev/null
@@ -0,0 +1,185 @@
+CREATE OR REPLACE FUNCTION createBOMItem( INTEGER, INTEGER, INTEGER, INTEGER, CHAR,
+                                          INTEGER, NUMERIC, NUMERIC, NUMERIC,
+                                          DATE, DATE,
+                                          BOOL, INTEGER, BOOL, TEXT, CHAR, INTEGER,
+                                          INTEGER, TEXT, TEXT, TEXT )
+                           RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pBomitemid ALIAS FOR $1;
+  pParentItemid ALIAS FOR $2;
+  pComponentItemid ALIAS FOR $3;
+  pSeqNumber ALIAS FOR $4;
+  pIssueMethod ALIAS FOR $5;
+  pUomId ALIAS FOR $6;
+  pQtyFxd ALIAS FOR $7;
+  pQtyPer ALIAS FOR $8;
+  pScrap ALIAS FOR $9;
+  pEffective ALIAS FOR $10;
+  pExpires ALIAS FOR $11;
+  pCreateWo ALIAS FOR $12;
+  pBOOItemseqid ALIAS FOR $13;
+  pSchedAtWooper ALIAS FOR $14;
+  pECN ALIAS FOR $15;
+  pSubType ALIAS FOR $16;
+  pRevisionid ALIAS FOR $17;
+  pCharId ALIAS FOR $18;
+  pCharVal ALIAS FOR $19;
+  pNotes ALIAS FOR $20;
+  pRef ALIAS FOR $21;
+  _bomworksetid INTEGER;
+  _temp INTEGER;
+
+BEGIN
+
+--  Make sure that the parent and component are not the same
+  IF (pParentItemid = pComponentItemid) THEN
+    RETURN -1;
+  END IF;
+
+--  Make sure that the parent is not used in the component at some level
+  IF ( SELECT (item_type IN ('M', 'F'))
+       FROM item
+       WHERE (item_id=pComponentItemid) ) THEN
+    SELECT indentedWhereUsed(pParentItemid) INTO _bomworksetid;
+    SELECT bomwork_id INTO _temp
+    FROM bomwork
+    WHERE ( (bomwork_set_id=_bomworksetid)
+     AND (bomwork_item_id=pComponentItemid) )
+    LIMIT 1;
+    IF (FOUND) THEN
+      PERFORM deleteBOMWorkset(_bomworksetid);
+      RETURN -2;
+    END IF;
+  END IF;
+
+  PERFORM deleteBOMWorkset(_bomworksetid);
+
+--  Create the BOM Item
+  INSERT INTO bomitem
+  ( bomitem_id, bomitem_parent_item_id, bomitem_item_id,
+    bomitem_seqnumber, bomitem_issuemethod,
+    bomitem_uom_id, bomitem_qtyfxd, bomitem_qtyper, bomitem_scrap,
+    bomitem_effective, bomitem_expires,
+    bomitem_createwo,
+    bomitem_booitem_seq_id, bomitem_schedatwooper,
+    bomitem_ecn, bomitem_subtype, bomitem_moddate, bomitem_rev_id,
+    bomitem_char_id, bomitem_value, bomitem_notes, bomitem_ref )
+  VALUES
+  ( pBomitemid, pParentItemid, pComponentItemid,
+    pSeqNumber, pIssueMethod,
+    pUomId, pQtyFxd, pQtyPer, pScrap,
+    pEffective, pExpires,
+    pCreateWo,
+    pBOOItemseqid, COALESCE(pSchedAtWooper, FALSE),
+    pECN, pSubType, CURRENT_DATE, pRevisionid,
+    pCharId,pCharVal,pNotes, pRef );
+
+  RETURN pBomitemid;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION createBOMItem( INTEGER, INTEGER, INTEGER, INTEGER, CHAR,
+                                          INTEGER, NUMERIC, NUMERIC, NUMERIC,
+                                          DATE, DATE,
+                                          BOOL, INTEGER, BOOL, TEXT, CHAR, INTEGER,
+                                          INTEGER, TEXT )
+                           RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pBomitemid ALIAS FOR $1;
+  pParentItemid ALIAS FOR $2;
+  pComponentItemid ALIAS FOR $3;
+  pSeqNumber ALIAS FOR $4;
+  pIssueMethod ALIAS FOR $5;
+  pUomId ALIAS FOR $6;
+  pQtyFxd ALIAS FOR $7;
+  pQtyPer ALIAS FOR $8;
+  pScrap ALIAS FOR $9;
+  pEffective ALIAS FOR $10;
+  pExpires ALIAS FOR $11;
+  pCreateWo ALIAS FOR $12;
+  pBOOItemseqid ALIAS FOR $13;
+  pSchedAtWooper ALIAS FOR $14;
+  pECN ALIAS FOR $15;
+  pSubType ALIAS FOR $16;
+  pRevisionid ALIAS FOR $17;
+  pCharId ALIAS FOR $18;
+  pCharVal ALIAS FOR $19;
+  _bomworksetid INTEGER;
+  _temp INTEGER;
+  _bomitemid INTEGER;
+
+BEGIN
+
+  SELECT createBOMItem( pBomitemid, pParentItemid, pComponentItemid,
+                        pSeqNumber, pIssueMethod,
+                        pUomId, pQtyFxd, pQtyPer, pScrap,
+                        pEffective, pExpires,
+                        pCreateWo, pBOOItemseqid, pSchedAtWooper, pECN, pSubType, pRevisionid, pCharId, pCharVal, NULL, NULL ) INTO _bomitemid;
+
+  RETURN _bomitemid;
+  
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+
+CREATE OR REPLACE FUNCTION createBOMItem( INTEGER, INTEGER, INTEGER, CHAR,
+                                          INTEGER, NUMERIC, NUMERIC, NUMERIC,
+                                          DATE, DATE,
+                                          BOOL, INTEGER, BOOL, TEXT, CHAR(1), INTEGER,
+                                          INTEGER, TEXT, TEXT, TEXT )
+                           RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pBomitemid ALIAS FOR $1;
+  pParentItemid ALIAS FOR $2;
+  pComponentItemid ALIAS FOR $3;
+  pIssueMethod ALIAS FOR $4;
+  pUomId ALIAS FOR $5;
+  pQtyFxd ALIAS FOR $6;
+  pQtyPer ALIAS FOR $7;
+  pScrap ALIAS FOR $8;
+  pEffective ALIAS FOR $9;
+  pExpires ALIAS FOR $10;
+  pCreateWo ALIAS FOR $11;
+  pBOOItemseqid ALIAS FOR $12;
+  pSchedAtWooper ALIAS FOR $13;
+  pECN ALIAS FOR $14;
+  pSubType ALIAS FOR $15;
+  pRevisionid ALIAS FOR $16;
+  pCharId ALIAS FOR $17;
+  pCharVal ALIAS FOR $18;
+  pNotes ALIAS FOR $19;
+  pRef ALIAS FOR $20;
+  _seqNumber INTEGER;
+  _bomitemid INTEGER;
+
+BEGIN
+
+--  Grab the next Sequence Number, if any
+  SELECT MAX(bomitem_seqnumber) INTO _seqNumber
+  FROM bomitem(pParentItemid,pRevisionid);
+
+  IF (_seqNumber IS NOT NULL) THEN
+   _seqNumber := (_seqNumber + 10);
+  ELSE
+   _seqNumber := 10;
+  END IF;
+
+  SELECT createBOMItem( pBomitemid, pParentItemid, pComponentItemid,
+                        _seqNumber, pIssueMethod,
+                        pUomId, pQtyFxd, pQtyPer, pScrap,
+                        pEffective, pExpires,
+                        pCreateWo, pBOOItemseqid, pSchedAtWooper, pECN, pSubType, pRevisionid, pCharId, pCharVal, pNotes, pRef ) INTO _bomitemid;
+
+  RETURN _bomitemid;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/createcheck.sql b/foundation-database/public/functions/createcheck.sql
new file mode 100644 (file)
index 0000000..afac771
--- /dev/null
@@ -0,0 +1,128 @@
+CREATE OR REPLACE FUNCTION createCheck(INTEGER, TEXT, INTEGER, DATE, NUMERIC, INTEGER, INTEGER, INTEGER, TEXT, TEXT, BOOL) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pBankaccntid         ALIAS FOR  $1;
+  pRecipType           ALIAS FOR  $2;
+  pRecipId             ALIAS FOR  $3;
+  pCheckDate           ALIAS FOR  $4;
+  pAmount              ALIAS FOR  $5;
+  pCurrid              ALIAS FOR  $6;
+  pExpcatid            ALIAS FOR  $7;
+  _journalNumber       INTEGER := $8;
+  pFor                 ALIAS FOR  $9;
+  pNotes               ALIAS FOR $10;
+  pMisc                        ALIAS FOR $11;
+  _checkid INTEGER;
+BEGIN
+
+  SELECT createCheck(pBankaccntid,pRecipType,pRecipId,pCheckDate,pAmount,pCurrid,pExpcatid,_journalNumber,pFor,pNotes,pMisc,NULL) INTO _checkid;
+  RETURN _checkid;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+
+CREATE OR REPLACE FUNCTION createCheck(INTEGER, TEXT, INTEGER, DATE, NUMERIC, INTEGER, INTEGER, INTEGER, TEXT, TEXT, BOOL, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pBankaccntid         ALIAS FOR  $1;
+  pRecipType           ALIAS FOR  $2;
+  pRecipId             ALIAS FOR  $3;
+  pCheckDate           ALIAS FOR  $4;
+  pAmount              ALIAS FOR  $5;
+  pCurrid              ALIAS FOR  $6;
+  pExpcatid            ALIAS FOR  $7;
+  _journalNumber       INTEGER := $8;
+  pFor                 ALIAS FOR  $9;
+  pNotes               ALIAS FOR $10;
+  pMisc                        ALIAS FOR $11;
+  pAropenid             ALIAS FOR $12;
+  _checkid             INTEGER;
+  _check_curr_rate      NUMERIC;
+  _bankaccnt_currid    INTEGER;
+
+BEGIN
+  SELECT bankaccnt_curr_id,currRate(bankaccnt_curr_id,pCheckDate) INTO _bankaccnt_currid, _check_curr_rate
+  FROM bankaccnt
+  WHERE bankaccnt_id = pBankaccntid;
+  IF (NOT FOUND) THEN
+    RETURN -1;
+  END IF;
+
+  IF (pRecipType NOT IN ('C', 'T', 'V')) THEN
+    RETURN -2;
+  END IF;
+
+  IF (pCheckDate IS NULL) THEN
+    RETURN -3;
+  END IF;
+
+  IF (pAmount <= 0) THEN
+    RETURN -4;
+  END IF;
+
+  IF (pCurrid IS NULL
+      OR NOT EXISTS(SELECT * FROM curr_symbol WHERE (curr_id=pCurrid))) THEN
+    RETURN -5;
+  END IF;
+
+  IF (pExpcatid IS NOT NULL
+      AND NOT EXISTS(SELECT * FROM expcat WHERE (expcat_id=pExpcatid))) THEN
+    RETURN -6;
+  END IF;
+
+-- Do not assign Journal Number until check is posted
+--  if (_journalNumber IS NULL) THEN
+--    _journalNumber := fetchJournalNumber('AP-CK');
+--  END IF;
+
+  _checkid := NEXTVAL('checkhead_checkhead_id_seq');
+
+  INSERT INTO checkhead
+  ( checkhead_id,              checkhead_recip_type,   checkhead_recip_id,
+    checkhead_bankaccnt_id,    checkhead_number,
+    checkhead_amount,
+    checkhead_checkdate,       checkhead_misc,         checkhead_expcat_id,
+    checkhead_journalnumber,   checkhead_for,          checkhead_notes,
+    checkhead_curr_id )
+  VALUES
+  ( _checkid,                  pRecipType,             pRecipId,
+    pBankaccntid,              -1, --fetchNextCheckNumber(pBankaccntid),
+    currToCurr(pCurrid, _bankaccnt_currid, pAmount, pCheckDate),
+    pCheckDate,                        COALESCE(pMisc, FALSE), pExpcatid,
+    _journalNumber,            pFor,                   pNotes,
+    _bankaccnt_currid );
+
+  IF (pAropenid IS NOT NULL AND fetchmetricbool('EnableReturnAuth')) THEN
+    INSERT INTO checkitem (checkitem_checkhead_id,checkitem_amount,checkitem_discount,checkitem_ponumber,
+                           checkitem_aropen_id,checkitem_docdate,checkitem_curr_id,checkitem_cmnumber,
+                           checkitem_ranumber, checkitem_curr_rate)
+    SELECT _checkid, currToCurr(checkhead_curr_id, aropen_curr_id, pAmount, checkhead_checkdate),
+      0,cmhead_custponumber,pAropenid,aropen_docdate,aropen_curr_id,cmhead_number,rahead_number,
+      1 / (_check_curr_rate / aropen_curr_rate)
+    FROM checkhead, aropen
+      LEFT OUTER JOIN cmhead ON (aropen_docnumber=cmhead_number)
+      LEFT OUTER JOIN rahead ON (cmhead_rahead_id=rahead_id)
+    WHERE ((aropen_id=pAropenid)
+     AND (checkhead_id=_checkid));
+  ELSIF (pAropenid IS NOT NULL) THEN
+    INSERT INTO checkitem (checkitem_checkhead_id,checkitem_amount,checkitem_discount,checkitem_ponumber,
+                           checkitem_aropen_id,checkitem_docdate,checkitem_curr_id,checkitem_cmnumber,
+                           checkitem_ranumber, checkitem_curr_rate)
+    SELECT _checkid,currToCurr(checkhead_curr_id, aropen_curr_id, pAmount, checkhead_checkdate),
+      0,cmhead_custponumber,pAropenid,aropen_docdate,aropen_curr_id,cmhead_number,NULL,
+      1 / (_check_curr_rate / aropen_curr_rate)
+    FROM checkhead, aropen
+      LEFT OUTER JOIN cmhead ON (aropen_docnumber=cmhead_number)
+    WHERE ((aropen_id=pAropenid)
+     AND (checkhead_id=_checkid));
+  END IF;
+  
+
+  RETURN _checkid;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/createchecks.sql b/foundation-database/public/functions/createchecks.sql
new file mode 100644 (file)
index 0000000..c791716
--- /dev/null
@@ -0,0 +1,80 @@
+CREATE OR REPLACE FUNCTION createChecks(INTEGER, DATE) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pBankaccntid ALIAS FOR $1;
+  pCheckDate ALIAS FOR $2;
+  _v RECORD;
+  _r RECORD;
+  _c RECORD;
+  _checkid             INTEGER;
+  _counter             INTEGER := 0;
+  _check_curr_id       INTEGER;
+  _check_curr_rate      NUMERIC;
+
+BEGIN
+
+  SELECT bankaccnt_curr_id, currRate(bankaccnt_curr_id, pCheckDate) 
+    INTO _check_curr_id, _check_curr_rate
+    FROM bankaccnt
+    WHERE ( bankaccnt_id = pBankaccntid );
+  FOR _v IN SELECT DISTINCT vend_id, vend_number, vend_name
+              FROM apselect
+              JOIN apopen   ON (apselect_apopen_id=apopen_id)
+              JOIN vendinfo ON (apopen_vend_id=vend_id)
+            WHERE ((apselect_bankaccnt_id=pBankaccntid)
+               AND (apselect_date <= pCheckDate)) LOOP
+
+    -- if we owe this vendor anything (we might not) then create a check
+    IF ((SELECT 
+                SUM(apselect_amount * _check_curr_rate / apopen_curr_rate)          
+        FROM apselect, apopen
+        WHERE ((apselect_apopen_id=apopen_id)
+          AND  (apopen_vend_id=_v.vend_id)
+          AND  (apselect_bankaccnt_id=pBankaccntid)) ) > 0) THEN
+      -- 0.01 is a temporary amount; we''ll update the check amount later
+      _checkid := createCheck(pBankaccntid,    'V',    _v.vend_id,
+                             pCheckDate,               0.01,   _check_curr_id,
+                             NULL,             NULL, '',       '',     FALSE);
+
+      FOR _r IN SELECT apopen_id, apselect_id,
+                      apopen_docnumber, apopen_invcnumber, apopen_ponumber,
+                      apopen_docdate, apselect_curr_id,
+                      apselect_amount, apselect_discount
+               FROM apselect, apopen
+               WHERE ( (apselect_apopen_id=apopen_id)
+                AND (apopen_vend_id=_v.vend_id)
+                AND (apselect_bankaccnt_id=pBankaccntid) ) LOOP
+       INSERT INTO checkitem
+       ( checkitem_checkhead_id, checkitem_apopen_id,
+         checkitem_vouchernumber, checkitem_invcnumber, checkitem_ponumber,
+         checkitem_amount, checkitem_discount, checkitem_docdate,
+          checkitem_curr_id, checkitem_curr_rate )
+       VALUES
+       ( _checkid, _r.apopen_id,
+         _r.apopen_docnumber, _r.apopen_invcnumber, _r.apopen_ponumber,
+         _r.apselect_amount, _r.apselect_discount, _r.apopen_docdate,
+         _r.apselect_curr_id, 
+          1 / (_check_curr_rate / currRate(_r.apselect_curr_id, pCheckdate))  );
+
+       DELETE FROM apselect
+       WHERE (apselect_id=_r.apselect_id);
+
+      END LOOP;
+
+      -- one check can pay for purchases on multiple dates in multiple currencies
+      UPDATE checkhead
+      SET checkhead_amount = (SELECT SUM(checkitem_amount / checkitem_curr_rate)
+                             FROM checkitem
+                             WHERE (checkitem_checkhead_id=checkhead_id))
+      WHERE (checkhead_id=_checkid);
+
+      _counter := (_counter + 1);
+    END IF;
+
+  END LOOP;
+
+  RETURN _counter;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/createcounttag.sql b/foundation-database/public/functions/createcounttag.sql
new file mode 100644 (file)
index 0000000..6f61da9
--- /dev/null
@@ -0,0 +1,92 @@
+CREATE OR REPLACE FUNCTION createCountTag(int, text, bool, bool) RETURNS integer AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  pComments ALIAS FOR $2;
+  pPriority ALIAS FOR $3;
+  pFreeze ALIAS FOR $4;
+BEGIN
+  RETURN createCountTag(pItemsiteid, pComments, pPriority, pFreeze, NULL);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION createcounttag(integer, text, boolean, boolean, integer)
+  RETURNS integer AS $$
+
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  pComments ALIAS FOR $2;
+  pPriority ALIAS FOR $3;
+  pFreeze ALIAS FOR $4;
+  pLocationid ALIAS FOR $5;
+  _invcntid INTEGER;
+  _whs         RECORD;
+  _type CHARACTER;
+  _controlmethod        CHARACTER;
+
+BEGIN
+
+  SELECT item_type, itemsite_controlmethod INTO _type, _controlmethod
+    FROM itemsite, item
+   WHERE ((itemsite_item_id=item_id)
+     AND  (itemsite_id=pItemsiteid));
+
+  IF (NOT FOUND OR _type IN ('F', 'R', 'L','J') OR _controlmethod = 'N') THEN
+    RETURN 0; -- We simply do not do these item types.
+  END IF;
+
+  -- Test for existing tags
+   IF (pLocationid IS NULL) THEN
+       SELECT invcnt_id INTO _invcntid
+       FROM invcnt
+       WHERE ((NOT invcnt_posted)
+       AND (invcnt_location_id IS NULL)
+       AND (invcnt_itemsite_id=pItemsiteid));
+  
+  ELSE
+
+    SELECT invcnt_id INTO _invcntid
+     FROM invcnt
+     WHERE ((NOT invcnt_posted)
+     AND (invcnt_itemsite_id=pItemsiteid)
+     AND (invcnt_location_id=pLocationid));
+  END IF;
+
+  IF (NOT FOUND) THEN
+    SELECT NEXTVAL('invcnt_invcnt_id_seq') INTO _invcntid;
+
+    SELECT whsinfo.* INTO _whs
+      FROM whsinfo, itemsite
+     WHERE ((warehous_id=itemsite_warehous_id)
+       AND  (itemsite_id=pItemsiteid));
+
+    INSERT INTO invcnt (
+      invcnt_id, invcnt_itemsite_id, invcnt_tagdate,
+      invcnt_tagnumber,
+      invcnt_tag_username, invcnt_posted,
+      invcnt_priority, invcnt_comments, invcnt_location_id
+    ) VALUES (
+      _invcntid, pItemsiteid, CURRENT_TIMESTAMP,
+      (_whs.warehous_counttag_prefix || _whs.warehous_counttag_number::TEXT),
+      getEffectiveXtUser(), FALSE,
+      pPriority, pComments, pLocationid
+    );
+
+    UPDATE whsinfo
+    SET warehous_counttag_number=(warehous_counttag_number + 1)
+    WHERE (warehous_id=_whs.warehous_id);
+
+    IF (pFreeze) THEN
+      UPDATE itemsite
+      SET itemsite_freeze=TRUE
+      WHERE (itemsite_id=pItemsiteid);
+    END IF;
+
+  END IF;
+
+  RETURN _invcntid;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/createcyclecountsbywarehouse.sql b/foundation-database/public/functions/createcyclecountsbywarehouse.sql
new file mode 100644 (file)
index 0000000..9e676ee
--- /dev/null
@@ -0,0 +1,117 @@
+CREATE OR REPLACE FUNCTION createCycleCountsByWarehouse(INTEGER, INTEGER, TEXT, BOOLEAN, BOOLEAN) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWarehousid ALIAS FOR $1;
+  pMaxNumber ALIAS FOR $2;
+  pComments ALIAS FOR $3;
+  pPriority ALIAS FOR $4;
+  pFreeze ALIAS FOR $5;
+
+BEGIN
+  RETURN createCycleCountsByWarehouse(pWarehousid, pMaxNumber, pComments, pPriority, pFreeze, NULL, FALSE);
+END;
+' LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION createcyclecountsbywarehouse(integer, integer, text, boolean, boolean, integer, boolean)
+  RETURNS integer AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWarehousid ALIAS FOR $1;
+  pMaxNumber ALIAS FOR $2;
+  pComments ALIAS FOR $3;
+  pPriority ALIAS FOR $4;
+  pFreeze ALIAS FOR $5;
+  pLocationid ALIAS FOR $6;
+  pIgnoreZeroBalance ALIAS FOR $7;
+  _itemsites RECORD;
+  _returnVal   INTEGER;
+  
+BEGIN
+
+IF (pLocationid IS NULL) THEN
+  FOR _itemsites IN SELECT itemsite_id, itemsite_warehous_id, itemsite_qtyonhand
+                    FROM itemsite, item
+                    WHERE ( (itemsite_active)
+                     AND (itemsite_item_id=item_id)
+                     AND (itemsite_cyclecountfreq > 0)
+                     AND ((COALESCE(itemsite_datelastcount, startOfTime()) + itemsite_cyclecountfreq) < CURRENT_DATE)
+                     AND (itemsite_id NOT IN ( SELECT invcnt_itemsite_id
+                                               FROM invcnt, itemsite
+                                               WHERE ( (invcnt_itemsite_id=itemsite_id)
+                                                AND (itemsite_warehous_id=pWarehousid)
+                                               AND (invcnt_location_id IS NULL)
+                                                AND (NOT invcnt_posted) ) ) )
+                     AND ((NOT pIgnoreZeroBalance) OR (itemsite_qtyonhand <> 0))
+                     AND ((pLocationid IS NULL) OR (validLocation(pLocationid, itemsite_id)))
+                     AND (itemsite_warehous_id=pWarehousid) )
+                    ORDER BY (COALESCE(itemsite_datelastcount, startOfTime()) + itemsite_cyclecountfreq), itemsite_abcclass, item_number
+
+LIMIT pMaxNumber LOOP
+    _returnVal := createCountTag(_itemsites.itemsite_id, pComments,
+                                   pPriority, pFreeze, pLocationid);
+    IF (_returnVal < 0) THEN
+      RETURN _returnVal;
+    END IF;
+  END LOOP;            
+
+ELSE
+  FOR _itemsites IN SELECT itemsite_id, itemsite_warehous_id, SUM(itemloc_qty)
+                    FROM itemsite, itemloc
+                    WHERE ( (itemsite_active)
+                     AND (itemsite_cyclecountfreq > 0)
+                     AND ((COALESCE(itemsite_datelastcount, startOfTime()) + itemsite_cyclecountfreq) < CURRENT_DATE)
+                     AND ((NOT pIgnoreZeroBalance) OR (itemsite_qtyonhand <> 0))
+                     AND (pLocationid = itemloc_location_id)
+                     AND (itemloc_itemsite_id = itemsite_id)
+                     AND (itemsite_warehous_id=pWarehousid) )
+                   GROUP BY itemsite_id, itemsite_warehous_id,
+                            itemsite_datelastcount, itemsite_cyclecountfreq,
+                            itemsite_abcclass
+                    ORDER BY (COALESCE(itemsite_datelastcount, startOfTime()) + itemsite_cyclecountfreq), itemsite_abcclass
+
+LIMIT pMaxNumber LOOP
+    _returnVal := createCountTag(_itemsites.itemsite_id, pComments,
+                                   pPriority, pFreeze, pLocationid);
+    IF (_returnVal < 0) THEN
+      RETURN _returnVal;
+    END IF;
+  END LOOP;  
+                   
+END IF;
+
+  RETURN 0;
+END;
+' LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION createCycleCountsByWarehouse(INTEGER, INTEGER, INTEGER, TEXT, BOOLEAN, BOOLEAN) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWarehousid ALIAS FOR $1;
+  pClasscodeid ALIAS FOR $2;
+  pMaxNumber ALIAS FOR $3;
+  pComments ALIAS FOR $4;
+  pPriority ALIAS FOR $5;
+  pFreeze ALIAS FOR $6;
+BEGIN
+  RETURN createCycleCountsByWarehouseByClassCode(pWarehousid, pClasscodeid, pMaxNumber, pComments, pPriority, pFreeze, NULL, FALSE);
+END;
+' LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION createCycleCountsByWarehouse(INTEGER, TEXT, INTEGER, TEXT, BOOLEAN, BOOLEAN) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWarehousid ALIAS FOR $1;
+  pClasscodePattern ALIAS FOR $2;
+  pMaxNumber ALIAS FOR $3;
+  pComments ALIAS FOR $4;
+  pPriority ALIAS FOR $5;
+  pFreeze ALIAS FOR $6;
+BEGIN
+  RETURN createCycleCountsByWarehouseByClassCode(pWarehousid, pClasscodePattern, pMaxNumber, pComments, pPriority, pFreeze, NULL, FALSE);
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/createcyclecountsbywarehousebyclasscode.sql b/foundation-database/public/functions/createcyclecountsbywarehousebyclasscode.sql
new file mode 100644 (file)
index 0000000..694a8e9
--- /dev/null
@@ -0,0 +1,149 @@
+CREATE OR REPLACE FUNCTION createcyclecountsbywarehousebyclasscode(integer, integer, integer, text, boolean, boolean, integer, boolean)
+  RETURNS integer AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWarehousid ALIAS FOR $1;
+  pClasscodeid ALIAS FOR $2;
+  pMaxNumber ALIAS FOR $3;
+  pComments ALIAS FOR $4;
+  pPriority ALIAS FOR $5;
+  pFreeze ALIAS FOR $6;
+  pLocationid ALIAS FOR $7;
+  pIgnoreZeroBalance ALIAS FOR $8;
+  _itemsites RECORD;
+  _returnVal   INTEGER;
+  
+BEGIN
+
+IF (pLocationid IS NULL) THEN
+  FOR _itemsites IN SELECT itemsite_id, itemsite_warehous_id, itemsite_qtyonhand
+                    FROM itemsite, item
+                    WHERE ( (itemsite_active)
+                     AND (itemsite_item_id=item_id)
+                     AND (itemsite_cyclecountfreq > 0)
+                     AND ((COALESCE(itemsite_datelastcount, startOfTime()) + itemsite_cyclecountfreq) < CURRENT_DATE)
+                     AND (itemsite_id NOT IN ( SELECT invcnt_itemsite_id
+                                               FROM invcnt, itemsite
+                                               WHERE ( (invcnt_itemsite_id=itemsite_id)
+                                                AND (itemsite_warehous_id=pWarehousid)
+                                               AND (invcnt_location_id IS NULL)
+                                                AND (NOT invcnt_posted) ) ) )
+                     AND ((NOT pIgnoreZeroBalance) OR (itemsite_qtyonhand <> 0))
+                     AND ((pLocationid IS NULL) OR (validLocation(pLocationid, itemsite_id)))
+                     AND (itemsite_warehous_id=pWarehousid)
+                     AND (item_classcode_id=pClasscodeid) )
+                    ORDER BY (COALESCE(itemsite_datelastcount, startOfTime()) + itemsite_cyclecountfreq), itemsite_abcclass, item_number
+                    LIMIT pMaxNumber LOOP
+    _returnVal := createCountTag(_itemsites.itemsite_id, pComments,
+                                   pPriority, pFreeze, pLocationid);
+    IF (_returnVal < 0) THEN
+      RETURN _returnVal;
+    END IF;
+  END LOOP;
+
+ELSE
+  FOR _itemsites IN SELECT itemsite_id, itemsite_warehous_id, SUM(itemloc_qty)
+                    FROM itemsite, item, itemloc
+                    WHERE ( (itemsite_active)
+                     AND (itemsite_item_id=item_id)
+                     AND (itemsite_cyclecountfreq > 0)
+                     AND ((COALESCE(itemsite_datelastcount, startOfTime()) + itemsite_cyclecountfreq) < CURRENT_DATE)
+                     AND ((NOT pIgnoreZeroBalance) OR (itemsite_qtyonhand <> 0))
+                     AND (pLocationid = itemloc_location_id)
+                     AND (itemloc_itemsite_id = itemsite_id)
+                     AND (itemsite_warehous_id=pWarehousid) 
+                     AND (item_classcode_id=pClasscodeid) )
+                   GROUP BY itemsite_id, itemsite_warehous_id,
+                            itemsite_datelastcount, itemsite_cyclecountfreq,
+                            itemsite_abcclass
+                    ORDER BY (COALESCE(itemsite_datelastcount, startOfTime()) + itemsite_cyclecountfreq), itemsite_abcclass
+                    LIMIT pMaxNumber LOOP
+    _returnVal := createCountTag(_itemsites.itemsite_id, pComments,
+                                   pPriority, pFreeze, pLocationid);
+    IF (_returnVal < 0) THEN
+      RETURN _returnVal;
+    END IF;
+  END LOOP;
+
+END IF;
+
+  RETURN 0;
+END;
+' LANGUAGE 'plpgsql' ;
+
+CREATE OR REPLACE FUNCTION createcyclecountsbywarehousebyclasscode(integer, text, integer, text, boolean, boolean, integer, boolean)
+  RETURNS integer AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWarehousid ALIAS FOR $1;
+  pClasscodePattern ALIAS FOR $2;
+  pMaxNumber ALIAS FOR $3;
+  pComments ALIAS FOR $4;
+  pPriority ALIAS FOR $5;
+  pFreeze ALIAS FOR $6;
+  pLocationid ALIAS FOR $7;
+  pIgnoreZeroBalance ALIAS FOR $8;
+  _itemsites RECORD;
+  _returnVal   INTEGER;
+  
+BEGIN
+
+IF (pLocationid IS NULL) THEN
+  FOR _itemsites IN SELECT itemsite_id, itemsite_warehous_id, itemsite_qtyonhand
+                    FROM itemsite, item, classcode
+                    WHERE ( (itemsite_active)
+                     AND (itemsite_item_id=item_id)
+                     AND (item_classcode_id=classcode_id)
+                     AND (itemsite_cyclecountfreq > 0)
+                     AND ((COALESCE(itemsite_datelastcount, startOfTime()) + itemsite_cyclecountfreq) < CURRENT_DATE)
+                     AND (itemsite_id NOT IN ( SELECT invcnt_itemsite_id
+                                               FROM invcnt, itemsite
+                                               WHERE ( (invcnt_itemsite_id=itemsite_id)
+                                                AND (itemsite_warehous_id=pWarehousid)
+                                               AND (invcnt_location_id IS NULL)
+                                                AND (NOT invcnt_posted) ) ) )
+                     AND ((NOT pIgnoreZeroBalance) OR (itemsite_qtyonhand <> 0))
+                     AND ((pLocationid IS NULL) OR (validLocation(pLocationid, itemsite_id)))
+                     AND (itemsite_warehous_id=pWarehousid)
+                     AND (classcode_code ~ pClasscodePattern) )
+                    ORDER BY (COALESCE(itemsite_datelastcount, startOfTime()) + itemsite_cyclecountfreq), itemsite_abcclass, item_number
+                    LIMIT pMaxNumber LOOP
+    _returnVal := createCountTag(_itemsites.itemsite_id, pComments,
+                                   pPriority, pFreeze, pLocationid);
+    IF (_returnVal < 0) THEN
+      RETURN _returnVal;
+    END IF;
+  END LOOP;
+
+ELSE
+  FOR _itemsites IN SELECT itemsite_id, itemsite_warehous_id, SUM(itemloc_qty)
+                    FROM itemsite, item, classcode, itemloc
+                    WHERE ( (itemsite_active)
+                     AND (itemsite_item_id=item_id)
+                     AND (item_classcode_id=classcode_id)
+                     AND (itemsite_cyclecountfreq > 0)
+                     AND ((COALESCE(itemsite_datelastcount, startOfTime()) + itemsite_cyclecountfreq) < CURRENT_DATE)
+                     AND ((NOT pIgnoreZeroBalance) OR (itemsite_qtyonhand <> 0))
+                     AND (pLocationid = itemloc_location_id)
+                     AND (itemloc_itemsite_id = itemsite_id)
+                     AND (itemsite_warehous_id=pWarehousid)
+                     AND (classcode_code ~ pClasscodePattern) )
+                   GROUP BY itemsite_id, itemsite_warehous_id,
+                            itemsite_datelastcount, itemsite_cyclecountfreq,
+                            itemsite_abcclass
+                    ORDER BY (COALESCE(itemsite_datelastcount, startOfTime()) + itemsite_cyclecountfreq), itemsite_abcclass
+                    LIMIT pMaxNumber LOOP
+    _returnVal := createCountTag(_itemsites.itemsite_id, pComments,
+                                   pPriority, pFreeze, pLocationid);
+    IF (_returnVal < 0) THEN
+      RETURN _returnVal;
+    END IF;
+  END LOOP;
+
+END IF;
+
+  RETURN 0;
+END;
+' LANGUAGE 'plpgsql' ;
diff --git a/foundation-database/public/functions/createcyclecountsbywarehousebyplannercode.sql b/foundation-database/public/functions/createcyclecountsbywarehousebyplannercode.sql
new file mode 100644 (file)
index 0000000..c7d2fd4
--- /dev/null
@@ -0,0 +1,147 @@
+CREATE OR REPLACE FUNCTION createcyclecountsbywarehousebyplannercode(integer, integer, integer, text, boolean, boolean, integer, boolean)
+  RETURNS integer AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWarehousid ALIAS FOR $1;
+  pPlancodeid ALIAS FOR $2;
+  pMaxNumber ALIAS FOR $3;
+  pComments ALIAS FOR $4;
+  pPriority ALIAS FOR $5;
+  pFreeze ALIAS FOR $6;
+  pLocationid ALIAS FOR $7;
+  pIgnoreZeroBalance ALIAS FOR $8;
+  _itemsites RECORD;
+  _returnVal   INTEGER;
+  
+BEGIN
+
+IF (pLocationid IS NULL) THEN
+  FOR _itemsites IN SELECT itemsite_id, itemsite_warehous_id, itemsite_qtyonhand
+                    FROM itemsite, item
+                    WHERE ( (itemsite_active)
+                     AND (itemsite_item_id=item_id)
+                     AND (itemsite_cyclecountfreq > 0)
+                     AND ((COALESCE(itemsite_datelastcount, startOfTime()) + itemsite_cyclecountfreq) < CURRENT_DATE)
+                     AND (itemsite_id NOT IN ( SELECT invcnt_itemsite_id
+                                               FROM invcnt, itemsite
+                                               WHERE ( (invcnt_itemsite_id=itemsite_id)
+                                                AND (itemsite_warehous_id=pWarehousid)
+                                               AND (invcnt_location_id IS NULL)
+                                                AND (NOT invcnt_posted) ) ) )
+                     AND ((NOT pIgnoreZeroBalance) OR (itemsite_qtyonhand <> 0))
+                     AND ((pLocationid IS NULL) OR (validLocation(pLocationid, itemsite_id)))
+                     AND (itemsite_warehous_id=pWarehousid)
+                     AND (itemsite_plancode_id=pPlancodeid) )
+                    ORDER BY (COALESCE(itemsite_datelastcount, startOfTime()) + itemsite_cyclecountfreq), itemsite_abcclass, item_number
+                    LIMIT pMaxNumber LOOP
+    _returnVal := createCountTag(_itemsites.itemsite_id, pComments,
+                                   pPriority, pFreeze, pLocationid);
+    IF (_returnVal < 0) THEN
+      RETURN _returnVal;
+    END IF;
+  END LOOP;
+
+ELSE
+  FOR _itemsites IN SELECT itemsite_id, itemsite_warehous_id, SUM(itemloc_qty)
+                    FROM itemsite, itemloc
+                    WHERE ( (itemsite_active)
+                     AND (itemsite_cyclecountfreq > 0)
+                     AND ((COALESCE(itemsite_datelastcount, startOfTime()) + itemsite_cyclecountfreq) < CURRENT_DATE)
+                     AND ((NOT pIgnoreZeroBalance) OR (itemsite_qtyonhand <> 0))
+                     AND (itemloc_itemsite_id = itemsite_id)
+                     AND (itemsite_warehous_id=pWarehousid)
+                     AND (pLocationid = itemloc_location_id)
+                     AND (itemsite_plancode_id=pPlancodeid) )
+                   GROUP BY itemsite_id, itemsite_warehous_id,
+                            itemsite_datelastcount, itemsite_cyclecountfreq,
+                            itemsite_abcclass
+                    ORDER BY (COALESCE(itemsite_datelastcount, startOfTime()) + itemsite_cyclecountfreq), itemsite_abcclass
+                    LIMIT pMaxNumber LOOP
+    _returnVal := createCountTag(_itemsites.itemsite_id, pComments,
+                                   pPriority, pFreeze, pLocationid);
+    IF (_returnVal < 0) THEN
+      RETURN _returnVal;
+    END IF;
+  END LOOP;
+
+END IF;
+
+  RETURN 0;
+END;
+' LANGUAGE 'plpgsql' ;
+
+CREATE OR REPLACE FUNCTION createcyclecountsbywarehousebyplannercode(integer, text, integer, text, boolean, boolean, integer, boolean)
+  RETURNS integer AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWarehousid ALIAS FOR $1;
+  pPlancodePattern ALIAS FOR $2;
+  pMaxNumber ALIAS FOR $3;
+  pComments ALIAS FOR $4;
+  pPriority ALIAS FOR $5;
+  pFreeze ALIAS FOR $6;
+  pLocationid ALIAS FOR $7;
+  pIgnoreZeroBalance ALIAS FOR $8;
+  _itemsites RECORD;
+  _returnVal   INTEGER;
+  
+BEGIN
+
+IF (pLocationid IS NULL) THEN
+  FOR _itemsites IN SELECT itemsite_id, itemsite_warehous_id, itemsite_qtyonhand
+                    FROM itemsite, item, plancode
+                    WHERE ( (itemsite_active)
+                     AND (itemsite_item_id=item_id)
+                     AND (itemsite_plancode_id=plancode_id)
+                     AND (itemsite_cyclecountfreq > 0)
+                     AND ((COALESCE(itemsite_datelastcount, startOfTime()) + itemsite_cyclecountfreq) < CURRENT_DATE)
+                     AND (itemsite_id NOT IN ( SELECT invcnt_itemsite_id
+                                               FROM invcnt, itemsite
+                                               WHERE ( (invcnt_itemsite_id=itemsite_id)
+                                                AND (itemsite_warehous_id=pWarehousid)
+                                               AND (invcnt_location_id IS NULL)
+                                                AND (NOT invcnt_posted) ) ) )
+                     AND ((NOT pIgnoreZeroBalance) OR (itemsite_qtyonhand <> 0))
+                     AND ((pLocationid IS NULL) OR (validLocation(pLocationid, itemsite_id)))
+                     AND (itemsite_warehous_id=pWarehousid)
+                     AND (plancode_code ~ pPlancodePattern) )
+                    ORDER BY (COALESCE(itemsite_datelastcount, startOfTime()) + itemsite_cyclecountfreq), itemsite_abcclass, item_number
+                    LIMIT pMaxNumber LOOP
+    _returnVal := createCountTag(_itemsites.itemsite_id, pComments,
+                                   pPriority, pFreeze, pLocationid);
+    IF (_returnVal < 0) THEN
+      RETURN _returnVal;
+    END IF;
+  END LOOP;
+
+ELSE
+  FOR _itemsites IN SELECT itemsite_id, itemsite_warehous_id, SUM(itemloc_qty)
+                    FROM itemsite, plancode, itemloc
+                    WHERE ( (itemsite_active)
+                     AND (itemsite_plancode_id=plancode_id)
+                     AND (itemsite_cyclecountfreq > 0)
+                     AND ((COALESCE(itemsite_datelastcount, startOfTime()) + itemsite_cyclecountfreq) < CURRENT_DATE)
+                     AND ((NOT pIgnoreZeroBalance) OR (itemsite_qtyonhand <> 0))
+                     AND (pLocationid = itemloc_location_id)
+                     AND (itemloc_itemsite_id = itemsite_id)
+                     AND (itemsite_warehous_id=pWarehousid)
+                     AND (plancode_code ~ pPlancodePattern) )
+                   GROUP BY itemsite_id, itemsite_warehous_id,
+                            itemsite_datelastcount, itemsite_cyclecountfreq,
+                            itemsite_abcclass
+                    ORDER BY (COALESCE(itemsite_datelastcount, startOfTime()) + itemsite_cyclecountfreq), itemsite_abcclass
+                    LIMIT pMaxNumber LOOP
+    _returnVal := createCountTag(_itemsites.itemsite_id, pComments,
+                                   pPriority, pFreeze, pLocationid);
+    IF (_returnVal < 0) THEN
+      RETURN _returnVal;
+    END IF;
+  END LOOP;
+
+END IF;
+
+  RETURN 0;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/createfile.sql b/foundation-database/public/functions/createfile.sql
new file mode 100644 (file)
index 0000000..bb06a35
--- /dev/null
@@ -0,0 +1,12 @@
+create or replace function createFile(text, text, bytea) returns integer as $$
+declare
+  pTitle ALIAS FOR $1;
+  pDescription ALIAS FOR $2;
+  pStream ALIAS FOR $3;
+  _id integer;
+begin
+  _id := nextval('file_file_id_seq');
+  insert into file (file_id, file_title, file_descrip, file_stream) values (_id, pTitle, pDescription, pStream);
+  return _id;
+end;
+$$ language 'plpgsql';
diff --git a/foundation-database/public/functions/createinvoice.sql b/foundation-database/public/functions/createinvoice.sql
new file mode 100644 (file)
index 0000000..c1ade99
--- /dev/null
@@ -0,0 +1,183 @@
+
+CREATE OR REPLACE FUNCTION createInvoice(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCobmiscid ALIAS FOR $1;
+  _invcheadid INTEGER;
+  _invcitemid INTEGER;
+  _qtyToInvoice        NUMERIC;
+  _r           RECORD;
+  _s           RECORD;
+  _lastlinenumber INTEGER := 1;
+  
+BEGIN
+
+  IF ( ( SELECT cobmisc_posted
+         FROM cobmisc
+         WHERE (cobmisc_id=pCobmiscid) ) ) THEN
+    RETURN -1;
+  END IF;
+
+  SELECT NEXTVAL('invchead_invchead_id_seq') INTO _invcheadid;
+
+--  Give this selection a number if it has not been assigned one
+  UPDATE cobmisc
+  SET cobmisc_invcnumber=fetchInvcNumber()
+  WHERE ( (cobmisc_invcnumber IS NULL)
+   AND (cobmisc_id=pCobmiscid) );
+
+--  Create the Invoice header
+  INSERT INTO invchead
+  ( 
+       invchead_id,invchead_cust_id,invchead_shipto_id,invchead_ordernumber,invchead_orderdate,
+       invchead_posted,invchead_printed,invchead_invcnumber,invchead_invcdate,invchead_shipdate,
+       invchead_ponumber,invchead_shipvia,invchead_fob,invchead_billto_name,invchead_billto_address1,
+       invchead_billto_address2,invchead_billto_address3,invchead_billto_city,invchead_billto_state,invchead_billto_zipcode,
+       invchead_billto_phone,invchead_billto_country,invchead_shipto_name,invchead_shipto_address1,invchead_shipto_address2,
+       invchead_shipto_address3,invchead_shipto_city,invchead_shipto_state,invchead_shipto_zipcode,invchead_shipto_phone,
+       invchead_shipto_country,invchead_salesrep_id,invchead_commission,invchead_terms_id,invchead_freight,
+       invchead_misc_amount,invchead_misc_descrip,invchead_misc_accnt_id,invchead_payment,
+       invchead_paymentref,invchead_notes,invchead_prj_id,invchead_curr_id,
+       invchead_taxzone_id, invchead_shipchrg_id,
+        invchead_saletype_id, invchead_shipzone_id
+   )
+  SELECT 
+       _invcheadid,cohead_cust_id,cohead_shipto_id,cohead_number,cohead_orderdate,
+       FALSE,FALSE,cobmisc_invcnumber,cobmisc_invcdate,cobmisc_shipdate,
+       cohead_custponumber,cobmisc_shipvia,cohead_fob,cohead_billtoname,cohead_billtoaddress1,
+       cohead_billtoaddress2,cohead_billtoaddress3,cohead_billtocity,cohead_billtostate,cohead_billtozipcode,
+       cntct_phone AS cust_phone,cohead_billtocountry,cohead_shiptoname,cohead_shiptoaddress1,cohead_shiptoaddress2,
+       cohead_shiptoaddress3,cohead_shiptocity,cohead_shiptostate,cohead_shiptozipcode,cohead_shipto_cntct_phone,
+       cohead_shiptocountry,cohead_salesrep_id,COALESCE(cohead_commission,0),cohead_terms_id,cobmisc_freight,
+       COALESCE(cobmisc_misc, 0.00),cobmisc_misc_descrip,cobmisc_misc_accnt_id,cobmisc_payment,
+       cobmisc_paymentref,cobmisc_notes,cohead_prj_id,cobmisc_curr_id,
+       cobmisc_taxzone_id, cohead_shipchrg_id,
+        cohead_saletype_id, cohead_shipzone_id
+    FROM cobmisc, cohead, custinfo
+    LEFT OUTER JOIN cntct ON (cust_cntct_id=cntct_id)
+  WHERE ( (cobmisc_cohead_id=cohead_id)
+   AND (cohead_cust_id=cust_id)
+   AND (cobmisc_id=pCobmiscid) );
+
+       INSERT INTO invcheadtax(taxhist_parent_id, taxhist_taxtype_id, taxhist_tax_id, taxhist_basis, 
+                       taxhist_basis_tax_id, taxhist_sequence, taxhist_percent, taxhist_amount, taxhist_tax, taxhist_docdate)
+        SELECT _invcheadid,taxhist_taxtype_id, taxhist_tax_id, taxhist_basis, 
+                       taxhist_basis_tax_id, taxhist_sequence, taxhist_percent, taxhist_amount, taxhist_tax, taxhist_docdate
+        FROM cobmisctax 
+       WHERE taxhist_parent_id = pCobmiscid 
+       AND taxhist_taxtype_id = getadjustmenttaxtypeid();
+
+--  Create the Invoice items
+  FOR _r IN SELECT coitem_id, coitem_linenumber, coitem_subnumber, coitem_custpn,
+                   coitem_qtyord, cobill_qty,
+                   coitem_qty_uom_id, coitem_qty_invuomratio,
+                   coitem_custprice, coitem_price,
+                   coitem_price_uom_id, coitem_price_invuomratio,
+                   coitem_memo, coitem_rev_accnt_id,
+                   itemsite_item_id, itemsite_warehous_id,
+                   cobill_taxtype_id,
+                   formatSoItemNumber(coitem_id) AS ordnumber
+            FROM coitem, cobill, itemsite
+            WHERE ( (cobill_coitem_id=coitem_id)
+             AND (coitem_itemsite_id=itemsite_id)
+             AND (cobill_cobmisc_id=pCobmiscid) )
+            ORDER BY coitem_linenumber, coitem_subnumber LOOP
+
+    SELECT NEXTVAL('invcitem_invcitem_id_seq') INTO _invcitemid;
+    INSERT INTO invcitem
+    ( invcitem_id, invcitem_invchead_id,
+      invcitem_linenumber, invcitem_item_id, invcitem_warehous_id,
+      invcitem_custpn, invcitem_number, invcitem_descrip,
+      invcitem_ordered, invcitem_billed,
+      invcitem_qty_uom_id, invcitem_qty_invuomratio,
+      invcitem_custprice, invcitem_price,
+      invcitem_price_uom_id, invcitem_price_invuomratio,
+      invcitem_notes, invcitem_taxtype_id,
+      invcitem_coitem_id, invcitem_rev_accnt_id )
+    VALUES
+    ( _invcitemid, _invcheadid,
+      _lastlinenumber,
+      _r.itemsite_item_id, _r.itemsite_warehous_id,
+      _r.coitem_custpn, '', '',
+      _r.coitem_qtyord, _r.cobill_qty,
+      _r.coitem_qty_uom_id, _r.coitem_qty_invuomratio,
+      _r.coitem_custprice, _r.coitem_price,
+      _r.coitem_price_uom_id, _r.coitem_price_invuomratio,
+      _r.coitem_memo, _r.cobill_taxtype_id,
+      _r.coitem_id, _r.coitem_rev_accnt_id );
+
+--  Find and mark any Lot/Serial invdetail records associated with this bill
+    UPDATE invdetail SET invdetail_invcitem_id = _invcitemid
+     WHERE (invdetail_id IN (SELECT invdetail_id
+                               FROM invhist JOIN invdetail ON (invdetail_invhist_id=invhist_id)
+                              WHERE ( (invhist_ordnumber = _r.ordnumber)
+                                AND   (invhist_ordtype = 'SO')
+                                AND   (invhist_transtype = 'SH')
+                                AND   (invdetail_invcitem_id IS NULL) ) ));
+
+--  Mark any shipped, uninvoiced shipitems for the current coitem as invoiced
+    _qtyToInvoice :=  _r.cobill_qty;
+    FOR _s IN SELECT shipitem.*, shipitem_qty = _r.cobill_qty AS matched
+             FROM shipitem, shiphead
+             WHERE ((shipitem_shiphead_id=shiphead_id)
+               AND  (shipitem_orderitem_id=_r.coitem_id)
+               AND  (shiphead_shipped)
+               AND  (shiphead_order_type='SO')
+               AND  (NOT shipitem_invoiced))
+             ORDER BY matched DESC, shipitem_qty DESC FOR UPDATE LOOP
+      IF (_qtyToInvoice >= _s.shipitem_qty) THEN
+       UPDATE shipitem
+       SET shipitem_invoiced=TRUE, shipitem_invcitem_id=_invcitemid
+       WHERE (shipitem_id=_s.shipitem_id);
+       _qtyToInvoice := _qtyToInvoice - _s.shipitem_qty;
+      END IF;
+      IF (_qtyToInvoice <= 0) THEN
+       EXIT;
+      END IF;
+    END LOOP;
+
+    UPDATE cobill SET cobill_invcnum=cobmisc_invcnumber,
+                     cobill_invcitem_id=invcitem_id
+    FROM invcitem, coitem, cobmisc
+    WHERE ((invcitem_linenumber=_lastlinenumber)
+      AND  (coitem_id=cobill_coitem_id)
+      AND  (cobmisc_id=cobill_cobmisc_id)
+      AND  (cobill_cobmisc_id=pCobmiscid)
+      AND  (invcitem_invchead_id=_invcheadid));
+    
+    _lastlinenumber := _lastlinenumber + 1;
+
+  END LOOP;
+
+--  Close all requested coitem's
+  IF ( ( SELECT cobmisc_closeorder
+         FROM cobmisc
+         WHERE (cobmisc_id=pCobmiscid) ) ) THEN
+    UPDATE coitem
+    SET coitem_status='C'
+    FROM cobmisc
+    WHERE ( (coitem_status NOT IN ('C', 'X'))
+     AND (coitem_cohead_id=cobmisc_cohead_id)
+     AND (cobmisc_id=pCobmiscid) );
+  ELSE
+    UPDATE coitem
+    SET coitem_status='C'
+    FROM cobill
+    WHERE ( (cobill_coitem_id=coitem_id)
+     AND (coitem_status <> 'X')
+     AND (cobill_toclose)
+     AND (cobill_cobmisc_id=pCobmiscid) );
+  END IF;
+
+--  Mark the cobmisc as posted
+  UPDATE cobmisc
+  SET cobmisc_posted=TRUE, cobmisc_invchead_id=_invcheadid
+  WHERE (cobmisc_id=pCobmiscid);
+
+--  All done
+  RETURN _invcheadid;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/createinvoiceconsolidated.sql b/foundation-database/public/functions/createinvoiceconsolidated.sql
new file mode 100644 (file)
index 0000000..cd670c2
--- /dev/null
@@ -0,0 +1,285 @@
+
+CREATE OR REPLACE FUNCTION createInvoiceConsolidated(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCustid ALIAS FOR $1;
+  _invcheadid INTEGER;
+  _invcitemid INTEGER;
+  _qtyToInvoice        NUMERIC;
+  _r           RECORD;
+  _s           RECORD;
+  _c           RECORD;
+  _i           RECORD;
+  _count      INTEGER;
+  _invcnumber INTEGER;
+  _lastlinenumber INTEGER;
+
+BEGIN
+  _count := 0;
+
+  FOR _c IN SELECT min(cobmisc_id) AS cobmisc_id, count(*) AS cnt,
+-- there are the key values for consolidation
+                   cohead_billtoname, cohead_billtoaddress1,
+                   cohead_billtoaddress2, cohead_billtoaddress3,
+                   cohead_billtocity, cohead_billtostate,
+                   cohead_billtozipcode, cntct_phone AS cust_phone,
+                   cohead_billtocountry,
+                   cohead_salesrep_id, cohead_commission,
+                   cohead_terms_id,
+                   cobmisc_misc_accnt_id,
+                   cohead_prj_id, cobmisc_curr_id,
+                   cobmisc_taxzone_id,
+                   cohead_shipchrg_id,
+                   cohead_saletype_id,
+                   cohead_shipzone_id,
+                   
+               -- the following are consolidated values to use in creating the header
+                   MIN(cohead_number) AS cohead_number,
+                   MIN(cohead_orderdate) AS cohead_orderdate,
+                   MIN(cobmisc_invcdate) AS cobmisc_invcdate,
+                   MIN(cobmisc_shipdate) AS cobmisc_shipdate,
+                   SUM(cobmisc_freight) AS cobmisc_freight,
+                   SUM(cobmisc_misc) AS cobmisc_misc,
+                   SUM(cobmisc_payment) AS cobmisc_payment
+                   
+              FROM cobmisc
+              JOIN cohead   ON (cobmisc_cohead_id=cohead_id)
+              JOIN custinfo ON (cohead_cust_id=cust_id)
+              LEFT OUTER JOIN cntct ON (cust_cntct_id=cntct_id)
+             WHERE(NOT cobmisc_posted
+               AND (cohead_cust_id=pCustid)
+               )
+          GROUP BY cohead_billtoname, cohead_billtoaddress1,
+                   cohead_billtoaddress2, cohead_billtoaddress3,
+                   cohead_billtocity, cohead_billtostate,
+                   cohead_billtozipcode, cust_phone,
+                   cohead_billtocountry,
+                   cohead_salesrep_id, cohead_commission,
+                   cohead_terms_id,
+                   cobmisc_misc_accnt_id,
+                   cohead_prj_id, cobmisc_curr_id,
+                   cobmisc_taxzone_id,
+                   cohead_shipchrg_id,
+                   cohead_saletype_id,
+                   cohead_shipzone_id
+               LOOP
+
+    IF(_c.cnt = 1) THEN
+      PERFORM createInvoice(_c.cobmisc_id);
+      _count := (_count + 1);
+    ELSE
+      SELECT NEXTVAL('invchead_invchead_id_seq'), fetchInvcNumber() INTO _invcheadid, _invcnumber;
+  
+  --  Create the Invoice header
+      INSERT INTO invchead
+      ( invchead_id, invchead_cust_id, invchead_shipto_id,
+        invchead_ordernumber, invchead_orderdate,
+        invchead_posted, invchead_printed,
+        invchead_invcnumber, invchead_invcdate, invchead_shipdate,
+        invchead_ponumber, invchead_shipvia, invchead_fob,
+        invchead_billto_name, invchead_billto_address1,
+        invchead_billto_address2, invchead_billto_address3,
+        invchead_billto_city, invchead_billto_state,
+        invchead_billto_zipcode, invchead_billto_phone,
+        invchead_billto_country,
+        invchead_shipto_name, invchead_shipto_address1,
+        invchead_shipto_address2, invchead_shipto_address3,
+        invchead_shipto_city, invchead_shipto_state,
+        invchead_shipto_zipcode, invchead_shipto_phone,
+        invchead_shipto_country,
+        invchead_salesrep_id, invchead_commission,
+        invchead_terms_id,
+        invchead_freight,
+        invchead_misc_amount, invchead_misc_descrip, invchead_misc_accnt_id,
+        invchead_payment, invchead_paymentref,
+        invchead_notes, invchead_prj_id, invchead_curr_id,
+        invchead_taxzone_id,
+        invchead_shipchrg_id,
+        invchead_saletype_id, invchead_shipzone_id )
+      VALUES(_invcheadid,
+             pCustid, -1,
+             NULL, _c.cohead_orderdate,
+             FALSE, FALSE,
+             _invcnumber, _c.cobmisc_invcdate, _c.cobmisc_shipdate,
+             'MULTIPLE', '', '',
+             _c.cohead_billtoname, _c.cohead_billtoaddress1,
+             _c.cohead_billtoaddress2, _c.cohead_billtoaddress3,
+             _c.cohead_billtocity, _c.cohead_billtostate,
+             _c.cohead_billtozipcode, _c.cust_phone,
+             _c.cohead_billtocountry,
+             '', '', '', '', '', '', '', '', '',
+             _c.cohead_salesrep_id, COALESCE(_c.cohead_commission, 0),
+             _c.cohead_terms_id,
+             _c.cobmisc_freight,
+             _c.cobmisc_misc, CASE WHEN(_c.cobmisc_misc <> 0) THEN 'Multiple' ELSE '' END,
+             _c.cobmisc_misc_accnt_id,
+             _c.cobmisc_payment, '',
+             'Multiple Sales Order # Invoice', _c.cohead_prj_id, _c.cobmisc_curr_id,
+             _c.cobmisc_taxzone_id,
+             _c.cohead_shipchrg_id,
+             _c.cohead_saletype_id, _c.cohead_shipzone_id
+             );
+    _lastlinenumber := 1;
+    FOR _i IN SELECT cobmisc_id
+                FROM cobmisc
+                JOIN cohead   ON (cobmisc_cohead_id=cohead_id)
+                JOIN custinfo ON (cohead_cust_id=cust_id)
+                LEFT OUTER JOIN cntct ON (cust_cntct_id=cntct_id)
+               WHERE(NOT cobmisc_posted
+                 AND (cohead_cust_id=pCustid)
+                 AND (COALESCE(cohead_billtoname,'')         = COALESCE(_c.cohead_billtoname,''))
+                 AND (COALESCE(cohead_billtoaddress1,'')     = COALESCE(_c.cohead_billtoaddress1,''))
+                 AND (COALESCE(cohead_billtoaddress2,'')     = COALESCE(_c.cohead_billtoaddress2,''))
+                 AND (COALESCE(cohead_billtoaddress3,'')     = COALESCE(_c.cohead_billtoaddress3,''))
+                 AND (COALESCE(cohead_billtocity,'')         = COALESCE(_c.cohead_billtocity,''))
+                 AND (COALESCE(cohead_billtostate,'')        = COALESCE(_c.cohead_billtostate,''))
+                 AND (COALESCE(cohead_billtozipcode,'')      = COALESCE(_c.cohead_billtozipcode,''))
+                 AND (COALESCE(cntct_phone,'')               = COALESCE(_c.cust_phone,''))
+                 AND (COALESCE(cohead_billtocountry,'')      = COALESCE(_c.cohead_billtocountry,''))
+                 AND (COALESCE(cohead_salesrep_id, 0)        = COALESCE(_c.cohead_salesrep_id, 0))
+                 AND (COALESCE(cohead_commission, 0)         = COALESCE(_c.cohead_commission, 0))
+                 AND (COALESCE(cohead_terms_id, 0)           = COALESCE(_c.cohead_terms_id, 0))
+                 AND (COALESCE(cobmisc_misc_accnt_id, 0)     = COALESCE(_c.cobmisc_misc_accnt_id, 0))
+                 AND (COALESCE(cohead_prj_id, 0)             = COALESCE(_c.cohead_prj_id, 0))
+                 AND (COALESCE(cobmisc_curr_id, 0)           = COALESCE(_c.cobmisc_curr_id, 0))
+                 AND (COALESCE(cobmisc_taxzone_id, 0)        = COALESCE(_c.cobmisc_taxzone_id, 0))
+                 AND (COALESCE(cohead_saletype_id, 0)        = COALESCE(_c.cohead_saletype_id, 0))
+                 AND (COALESCE(cohead_shipzone_id, 0)        = COALESCE(_c.cohead_shipzone_id, 0))
+                ) LOOP
+
+    --  Create the Invoice Head tax
+        INSERT INTO invcheadtax(taxhist_parent_id, taxhist_taxtype_id, taxhist_tax_id, taxhist_basis, 
+                                taxhist_basis_tax_id, taxhist_sequence, taxhist_percent, taxhist_amount, taxhist_tax, taxhist_docdate)
+        SELECT _invcheadid,taxhist_taxtype_id, taxhist_tax_id, taxhist_basis, 
+               taxhist_basis_tax_id, taxhist_sequence, taxhist_percent, taxhist_amount, taxhist_tax, taxhist_docdate
+        FROM cobmisctax 
+        WHERE taxhist_parent_id = _i.cobmisc_id
+          AND taxhist_taxtype_id = getadjustmenttaxtypeid();
+
+    --  Give this selection a number if it has not been assigned one
+        UPDATE cobmisc
+           SET cobmisc_invcnumber=_invcnumber
+         WHERE(cobmisc_id=_i.cobmisc_id);
+      
+    --  Create the Invoice items
+        FOR _r IN SELECT coitem_id, coitem_linenumber, coitem_subnumber, coitem_custpn,
+                         coitem_qtyord, cobill_qty,
+                         coitem_qty_uom_id, coitem_qty_invuomratio,
+                         coitem_custprice, coitem_price,
+                         coitem_price_uom_id, coitem_price_invuomratio,
+                         coitem_memo,
+                         itemsite_item_id, itemsite_warehous_id,
+                         cobill_taxtype_id
+                    FROM cohead, coitem, cobill, itemsite
+                   WHERE((cobill_coitem_id=coitem_id)
+                     AND (cohead_id=coitem_cohead_id)
+                     AND (coitem_itemsite_id=itemsite_id)
+                     AND (cobill_cobmisc_id=_i.cobmisc_id) ) 
+                   ORDER BY cohead_number, coitem_linenumber, coitem_subnumber LOOP
+      
+          SELECT NEXTVAL('invcitem_invcitem_id_seq') INTO _invcitemid;
+          INSERT INTO invcitem
+          ( invcitem_id, invcitem_invchead_id,
+            invcitem_linenumber, invcitem_item_id, invcitem_warehous_id,
+            invcitem_custpn, invcitem_number, invcitem_descrip,
+            invcitem_ordered, invcitem_billed,
+            invcitem_qty_uom_id, invcitem_qty_invuomratio,
+            invcitem_custprice, invcitem_price,
+            invcitem_price_uom_id, invcitem_price_invuomratio,
+            invcitem_notes,
+            invcitem_taxtype_id,
+            invcitem_coitem_id )
+          VALUES
+          ( _invcitemid, _invcheadid,
+            _lastlinenumber,
+            _r.itemsite_item_id, _r.itemsite_warehous_id,
+            _r.coitem_custpn, '', '',
+            _r.coitem_qtyord, _r.cobill_qty,
+            _r.coitem_qty_uom_id, _r.coitem_qty_invuomratio,
+            _r.coitem_custprice, _r.coitem_price,
+            _r.coitem_price_uom_id, _r.coitem_price_invuomratio,
+            _r.coitem_memo,
+            _r.cobill_taxtype_id,
+            _r.coitem_id );
+          
+      --  Find and mark any Lot/Serial invdetail records associated with this bill
+          UPDATE invdetail SET invdetail_invcitem_id = _invcitemid
+           WHERE (invdetail_id IN (SELECT invdetail_id
+                                     FROM coitem, cohead, invhist, invdetail
+                                    WHERE ((coitem_cohead_id=cohead_id)
+                                      AND  (invdetail_invhist_id=invhist_id)
+                                      AND  (invhist_ordnumber = text(cohead_number||'-'||formatSoLineNumber(coitem_id)))
+                                      AND  (invdetail_invcitem_id IS NULL)
+                                      AND  (coitem_id=_r.coitem_id)) ) );
+      
+      --  Mark any shipped, uninvoiced shipitems for the current coitem as invoiced
+          _qtyToInvoice :=  _r.cobill_qty;
+          FOR _s IN SELECT shipitem.*, shipitem_qty = _r.cobill_qty AS matched
+                   FROM shipitem, shiphead
+                   WHERE ((shipitem_shiphead_id=shiphead_id)
+                     AND  (shipitem_orderitem_id=_r.coitem_id)
+                     AND  (shiphead_shipped)
+                     AND  (shiphead_order_type='SO')
+                     AND  (NOT shipitem_invoiced))
+                   ORDER BY matched DESC, shipitem_qty DESC FOR UPDATE LOOP
+            IF (_qtyToInvoice >= _s.shipitem_qty) THEN
+             UPDATE shipitem
+             SET shipitem_invoiced=TRUE, shipitem_invcitem_id=_invcitemid
+             WHERE (shipitem_id=_s.shipitem_id);
+             _qtyToInvoice := _qtyToInvoice - _s.shipitem_qty;
+            END IF;
+            IF (_qtyToInvoice <= 0) THEN
+             EXIT;
+            END IF;
+          END LOOP;
+
+          UPDATE cobill SET cobill_invcnum=cobmisc_invcnumber,
+                         cobill_invcitem_id=invcitem_id
+          FROM invcitem, coitem, cobmisc
+          WHERE ((invcitem_linenumber=_lastlinenumber )
+            AND  (coitem_id=cobill_coitem_id)
+            AND  (cobmisc_id=cobill_cobmisc_id)
+            AND  (cobill_cobmisc_id=_i.cobmisc_id)
+            AND  (invcitem_invchead_id=_invcheadid));
+
+
+          _lastlinenumber := _lastlinenumber + 1;
+          
+        END LOOP;
+  
+      --  Close all requested coitem's
+        IF ( ( SELECT cobmisc_closeorder
+               FROM cobmisc
+               WHERE (cobmisc_id=_i.cobmisc_id) ) ) THEN
+          UPDATE coitem
+          SET coitem_status='C'
+          FROM cobmisc
+          WHERE ( (coitem_status NOT IN ('C', 'X'))
+           AND (coitem_cohead_id=cobmisc_cohead_id)
+           AND (cobmisc_id=_i.cobmisc_id) );
+        ELSE
+          UPDATE coitem
+          SET coitem_status='C'
+          FROM cobill
+          WHERE ( (cobill_coitem_id=coitem_id)
+           AND (coitem_status <> 'X')
+           AND (cobill_toclose)
+           AND (cobill_cobmisc_id=_i.cobmisc_id) );
+        END IF;
+      
+      --  Mark the cobmisc as posted
+        UPDATE cobmisc
+        SET cobmisc_posted=TRUE, cobmisc_invchead_id=_invcheadid
+        WHERE (cobmisc_id=_i.cobmisc_id);
+    
+      --  All done
+        _count := (_count + 1);
+      END LOOP;
+    END IF;
+  END LOOP;
+  RETURN _count;
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/createinvoices.sql b/foundation-database/public/functions/createinvoices.sql
new file mode 100644 (file)
index 0000000..390213a
--- /dev/null
@@ -0,0 +1,77 @@
+
+CREATE OR REPLACE FUNCTION createInvoices() RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _counter INTEGER;
+  _cobmisc RECORD;
+
+BEGIN
+
+  _counter := 0;
+
+  FOR _cobmisc IN SELECT cobmisc_id
+                  FROM cobmisc
+                  WHERE (NOT cobmisc_posted) LOOP
+
+    PERFORM createinvoice(_cobmisc.cobmisc_id);
+    _counter := (_counter + 1);
+
+  END LOOP;
+
+  RETURN _counter;
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION createInvoices(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN createinvoices($1, false);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION createInvoices(INTEGER, BOOLEAN) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCustTypeId ALIAS FOR $1;
+  pConsolidate ALIAS FOR $2;
+  _counter INTEGER;
+  _tcounter INTEGER;
+  _cobmisc RECORD;
+
+BEGIN
+
+  _counter := 0;
+
+  IF (pConsolidate) THEN
+    FOR _cobmisc IN SELECT DISTINCT cust_id
+                      FROM cobmisc, cohead, custinfo
+                     WHERE((NOT cobmisc_posted)
+                       AND (cohead_id=cobmisc_cohead_id)
+                       AND (cust_id=cohead_cust_id)
+                       AND (cust_custtype_id=pCustTypeId)) LOOP
+
+      SELECT createinvoiceConsolidated(_cobmisc.cust_id)
+        INTO _tcounter;
+      _counter := (_counter + _tcounter);
+    END LOOP;
+  ELSE
+    FOR _cobmisc IN SELECT cobmisc_id
+                      FROM cobmisc, cohead, custinfo
+                     WHERE((NOT cobmisc_posted)
+                       AND (cohead_id=cobmisc_cohead_id)
+                       AND (cust_id=cohead_cust_id)
+                       AND (cust_custtype_id=pCustTypeId)) LOOP
+  
+      PERFORM createinvoice(_cobmisc.cobmisc_id);
+      _counter := (_counter + 1);
+
+    END LOOP;
+  END IF;
+
+  RETURN _counter;
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/createmiscapcheck.sql b/foundation-database/public/functions/createmiscapcheck.sql
new file mode 100644 (file)
index 0000000..04454cf
--- /dev/null
@@ -0,0 +1,18 @@
+CREATE OR REPLACE FUNCTION createMiscAPCheck(INTEGER, INTEGER, DATE, NUMERIC, INTEGER, TEXT, TEXT) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RAISE NOTICE ''createMiscAPCheck() is deprecated - use createCheck() instead'';
+  RETURN createCheck($1, ''V'', $2, $3, $4, baseCurrId(), $5, NULL, $6, $7, FALSE);
+END;
+' LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION createMiscAPCheck(INTEGER, INTEGER, DATE, NUMERIC, INTEGER, INTEGER, TEXT, TEXT) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RAISE NOTICE ''createMiscAPCheck() is deprecated - use createCheck() instead'';
+  RETURN createCheck($1, ''V'', $2, $3, pAmount, $5, $6, NULL, $7, $8, FALSE);
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/createpkgschema.sql b/foundation-database/public/functions/createpkgschema.sql
new file mode 100644 (file)
index 0000000..458d1bc
--- /dev/null
@@ -0,0 +1,87 @@
+CREATE OR REPLACE FUNCTION createPkgSchema(TEXT, TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pname         ALIAS FOR $1;
+  pcomment      ALIAS FOR $2;
+  _createtable  TEXT;
+  _debug        BOOL    := true;
+  _namespaceoid INTEGER := -1;
+  _tabs         TEXT[] := ARRAY['cmd',  'cmdarg', 'image',  'metasql',
+                                'priv', 'report', 'script', 'uiform'] ;
+  _pkgtab       TEXT;
+
+BEGIN
+  IF (LENGTH(COALESCE(pname, '')) <= 0) THEN
+    RAISE EXCEPTION 'Cannot create a schema for this package without a name.';
+  END IF;
+
+  SELECT oid INTO _namespaceoid
+  FROM pg_namespace
+  WHERE (LOWER(nspname)=LOWER(pname));
+  IF (NOT FOUND) THEN
+    EXECUTE 'CREATE SCHEMA ' || LOWER(pname);
+    EXECUTE 'GRANT ALL ON SCHEMA ' || LOWER(pname) || ' TO GROUP xtrole;';
+
+    SELECT oid INTO _namespaceoid
+    FROM pg_namespace
+    WHERE (LOWER(nspname)=LOWER(pname));
+  END IF;
+
+  FOR i IN ARRAY_LOWER(_tabs,1)..ARRAY_UPPER(_tabs,1) LOOP
+    _pkgtab := pname || '.pkg' || _tabs[i];
+
+    IF NOT EXISTS(SELECT oid
+                  FROM pg_class
+                  WHERE ((relname=_pkgtab)
+                     AND (relnamespace=_namespaceoid))) THEN
+      _createtable := 'CREATE TABLE ' || _pkgtab || ' () INHERITS (' || _tabs[i] || ');';
+      IF (_debug) THEN RAISE NOTICE '%', _createtable; END IF;
+      EXECUTE _createtable;
+
+      EXECUTE 'ALTER TABLE ' || _pkgtab ||
+              ' ALTER ' || _tabs[i] || '_id SET NOT NULL,' ||
+              ' ADD PRIMARY KEY (' || _tabs[i] || '_id),' ||
+              ' ALTER ' || _tabs[i] || '_id SET DEFAULT NEXTVAL(''' ||
+              _tabs[i] || '_' || _tabs[i] || '_id_seq'');';
+
+      EXECUTE 'REVOKE ALL ON ' || _pkgtab || ' FROM PUBLIC;';
+      EXECUTE 'GRANT  ALL ON ' || _pkgtab || ' TO GROUP xtrole;';
+
+      IF (_tabs[i] = 'cmdarg') THEN
+        EXECUTE 'ALTER TABLE ' || _pkgtab ||
+                ' ADD FOREIGN KEY (cmdarg_cmd_id) REFERENCES ' ||
+                pname || '.pkgcmd(cmd_id);';
+      END IF;
+
+      EXECUTE 'SELECT dropIfExists(''TRIGGER'', ''pkg' ||
+                                   _tabs[i] || 'beforetrigger'', ''' ||
+                                   pname || ''');' ;
+      EXECUTE 'CREATE TRIGGER pkg' || _tabs[i] || 'beforetrigger ' ||
+              'BEFORE INSERT OR UPDATE OR DELETE ON ' || _pkgtab ||
+              ' FOR EACH ROW EXECUTE PROCEDURE _pkg' || _tabs[i] || 'beforetrigger();';
+
+      EXECUTE 'SELECT dropIfExists(''TRIGGER'', ''pkg' ||
+                                   _tabs[i] || 'altertrigger'', ''' ||
+                                   pname || ''');' ;
+      EXECUTE 'CREATE TRIGGER pkg' || _tabs[i] || 'altertrigger ' ||
+              'BEFORE INSERT OR UPDATE OR DELETE ON ' || _pkgtab ||
+              ' FOR EACH ROW EXECUTE PROCEDURE _pkg' || _tabs[i] || 'altertrigger();';
+
+      EXECUTE 'SELECT dropIfExists(''TRIGGER'', ''pkg' ||
+                                   _tabs[i] || 'aftertrigger'', ''' ||
+                                   pname || ''');' ;
+      EXECUTE 'CREATE TRIGGER pkg' || _tabs[i] || 'aftertrigger ' ||
+              'AFTER INSERT OR UPDATE OR DELETE ON ' || _pkgtab ||
+              ' FOR EACH ROW EXECUTE PROCEDURE _pkg' || _tabs[i] || 'aftertrigger();';
+
+    END IF;
+  END LOOP;
+
+  EXECUTE 'COMMENT ON SCHEMA ' || quote_ident(pname) || ' IS ' ||
+           quote_literal(pcomment) || ';';
+
+  RETURN _namespaceoid;
+END;
+$$
+LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/createpr.sql b/foundation-database/public/functions/createpr.sql
new file mode 100644 (file)
index 0000000..34450fe
--- /dev/null
@@ -0,0 +1,241 @@
+CREATE OR REPLACE FUNCTION createPr(INTEGER, INTEGER, NUMERIC, DATE, TEXT, CHARACTER(1), INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pOrderNumber ALIAS FOR $1;
+  pItemsiteid ALIAS FOR $2;
+  pQty ALIAS FOR $3;
+  pDueDate ALIAS FOR $4;
+  pNotes ALIAS FOR $5;
+  pOrderType ALIAS FOR $6;
+  pOrderId ALIAS FOR $7;
+  _prid INTEGER;
+
+BEGIN
+
+  SELECT NEXTVAL('pr_pr_id_seq') INTO _prid;
+  INSERT INTO pr
+  ( pr_id, pr_number, pr_subnumber, pr_status,
+    pr_order_type, pr_order_id,
+    pr_itemsite_id, pr_qtyreq, pr_duedate, pr_releasenote )
+  VALUES
+  ( _prid, pOrderNumber, nextPrSubnumber(pOrderNumber), 'O',
+    pOrderType, pOrderId,
+    pItemsiteid, pQty, pDuedate, pNotes );
+
+  RETURN _prid;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION createPr(INTEGER, INTEGER, NUMERIC, DATE, TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pOrderNumber ALIAS FOR $1;
+  pItemsiteid ALIAS FOR $2;
+  pQty ALIAS FOR $3;
+  pDueDate ALIAS FOR $4;
+  pNotes ALIAS FOR $5;
+  _prid INTEGER;
+
+BEGIN
+
+  SELECT NEXTVAL('pr_pr_id_seq') INTO _prid;
+  INSERT INTO pr
+  ( pr_id, pr_number, pr_subnumber, pr_status,
+    pr_order_type, pr_order_id,
+    pr_itemsite_id, pr_qtyreq, pr_duedate, pr_releasenote )
+  VALUES
+  ( _prid, pOrderNumber, nextPrSubnumber(pOrderNumber), 'O',
+    'M', -1,
+    pItemsiteid, pQty, pDuedate, pNotes);
+
+  RETURN _prid;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION createPr(INTEGER, CHAR, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pOrderNumber ALIAS FOR $1;
+  pParentType ALIAS FOR $2;
+  pParentId ALIAS FOR $3;
+  _parent RECORD;
+  _prid INTEGER;
+  _orderNumber INTEGER;
+
+BEGIN
+
+  IF (pOrderNumber = -1) THEN
+    SELECT fetchPrNumber() INTO _orderNumber;
+  ELSE
+    _orderNumber := pOrderNumber;
+  END IF;
+
+  IF (pParentType = 'W') THEN
+    SELECT womatl_itemsite_id AS itemsiteid,
+           itemuomtouom(itemsite_item_id, womatl_uom_id, NULL, womatl_qtyreq) AS qty,
+           womatl_duedate AS duedate, wo_prj_id AS prjid,
+           womatl_notes AS notes INTO _parent
+    FROM wo, womatl, itemsite
+    WHERE ((womatl_wo_id=wo_id)
+     AND (womatl_itemsite_id=itemsite_id)
+     AND (womatl_id=pParentId));
+
+  ELSIF (pParentType = 'S') THEN
+    SELECT coitem_itemsite_id AS itemsiteid,
+           (coitem_qtyord - coitem_qtyshipped + coitem_qtyreturned) AS qty,
+           coitem_scheddate AS duedate, cohead_prj_id AS prjid,
+           coitem_memo AS notes INTO _parent
+    FROM coitem, cohead
+    WHERE ((cohead_id=coitem_cohead_id)
+     AND (coitem_id=pParentId));
+
+  ELSIF (pParentType = 'F') THEN
+    SELECT planord_itemsite_id AS itemsiteid,
+           planord_qty AS qty,
+           planord_duedate AS duedate, NULL::INTEGER AS prjid,
+           planord_comments AS notes INTO _parent
+    FROM planord
+    WHERE (planord_id=pParentId);
+
+  ELSE
+    RETURN -2;
+  END IF;
+
+  IF (NOT FOUND) THEN
+    RETURN -1;
+  END IF;
+
+  SELECT NEXTVAL('pr_pr_id_seq') INTO _prid;
+  INSERT INTO pr
+  ( pr_id, pr_number, pr_subnumber, pr_status,
+    pr_order_type, pr_order_id, pr_prj_id,
+    pr_itemsite_id, pr_qtyreq,
+    pr_duedate, pr_releasenote )
+  VALUES
+  ( _prid, _orderNumber, nextPrSubnumber(_orderNumber), 'O',
+    pParentType, pParentId, _parent.prjid,
+    _parent.itemsiteid, validateOrderQty(_parent.itemsiteid, _parent.qty, TRUE),
+    _parent.duedate, _parent.notes );
+
+  RETURN _prid;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION createPr(CHAR, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pParentType ALIAS FOR $1;
+  pParentId ALIAS FOR $2;
+  _orderNumber INTEGER;
+  _prid INTEGER;
+
+BEGIN
+
+  IF (pParentType = 'W') THEN
+    SELECT wo_number INTO _orderNumber
+    FROM wo, womatl
+    WHERE ((womatl_wo_id=wo_id)
+     AND (womatl_id=pParentId));
+
+  ELSIF (pParentType = 'S') THEN
+    SELECT CAST(cohead_number AS INTEGER) INTO _orderNumber
+    FROM cohead, coitem
+    WHERE ((coitem_cohead_id=cohead_id)
+     AND (coitem_id=pParentId));
+
+  ELSIF (pParentType = 'F') THEN
+    SELECT fetchPrNumber() INTO _orderNumber;
+
+  ELSE
+    RETURN -2;
+  END IF;
+
+  IF (NOT FOUND) THEN
+    RETURN -1;
+  END IF;
+
+  SELECT createPr(_orderNumber, pParentType, pParentId) INTO _prid;
+
+  RETURN _prid;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION createpr(INTEGER, CHARACTER, INTEGER, TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pOrderNumber ALIAS FOR $1;
+  pParentType ALIAS FOR $2;
+  pParentId ALIAS FOR $3;
+  pParentNotes ALIAS FOR $4;
+  _parent RECORD;
+  _prid INTEGER;
+  _orderNumber INTEGER;
+
+BEGIN
+
+  IF (pOrderNumber = -1) THEN
+    SELECT fetchPrNumber() INTO _orderNumber;
+  ELSE
+    _orderNumber := pOrderNumber;
+  END IF;
+
+  IF (pParentType = 'W') THEN
+    SELECT womatl_itemsite_id AS itemsiteid,
+           itemuomtouom(itemsite_item_id, womatl_uom_id, NULL, womatl_qtyreq) AS qty,
+           womatl_duedate AS duedate, wo_prj_id AS prjid INTO _parent
+    FROM wo, womatl, itemsite
+    WHERE ((womatl_wo_id=wo_id)
+     AND (womatl_itemsite_id=itemsite_id)
+     AND (womatl_id=pParentId));
+
+  ELSIF (pParentType = 'S') THEN
+    SELECT coitem_itemsite_id AS itemsiteid,
+           (coitem_qtyord - coitem_qtyshipped + coitem_qtyreturned) AS qty,
+           coitem_scheddate AS duedate, cohead_prj_id AS prjid INTO _parent
+    FROM coitem, cohead
+    WHERE ((cohead_id=coitem_cohead_id)
+     AND (coitem_id=pParentId));
+
+  ELSIF (pParentType = 'F') THEN
+    SELECT planord_itemsite_id AS itemsiteid,
+           planord_qty AS qty,
+           planord_duedate AS duedate, NULL::INTEGER AS prjid 
+           INTO _parent
+    FROM planord
+    WHERE (planord_id=pParentId);
+
+  ELSE
+    RETURN -2;
+  END IF;
+
+  IF (NOT FOUND) THEN
+    RETURN -1;
+  END IF;
+
+  SELECT NEXTVAL('pr_pr_id_seq') INTO _prid;
+  INSERT INTO pr
+  ( pr_id, pr_number, pr_subnumber, pr_status,
+    pr_order_type, pr_order_id, pr_prj_id,
+    pr_itemsite_id, pr_qtyreq, pr_duedate, pr_releasenote )
+  VALUES
+  ( _prid, _orderNumber, nextPrSubnumber(_orderNumber), 'O',
+    pParentType, pParentId, _parent.prjid,
+    _parent.itemsiteid, validateOrderQty(_parent.itemsiteid, _parent.qty, TRUE),
+    _parent.duedate, pParentNotes );
+
+  RETURN _prid;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/createpriv.sql b/foundation-database/public/functions/createpriv.sql
new file mode 100644 (file)
index 0000000..1d6754c
--- /dev/null
@@ -0,0 +1,31 @@
+
+CREATE OR REPLACE FUNCTION createPriv(text, text, text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pModule ALIAS FOR $1;
+  pName   ALIAS FOR $2;
+  pDesc   ALIAS FOR $3;
+  _id     INTEGER;
+BEGIN
+
+  SELECT priv_id
+    INTO _id
+    FROM priv
+   WHERE(priv_name=pName);
+
+  IF (FOUND) THEN
+    UPDATE priv
+       SET priv_module=pModule,
+           priv_descrip=pDesc
+     WHERE(priv_id=_id);
+  ELSE
+    SELECT nextval(''priv_priv_id_seq'') INTO _id;
+    INSERT INTO priv
+          (priv_id, priv_module, priv_name, priv_descrip)
+    VALUES(_id, pModule, pName, pDesc);
+  END IF;
+
+  RETURN _id;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/createpurchasetosale.sql b/foundation-database/public/functions/createpurchasetosale.sql
new file mode 100644 (file)
index 0000000..4a9caa7
--- /dev/null
@@ -0,0 +1,327 @@
+SELECT dropIfExists('FUNCTION', 'createPurchaseToSale(integer, integer, boolean)', 'fixcountry');
+SELECT dropIfExists('FUNCTION', 'createPurchaseToSale(integer, integer, boolean, numeric)', 'fixcountry');
+SELECT dropIfExists('FUNCTION', 'createPurchaseToSale(integer, integer, boolean, numeric, date, numeric)', 'fixcountry');
+
+CREATE OR REPLACE FUNCTION createPurchaseToSale(INTEGER, INTEGER, BOOLEAN) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCoitemId ALIAS FOR $1;
+  pItemSourceId ALIAS FOR $2;
+  pDropShip ALIAS FOR $3;
+
+BEGIN
+
+  RETURN createPurchaseToSale(pCoitemId, pItemSourceId, pDropShip, NULL, NULL, NULL);
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION createPurchaseToSale(INTEGER, INTEGER, BOOLEAN, NUMERIC) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCoitemId ALIAS FOR $1;
+  pItemSourceId ALIAS FOR $2;
+  pDropShip ALIAS FOR $3;
+  pPrice ALIAS FOR $4;
+
+BEGIN
+
+  RETURN createPurchaseToSale(pCoitemId, pItemSourceId, pDropShip, NULL, NULL, pPrice);
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION createPurchaseToSale(INTEGER, INTEGER, BOOLEAN, NUMERIC, DATE, NUMERIC) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCoitemId ALIAS FOR $1;
+  pItemSourceId ALIAS FOR $2;
+  pDropShip ALIAS FOR $3;
+  pQty ALIAS FOR $4;
+  pDueDate ALIAS FOR $5;
+  pPrice ALIAS FOR $6;
+
+  _s RECORD;
+  _w RECORD;
+  _i RECORD;
+  _shipto RECORD;
+  _poheadid INTEGER := -1;
+  _poitemid INTEGER := -1;
+  _taxtypeid INTEGER := -1;
+  _polinenumber INTEGER;
+  _ponumber NUMERIC;
+  _price NUMERIC;
+  _temp INTEGER;
+
+BEGIN
+
+  -- Check for existing poitem for this coitem
+  SELECT poitem_id INTO _poitemid
+  FROM poitem
+  WHERE (poitem_order_id=pCoitemId)
+    AND (poitem_order_type='S');
+  IF (FOUND) THEN
+    RETURN _poitemid;
+  END IF;
+
+  SELECT *,
+         COALESCE(roundQty(item_fractional, (coitem_qtyord * coitem_qty_invuomratio)), 0.0) AS orderqty
+  INTO _s
+  FROM cohead JOIN coitem ON (cohead_id = coitem_cohead_id)
+    LEFT OUTER JOIN shiptoinfo ON (cohead_shipto_id = shipto_id)
+    LEFT OUTER JOIN itemsite ON (coitem_itemsite_id = itemsite_id)
+    LEFT OUTER JOIN item ON (item_id = itemsite_item_id)
+  WHERE (coitem_id = pCoitemId);
+  IF (NOT FOUND) THEN
+    RETURN -1;
+  END IF;
+
+  SELECT * INTO _w
+  FROM whsinfo JOIN addr ON (warehous_addr_id = addr_id)
+    JOIN cntct ON (warehous_cntct_id = cntct_id)
+    JOIN itemsite ON (warehous_id = itemsite_warehous_id)
+  WHERE (itemsite_id = _s.itemsite_id);
+
+  SELECT * INTO _i
+  FROM itemsrc JOIN vendinfo ON (itemsrc_vend_id = vend_id)
+    LEFT OUTER JOIN cntct ON (vend_cntct1_id = cntct_id)
+    LEFT OUTER JOIN addr ON (vend_addr_id = addr_id)
+  WHERE (itemsrc_id = pItemSourceId);
+  IF (NOT FOUND) THEN
+    RETURN -2;
+  END IF;
+
+  SELECT * INTO _shipto
+  FROM shiptoinfo JOIN cntct ON (shipto_cntct_id = cntct_id)
+    JOIN addr ON (shipto_addr_id = addr_id)
+    RIGHT OUTER JOIN cohead ON (cohead_cust_id = shipto_cust_id)
+  WHERE (cohead_id = _s.cohead_id)
+  LIMIT 1;
+
+  IF (pDropShip) THEN
+    SELECT COALESCE(pohead_id, -1) INTO _temp
+    FROM pohead
+    WHERE ( (pohead_status = 'U')
+      AND (pohead_vend_id = _i.itemsrc_vend_id)
+      AND (pohead_shiptoaddress_id = _s.shipto_addr_id) );
+  ELSE
+    SELECT COALESCE(pohead_id, -1) INTO _temp
+    FROM pohead
+    WHERE ( (pohead_status = 'U')
+      AND (pohead_vend_id = _i.itemsrc_vend_id)
+      AND (pohead_shiptoaddress_id = _w.addr_id) );
+  END IF;
+
+  IF (FOUND) THEN
+    _poheadid := _temp;
+    UPDATE pohead
+    SET pohead_dropship = pDropShip
+    WHERE (pohead_id = _poheadid);
+  ELSE
+    SELECT NEXTVAL('pohead_pohead_id_seq') INTO _poheadid;
+    SELECT fetchPoNumber() INTO _ponumber;
+
+    IF (pDropShip) THEN
+      INSERT INTO pohead
+        ( pohead_id, pohead_number, pohead_status, pohead_dropship,
+          pohead_agent_username, pohead_vend_id, pohead_taxzone_id,
+          pohead_orderdate, pohead_curr_id, pohead_cohead_id,
+          pohead_warehous_id, pohead_shipvia,
+          pohead_terms_id, pohead_shipto_cntct_id,
+          pohead_shipto_cntct_honorific, pohead_shipto_cntct_first_name,
+          pohead_shipto_cntct_middle, pohead_shipto_cntct_last_name,
+          pohead_shipto_cntct_suffix, pohead_shipto_cntct_phone,
+          pohead_shipto_cntct_title, pohead_shipto_cntct_fax, 
+          pohead_shipto_cntct_email, pohead_shiptoaddress_id,
+          pohead_shiptoaddress1,
+          pohead_shiptoaddress2,
+          pohead_shiptoaddress3,
+          pohead_shiptocity, 
+          pohead_shiptostate, pohead_shiptozipcode,
+          pohead_shiptocountry, pohead_vend_cntct_id,
+          pohead_vend_cntct_honorific, pohead_vend_cntct_first_name,
+          pohead_vend_cntct_middle, pohead_vend_cntct_last_name,
+          pohead_vend_cntct_suffix, pohead_vend_cntct_phone,
+          pohead_vend_cntct_title, pohead_vend_cntct_fax,
+          pohead_vend_cntct_email, pohead_vendaddress1,
+          pohead_vendaddress2, pohead_vendaddress3,
+          pohead_vendcity, pohead_vendstate,
+          pohead_vendzipcode, pohead_vendcountry, pohead_comments )
+      VALUES
+        ( _poheadid, _ponumber, 'U', pDropShip,
+          getEffectiveXtUser(), _i.itemsrc_vend_id, _i.vend_taxzone_id,
+         CURRENT_DATE, COALESCE(_i.vend_curr_id, basecurrid()), _s.cohead_id,
+          COALESCE(_s.cohead_warehous_id, -1), COALESCE(_i.vend_shipvia, TEXT('')),
+          COALESCE(_i.vend_terms_id, -1), COALESCE(_s.cohead_shipto_cntct_id, _shipto.shipto_cntct_id),
+          COALESCE(_s.cohead_shipto_cntct_honorific, _shipto.cntct_honorific), COALESCE(_s.cohead_shipto_cntct_first_name, _shipto.cntct_first_name),
+          COALESCE(_s.cohead_shipto_cntct_middle, _shipto.cntct_middle), COALESCE(_s.cohead_shipto_cntct_last_name, _shipto.cntct_last_name),
+          COALESCE(_s.cohead_shipto_cntct_suffix, _shipto.cntct_suffix), COALESCE(_s.cohead_shipto_cntct_phone, _shipto.cntct_phone),
+          COALESCE(_s.cohead_shipto_cntct_title, _shipto.cntct_title), COALESCE(_s.cohead_shipto_cntct_fax, _shipto.cntct_fax),
+          COALESCE(_s.cohead_shipto_cntct_email, _shipto.cntct_email), COALESCE(_s.shipto_addr_id, _shipto.addr_id),
+          COALESCE(_s.cohead_shiptoaddress1, _shipto.addr_line1),
+          COALESCE(_s.cohead_shiptoaddress2, _shipto.addr_line2),
+          COALESCE(_s.cohead_shiptoaddress3, _shipto.addr_line3),
+          COALESCE(_s.cohead_shiptocity, _shipto.addr_city),
+          COALESCE(_s.cohead_shiptostate, _shipto.addr_state), COALESCE(_s.cohead_shiptozipcode, _shipto.addr_postalcode),
+          COALESCE(_s.cohead_shiptocountry, _shipto.addr_country), _i.cntct_id,
+          COALESCE(_i.cntct_honorific, TEXT('')), COALESCE(_i.cntct_first_name, TEXT('')),
+          COALESCE(_i.cntct_middle, TEXT('')), COALESCE(_i.cntct_last_name, TEXT('')),
+          COALESCE(_i.cntct_suffix, TEXT('')), COALESCE(_i.cntct_phone, TEXT('')),
+          COALESCE(_i.cntct_title, TEXT('')), COALESCE(_i.cntct_fax, TEXT('')),
+          COALESCE(_i.cntct_email, TEXT('')), COALESCE(_i.addr_line1, TEXT('')),
+          COALESCE(_i.addr_line2, TEXT('')), COALESCE(_i.addr_line3, TEXT('')),
+          COALESCE(_i.addr_city, TEXT('')), COALESCE(_i.addr_state, TEXT('')),
+          COALESCE(_i.addr_postalcode, TEXT('')), COALESCE(_i.addr_country, TEXT('')), COALESCE(_s.cohead_shipcomments, TEXT('')) );
+    ELSE
+      INSERT INTO pohead
+        ( pohead_id, pohead_number, pohead_status, pohead_dropship,
+          pohead_agent_username, pohead_vend_id, pohead_taxzone_id,
+          pohead_orderdate, pohead_curr_id, pohead_cohead_id,
+          pohead_warehous_id, pohead_shipvia,
+          pohead_terms_id, pohead_shipto_cntct_id,
+          pohead_shipto_cntct_honorific, pohead_shipto_cntct_first_name,
+          pohead_shipto_cntct_middle, pohead_shipto_cntct_last_name,
+          pohead_shipto_cntct_suffix, pohead_shipto_cntct_phone,
+          pohead_shipto_cntct_title, pohead_shipto_cntct_fax, 
+          pohead_shipto_cntct_email, pohead_shiptoaddress_id,
+          pohead_shiptoaddress1,
+          pohead_shiptoaddress2,
+          pohead_shiptoaddress3,
+          pohead_shiptocity, 
+          pohead_shiptostate, pohead_shiptozipcode,
+          pohead_shiptocountry, pohead_vend_cntct_id,
+          pohead_vend_cntct_honorific, pohead_vend_cntct_first_name,
+          pohead_vend_cntct_middle, pohead_vend_cntct_last_name,
+          pohead_vend_cntct_suffix, pohead_vend_cntct_phone,
+          pohead_vend_cntct_title, pohead_vend_cntct_fax,
+          pohead_vend_cntct_email, pohead_vendaddress1,
+          pohead_vendaddress2, pohead_vendaddress3,
+          pohead_vendcity, pohead_vendstate,
+          pohead_vendzipcode, pohead_vendcountry )
+      VALUES
+        ( _poheadid, _ponumber, 'U', pDropShip,
+          getEffectiveXtUser(), _i.itemsrc_vend_id, _i.vend_taxzone_id,
+         CURRENT_DATE, COALESCE(_i.vend_curr_id, basecurrid()), _s.cohead_id,
+          COALESCE(_s.cohead_warehous_id, -1), COALESCE(_i.vend_shipvia, TEXT('')),
+          COALESCE(_i.vend_terms_id, -1), _w.cntct_id,
+          _w.cntct_honorific, _w.cntct_first_name,
+          _w.cntct_middle, _w.cntct_last_name,
+          _w.cntct_suffix, _w.cntct_phone,
+          _w.cntct_title, _w.cntct_fax,
+          _w.cntct_email, _w.addr_id,
+          _w.addr_line1,
+          _w.addr_line2,
+          _w.addr_line3,
+          _w.addr_city,
+          _w.addr_state, _w.addr_postalcode,
+          _w.addr_country, _i.cntct_id,
+          COALESCE(_i.cntct_honorific, TEXT('')), COALESCE(_i.cntct_first_name, TEXT('')),
+          COALESCE(_i.cntct_middle, TEXT('')), COALESCE(_i.cntct_last_name, TEXT('')),
+          COALESCE(_i.cntct_suffix, TEXT('')), COALESCE(_i.cntct_phone, TEXT('')),
+          COALESCE(_i.cntct_title, TEXT('')), COALESCE(_i.cntct_fax, TEXT('')),
+          COALESCE(_i.cntct_email, TEXT('')), COALESCE(_i.addr_line1, TEXT('')),
+          COALESCE(_i.addr_line2, TEXT('')), COALESCE(_i.addr_line3, TEXT('')),
+          COALESCE(_i.addr_city, TEXT('')), COALESCE(_i.addr_state, TEXT('')),
+          COALESCE(_i.addr_postalcode, TEXT('')), COALESCE(_i.addr_country, TEXT('')) );
+    END IF;
+  END IF;
+
+  SELECT NEXTVAL('poitem_poitem_id_seq') INTO _poitemid;
+
+  SELECT (COALESCE(MAX(poitem_linenumber), 0) + 1) INTO _polinenumber
+  FROM poitem
+  WHERE (poitem_pohead_id = _poheadid);
+
+  SELECT COALESCE(itemtax_taxtype_id, -1) INTO _taxtypeid
+  FROM itemtax
+  WHERE (itemtax_item_id = _i.itemsrc_item_id);
+
+  IF (pPrice IS NULL) THEN
+    SELECT itemsrcPrice(pItemSourceId, COALESCE(_s.cohead_warehous_id, -1), pDropShip,
+                        COALESCE(pQty, _s.orderqty), COALESCE(_i.vend_curr_id, baseCurrId()), CURRENT_DATE) INTO _price;
+  ELSE
+    _price := pPrice;
+  END IF;
+  raise notice '_price=%', _price;
+
+  IF (pDropShip) THEN
+    INSERT INTO poitem
+      ( poitem_id, poitem_status, poitem_pohead_id, poitem_linenumber, 
+        poitem_duedate, poitem_itemsite_id,
+        poitem_vend_item_descrip, poitem_vend_uom,
+        poitem_invvenduomratio, poitem_qty_ordered, 
+        poitem_unitprice, poitem_vend_item_number, 
+        poitem_itemsrc_id, poitem_order_id, poitem_order_type, poitem_prj_id, poitem_stdcost, 
+        poitem_manuf_name, poitem_manuf_item_number, 
+        poitem_manuf_item_descrip, poitem_taxtype_id, poitem_comments )
+    VALUES
+      ( _poitemid, 'U', _poheadid, _polinenumber,
+        COALESCE(pDueDate, _s.coitem_scheddate), _s.coitem_itemsite_id,
+        COALESCE(_i.itemsrc_vend_item_descrip, TEXT('')), COALESCE(_i.itemsrc_vend_uom, TEXT('')),
+        COALESCE(_i.itemsrc_invvendoruomratio, 1.00), (COALESCE(pQty, _s.orderqty) / COALESCE(_i.itemsrc_invvendoruomratio, 1.00)),
+        _price, COALESCE(_i.itemsrc_vend_item_number, TEXT('')),
+        pItemSourceId, pCoitemId, 'S', _s.cohead_prj_id, stdcost(_i.itemsrc_item_id),
+        COALESCE(_i.itemsrc_manuf_name, TEXT('')), COALESCE(_i.itemsrc_manuf_item_number, TEXT('')),
+        COALESCE(_i.itemsrc_manuf_item_descrip, TEXT('')), _taxtypeid,
+        COALESCE(_s.coitem_memo, TEXT('')));
+  ELSE
+    INSERT INTO poitem
+      ( poitem_id, poitem_status, poitem_pohead_id, poitem_linenumber, 
+        poitem_duedate, poitem_itemsite_id,
+        poitem_vend_item_descrip, poitem_vend_uom,
+        poitem_invvenduomratio, poitem_qty_ordered, 
+        poitem_unitprice, poitem_vend_item_number, 
+        poitem_itemsrc_id, poitem_order_id, poitem_order_type, poitem_prj_id, poitem_stdcost, 
+        poitem_manuf_name, poitem_manuf_item_number, 
+        poitem_manuf_item_descrip, poitem_taxtype_id )
+    VALUES
+      ( _poitemid, 'U', _poheadid, _polinenumber,
+        COALESCE(pDueDate, _s.coitem_scheddate), _s.coitem_itemsite_id,
+        COALESCE(_i.itemsrc_vend_item_descrip, TEXT('')), COALESCE(_i.itemsrc_vend_uom, TEXT('')),
+        COALESCE(_i.itemsrc_invvendoruomratio, 1.00), (COALESCE(pQty, _s.orderqty) / COALESCE(_i.itemsrc_invvendoruomratio, 1.00)),
+        _price, COALESCE(_i.itemsrc_vend_item_number, TEXT('')),
+        pItemSourceId, pCoitemId, 'S', _s.cohead_prj_id, stdcost(_i.itemsrc_item_id),
+        COALESCE(_i.itemsrc_manuf_name, TEXT('')), COALESCE(_i.itemsrc_manuf_item_number, TEXT('')),
+        COALESCE(_i.itemsrc_manuf_item_descrip, TEXT('')), _taxtypeid );
+  END IF;
+  -- Copy characteristics from the coitem to the poitem
+  INSERT INTO charass
+    ( charass_target_type, charass_target_id, charass_char_id,
+      charass_value, charass_default, charass_price )
+  SELECT 'PI', _poitemid, charass_char_id,
+         charass_value, charass_default, charass_price
+  FROM charass
+  WHERE ( (charass_target_type='SI')
+    AND   (charass_target_id=pCoitemId) );
+
+  UPDATE coitem
+  SET coitem_order_type = 'P',
+      coitem_order_id = _poitemid
+  WHERE ( coitem_id = pCoitemId );
+
+  -- Generate the PoItemCreatedBySo event notice
+  INSERT INTO evntlog
+              ( evntlog_evnttime, evntlog_username, evntlog_evnttype_id,
+                evntlog_ordtype, evntlog_ord_id, evntlog_warehous_id,
+                evntlog_number )
+  SELECT CURRENT_TIMESTAMP, evntnot_username, evnttype_id ,
+         'P', poitem_id, itemsite_warehous_id,
+         (pohead_number || '-' || poitem_linenumber || ': ' || item_number)
+  FROM evntnot JOIN evnttype ON (evntnot_evnttype_id=evnttype_id)
+       JOIN itemsite ON (evntnot_warehous_id=itemsite_warehous_id)
+       JOIN item ON (itemsite_item_id=item_id)
+       JOIN poitem ON (poitem_itemsite_id=itemsite_id)
+       JOIN pohead ON (poitem_pohead_id=pohead_id)
+  WHERE ( (poitem_id=_poitemid)
+    AND (poitem_duedate <= (CURRENT_DATE + itemsite_eventfence))
+    AND (evnttype_name='PoItemCreatedBySo') );
+
+  RETURN _poitemid;
+
+END;
+$$ LANGUAGE 'plpgsql' VOLATILE;
diff --git a/foundation-database/public/functions/createrecurringinvoices.sql b/foundation-database/public/functions/createrecurringinvoices.sql
new file mode 100644 (file)
index 0000000..4db87e5
--- /dev/null
@@ -0,0 +1,9 @@
+CREATE OR REPLACE FUNCTION createRecurringInvoices() RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RAISE NOTICE 'createRecurringInvoices() has been deprecated; use createRecurringItems(NULL, ''I'') instead.';
+
+  RETURN createRecurringItems(NULL, 'I');
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/createrecurringitems.sql b/foundation-database/public/functions/createrecurringitems.sql
new file mode 100644 (file)
index 0000000..50e3937
--- /dev/null
@@ -0,0 +1,136 @@
+CREATE OR REPLACE FUNCTION createRecurringItems(INTEGER, TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pParentid  ALIAS FOR $1;      -- if NULL then all items with the given pType
+  pType      TEXT := UPPER($2); -- if NULL then all types
+                                -- if both are null then all items of all types
+  _copystmt  TEXT;
+  _count     INTEGER := 0;
+  _countstmt TEXT;
+  _existcnt  INTEGER;
+  _id        INTEGER;
+  _interval  TEXT;
+  _last      TIMESTAMP WITH TIME ZONE;
+  _loopcount INTEGER := 1;
+  _maxstmt   TEXT;
+  _maxdate   TIMESTAMP WITH TIME ZONE := endOfTime();
+  _result    INTEGER := 0;
+  _next      TIMESTAMP WITH TIME ZONE;
+  _r         RECORD;
+  _rt        RECORD;
+  _tmp       INTEGER;
+
+BEGIN
+  RAISE DEBUG 'createRecurringItems(%, %) entered', pParentid, pType;
+
+  FOR _r IN SELECT *
+              FROM recur
+             WHERE ((COALESCE(recur_end, endOfTime()) >= CURRENT_TIMESTAMP)
+                AND (pParentid IS NULL OR recur_parent_id=pParentid)
+                AND (pType IS NULL OR UPPER(recur_parent_type)=UPPER(pType))) LOOP
+
+    RAISE DEBUG 'createRecurringItems looking at recur %, %',
+                _r.recur_id, _r.recur_parent_type;
+    _r.recur_max := COALESCE(_r.recur_max,
+                             CAST(fetchMetricValue('RecurringInvoiceBuffer') AS INTEGER),
+                             1);
+    _interval := CASE _r.recur_period WHEN 'Y' THEN ' year'
+                                      WHEN 'M' THEN ' month'
+                                      WHEN 'W' THEN ' week'
+                                      WHEN 'D' THEN ' day'
+                                      WHEN 'H' THEN ' hour'
+                                      WHEN 'm' THEN ' minute'
+                                      ELSE NULL
+                 END;
+
+    IF (_interval IS NULL OR COALESCE(_r.recur_freq, 0) <= 0) THEN
+      RAISE EXCEPTION 'Unknown recurrence frequency % % ON % %',
+                      _r.recur_freq,        _r.recur_period,
+                      _r.recur_parent_type, _r.recur_parent_id;
+    END IF;
+
+    SELECT * INTO _rt FROM recurtype WHERE (UPPER(recurtype_type)=UPPER(pType));
+    GET DIAGNOSTICS _count = ROW_COUNT;
+    IF (_count <= 0) THEN
+      RETURN -10;
+    END IF;
+
+    -- if the recurrence type has a max lookahead window, use it
+    IF (_r.recur_parent_type = 'I') THEN
+      _maxdate := CURRENT_TIMESTAMP + CAST(fetchMetricText('RecurringInvoiceBuffer') || ' days' AS INTERVAL);
+    END IF;
+    IF (_r.recur_parent_type = 'V') THEN
+      _maxdate := CURRENT_TIMESTAMP + CAST(fetchMetricText('RecurringVoucherBuffer') || ' days' AS INTERVAL);
+    END IF;
+    IF (_maxdate > _r.recur_end) THEN   -- if recur_end is null, _maxdate is ok
+      _maxdate = _r.recur_end;
+    END IF;
+
+    -- build statements dynamically from the recurtype table because packages
+    -- might also require recurring items. this way the algorithm is fixed
+    -- and the details are data-driven
+    _countstmt := 'SELECT COUNT(*) FROM [fulltable]' 
+               || ' WHERE (($1=[table]_recurring_[table]_id)'
+               || ' AND NOT([done]) '
+               || ' AND ([limit]));';
+    _countstmt := REPLACE(_countstmt, '[fulltable]', _rt.recurtype_table);
+    _countstmt := REPLACE(_countstmt, '[table]',
+                          REGEXP_REPLACE(_rt.recurtype_table, E'.*\\.', ''));
+    _countstmt := REPLACE(_countstmt, '[done]',  _rt.recurtype_donecheck);
+    _countstmt := REPLACE(_countstmt, '[limit]',
+                          COALESCE(_rt.recurtype_limit, 'TRUE'));
+
+    _maxstmt := 'SELECT MAX([schedcol]) FROM [fulltable]'
+               || ' WHERE (($1=[table]_recurring_[table]_id)'
+               || '    AND ([limit]));';
+    _maxstmt := REPLACE(_maxstmt, '[schedcol]', _rt.recurtype_schedcol);
+    _maxstmt := REPLACE(_maxstmt, '[fulltable]',_rt.recurtype_table);
+    _maxstmt := REPLACE(_maxstmt, '[table]',
+                          REGEXP_REPLACE(_rt.recurtype_table, E'.*\\.', ''));
+    _maxstmt := REPLACE(_maxstmt, '[limit]', COALESCE(_rt.recurtype_limit,
+                                                     'TRUE'));
+
+    _copystmt := 'SELECT [copy]($1, [datetime] [more]);';
+    _copystmt := REPLACE(_copystmt, '[copy]', _rt.recurtype_copyfunc);
+    _copystmt := REPLACE(_copystmt, '[datetime]',
+                         CASE WHEN UPPER(_rt.recurtype_copyargs[2])='DATE' THEN
+                                    'CAST(''$2'' AS DATE)'
+                              ELSE '''$2''' END);
+    -- 8.4+:
+    -- _copystmt := REPLACE(_copystmt, '[more]',
+    --                      REPEAT(', NULL',
+    --                             array_length(_rt.recurtype_copyargs) - 2));
+    _tmp := CAST(REPLACE(REGEXP_REPLACE(array_dims(_rt.recurtype_copyargs),
+                                        '.*:', ''), ']', '') AS INTEGER);
+    _copystmt := REPLACE(_copystmt, '[more]', REPEAT(', NULL', _tmp - 2));
+
+    EXECUTE REPLACE(_countstmt, '$1', _r.recur_parent_id::TEXT) INTO _existcnt;
+    EXECUTE REPLACE(_maxstmt,   '$1', _r.recur_parent_id::TEXT) INTO _last;
+    RAISE DEBUG E'% got %, % got %', _countstmt, _existcnt, _maxstmt, _last;
+
+    _next := _last;
+    _loopcount := 1;
+    WHILE (_existcnt < _r.recur_max AND _next < _maxdate) LOOP
+      _next := _last +
+               CAST(_r.recur_freq * _loopcount || _interval AS INTERVAL);
+      RAISE DEBUG 'createrecurringitems looping, existcnt = %, max = %, is % between % and %?',
+                  _existcnt, _r.recur_max, _next, _r.recur_start, _r.recur_end;
+
+      IF (_next BETWEEN _r.recur_start AND _maxdate) THEN
+        RAISE DEBUG 'createrecurringitems executing % with % and %',
+                    _copystmt, _r.recur_parent_id, _next;
+        -- 8.4+: EXECUTE _copystmt INTO _id USING _r.recur_parent_id, _next;
+        EXECUTE REPLACE(REPLACE(_copystmt, '$1', _r.recur_parent_id::TEXT),
+                                           '$2', _next::TEXT) INTO _id;
+        RAISE DEBUG 'Copying for % returned %', _next, _id;
+        _result   := _result   + 1;
+        _existcnt := _existcnt + 1;
+      END IF;
+      _loopcount := _loopcount + 1;
+    END LOOP;
+  END LOOP;
+
+  RETURN _result;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/createtodoitem.sql b/foundation-database/public/functions/createtodoitem.sql
new file mode 100644 (file)
index 0000000..d366a47
--- /dev/null
@@ -0,0 +1,109 @@
+
+CREATE OR REPLACE FUNCTION createTodoItem(INTEGER, TEXT, TEXT, TEXT, INTEGER, INTEGER, INTEGER, DATE, DATE, CHARACTER(1), DATE, DATE, INTEGER, TEXT, TEXT) RETURNS INTEGER AS $$  
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN createTodoItem($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, NULL);
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION createTodoItem(INTEGER, TEXT, TEXT, TEXT, INTEGER, INTEGER, INTEGER, DATE, DATE, CHARACTER(1), DATE, DATE, INTEGER, TEXT, TEXT, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  ptodoid     ALIAS FOR  $1;
+  pusername   ALIAS FOR  $2;
+  pname       ALIAS FOR  $3;
+  pdesc       ALIAS FOR  $4;
+  pincdtid    ALIAS FOR  $5;
+  pcrmacctid  ALIAS FOR  $6;
+  pOpheadid   ALIAS FOR  $7;
+  pstarted    ALIAS FOR  $8;
+  pdue        ALIAS FOR  $9;
+  pstatus     ALIAS FOR $10;
+  passigned   ALIAS FOR $11;
+  pcompleted  ALIAS FOR $12;
+  ppriority   ALIAS FOR $13;
+  pnotes      ALIAS FOR $14;
+  powner      ALIAS FOR $15;
+  pcntctid    ALIAS FOR $16;
+
+  _todoid     INTEGER;
+  _priority   INTEGER         := ppriority;
+  _status     CHARACTER(1)    := pstatus;
+  _incdtid    INTEGER         := pincdtid;
+  _crmacctid  INTEGER         := pcrmacctid;
+  _opheadid   INTEGER         := pOpheadid;
+  _assigned   DATE            := passigned;
+  _result     INTEGER;
+
+BEGIN
+  IF (pusername IS NULL OR pusername = '') THEN
+    RETURN -1;
+  END IF;
+
+  IF (pname IS NULL OR pname = '') THEN
+    RETURN -2;
+  END IF;
+
+  IF (pdue IS NULL) THEN
+    RETURN -3;
+  END IF;
+
+  IF (pcompleted IS NOT NULL) THEN
+    _status := 'C';
+  ELSIF (pstatus IS NULL AND pstarted IS NOT NULL) THEN
+    _status := 'I';
+  ELSIF (pstatus IS NULL) THEN
+    _status := 'N';
+  END IF;
+
+  IF (_incdtid <= 0) THEN
+    _incdtid := NULL;
+  END IF;
+
+  IF (_crmacctid <= 0) THEN
+    _crmacctid := NULL;
+  END IF;
+
+  IF (_opheadid <= 0) THEN
+    _opheadid := NULL;
+  END IF;
+
+  IF (_priority <= 0) THEN
+    _priority := NULL;
+  END IF;
+
+  IF (_assigned IS NULL) THEN
+    _assigned := CURRENT_DATE;
+  END IF;
+
+  IF (ptodoid IS NULL) THEN
+    SELECT NEXTVAL('todoitem_todoitem_id_seq') INTO _todoid;
+  ELSE
+    _todoid := ptodoid;
+  END IF;
+
+  INSERT INTO todoitem ( todoitem_id, todoitem_username, todoitem_name,
+                         todoitem_description, todoitem_incdt_id,
+                         todoitem_creator_username, todoitem_status,
+                         todoitem_active, todoitem_start_date,
+                         todoitem_due_date, todoitem_assigned_date,
+                         todoitem_completed_date, todoitem_priority_id,
+                         todoitem_notes, todoitem_crmacct_id,
+                         todoitem_ophead_id, todoitem_owner_username,
+                         todoitem_cntct_id
+              ) VALUES ( _todoid, pusername, pname,
+                         pdesc, _incdtid,
+                         getEffectiveXtUser(), _status,
+                         TRUE, pstarted,
+                         pdue, _assigned,
+                         pcompleted, _priority, pnotes, _crmacctid, _opheadid, powner,
+                         pcntctid );
+
+  RETURN _todoid;
+END;
+$$ LANGUAGE 'plpgsql';
+
+
diff --git a/foundation-database/public/functions/createurl.sql b/foundation-database/public/functions/createurl.sql
new file mode 100644 (file)
index 0000000..e884aab
--- /dev/null
@@ -0,0 +1,11 @@
+create or replace function createUrl(text, text) returns integer as $$
+declare
+  pTitle ALIAS FOR $1;
+  pUrl ALIAS FOR $2;
+  _id integer;
+begin
+  _id := nextval('urlinfo_url_id_seq');
+  insert into urlinfo (url_id, url_title, url_url) values (_id, pTitle, pUrl);
+  return _id;
+end;
+$$ language 'plpgsql';
diff --git a/foundation-database/public/functions/createuser.sql b/foundation-database/public/functions/createuser.sql
new file mode 100644 (file)
index 0000000..9206ca3
--- /dev/null
@@ -0,0 +1,13 @@
+DROP FUNCTION IF EXISTS createUser(TEXT, BOOLEAN);
+CREATE OR REPLACE FUNCTION createUser(pUsername TEXT, pCreateUsers BOOLEAN) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  IF (pCreateUsers) THEN
+    EXECUTE 'CREATE USER "' || pUsername || '" CREATEROLE   IN GROUP xtrole;';
+  ELSE
+    EXECUTE 'CREATE USER "' || pUsername || '" NOCREATEROLE IN GROUP xtrole;';
+  END IF;
+  RETURN 1;
+END;
+$$ LANGUAGE PLPGSQL;
diff --git a/foundation-database/public/functions/createwo.sql b/foundation-database/public/functions/createwo.sql
new file mode 100644 (file)
index 0000000..7809cf2
--- /dev/null
@@ -0,0 +1,283 @@
+CREATE OR REPLACE FUNCTION createWo(INTEGER, INTEGER, INTEGER, NUMERIC, INTEGER, DATE, TEXT, CHAR, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWoNumber ALIAS FOR $1;
+  pItemsiteid ALIAS FOR $2;
+  pPriority ALIAS FOR $3;
+  pQtyOrdered ALIAS FOR $4;
+  pLeadTime ALIAS FOR $5;
+  pDueDate ALIAS FOR $6;
+  pProductionNotes ALIAS FOR $7;
+  pParentType ALIAS FOR $8;
+  pParentId ALIAS FOR $9;
+
+BEGIN
+  RETURN createWo(pWoNumber, pItemsiteid, pPriority, pQtyOrdered,
+                  (pDueDate - pLeadTime), pDueDate, pProductionNotes,
+                  pParentType, pParentId, -1);
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION createWo(INTEGER, INTEGER, INTEGER, NUMERIC, INTEGER, DATE, TEXT, CHAR, INTEGER, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWoNumber ALIAS FOR $1;
+  pItemsiteid ALIAS FOR $2;
+  pPriority ALIAS FOR $3;
+  pQtyOrdered ALIAS FOR $4;
+  pLeadTime ALIAS FOR $5;
+  pDueDate ALIAS FOR $6;
+  pProductionNotes ALIAS FOR $7;
+  pParentType ALIAS FOR $8;
+  pParentId ALIAS FOR $9;
+  pProjectId ALIAS FOR $10;
+
+BEGIN
+  RETURN createWo(pWoNumber, pItemsiteid, pPriority, pQtyOrdered,
+                  (pDueDate - pLeadTime), pDueDate, pProductionNotes,
+                  pParentType, pParentId, pProjectId);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION createWo(INTEGER, INTEGER, INTEGER, NUMERIC, INTEGER, DATE, TEXT, CHAR, INTEGER, INTEGER, INTEGER, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWoNumber ALIAS FOR $1;
+  pItemsiteid ALIAS FOR $2;
+  pPriority ALIAS FOR $3;
+  pQtyOrdered ALIAS FOR $4;
+  pLeadTime ALIAS FOR $5;
+  pDueDate ALIAS FOR $6;
+  pProductionNotes ALIAS FOR $7;
+  pParentType ALIAS FOR $8;
+  pParentId ALIAS FOR $9;
+  pProjectId ALIAS FOR $10;
+  pBomRevId ALIAS FOR $11;
+  pBooRevId ALIAS FOR $12;
+
+BEGIN
+  RETURN createWo(pWoNumber, pItemsiteid, pPriority, pQtyOrdered,
+                  (pDueDate - pLeadTime), pDueDate, pProductionNotes,
+                  pParentType, pParentId, pProjectId, pBomRevId, pBooRevId, NULL);
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION createWo(INTEGER, INTEGER, INTEGER, NUMERIC, DATE, DATE, TEXT, CHAR, INTEGER, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWoNumber ALIAS FOR $1;
+  pItemsiteid ALIAS FOR $2;
+  pPriority ALIAS FOR $3;
+  pQtyOrdered ALIAS FOR $4;
+  pStartDate ALIAS FOR $5;
+  pDueDate ALIAS FOR $6;
+  pProductionNotes ALIAS FOR $7;
+  pParentType ALIAS FOR $8;
+  pParentId ALIAS FOR $9;
+  pProjectId ALIAS FOR $10;
+  _woid INTEGER;
+  _result INTEGER;
+  _parentType char(1);
+  _bomrevid INTEGER;
+  _boorevid INTEGER;
+
+BEGIN
+
+  SELECT getActiveRevId('BOM',itemsite_item_id) INTO _bomrevid
+  FROM itemsite
+  WHERE (itemsite_id=pItemsiteid);
+
+  SELECT getActiveRevId('BOO',itemsite_item_id) INTO _boorevid
+  FROM itemsite
+  WHERE (itemsite_id=pItemsiteid);
+  
+  RETURN createWo(pWoNumber, pItemsiteid, pPriority, pQtyOrdered,
+                  pStartDate, pDueDate, pProductionNotes,
+                  pParentType, pParentId, pProjectId, _bomrevid, _boorevid, NULL);
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION createWo(INTEGER, INTEGER, INTEGER, NUMERIC, DATE, DATE, TEXT, CHAR, INTEGER, INTEGER, INTEGER, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWoNumber ALIAS FOR $1;
+  pItemsiteid ALIAS FOR $2;
+  pPriority ALIAS FOR $3;
+  pQtyOrdered ALIAS FOR $4;
+  pStartDate ALIAS FOR $5;
+  pDueDate ALIAS FOR $6;
+  pProductionNotes ALIAS FOR $7;
+  pParentType ALIAS FOR $8;
+  pParentId ALIAS FOR $9;
+  pProjectId ALIAS FOR $10;
+  pBomRevId ALIAS FOR $11;
+  pBooRevId ALIAS FOR $12;
+BEGIN
+  RETURN createWo(pWoNumber, pItemsiteid, pPriority, pQtyOrdered,
+                  pStartDate, pDueDate, pProductionNotes,
+                  pParentType, pParentId, pProjectId, pBomRevId, pBooRevId, NULL);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION createWo(INTEGER, INTEGER, INTEGER, NUMERIC, DATE, DATE, TEXT, CHAR, INTEGER, INTEGER, INTEGER, INTEGER, TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWoNumber ALIAS FOR $1;
+  pItemsiteid ALIAS FOR $2;
+  pPriority ALIAS FOR $3;
+  pQtyOrdered ALIAS FOR $4;
+  pStartDate ALIAS FOR $5;
+  pDueDate ALIAS FOR $6;
+  pProductionNotes ALIAS FOR $7;
+  pParentType ALIAS FOR $8;
+  pParentId ALIAS FOR $9;
+  pProjectId ALIAS FOR $10;
+  pBomRevId ALIAS FOR $11;
+  pBooRevId ALIAS FOR $12;
+  pCosMethod ALIAS FOR $13;
+  _startDate DATE;
+  _woid INTEGER;
+  _result INTEGER;
+  _parentType char(1);
+  _parentId INTEGER;
+  _cosmethod char(1);
+  _itemsite RECORD;
+  
+BEGIN
+  
+  _parentType := COALESCE(pParentType, ' ');
+  _parentId   := COALESCE(pParentId, -1);
+
+  SELECT * INTO _itemsite FROM itemsite WHERE itemsite_id = pItemsiteid;
+
+--  Check to make sure the itemsite specified is supplied at itemsite
+  IF (NOT _itemsite.itemsite_wosupply) THEN
+    RETURN -1;
+  END IF;
+
+--  Check to make sure if this is a job item that it is tied to a sales order
+--  Or if it is just an avarage costed item
+  IF (pCosMethod IN ('D', 'P')) THEN
+    _cosmethod := pCosMethod;
+  ELSE
+    IF (_itemsite.itemsite_costmethod = 'J') THEN
+      IF (_parentType = ' ' OR _parentId = -1) THEN
+        RAISE EXCEPTION 'Work Orders for Item Sites that are Job cost must have a parent order.';
+      ELSE
+        SELECT COALESCE(itemsite_cosdefault,fetchmetrictext('JobItemCosDefault'),'D') INTO _cosmethod FROM itemsite WHERE itemsite_id=pItemsiteid;
+      END IF;
+    ELSIF (_itemsite.itemsite_costmethod = 'A') THEN
+      _cosmethod := COALESCE(_itemsite.itemsite_cosdefault,fetchmetrictext('JobItemCosDefault'),'D');
+    END IF;
+  END IF;
+
+--  Check to see if the site calendar metric is set, and if so adjust the start date if necessary
+  IF (fetchmetricbool('UseSiteCalendar')) THEN
+    _startDate := calculatenextworkingdate(_itemsite.itemsite_warehous_id, pStartDate, 0);
+    IF (_startDate != pStartDate) THEN
+      _startDate := calculatenextworkingdate(_itemsite.itemsite_warehous_id, pDueDate, -_itemsite.itemsite_leadtime);
+    END IF;
+  ELSE
+    _startDate := pStartDate;
+  END IF;
+  
+--  Grab the next wo_id
+  SELECT NEXTVAL('wo_wo_id_seq') INTO _woid;
+
+--  Create the W/O
+  INSERT INTO wo
+  ( wo_id, wo_number, wo_subnumber, wo_itemsite_id,
+    wo_priority, wo_ordtype, wo_ordid,
+    wo_status, wo_startdate, wo_duedate,
+    wo_qtyord, wo_qtyrcv, wo_prodnotes, wo_prj_id,
+    wo_bom_rev_id, wo_boo_rev_id, wo_cosmethod )
+  SELECT _woid, pWoNumber, nextWoSubnumber(pWoNumber), itemsite_id,
+         pPriority, _parentType, pParentId,
+         'O', _startDate, pDueDate,
+         roundQty(item_fractional, pQtyOrdered), 0, pProductionNotes, pProjectId, 
+         pBomRevid, pBooRevid, _cosmethod
+  FROM itemsite, item
+  WHERE ((itemsite_item_id=item_id)
+   AND (itemsite_id=pItemsiteid));
+
+--  Explode the newly created W/O according to metrics
+  IF ( ( SELECT (metric_value='t')
+         FROM metric
+         WHERE (metric_name='AutoExplodeWO') ) ) THEN
+    SELECT explodeWo( _woid, ( SELECT (metric_value = 'M')
+                               FROM metric
+                               WHERE (metric_name='WOExplosionLevel') ) ) INTO _result;
+  ELSE
+    _result := _woid;
+  END IF;
+
+  RETURN _result;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION createWo(INTEGER, INTEGER, NUMERIC, INTEGER, DATE, TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWoNumber ALIAS FOR $1;
+  pItemsiteid ALIAS FOR $2;
+  pQtyOrdered ALIAS FOR $3;
+  pLeadTime ALIAS FOR $4;
+  pDueDate ALIAS FOR $5;
+  pProductionNotes ALIAS FOR $6;
+
+BEGIN
+  RETURN createWo( pWoNumber, pItemsiteid, 1, pQtyOrdered,
+                   (pDueDate - pLeadTime), pDueDate,
+                   pProductionNotes, NULL, NULL, -1 ); 
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION createWo(INTEGER, INTEGER, NUMERIC, INTEGER, DATE, TEXT, CHAR, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWoNumber ALIAS FOR $1;
+  pItemsiteid ALIAS FOR $2;
+  pQtyOrdered ALIAS FOR $3;
+  pLeadTime ALIAS FOR $4;
+  pDueDate ALIAS FOR $5;
+  pProductionNotes ALIAS FOR $6;
+  pParentType ALIAS FOR $7;
+  pParentId ALIAS FOR $8;
+
+BEGIN
+  RETURN createWo( pWoNumber, pItemsiteid, 1, pQtyOrdered,
+                   (pDueDate - pLeadTime), pDueDate,
+                   pProductionNotes, pParentType, pParentId, -1 ); 
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION createWo(INTEGER, INTEGER, NUMERIC, DATE, DATE, TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWoNumber ALIAS FOR $1;
+  pItemsiteid ALIAS FOR $2;
+  pQtyOrdered ALIAS FOR $3;
+  pStartDate ALIAS FOR $4;
+  pDueDate ALIAS FOR $5;
+  pProductionNotes ALIAS FOR $6;
+
+BEGIN
+  RETURN createWo( pWoNumber, pItemsiteid, 1, pQtyOrdered,
+                   pStartDate, pDueDate, pProductionNotes, NULL, NULL, -1);
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/createwomaterial.sql b/foundation-database/public/functions/createwomaterial.sql
new file mode 100644 (file)
index 0000000..b7557dc
--- /dev/null
@@ -0,0 +1,171 @@
+CREATE OR REPLACE FUNCTION createWoMaterial(INTEGER, INTEGER, char(1), NUMERIC, NUMERIC, NUMERIC) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWoid ALIAS FOR $1;
+  pItemsiteid ALIAS FOR $2;
+  pIssueMethod ALIAS FOR $3;
+  pQtyFxd ALIAS FOR $4;
+  pQtyPer ALIAS FOR $5;
+  pScrap ALIAS FOR $6;
+  _result INTEGER;
+BEGIN
+  SELECT createWoMaterial(pWoid, pItemsiteid, pIssueMethod, item_inv_uom_id, pQtyFxd, pQtyPer, pScrap)
+    INTO _result
+    FROM itemsite JOIN item ON (itemsite_item_id=item_id)
+   WHERE(itemsite_id=pItemsiteid);
+  RETURN _result;
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION createWoMaterial(INTEGER, INTEGER, char(1), INTEGER, NUMERIC, NUMERIC, NUMERIC) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWoid ALIAS FOR $1;
+  pItemsiteid ALIAS FOR $2;
+  pIssueMethod ALIAS FOR $3;
+  pUomId ALIAS FOR $4;
+  pQtyFxd ALIAS FOR $5;
+  pQtyPer ALIAS FOR $6;
+  pScrap ALIAS FOR $7;
+  _womatlid INTEGER;
+
+BEGIN
+
+  SELECT createWoMaterial(pWoid,pItemsiteid,pIssueMethod,pUomId,pQtyFxd,pQtyPer,pScrap,-1, NULL, NULL) INTO _womatlid;
+
+  RETURN _womatlid;
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION createWoMaterial(INTEGER, INTEGER, char(1), INTEGER, NUMERIC, NUMERIC, NUMERIC, INTEGER, TEXT, TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWoid ALIAS FOR $1;
+  pItemsiteid ALIAS FOR $2;
+  pIssueMethod ALIAS FOR $3;
+  pUomId ALIAS FOR $4;
+  pQtyFxd ALIAS FOR $5;
+  pQtyPer ALIAS FOR $6;
+  pScrap ALIAS FOR $7;
+  pBomitemId ALIAS FOR $8;
+  pNotes ALIAS FOR $9;
+  pRef ALIAS FOR $10;
+  _womatlid INTEGER;
+
+BEGIN
+
+  SELECT createWoMaterial(pWoid,pItemsiteid,pIssueMethod,pUomId,pQtyFxd,pQtyPer,pScrap,pBomitemId,pNotes,pRef,NULL,NULL) INTO _womatlid;
+
+  RETURN _womatlid;
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION createWoMaterial(pWoid INTEGER,
+                                            pItemsiteid INTEGER,
+                                            pIssueMethod CHAR(1),
+                                            pUomId INTEGER,
+                                            pQtyFxd NUMERIC,
+                                            pQtyPer NUMERIC,
+                                            pScrap NUMERIC,
+                                            pBomitemId INTEGER,
+                                            pNotes TEXT,
+                                            pRef TEXT,
+                                            pWooperId INTEGER,
+                                            pPickList BOOLEAN) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _womatlid INTEGER;
+  _p RECORD;
+
+BEGIN
+
+  _womatlid := (SELECT NEXTVAL('womatl_womatl_id_seq'));
+
+  INSERT INTO womatl
+  ( womatl_id, womatl_wo_id, womatl_itemsite_id,
+    womatl_issuemethod, womatl_uom_id, womatl_qtyfxd,
+    womatl_qtyper, womatl_scrap, womatl_qtyreq,
+    womatl_qtyiss, womatl_qtywipscrap, womatl_wooper_id,
+    womatl_bomitem_id, womatl_duedate, womatl_notes,
+    womatl_ref, womatl_picklist )
+  SELECT _womatlid, wo_id, pItemsiteid,
+         pIssueMethod, pUomId, pQtyFxd,
+         pQtyPer, pScrap, roundQty(item_fractional, (pQtyFxd + wo_qtyord * pQtyPer) * (1 + pScrap) ),
+         0, 0, COALESCE(pWooperId, -1),
+         pBomitemId, wo_startdate, pNotes,
+         pRef, COALESCE(pPickList, item_picklist) 
+  FROM wo, itemsite JOIN item ON (item_id=itemsite_item_id)
+  WHERE ( (wo_id=pWoid)
+   AND (itemsite_id=pItemsiteid) );
+
+-- Handle all of the Phantom material requirements
+  WHILE ( ( SELECT COUNT(*)
+            FROM womatl, itemsite, item
+            WHERE ( (womatl_itemsite_id=itemsite_id)
+             AND (itemsite_item_id=item_id)
+             AND (womatl_wo_id=pWoid)
+             AND (item_type='F') ) ) > 0 ) LOOP
+
+    FOR _p IN SELECT wo_qtyord, wo_startdate, womatl_id, womatl_wooper_id
+              FROM wo, womatl, itemsite, item
+              WHERE ( (womatl_itemsite_id=itemsite_id)
+               AND (itemsite_item_id=item_id)
+               AND (item_type='F')
+               AND (womatl_wo_id=wo_id)
+               AND (wo_id=pWoid) ) LOOP
+
+      INSERT INTO womatl
+      ( womatl_wo_id, womatl_itemsite_id, womatl_wooper_id,
+        womatl_schedatwooper, womatl_duedate,
+        womatl_uom_id, womatl_qtyfxd, womatl_qtyper, womatl_scrap,
+        womatl_qtyreq,
+        womatl_qtyiss, womatl_qtywipscrap,
+        womatl_lastissue, womatl_lastreturn,
+        womatl_cost, womatl_picklist, womatl_createwo,
+        womatl_issuemethod, womatl_notes, womatl_ref )
+      SELECT pWoid, cs.itemsite_id, _p.womatl_wooper_id,
+             womatl_schedatwooper, womatl_duedate,
+             bomitem_uom_id, bomitem_qtyfxd, (bomitem_qtyper * womatl_qtyper), bomitem_scrap,
+             roundQty(itemuomfractionalbyuom(bomitem_item_id, bomitem_uom_id), 
+                     ((bomitem_qtyfxd + _p.wo_qtyord * bomitem_qtyper) * womatl_qtyper * (1 + bomitem_scrap))),
+             0, 0,
+             startOfTime(), startOfTime(),
+             0, ci.item_picklist, ( (ci.item_type='M') AND (bomitem_createwo) ),
+             bomitem_issuemethod, bomitem_notes, bomitem_ref 
+      FROM wo, womatl, bomitem, 
+           itemsite AS cs, itemsite AS ps,
+           item AS ci, item AS pi
+      WHERE ( (womatl_itemsite_id=ps.itemsite_id)
+       AND (womatl_wo_id=wo_id)
+       AND (bomitem_parent_item_id=pi.item_id)
+       AND (bomitem_item_id=ci.item_id)
+       AND (ps.itemsite_warehous_id=cs.itemsite_warehous_id)
+       AND (cs.itemsite_item_id=ci.item_id)
+       AND (ps.itemsite_item_id=pi.item_id)
+       AND (woEffectiveDate(_p.wo_startdate) BETWEEN bomitem_effective AND (bomitem_expires - 1))
+       AND (womatl_id=_p.womatl_id));
+
+      DELETE FROM womatl
+      WHERE (womatl_id=_p.womatl_id);
+
+    END LOOP;
+  END LOOP;
+
+  UPDATE wo
+  SET wo_adhoc=TRUE
+  WHERE (wo_id=pWoid);
+
+  UPDATE wo
+  SET wo_status='E'
+  WHERE ( (wo_status='O')
+   AND (wo_id=pWoid) );
+
+  RETURN _womatlid;
+END;
+$$ LANGUAGE 'plpgsql';
+
+
diff --git a/foundation-database/public/functions/creditmemototal.sql b/foundation-database/public/functions/creditmemototal.sql
new file mode 100644 (file)
index 0000000..9d22e0d
--- /dev/null
@@ -0,0 +1,33 @@
+CREATE OR REPLACE FUNCTION creditmemototal(INTEGER) RETURNS NUMERIC AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCreditmemoId ALIAS FOR $1;
+  _result       NUMERIC;
+
+BEGIN
+
+  -- TO DO:  Add in line item taxes
+  SELECT COALESCE(cmhead_freight,0.0) + COALESCE(cmhead_misc,0.0) +
+         ( SELECT COALESCE(ROUND(SUM((cmitem_qtycredit * cmitem_qty_invuomratio) * cmitem_unitprice / cmitem_price_invuomratio), 2), 0.0)
+             FROM cmitem
+            WHERE (cmitem_cmhead_id=cmhead_id)
+           ) +
+         (SELECT COALESCE(SUM(tax) * -1, 0) AS tax
+           FROM ( SELECT ROUND(SUM(taxdetail_tax),2) AS tax
+                  FROM tax
+                  JOIN calculateTaxDetailSummary('CM', cmhead_id, 'T') ON (taxdetail_tax_id=tax_id)
+                  GROUP BY tax_id) AS data)
+           INTO _result
+  FROM cmhead
+  WHERE (cmhead_id=pCreditmemoId);
+
+  IF (NOT FOUND) THEN
+    return 0;
+  ELSE
+    RETURN _result;
+  END IF;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/crmacct.sql b/foundation-database/public/functions/crmacct.sql
new file mode 100644 (file)
index 0000000..f12ed0a
--- /dev/null
@@ -0,0 +1,39 @@
+CREATE OR REPLACE FUNCTION crmacct() RETURNS SETOF crmacct AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _row crmacct%ROWTYPE;
+  _priv TEXT;
+  _grant BOOLEAN;
+
+BEGIN
+  -- This query will give us the most permissive privilege the user has been granted
+  SELECT privilege, granted INTO _priv, _grant
+  FROM privgranted 
+  WHERE privilege IN ('MaintainAllCRMAccounts','ViewAllCRMAccounts','MaintainPersonalCRMAccounts','ViewPersonalCRMAccounts')
+  ORDER BY granted DESC, sequence
+  LIMIT 1;
+
+  -- If have an 'All' privilege return all results
+  IF (_priv ~ 'All' AND _grant) THEN
+    FOR _row IN 
+      SELECT * FROM crmacct
+    LOOP
+      RETURN NEXT _row;
+    END LOOP;
+  -- Otherwise if have any other grant, must be personal privilege.
+  ELSIF (_grant) THEN
+    FOR _row IN 
+      SELECT * FROM crmacct 
+      WHERE crmacct_owner_username = getEffectiveXtUser()
+    LOOP
+      RETURN NEXT _row;
+    END LOOP;
+  END IF;
+
+  RETURN;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+COMMENT ON FUNCTION crmacct() IS 'A table function that returns CRM Account results according to privilege settings.';
diff --git a/foundation-database/public/functions/currconcat.sql b/foundation-database/public/functions/currconcat.sql
new file mode 100644 (file)
index 0000000..6defe5e
--- /dev/null
@@ -0,0 +1,35 @@
+CREATE OR REPLACE FUNCTION currConcat(VARCHAR(3), VARCHAR(9))
+       RETURNS VARCHAR(15) AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple.
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  curr_abbr   ALIAS FOR $1;
+  curr_symbol ALIAS FOR $2;
+  returnVal   VARCHAR(15) := '';
+BEGIN
+  IF length(trim(curr_abbr)) > 0 AND length(trim(curr_symbol)) > 0 THEN
+      returnVal := trim(curr_abbr) || ' - ' || trim(curr_symbol);
+
+  ELSIF length(trim(curr_abbr)) > 0 THEN
+      returnVal := curr_abbr;
+
+  ELSIF length(trim(curr_symbol)) > 0 THEN
+      returnVal := curr_symbol;
+  END IF;
+
+  RETURN returnVal;
+END;
+$$ LANGUAGE plpgsql;
+
+CREATE OR REPLACE FUNCTION currConcat(INTEGER) RETURNS VARCHAR(15) AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple.
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  id ALIAS FOR $1;
+  returnVal   VARCHAR(15) := '';
+BEGIN
+  SELECT currConcat(curr_abbr, curr_symbol) INTO returnVal
+      FROM curr_symbol WHERE curr_id = id;
+  RETURN returnVal;
+END;
+$$ LANGUAGE plpgsql;
diff --git a/foundation-database/public/functions/currentapmemonumber.sql b/foundation-database/public/functions/currentapmemonumber.sql
new file mode 100644 (file)
index 0000000..4f7cc91
--- /dev/null
@@ -0,0 +1,19 @@
+CREATE OR REPLACE FUNCTION currentAPMemoNumber() RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _number INTEGER;
+
+BEGIN
+
+  SELECT orderseq_number INTO _number
+  FROM orderseq
+  WHERE (orderseq_name=''APMemoNumber'');
+  IF (NOT FOUND) THEN
+    _number := 0;
+  END IF;
+
+  RETURN _number;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/currentarmemonumber.sql b/foundation-database/public/functions/currentarmemonumber.sql
new file mode 100644 (file)
index 0000000..29110df
--- /dev/null
@@ -0,0 +1,21 @@
+
+CREATE OR REPLACE FUNCTION currentARMemoNumber() RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _number INTEGER;
+
+BEGIN
+
+  SELECT orderseq_number INTO _number
+  FROM orderseq
+  WHERE (orderseq_name=''ARMemoNumber'');
+  IF (NOT FOUND) THEN
+    _number := 0;
+  END IF;
+
+  RETURN _number;
+
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/currentcashrcptnumber.sql b/foundation-database/public/functions/currentcashrcptnumber.sql
new file mode 100644 (file)
index 0000000..0fbe587
--- /dev/null
@@ -0,0 +1,21 @@
+
+CREATE OR REPLACE FUNCTION currentCashRcptNumber() RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _number INTEGER;
+
+BEGIN
+
+  SELECT orderseq_number INTO _number
+  FROM orderseq
+  WHERE (orderseq_name=''CashRcptNumber'');
+  IF (NOT FOUND) THEN
+    _number := 0;
+  END IF;
+
+  RETURN _number;
+
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/currentnumber.sql b/foundation-database/public/functions/currentnumber.sql
new file mode 100644 (file)
index 0000000..6c6717d
--- /dev/null
@@ -0,0 +1,19 @@
+CREATE OR REPLACE FUNCTION currentNumber(TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pName   ALIAS FOR $1;
+  _number INTEGER;
+
+BEGIN
+  SELECT orderseq_number INTO _number
+  FROM orderseq
+  WHERE (orderseq_name=pName);
+  IF (NOT FOUND) THEN
+    _number := 0;
+  END IF;
+
+  RETURN _number;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/currgain.sql b/foundation-database/public/functions/currgain.sql
new file mode 100644 (file)
index 0000000..4654077
--- /dev/null
@@ -0,0 +1,43 @@
+-- calculate the change in value caused by exchange rate fluctuations.
+-- we generally care about currency exchange gain/loss when adjusting the G/L,
+-- so this function returns its result in the base currency.
+-- however, we only care about fluctuations in the base value of a foreign
+-- quantity, so this function expects pValue ($2) in the local currency.
+-- negative values = a loss.
+CREATE OR REPLACE FUNCTION currGain(INTEGER, NUMERIC, DATE, DATE)
+RETURNS NUMERIC AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pId ALIAS FOR $1;
+  pValue ALIAS FOR $2;
+  pStart ALIAS FOR $3;
+  pEnd ALIAS FOR $4;
+  _start DATE;
+  _end DATE;
+  _gain NUMERIC;
+  _multiplier  INTEGER := 1;
+
+BEGIN
+  IF (pEnd = pStart OR pValue = 0) THEN
+    RETURN 0;
+  END IF;
+
+  IF (pStart > pEnd) THEN
+    _start := pEnd;
+    _end   := pStart;
+    _multiplier := -1;
+  ELSE
+    _start := pStart;
+    _end := pEnd;
+  END IF;
+
+  _gain := currToBase(pId, pValue, _start) - currToBase(pId, pValue, _end);
+  IF (_gain IS NULL) THEN
+    RAISE EXCEPTION 'Missing exchange rate for curr_id % on % or %',
+                    pId, _start, _end;
+  END IF;
+
+  RETURN _gain * _multiplier;
+END;
+$$ LANGUAGE plpgsql;
diff --git a/foundation-database/public/functions/currrate.sql b/foundation-database/public/functions/currrate.sql
new file mode 100644 (file)
index 0000000..fc67aac
--- /dev/null
@@ -0,0 +1,50 @@
+CREATE OR REPLACE FUNCTION currRate(INTEGER, DATE) RETURNS NUMERIC STABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN currRate($1, NULL, $2);
+
+END;
+$$ LANGUAGE plpgsql;
+
+CREATE OR REPLACE FUNCTION currRate(pFromCurr INTEGER,
+                                    pToCurr INTEGER,
+                                    pDate DATE) RETURNS NUMERIC STABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _fromRate NUMERIC := 1.0;
+  _toRate NUMERIC := 1.0;
+  _returnVal NUMERIC := 1.0;
+BEGIN
+  IF pFromCurr = pToCurr THEN
+    RETURN _returnVal;
+  END IF;
+
+  IF (pFromCurr IS NOT NULL) THEN
+    SELECT curr_rate INTO _fromRate
+    FROM curr_rate
+    WHERE ( (curr_id=pFromCurr)
+    AND (pDate BETWEEN curr_effective AND curr_expires) );
+
+    IF ( NOT FOUND) THEN
+      RAISE EXCEPTION 'Currency exchange rate for currency % not found on %', pFromCurr, formatDate(pDate);
+    END IF;
+  END IF;
+
+  IF (pToCurr IS NOT NULL) THEN
+    SELECT curr_rate INTO _toRate
+    FROM curr_rate
+    WHERE ( (curr_id=pToCurr)
+    AND (pDate BETWEEN curr_effective AND curr_expires) );
+
+    IF ( NOT FOUND) THEN
+      RAISE EXCEPTION 'Currency exchange rate for currency % not found on %', pToCurr, formatDate(pDate);
+    END IF;
+  END IF;
+
+  _returnVal := _fromRate / _toRate;
+
+  RETURN _returnVal;
+END;
+$$ LANGUAGE plpgsql;
diff --git a/foundation-database/public/functions/currtobase.sql b/foundation-database/public/functions/currtobase.sql
new file mode 100644 (file)
index 0000000..a0ec22d
--- /dev/null
@@ -0,0 +1,38 @@
+-- for currToLocal, currToBase, and currToCurr:
+-- the curr_rate column has one more digit of precision than the user typed
+-- in so if the metric CurrencyExchangeSense says to show rates as
+-- foreign * rate = base, we can show what the user typed without rounding
+-- loss. now we have to use the same rate the user sees, so round the
+-- curr_rate.  see mantis #3901.
+
+CREATE OR REPLACE FUNCTION currToBase (integer, numeric, date) RETURNS NUMERIC STABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pId     ALIAS FOR $1;
+  pValue  ALIAS FOR $2;
+  _date  DATE;
+  _output NUMERIC;
+BEGIN
+  _date := $3;
+  IF _date IS NULL THEN
+    _date := 'now';
+  END IF;
+
+  IF pValue = 0 OR pValue IS NULL THEN
+    _output := 0;
+  ELSIF (baseCurrId() = pId) THEN
+    _output := pValue;
+  ELSE
+    SELECT pValue / curr_rate
+        INTO  _output
+      FROM  curr_rate
+     WHERE curr_id = pId
+       AND _date BETWEEN curr_effective AND curr_expires;
+    IF (_output IS NULL OR NOT FOUND) THEN
+      RAISE EXCEPTION 'No exchange rate for % on %', pId, _date;
+    END IF;
+  END IF;
+  RETURN _output;
+END;
+$$ LANGUAGE plpgsql;
diff --git a/foundation-database/public/functions/currtocurr.sql b/foundation-database/public/functions/currtocurr.sql
new file mode 100644 (file)
index 0000000..0bf82f3
--- /dev/null
@@ -0,0 +1,51 @@
+-- for currToLocal, currToBase, and currToCurr:
+-- the curr_rate column has one more digit of precision than the user typed
+-- in so if the metric CurrencyExchangeSense says to show rates as
+-- foreign * rate = base, we can show what the user typed without rounding
+-- loss. now we have to use the same rate the user sees, so round the
+-- curr_rate.  see mantis #3901.
+
+CREATE OR REPLACE FUNCTION currToCurr(INTEGER, INTEGER, NUMERIC, DATE)
+    RETURNS NUMERIC AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pFromCurr ALIAS FOR $1;
+  pToCurr ALIAS FOR $2;
+  pValue   ALIAS FOR $3;
+  pEffective ALIAS FOR $4;
+  _convertedValue NUMERIC;
+  _fromRate NUMERIC;
+  _toRate NUMERIC;
+BEGIN
+  IF pFromCurr = pToCurr THEN
+    RETURN pValue;
+  END IF;
+
+  IF pValue = 0 OR pValue IS NULL THEN
+    RETURN 0;
+  END IF;
+
+  SELECT curr_rate INTO _fromRate
+  FROM curr_rate
+  WHERE curr_id = pFromCurr
+    AND pEffective BETWEEN curr_effective AND curr_expires;
+
+  IF (NOT FOUND) THEN
+    RAISE EXCEPTION 'No exchange rate for % on %', pFromCurr, pEffective;
+  END IF;
+
+  SELECT curr_rate INTO _toRate
+  FROM curr_rate
+  WHERE curr_id = pToCurr
+    AND pEffective BETWEEN curr_effective AND curr_expires;
+
+  IF (NOT FOUND) THEN
+    RAISE EXCEPTION 'No exchange rate for % on %', pToCurr, pEffective;
+  END IF;
+
+  _convertedValue := pValue * _toRate / _fromRate;
+
+  RETURN _convertedValue;
+END;
+$$ LANGUAGE plpgsql;
diff --git a/foundation-database/public/functions/currtolocal.sql b/foundation-database/public/functions/currtolocal.sql
new file mode 100644 (file)
index 0000000..c8c6f28
--- /dev/null
@@ -0,0 +1,38 @@
+-- for currToLocal, currToBase, and currToCurr:
+-- the curr_rate column has one more digit of precision than the user typed
+-- in so if the metric CurrencyExchangeSense says to show rates as
+-- foreign * rate = base, we can show what the user typed without rounding
+-- loss. now we have to use the same rate the user sees, so round the
+-- curr_rate.  see mantis #3901.
+
+CREATE OR REPLACE FUNCTION currToLocal (integer, numeric, date) RETURNS NUMERIC AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+    pId     ALIAS FOR $1;
+    pValue  ALIAS FOR $2;
+    _date   DATE;
+    _output NUMERIC;
+BEGIN
+    _date := $3;
+    IF _date IS NULL THEN
+        _date := 'now';
+    END IF;
+
+    IF pValue = 0 OR pValue IS NULL THEN
+        _output := 0;
+    ELSIF (baseCurrId() = pId) THEN
+      _output := pValue;
+    ELSE
+        SELECT pValue * curr_rate
+            INTO  _output
+            FROM  curr_rate
+            WHERE curr_id = pId
+              AND _date BETWEEN curr_effective AND curr_expires;
+        IF (_output IS NULL OR NOT FOUND) THEN
+          RAISE EXCEPTION 'No exchange rate for % on %', pId, _date;
+        END IF;
+    END IF;
+    RETURN _output;
+END;
+$$ LANGUAGE plpgsql;
diff --git a/foundation-database/public/functions/custitem.sql b/foundation-database/public/functions/custitem.sql
new file mode 100644 (file)
index 0000000..06aaa59
--- /dev/null
@@ -0,0 +1,143 @@
+CREATE OR REPLACE FUNCTION custitem(cust_id INTEGER, shipto_id INTEGER DEFAULT -1, asof DATE DEFAULT CURRENT_DATE) RETURNS SETOF integer AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+
+  -- Non Exclusive
+  SELECT item_id
+  FROM item 
+  WHERE (NOT item_exclusive)
+   AND (item_sold)
+  UNION
+  -- Exclusive, Shipto match
+  SELECT item_id
+  FROM item
+    JOIN ipsiteminfo ON (ipsitem_item_id=item_id)
+    JOIN ipshead ON (ipshead_id=ipsitem_ipshead_id)
+    JOIN ipsass ON (ipsass_ipshead_id=ipshead_id)
+  WHERE (item_exclusive)
+   AND (item_sold)
+   AND ($2 != -1)
+   AND (ipsass_shipto_id=$2)
+   AND ($3 BETWEEN ipshead_effective AND (ipshead_expires - 1))
+  UNION
+  SELECT item_id
+  FROM item
+    JOIN ipsiteminfo ON (ipsitem_prodcat_id=item_prodcat_id)
+    JOIN ipshead ON (ipshead_id=ipsitem_ipshead_id)
+    JOIN ipsass ON (ipsass_ipshead_id=ipshead_id)
+  WHERE (item_exclusive)
+   AND (item_sold)
+   AND ($2 != -1)
+   AND (ipsass_shipto_id=$2)
+   AND ($3 BETWEEN ipshead_effective AND (ipshead_expires - 1))
+  UNION
+   -- Exclusive, Shipto pattern match
+  SELECT item_id
+  FROM item
+    JOIN ipsiteminfo ON (ipsitem_item_id=item_id)
+    JOIN ipshead ON (ipshead_id=ipsitem_ipshead_id)
+    JOIN ipsass ON (ipsass_ipshead_id=ipshead_id)
+    JOIN shiptoinfo ON (shipto_num ~ ipsass_shipto_pattern)
+  WHERE (item_exclusive)
+   AND (item_sold)
+   AND (COALESCE(length(ipsass_shipto_pattern), 0) > 0)
+   AND (ipsass_cust_id=$1)
+   AND ($2 != -1)
+   AND (shipto_id=$2)
+   AND ($3 BETWEEN ipshead_effective AND (ipshead_expires - 1))
+  UNION
+  SELECT item_id
+  FROM item
+    JOIN ipsiteminfo ON (ipsitem_prodcat_id=item_prodcat_id)
+    JOIN ipshead ON (ipshead_id=ipsitem_ipshead_id)
+    JOIN ipsass ON (ipsass_ipshead_id=ipshead_id)
+    JOIN shiptoinfo ON (shipto_num ~ ipsass_shipto_pattern)
+  WHERE (item_exclusive)
+   AND (item_sold)
+   AND (COALESCE(length(ipsass_shipto_pattern), 0) > 0)
+   AND (ipsass_cust_id=$1)
+   AND ($2 != -1)
+   AND (shipto_id=$2)
+   AND ($3 BETWEEN ipshead_effective AND (ipshead_expires - 1))
+  UNION
+   -- Exclusive, Customer match
+  SELECT item_id
+  FROM item
+    JOIN ipsiteminfo ON (ipsitem_item_id=item_id)
+    JOIN ipshead ON (ipshead_id=ipsitem_ipshead_id)
+    JOIN ipsass ON (ipsass_ipshead_id=ipshead_id)
+  WHERE (item_exclusive)
+   AND (item_sold)
+   AND (ipsass_cust_id=$1)
+   AND (ipsass_shipto_id=-1)
+   AND (ipsass_shipto_pattern='')
+   AND (ipsass_custtype_id=-1)
+   AND (ipsass_custtype_pattern='')
+   AND ($3 BETWEEN ipshead_effective AND (ipshead_expires - 1))
+  UNION
+  SELECT item_id
+  FROM item
+    JOIN ipsiteminfo ON (ipsitem_prodcat_id=item_prodcat_id)
+    JOIN ipshead ON (ipshead_id=ipsitem_ipshead_id)
+    JOIN ipsass ON (ipsass_ipshead_id=ipshead_id)
+  WHERE (item_exclusive)
+   AND (item_sold)
+   AND (ipsass_cust_id=$1)
+   AND (ipsass_shipto_id=-1)
+   AND (ipsass_shipto_pattern='')
+   AND (ipsass_custtype_id=-1)
+   AND (ipsass_custtype_pattern='')
+   AND ($3 BETWEEN ipshead_effective AND (ipshead_expires - 1))
+  UNION
+  -- Exclusive, Customer Type match
+  SELECT item_id
+  FROM item
+    JOIN ipsiteminfo ON (ipsitem_item_id=item_id)
+    JOIN ipshead ON (ipshead_id=ipsitem_ipshead_id)
+    JOIN ipsass ON (ipsass_ipshead_id=ipshead_id)
+    JOIN custinfo ON (ipsass_custtype_id=cust_custtype_id)
+  WHERE (item_exclusive)
+   AND (item_sold)
+   AND (cust_id=$1)
+   AND ($3 BETWEEN ipshead_effective AND (ipshead_expires - 1))
+  UNION
+  SELECT item_id
+  FROM item
+    JOIN ipsiteminfo ON (ipsitem_prodcat_id=item_prodcat_id)
+    JOIN ipshead ON (ipshead_id=ipsitem_ipshead_id)
+    JOIN ipsass ON (ipsass_ipshead_id=ipshead_id)
+    JOIN custinfo ON (ipsass_custtype_id=cust_custtype_id)
+  WHERE (item_exclusive)
+   AND (item_sold)
+   AND (cust_id=$1)
+   AND ($3 BETWEEN ipshead_effective AND (ipshead_expires - 1))
+  UNION
+  -- Exclusive, Customer Type pattern match
+  SELECT item_id
+  FROM item
+    JOIN ipsiteminfo ON (ipsitem_item_id=item_id)
+    JOIN ipshead ON (ipshead_id=ipsitem_ipshead_id)
+    JOIN ipsass ON (ipsass_ipshead_id=ipshead_id)
+    JOIN custtype ON (custtype_code ~ ipsass_custtype_pattern)
+    JOIN custinfo ON (cust_custtype_id=custtype_id)
+  WHERE (item_exclusive)
+   AND (item_sold)
+   AND (COALESCE(length(ipsass_custtype_pattern), 0) > 0)
+   AND (cust_id=$1)
+   AND ($3 BETWEEN ipshead_effective AND (ipshead_expires - 1))
+  UNION
+  SELECT item_id
+  FROM item
+    JOIN ipsiteminfo ON (ipsitem_prodcat_id=item_prodcat_id)
+    JOIN ipshead ON (ipshead_id=ipsitem_ipshead_id)
+    JOIN ipsass ON (ipsass_ipshead_id=ipshead_id)
+    JOIN custtype ON (custtype_code ~ ipsass_custtype_pattern)
+    JOIN custinfo ON (cust_custtype_id=custtype_id)
+  WHERE (item_exclusive)
+   AND (item_sold)
+   AND (COALESCE(length(ipsass_custtype_pattern), 0) > 0)
+   AND (cust_id=$1)
+   AND ($3 BETWEEN ipshead_effective AND (ipshead_expires - 1))
+   
+$$ LANGUAGE 'sql';
+
diff --git a/foundation-database/public/functions/customercanpurchase.sql b/foundation-database/public/functions/customercanpurchase.sql
new file mode 100644 (file)
index 0000000..a849f09
--- /dev/null
@@ -0,0 +1,207 @@
+CREATE OR REPLACE FUNCTION customerCanPurchase(INTEGER, INTEGER) RETURNS BOOL AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pitemid ALIAS FOR $1;
+  pCustid ALIAS FOR $2;
+
+BEGIN
+  RETURN customerCanPurchase(pitemid, pCustid, -1);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION customerCanPurchase(INTEGER, INTEGER, INTEGER) RETURNS BOOL AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pitemid ALIAS FOR $1;
+  pCustid ALIAS FOR $2;
+  pShiptoid AlIAS FOR $3;
+  _id INTEGER;
+  _item RECORD;
+
+BEGIN
+  RETURN customerCanPurchase(pitemid, pCustid, pShiptoid, CURRENT_DATE);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION customerCanPurchase(INTEGER, INTEGER, INTEGER, DATE) RETURNS BOOL AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pitemid ALIAS FOR $1;
+  pCustid ALIAS FOR $2;
+  pShiptoid AlIAS FOR $3;
+  pAsOf ALIAS FOR $4;
+  _id INTEGER;
+  _item RECORD;
+
+BEGIN
+
+  SELECT item_sold, item_exclusive
+    INTO _item
+    FROM item
+   WHERE(item_id=pItemid);
+
+--  Make sure that this is at least a sold Item
+  IF (NOT _item.item_sold) THEN
+    RETURN FALSE;
+  END IF;
+
+--  Everyone can purchase a non-exclusive item
+  IF (NOT _item.item_exclusive) THEN
+    RETURN TRUE;
+  END IF;
+
+  IF(pShiptoid != -1) THEN
+--  Check for a shipto Assigned Price
+    SELECT ipsitem_id INTO _id
+      FROM ipsiteminfo, ipshead, ipsass
+     WHERE((ipsitem_ipshead_id=ipshead_id)
+       AND (ipsass_ipshead_id=ipshead_id)
+       AND (pAsOf BETWEEN ipshead_effective AND (ipshead_expires - 1))
+       AND (ipsitem_item_id=pItemid)
+       AND (ipsass_shipto_id != -1)
+       AND (ipsass_shipto_id=pShiptoid))
+     LIMIT 1;
+    IF (FOUND) THEN
+      RETURN TRUE;
+    END IF;
+    SELECT ipsitem_id INTO _id
+      FROM ipsiteminfo, item, ipshead, ipsass
+     WHERE((ipsitem_ipshead_id=ipshead_id)
+       AND (ipsitem_prodcat_id = item_prodcat_id)
+       AND (ipsass_ipshead_id=ipshead_id)
+       AND (pAsOf BETWEEN ipshead_effective AND (ipshead_expires - 1))
+       AND (item_id=pItemid)
+       AND (ipsass_shipto_id != -1)
+       AND (ipsass_shipto_id=pShiptoid))
+     LIMIT 1;
+    IF (FOUND) THEN
+      RETURN TRUE;
+    END IF;
+
+--  Check for a Shipto Pattern Assigned Price
+    SELECT ipsitem_id INTO _id
+      FROM ipsiteminfo, ipshead, ipsass, shiptoinfo
+     WHERE((ipsitem_ipshead_id=ipshead_id)
+       AND (ipsass_ipshead_id=ipshead_id)
+       AND (pAsOf BETWEEN ipshead_effective AND (ipshead_expires - 1))
+       AND (COALESCE(length(ipsass_shipto_pattern), 0) > 0)
+       AND (shipto_num ~ ipsass_shipto_pattern)
+       AND (ipsass_cust_id=pCustid)
+       AND (ipsitem_item_id=pItemid)
+       AND (shipto_id=pShiptoid))
+     LIMIT 1;
+    IF (FOUND) THEN
+      RETURN TRUE;
+    END IF;
+    SELECT ipsitem_id INTO _id
+      FROM ipsiteminfo, item, ipshead, ipsass, shiptoinfo
+     WHERE((ipsitem_ipshead_id=ipshead_id)
+       AND (ipsitem_prodcat_id = item_prodcat_id)
+       AND (ipsass_ipshead_id=ipshead_id)
+       AND (pAsOf BETWEEN ipshead_effective AND (ipshead_expires - 1))
+       AND (COALESCE(length(ipsass_shipto_pattern), 0) > 0)
+       AND (shipto_num ~ ipsass_shipto_pattern)
+       AND (ipsass_cust_id=pCustid)
+       AND (item_id=pItemid)
+       AND (shipto_id=pShiptoid))
+     LIMIT 1;
+    IF (FOUND) THEN
+      RETURN TRUE;
+    END IF;
+  END IF;
+
+--  Check for a Customer Assigned Price
+  SELECT ipsitem_id INTO _id
+    FROM ipsiteminfo, ipshead, ipsass
+   WHERE((ipsitem_ipshead_id=ipshead_id)
+     AND (ipsass_ipshead_id=ipshead_id)
+     AND (pAsOf BETWEEN ipshead_effective AND (ipshead_expires - 1))
+     AND (ipsitem_item_id=pItemid)
+     AND (COALESCE(length(ipsass_shipto_pattern), 0) = 0)
+     AND (ipsass_cust_id=pCustid))
+   LIMIT 1;
+  IF (FOUND) THEN
+    RETURN TRUE;
+  END IF;
+  SELECT ipsitem_id INTO _id
+    FROM ipsiteminfo, item, ipshead, ipsass
+   WHERE((ipsitem_ipshead_id=ipshead_id)
+     AND (ipsitem_prodcat_id = item_prodcat_id)
+     AND (ipsass_ipshead_id=ipshead_id)
+     AND (pAsOf BETWEEN ipshead_effective AND (ipshead_expires - 1))
+     AND (item_id=pItemid)
+     AND (COALESCE(length(ipsass_shipto_pattern), 0) = 0)
+     AND (ipsass_cust_id=pCustid))
+   LIMIT 1;
+  IF (FOUND) THEN
+    RETURN TRUE;
+  END IF;
+
+--  Check for a Customer Type Assigned Price
+  SELECT ipsitem_id INTO _id
+    FROM ipsiteminfo, ipshead, ipsass, custinfo
+   WHERE( (ipsitem_ipshead_id=ipshead_id)
+     AND  (ipsass_ipshead_id=ipshead_id)
+     AND  (ipsass_custtype_id != -1)
+     AND  (cust_custtype_id = ipsass_custtype_id)
+     AND  (pAsOf BETWEEN ipshead_effective AND (ipshead_expires - 1))
+     AND  (ipsitem_item_id=pItemid)
+     AND  (cust_id=pCustid))
+    LIMIT 1;
+  IF (FOUND) THEN
+    RETURN TRUE;
+  END IF;
+  SELECT ipsitem_id INTO _id
+    FROM ipsiteminfo, item, ipshead, ipsass, custinfo
+   WHERE( (ipsitem_ipshead_id=ipshead_id)
+     AND  (ipsitem_prodcat_id = item_prodcat_id)
+     AND  (ipsass_ipshead_id=ipshead_id)
+     AND  (ipsass_custtype_id != -1)
+     AND  (cust_custtype_id = ipsass_custtype_id)
+     AND  (pAsOf BETWEEN ipshead_effective AND (ipshead_expires - 1))
+     AND  (item_id=pItemid)
+     AND  (cust_id=pCustid))
+    LIMIT 1;
+  IF (FOUND) THEN
+    RETURN TRUE;
+  END IF;
+
+--  Check for a Customer Type Pattern Assigned Price
+  SELECT ipsitem_id INTO _id
+    FROM ipsiteminfo, ipshead, ipsass, custtype, custinfo
+   WHERE((ipsitem_ipshead_id=ipshead_id)
+     AND (ipsass_ipshead_id=ipshead_id)
+     AND (coalesce(length(ipsass_custtype_pattern), 0) > 0)
+     AND (custtype_code ~ ipsass_custtype_pattern)
+     AND (cust_custtype_id=custtype_id)
+     AND (pAsOf BETWEEN ipshead_effective AND (ipshead_expires - 1))
+     AND (ipsitem_item_id=pItemid)
+     AND (cust_id=pCustid))
+   LIMIT 1;
+  IF (FOUND) THEN
+    RETURN TRUE;
+  END IF;
+  SELECT ipsitem_id INTO _id
+    FROM ipsiteminfo, item, ipshead, ipsass, custtype, custinfo
+   WHERE((ipsitem_ipshead_id=ipshead_id)
+     AND (ipsitem_prodcat_id = item_prodcat_id)
+     AND (ipsass_ipshead_id=ipshead_id)
+     AND (coalesce(length(ipsass_custtype_pattern), 0) > 0)
+     AND (custtype_code ~ ipsass_custtype_pattern)
+     AND (cust_custtype_id=custtype_id)
+     AND (pAsOf BETWEEN ipshead_effective AND (ipshead_expires - 1))
+     AND (item_id=pItemid)
+     AND (cust_id=pCustid))
+   LIMIT 1;
+  IF (FOUND) THEN
+    RETURN TRUE;
+  END IF;
+
+--  That's it, Sales don't count - yet
+  RETURN FALSE;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/defaultlocationname.sql b/foundation-database/public/functions/defaultlocationname.sql
new file mode 100644 (file)
index 0000000..24a8089
--- /dev/null
@@ -0,0 +1,23 @@
+CREATE OR REPLACE FUNCTION defaultLocationName(INTEGER) RETURNS TEXT AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  _p RECORD;
+
+BEGIN
+
+  SELECT itemsite_location_id, itemsite_location INTO _p
+  FROM itemsite
+  WHERE (itemsite_id=pItemsiteid);
+
+  IF (NOT FOUND) THEN
+    RETURN ''Error'';
+  ELSIF (_p.itemsite_location_id = -1) THEN
+    RETURN _p.itemsite_location;
+  ELSE
+    RETURN formatLocationName(_p.itemsite_location_id);
+  END IF;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/deleteaccount.sql b/foundation-database/public/functions/deleteaccount.sql
new file mode 100644 (file)
index 0000000..20b09da
--- /dev/null
@@ -0,0 +1,172 @@
+
+CREATE OR REPLACE FUNCTION deleteAccount(integer) RETURNS integer
+    AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pAccntid ALIAS FOR $1;
+  _check INTEGER;
+
+BEGIN
+
+--  Check to see if the passed accnt is used in a Cost Category
+  SELECT costcat_id INTO _check
+  FROM costcat
+  WHERE ( (costcat_asset_accnt_id=pAccntid)
+     OR   (costcat_liability_accnt_id=pAccntid)
+     OR   (costcat_adjustment_accnt_id=pAccntid)
+     OR   (costcat_purchprice_accnt_id=pAccntid)
+     OR   (costcat_laboroverhead_accnt_id=pAccntid)
+     OR   (costcat_scrap_accnt_id=pAccntid)
+     OR   (costcat_invcost_accnt_id=pAccntid)
+     OR   (costcat_wip_accnt_id=pAccntid)
+     OR   (costcat_shipasset_accnt_id=pAccntid)
+     OR   (costcat_mfgscrap_accnt_id=pAccntid)
+     OR   (costcat_transform_accnt_id=pAccntid)
+     OR   (costcat_freight_accnt_id=pAccntid) )
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -1;
+  END IF;
+
+--  Check to see if the passed accnt is used in a Sales Account Assignment
+  SELECT salesaccnt_id INTO _check
+  FROM salesaccnt
+  WHERE ( (salesaccnt_sales_accnt_id=pAccntid)
+     OR   (salesaccnt_credit_accnt_id=pAccntid)
+     OR   (salesaccnt_cos_accnt_id=pAccntid) )
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -2;
+  END IF;
+
+--  Check to see if the passed accnt is used in a A/R Account Assignment
+  SELECT araccnt_id INTO _check
+  FROM araccnt
+  WHERE ( (araccnt_freight_accnt_id=pAccntid)
+     OR   (araccnt_ar_accnt_id=pAccntid)
+     OR   (araccnt_prepaid_accnt_id=pAccntid) )
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -3;
+  END IF;
+
+--  Check to see if the passed accnt is used in a Warehouse
+  IF EXISTS (SELECT 1
+               FROM whsinfo
+              WHERE (warehous_default_accnt_id=pAccntid)) THEN
+    RETURN -4;
+  END IF;
+
+--  Check to see if the passed accnt is used in a Bank Account
+  SELECT bankaccnt_id INTO _check
+  FROM bankaccnt
+  WHERE (bankaccnt_accnt_id=pAccntid)
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -5;
+  END IF;
+
+--  Check to see if the passed accnt is used in an Expense Category
+  SELECT expcat_id INTO _check
+  FROM expcat
+  WHERE ( (expcat_exp_accnt_id=pAccntid)
+     OR   (expcat_liability_accnt_id=pAccntid)
+     OR   (expcat_purchprice_accnt_id=pAccntid)
+     OR   (expcat_freight_accnt_id=pAccntid) )
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -6;
+  END IF;
+
+--  Check to see if the passed accnt is used in a Tax Code
+  SELECT tax_id INTO _check
+  FROM tax
+  WHERE (tax_sales_accnt_id=pAccntid)
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -7;
+  END IF;
+
+--  Check to see if the passed accnt is used in a Standard Journal Item
+  SELECT stdjrnlitem_id INTO _check
+  FROM stdjrnlitem
+  WHERE (stdjrnlitem_accnt_id=pAccntid)
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -8;
+  END IF;
+
+--  Check to see if the passed accnt is used in a A/P Account Assignment
+  SELECT apaccnt_ap_accnt_id INTO _check
+  FROM apaccnt
+  WHERE ( (apaccnt_ap_accnt_id=pAccntid)
+     OR   (apaccnt_prepaid_accnt_id=pAccntid)
+     OR   (apaccnt_discount_accnt_id=pAccntid) )
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -9;
+  END IF;
+
+--  Check to see if the passed accnt is used in an A/R Open Item record
+  SELECT aropen_accnt_id INTO _check
+    FROM aropen
+   WHERE (aropen_accnt_id=pAccntid)
+   LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -11;
+  END IF;
+
+--  Check to see if the passed accnt has been used in the G/L
+  SELECT gltrans_accnt_id INTO _check
+  FROM gltrans
+  WHERE (gltrans_accnt_id=pAccntid)
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -99;
+  END IF;
+
+  SELECT glseries_accnt_id INTO _check
+  FROM glseries
+  WHERE (glseries_accnt_id=pAccntid)
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -99;
+  END IF;
+
+  SELECT trialbal_accnt_id INTO _check
+  FROM trialbal
+  WHERE (trialbal_accnt_id=pAccntid)
+    AND (trialbal_beginning != 0 OR trialbal_ending != 0)
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -99;
+  END IF;
+
+  SELECT cashrcptmisc_accnt_id INTO _check
+  FROM cashrcptmisc
+  WHERE (cashrcptmisc_accnt_id=pAccntid)
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -99;
+  END IF;
+
+--  Delete any non-critical use
+  DELETE FROM flitem
+  WHERE (flitem_accnt_id=pAccntid);
+
+  -- only possible because of trialbal error-check above
+  DELETE FROM trialbal
+  WHERE (trialbal_accnt_id=pAccntid)
+    AND (trialbal_beginning=0)
+    AND (trialbal_ending=0);
+
+--  Delete the Account
+  DELETE FROM accnt
+  WHERE (accnt_id=pAccntid);
+
+  RETURN 0;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/deleteaccountingperiod.sql b/foundation-database/public/functions/deleteaccountingperiod.sql
new file mode 100644 (file)
index 0000000..b5a0958
--- /dev/null
@@ -0,0 +1,47 @@
+
+CREATE OR REPLACE FUNCTION deleteAccountingPeriod(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pPeriodid ALIAS FOR $1;
+  _check RECORD;
+
+BEGIN
+
+--  Check to make sure that the passed period is not closed
+  IF ( ( SELECT period_closed
+         FROM period
+         WHERE (period_id=pPeriodid) ) ) THEN
+    RETURN -1;
+  END IF;
+
+--  Check to make sure that there are not any posted G/L Transactions
+--  in the period.
+  SELECT gltrans_id INTO _check
+  FROM gltrans, period
+  WHERE ( (gltrans_date BETWEEN period_start AND period_end)
+   AND (gltrans_posted)
+   AND (period_id=pPeriodid) )
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -4;
+  END IF;
+
+  SELECT b.period_id INTO _check
+    FROM period AS a, period AS b
+   WHERE((a.period_id=pPeriodid)
+     AND (a.period_end < b.period_start))
+   LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -5;
+  END IF;
+
+--  Delete the period
+  DELETE FROM period
+  WHERE (period_id=pPeriodid);
+
+  RETURN 1;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/deleteaccountingyearperiod.sql b/foundation-database/public/functions/deleteaccountingyearperiod.sql
new file mode 100644 (file)
index 0000000..17ec9d0
--- /dev/null
@@ -0,0 +1,33 @@
+
+CREATE OR REPLACE FUNCTION deleteAccountingYearPeriod(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pPeriodid ALIAS FOR $1;
+  _check RECORD;
+
+BEGIN
+
+--  Check to make sure that the passed yearperiod is not closed
+  IF ( ( SELECT yearperiod_closed
+         FROM yearperiod
+         WHERE (yearperiod_id=pPeriodid) ) ) THEN
+    RETURN -1;
+  END IF;
+
+  -- this yearperiod is in use by existing periods
+  IF (EXISTS(SELECT period_id
+             FROM period
+             WHERE (period_yearperiod_id=pPeriodid))) THEN
+    RETURN -2;
+  END IF;
+
+--  Delete the yearperiod
+  DELETE FROM yearperiod
+  WHERE (yearperiod_id=pPeriodid);
+
+  RETURN 1;
+
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/deleteaddress.sql b/foundation-database/public/functions/deleteaddress.sql
new file mode 100644 (file)
index 0000000..ff7b568
--- /dev/null
@@ -0,0 +1,59 @@
+
+CREATE OR REPLACE FUNCTION deleteAddress(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  paddrId     ALIAS FOR $1;
+  _count      INTEGER := 0;
+BEGIN
+  SELECT count(*) INTO _count
+    FROM cntct
+    WHERE (cntct_active
+      AND  (cntct_addr_id = paddrId));
+  IF (_count > 0) THEN
+    RETURN -1;
+  END IF;
+
+  SELECT count(*) INTO _count
+    FROM vendinfo
+    WHERE (vend_active
+      AND  (vend_addr_id = paddrId));
+  IF (_count > 0) THEN
+    RETURN -2;
+  END IF;
+
+  SELECT count(*) INTO _count
+    FROM shiptoinfo
+    WHERE (shipto_active
+      AND  (shipto_addr_id = paddrId));
+  IF (_count > 0) THEN
+    RETURN -3;
+  END IF;
+
+  SELECT count(*) INTO _count
+    FROM vendaddrinfo
+    WHERE (vendaddr_addr_id = paddrId);
+  IF (_count > 0) THEN
+    RETURN -4;
+  END IF;
+
+  SELECT count(*) INTO _count
+    FROM whsinfo
+    WHERE (warehous_active
+      AND  (warehous_addr_id = paddrId));
+  IF (_count > 0) THEN
+    RETURN -5;
+  END IF;
+
+  UPDATE cntct SET cntct_addr_id = NULL WHERE (cntct_addr_id = paddrId);
+  UPDATE vendinfo SET vend_addr_id = NULL WHERE (vend_addr_id = paddrId);
+  UPDATE shiptoinfo SET shipto_addr_id = NULL WHERE (shipto_addr_id =paddrId);
+  UPDATE vendaddrinfo SET vendaddr_addr_id = NULL
+    WHERE (vendaddr_addr_id = paddrId);
+  UPDATE whsinfo SET warehous_addr_id = NULL WHERE (warehous_addr_id=paddrId);
+
+  DELETE FROM addr WHERE addr_id = paddrId;
+  RETURN 0;
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/deleteapcheck.sql b/foundation-database/public/functions/deleteapcheck.sql
new file mode 100644 (file)
index 0000000..e446a0e
--- /dev/null
@@ -0,0 +1,8 @@
+CREATE OR REPLACE FUNCTION deleteAPCheck(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RAISE NOTICE ''deleteAPCheck() is deprecated - use deleteCheck() instead'';
+  RETURN deleteCheck($1);
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/deletebankadjustmenttype.sql b/foundation-database/public/functions/deletebankadjustmenttype.sql
new file mode 100644 (file)
index 0000000..34b2106
--- /dev/null
@@ -0,0 +1,28 @@
+
+CREATE OR REPLACE FUNCTION deleteBankAdjustmentType(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pBankadjtypeid ALIAS FOR $1;
+  _check INTEGER;
+
+BEGIN
+
+-- Check to see if the the adjustment type is being used in any adjustments
+  SELECT bankadj_bankadjtype_id INTO _check
+    FROM bankadj
+   WHERE (bankadj_bankadjtype_id=pBankadjtypeid)
+   LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -1;
+  END IF;
+
+--  Delete the Account
+  DELETE FROM bankadjtype
+  WHERE (bankadjtype_id=pbankadjtypeid);
+
+  RETURN 0;
+
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/deletebankreconciliation.sql b/foundation-database/public/functions/deletebankreconciliation.sql
new file mode 100644 (file)
index 0000000..b6d444e
--- /dev/null
@@ -0,0 +1,17 @@
+
+CREATE OR REPLACE FUNCTION deleteBankReconciliation(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pbankrecid    ALIAS FOR $1;
+BEGIN
+  DELETE FROM bankrecitem
+  WHERE bankrecitem_bankrec_id=pbankrecid;
+
+  DELETE FROM bankrec
+  WHERE bankrec_id=pbankrecid;
+
+  RETURN 0;
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/deletebom.sql b/foundation-database/public/functions/deletebom.sql
new file mode 100644 (file)
index 0000000..acee7dd
--- /dev/null
@@ -0,0 +1,29 @@
+CREATE OR REPLACE FUNCTION deletebom(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemid ALIAS FOR $1;
+  _result INTEGER;
+
+BEGIN
+
+  IF (fetchmetricbool(''RevControl'')) THEN
+    SELECT rev_id INTO _result
+    FROM rev
+    WHERE ((rev_target_id=pItemid)
+    AND (rev_target_type = ''BOM''))
+    LIMIT 1;
+    IF (FOUND) THEN
+      RAISE EXCEPTION ''Bill of Materials has revision control records and may not be deleted.'';
+    END IF;
+  END IF;
+
+  DELETE FROM bomhead
+  WHERE (bomhead_item_id=pItemid);
+  DELETE FROM bomitem
+  WHERE (bomitem_parent_item_id=pItemid);
+
+  RETURN 0;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/deletebomworkset.sql b/foundation-database/public/functions/deletebomworkset.sql
new file mode 100644 (file)
index 0000000..01054fa
--- /dev/null
@@ -0,0 +1,16 @@
+CREATE OR REPLACE FUNCTION deleteBOMWorkset(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWorksetid ALIAS FOR $1;
+
+BEGIN
+
+--  All done with the bomwork set indicated by pWorksetid, delete all of it
+  DELETE FROM bomwork
+  WHERE (bomwork_set_id=pWorksetid);
+
+  RETURN 1;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/deletebudget.sql b/foundation-database/public/functions/deletebudget.sql
new file mode 100644 (file)
index 0000000..20aa8cb
--- /dev/null
@@ -0,0 +1,15 @@
+
+CREATE OR REPLACE FUNCTION deleteBudget(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pBudgheadid ALIAS FOR $1;
+
+BEGIN
+  DELETE FROM budgitem WHERE (budgitem_budghead_id=pBudgheadid);
+  DELETE FROM budghead WHERE (budghead_id=pBudgheadid);
+
+  RETURN pBudgheadid;
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/deletebudgetitems.sql b/foundation-database/public/functions/deletebudgetitems.sql
new file mode 100644 (file)
index 0000000..6f82227
--- /dev/null
@@ -0,0 +1,14 @@
+
+CREATE OR REPLACE FUNCTION deleteBudgetItems(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pBudgheadid ALIAS FOR $1;
+
+BEGIN
+  DELETE FROM budgitem WHERE (budgitem_budghead_id=pBudgheadid);
+
+  RETURN pBudgheadid;
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/deletecashrcpt.sql b/foundation-database/public/functions/deletecashrcpt.sql
new file mode 100644 (file)
index 0000000..e20033b
--- /dev/null
@@ -0,0 +1,60 @@
+CREATE OR REPLACE FUNCTION deleteCashrcpt(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pcashrcptid ALIAS FOR $1;
+  _ccreceipt    BOOLEAN;
+
+BEGIN
+
+  IF EXISTS(SELECT cashrcpt_id
+            FROM cashrcpt
+            JOIN ccpay ON (cashrcpt_cust_id=ccpay_cust_id)
+                       AND ((CASE WHEN TRIM(COALESCE(cashrcpt_docnumber, ''))='' THEN TEXT(cashrcpt_id)
+                                  ELSE cashrcpt_docnumber
+                             END)=ccpay_order_number)
+            WHERE ((cashrcpt_fundstype IN ('A', 'D', 'M', 'V'))
+               AND (ccpay_status NOT IN ('D', 'X'))
+               AND (ccpay_id NOT IN (SELECT payco_ccpay_id FROM payco))
+               AND (cashrcpt_id=pcashrcptid))) THEN
+    RETURN -1;
+  END IF;
+
+  IF EXISTS(SELECT cashrcpt_id
+            FROM cashrcpt
+            WHERE ( (cashrcpt_id=pcashrcptid)
+              AND   (cashrcpt_posted) )) THEN
+    RETURN -2;
+  END IF;
+
+  -- If there are applications for this Cash Receipt then
+  -- it has been posted and reversed.  Void instead of delete.
+  IF EXISTS(SELECT cashrcpt_id
+            FROM cashrcpt JOIN cashrcptitem ON (cashrcptitem_cashrcpt_id=cashrcpt_id)
+                          JOIN arapply ON ((arapply_reftype='CRA') AND
+                                           (arapply_ref_id=cashrcptitem_id))
+            WHERE (cashrcpt_id=pcashrcptid))
+     OR
+     EXISTS(SELECT cashrcpt_id
+            FROM cashrcpt JOIN cashrcptmisc ON (cashrcptmisc_cashrcpt_id=cashrcpt_id)
+                          JOIN arapply ON ((arapply_reftype='CRD') AND
+                                           (arapply_ref_id=cashrcptmisc_id))
+            WHERE (cashrcpt_id=pcashrcptid)) THEN
+    UPDATE cashrcpt SET cashrcpt_void = TRUE
+    WHERE (cashrcpt_id=pcashrcptid);
+    RETURN 1;
+  END IF;
+
+  DELETE FROM cashrcptitem
+  WHERE (cashrcptitem_cashrcpt_id=pcashrcptid);
+
+  DELETE FROM cashrcptmisc
+  WHERE (cashrcptmisc_cashrcpt_id=pcashrcptid);
+
+  DELETE FROM cashrcpt
+  WHERE (cashrcpt_id=pcashrcptid);
+
+  RETURN 1;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/deletecharacteristic.sql b/foundation-database/public/functions/deletecharacteristic.sql
new file mode 100644 (file)
index 0000000..dc5cb3e
--- /dev/null
@@ -0,0 +1,53 @@
+
+CREATE OR REPLACE FUNCTION deleteCharacteristic(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCharid ALIAS FOR $1;
+  _check INTEGER;
+  _r RECORD;
+
+BEGIN
+
+--  Cache the specifics of the characteristic
+  SELECT * INTO _r
+  FROM char
+  WHERE (char_id=pCharid);
+  IF (NOT(FOUND)) THEN
+    RETURN 0;
+  END IF;
+
+--  If the passed characteristic is used
+  SELECT * INTO _r
+  FROM charass
+  WHERE (charass_char_id=pCharid)
+  LIMIT 1;
+  IF (FOUND) THEN
+    IF (_r.charass_target_type = 'I') THEN
+      RETURN -1;
+    ELSIF (_r.charass_target_type = 'C') THEN
+      RETURN -2;
+    ELSIF (_r.charass_target_type = 'ADDR') THEN
+      RETURN -3;
+    ELSIF (_r.charass_target_type = 'CNTCT') THEN
+      RETURN -4;
+    ELSIF (_r.charass_target_type = 'CRMACCT') THEN
+      RETURN -5;
+    ELSIF (_r.charass_target_type = 'INCDT     ') THEN
+      RETURN -6;
+    ELSIF (_r.charass_target_type = 'EMP') THEN
+      RETURN -7;
+    ELSE
+      RETURN -99;
+    END IF;
+  END IF;
+
+--  Delete the passed characterisitic
+  DELETE FROM char
+  WHERE (char_id=pCharid);
+
+  RETURN pCharid;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/deletecheck.sql b/foundation-database/public/functions/deletecheck.sql
new file mode 100644 (file)
index 0000000..1424db3
--- /dev/null
@@ -0,0 +1,23 @@
+CREATE OR REPLACE FUNCTION deleteCheck(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCheckid ALIAS FOR $1;
+
+BEGIN
+  IF (SELECT (NOT checkhead_void) OR checkhead_posted OR checkhead_replaced
+              OR checkhead_deleted
+              OR (checkhead_ach_batch IS NOT NULL AND checkhead_printed)
+      FROM checkhead
+      WHERE (checkhead_id=pCheckid) ) THEN
+    RETURN -1;
+  END IF;
+
+  UPDATE checkhead
+  SET checkhead_deleted=TRUE
+  WHERE (checkhead_id=pCheckid);
+
+  RETURN 1;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/deleteclasscode.sql b/foundation-database/public/functions/deleteclasscode.sql
new file mode 100644 (file)
index 0000000..1971710
--- /dev/null
@@ -0,0 +1,26 @@
+CREATE OR REPLACE FUNCTION deleteClassCode(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pClasscodeid ALIAS FOR $1;
+  _check INTEGER;
+
+BEGIN
+
+--  Check to see if any items are assigned to the passed classcode
+  SELECT item_id INTO _check
+  FROM item
+  WHERE (item_classcode_id=pClasscodeid)
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -1;
+  END IF;
+
+--  Delete the passed classcode
+  DELETE FROM classcode
+  WHERE (classcode_id=pClasscodeid);
+
+  RETURN pClasscodeid;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/deletecompany.sql b/foundation-database/public/functions/deletecompany.sql
new file mode 100644 (file)
index 0000000..34979ea
--- /dev/null
@@ -0,0 +1,22 @@
+CREATE OR REPLACE FUNCTION deleteCompany(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pcompanyid ALIAS FOR $1;
+
+BEGIN
+  IF (EXISTS(SELECT accnt_id
+             FROM accnt, company
+             WHERE ((accnt_company=company_number)
+               AND  (company_id=pcompanyid))
+            )) THEN
+    RETURN -1;
+  END IF;
+
+  DELETE FROM company
+  WHERE (company_id=pcompanyid);
+
+  RETURN pcompanyid;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/deletecreditmemo.sql b/foundation-database/public/functions/deletecreditmemo.sql
new file mode 100644 (file)
index 0000000..bd9c473
--- /dev/null
@@ -0,0 +1,18 @@
+CREATE OR REPLACE FUNCTION deleteCreditMemo(INTEGER) RETURNS BOOLEAN AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCmheadid ALIAS FOR $1;
+
+BEGIN
+
+  DELETE FROM cmitem
+  WHERE (cmitem_cmhead_id=pCmheadid);
+
+  DELETE FROM cmhead
+  WHERE (cmhead_id=pCmheadid);
+
+  RETURN TRUE;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/deletecustomer.sql b/foundation-database/public/functions/deletecustomer.sql
new file mode 100644 (file)
index 0000000..97b2ed6
--- /dev/null
@@ -0,0 +1,92 @@
+
+CREATE OR REPLACE FUNCTION deleteCustomer(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCustid ALIAS FOR $1;
+
+BEGIN
+
+  PERFORM shipto_id
+  FROM shiptoinfo
+  WHERE (shipto_cust_id=pCustid)
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -1;
+  END IF;
+
+  PERFORM cohead_id
+  FROM cohead
+  WHERE (cohead_cust_id=pCustid)
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -2;
+  END IF;
+
+  PERFORM cmhead_id
+  FROM cmhead
+  WHERE (cmhead_cust_id=pCustid)
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -3;
+  END IF;
+
+  PERFORM cohist_id
+  FROM cohist
+  WHERE (cohist_cust_id=pCustid)
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -4;
+  END IF;
+
+  PERFORM aropen_id
+  FROM aropen
+  WHERE (aropen_cust_id=pCustid)
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -5;
+  END IF;
+
+  PERFORM checkhead_recip_id
+    FROM checkhead
+   WHERE ((checkhead_recip_id=pCustid)
+     AND  (checkhead_recip_type='C'))
+   LIMIT 1;
+   IF (FOUND) THEN
+     RETURN -6;
+   END IF;
+
+  PERFORM invchead_id
+     FROM invchead
+    WHERE(invchead_cust_id=pCustid)
+    LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -7;
+  END IF;
+
+  PERFORM quhead_id
+     FROM quhead
+    WHERE(quhead_cust_id=pCustid)
+    LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -8;
+  END IF;
+
+  DELETE FROM taxreg
+   WHERE ((taxreg_rel_type='C')
+     AND  (taxreg_rel_id=pCustid));
+
+  DELETE FROM ipsass
+  WHERE (ipsass_cust_id=pCustid);
+
+  DELETE FROM custinfo
+  WHERE (cust_id=pCustid);
+
+  UPDATE crmacct SET crmacct_cust_id = NULL
+  WHERE (crmacct_cust_id=pCustid);
+
+  RETURN 0;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/deletecustomertype.sql b/foundation-database/public/functions/deletecustomertype.sql
new file mode 100644 (file)
index 0000000..003bf84
--- /dev/null
@@ -0,0 +1,35 @@
+
+CREATE OR REPLACE FUNCTION deleteCustomerType(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCusttypeid ALIAS FOR $1;
+
+BEGIN
+
+  IF EXISTS(SELECT 1
+              FROM custinfo
+             WHERE (cust_custtype_id=pCusttypeid)) THEN
+    RETURN -1;
+  END IF;
+
+  DELETE FROM ipsass
+  WHERE (ipsass_custtype_id=pCusttypeid);
+
+  DELETE FROM salesaccnt
+  WHERE (salesaccnt_custtype_id=pCusttypeid);
+
+  DELETE FROM araccnt
+  WHERE (araccnt_custtype_id=pCusttypeid);
+
+  DELETE FROM custform
+  WHERE (custform_custtype_id=pCusttypeid);
+
+  DELETE FROM custtype
+  WHERE (custtype_id=pCusttypeid);
+
+  RETURN pCusttypeid;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/deleteempgrp.sql b/foundation-database/public/functions/deleteempgrp.sql
new file mode 100644 (file)
index 0000000..623441e
--- /dev/null
@@ -0,0 +1,21 @@
+CREATE OR REPLACE FUNCTION deleteEmpGrp(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pempgrpid ALIAS FOR $1;
+
+BEGIN
+--  Check to see if any employees are assigned to the passed empgrp
+  PERFORM empgrpitem_emp_id
+  FROM empgrpitem
+  WHERE (empgrpitem_empgrp_id=pempgrpid)
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -1;
+  END IF;
+
+  DELETE FROM empgrp     WHERE (empgrp_id=pempgrpid);
+
+  RETURN 0;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/deleteexpiredips.sql b/foundation-database/public/functions/deleteexpiredips.sql
new file mode 100644 (file)
index 0000000..df2f724
--- /dev/null
@@ -0,0 +1,27 @@
+CREATE OR REPLACE FUNCTION deleteexpiredips() RETURNS BOOLEAN AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _r RECORD;
+
+BEGIN
+
+  FOR _r IN SELECT ipshead_id
+    FROM ipshead
+    WHERE (ipshead_expires <= current_date)
+  LOOP
+
+    DELETE FROM ipsass
+      WHERE (ipsass_ipshead_id=_r.ipshead_id);
+    DELETE FROM ipsiteminfo
+      WHERE (ipsitem_ipshead_id=_r.ipshead_id);
+    DELETE FROM ipsfreight
+      WHERE (ipsfreight_ipshead_id=_r.ipshead_id);
+    DELETE FROM ipshead
+      WHERE (ipshead_id=_r.ipshead_id);
+  END LOOP;
+
+  RETURN TRUE;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/deletefile.sql b/foundation-database/public/functions/deletefile.sql
new file mode 100644 (file)
index 0000000..9127b32
--- /dev/null
@@ -0,0 +1,17 @@
+create or replace function deleteFile(integer) returns boolean as $$
+declare
+  pId ALIAS FOR $1;
+begin
+  delete from file
+  where ( file_id in (
+    select file_id
+    from file
+      join docass on (docass_target_id=file_id)
+                 and (docass_target_type='FILE')
+    where ( docass_id = pId ) ) );
+
+  delete from docass where docass_id = pId;
+
+  return true;
+end;
+$$ language 'plpgsql';
diff --git a/foundation-database/public/functions/deleteflgroup.sql b/foundation-database/public/functions/deleteflgroup.sql
new file mode 100644 (file)
index 0000000..70a3514
--- /dev/null
@@ -0,0 +1,28 @@
+CREATE OR REPLACE FUNCTION deleteFlgrp(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pFlgrpid ALIAS FOR $1;
+  _r RECORD;
+
+BEGIN
+
+  FOR _r IN SELECT flgrp_id
+            FROM flgrp
+            WHERE (flgrp_flgrp_id=pFlgrpid) LOOP
+    PERFORM deleteFlgrp(_r.flgrp_id);
+  END LOOP;
+
+  DELETE FROM flitem
+  WHERE (flitem_flgrp_id=pFlgrpid);
+
+  DELETE FROM flspec
+  WHERE (flspec_flgrp_id=pFlgrpid);
+
+  DELETE FROM flgrp
+  WHERE (flgrp_id=pFlgrpid);
+
+  RETURN 1;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/deleteform.sql b/foundation-database/public/functions/deleteform.sql
new file mode 100644 (file)
index 0000000..4d79f20
--- /dev/null
@@ -0,0 +1,40 @@
+
+CREATE OR REPLACE FUNCTION deleteForm(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pFormid ALIAS FOR $1;
+  _key TEXT;
+  _check INTEGER;
+
+BEGIN
+
+--  Cache the key of the passed form
+  SELECT form_key INTO _key
+  FROM form
+  WHERE (form_id=pFormid);
+  IF (NOT(FOUND)) THEN
+    RETURN 0;
+  END IF;
+
+--  Handle checks based on the type of the form
+  IF (_key=''Chck'') THEN
+    SELECT bankaccnt_id INTO _check
+    FROM bankaccnt
+    WHERE (bankaccnt_check_form_id=pFormid)
+    LIMIT 1;
+    IF (FOUND) THEN
+      RETURN -1;
+    END IF;
+
+  END IF;
+
+--  Delete the form
+  DELETE FROM form
+  WHERE (form_id=pFormid);
+
+  RETURN pFormid;
+
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/deletefreightclass.sql b/foundation-database/public/functions/deletefreightclass.sql
new file mode 100644 (file)
index 0000000..ddf83c7
--- /dev/null
@@ -0,0 +1,26 @@
+CREATE OR REPLACE FUNCTION deleteFreightClass(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pFreightClassid ALIAS FOR $1;
+  _check INTEGER;
+
+BEGIN
+
+--  Check to see if any items are assigned to the passed freightclass
+  SELECT item_id INTO _check
+  FROM item
+  WHERE (item_freightclass_id=pFreightClassid)
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -1;
+  END IF;
+
+--  Delete the passed freightclass
+  DELETE FROM freightclass
+  WHERE (freightclass_id=pFreightClassid);
+
+  RETURN pFreightClassid;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/deleteglseries.sql b/foundation-database/public/functions/deleteglseries.sql
new file mode 100644 (file)
index 0000000..dbdb47b
--- /dev/null
@@ -0,0 +1,97 @@
+CREATE OR REPLACE FUNCTION deleteGlSeries(INTEGER, TEXT) RETURNS BOOLEAN AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSequence ALIAS FOR $1;
+  pNotes ALIAS FOR $2;
+  _trialbalid INTEGER;
+  _count INTEGER;
+  _r RECORD;
+
+BEGIN
+
+--  March through all of the G/L Transactions for the passed sequence
+  FOR _r IN SELECT gltrans_id, gltrans_date, gltrans_accnt_id, gltrans_amount, gltrans_posted, gltrans_rec,
+                   accnt_forwardupdate, period_id, period_closed, period_freeze
+            FROM accnt, gltrans LEFT OUTER JOIN period ON (gltrans_date BETWEEN period_start AND period_end)
+            WHERE ( (gltrans_accnt_id=accnt_id)
+             AND (NOT gltrans_deleted)
+             AND (gltrans_sequence=pSequence) ) LOOP
+
+--  If we can post into a Trial Balance, do so
+    IF ( (NOT _r.period_closed) AND 
+       ( (NOT _r.period_freeze) OR (checkPrivilege('PostFrozenPeriod')) ) AND
+       (  NOT _r.gltrans_rec) AND
+       ( _r.gltrans_posted ) ) THEN
+
+--  Try to find an existing trialbal
+      SELECT trialbal_id INTO _trialbalid
+      FROM trialbal
+      WHERE ( (trialbal_period_id=_r.period_id)
+       AND (trialbal_accnt_id=_r.gltrans_accnt_id) );
+
+      GET DIAGNOSTICS _count = ROW_COUNT;
+      IF (_count > 0) THEN
+
+--  We found a trialbal, update it with the G/L Transaction
+--  Note - two stage update to avoid any funny value caching logic
+        IF (_r.gltrans_amount > 0) THEN
+          UPDATE trialbal
+          SET trialbal_credits = (trialbal_credits - _r.gltrans_amount)
+          WHERE (trialbal_id=_trialbalid);
+        ELSE
+          UPDATE trialbal
+          SET trialbal_debits = (trialbal_debits - (_r.gltrans_amount * -1))
+          WHERE (trialbal_id=_trialbalid);
+        END IF;
+
+        UPDATE trialbal
+        SET trialbal_ending = (trialbal_beginning - trialbal_debits + trialbal_credits),
+            trialbal_dirty=TRUE
+        WHERE (trialbal_id=_trialbalid);
+
+      ELSE
+        RAISE EXCEPTION 'Can not delete G/L Series.  Trial balance record not found.';
+      END IF;
+
+--  Forward update if we should
+      IF (_r.accnt_forwardupdate AND fetchmetricbool('ManualForwardUpdate')) THEN
+        PERFORM forwardUpdateTrialBalance(_trialbalid);
+      END IF;
+
+--  Delete any bank reconciliation records if this was marked cleared but non reconciled
+    DELETE FROM bankrecitem
+    WHERE ((bankrecitem_source='GL')
+    AND (bankrecitem_source_id=_r.gltrans_id));
+
+--  Unflag any journals as posted as a result of this series
+    UPDATE sltrans SET
+      sltrans_posted=false,
+      sltrans_gltrans_journalnumber=null
+    FROM gltrans
+    WHERE ((gltrans_sequence=pSequence)
+    AND (sltrans_gltrans_journalnumber=gltrans_journalnumber));
+    
+--  Mark the G/L Transaction as deleted
+      UPDATE gltrans SET
+        gltrans_posted=false,
+        gltrans_deleted=true,
+        gltrans_notes=gltrans_notes || E'\n' || pNotes
+      WHERE (gltrans_id=_r.gltrans_id);
+
+    ELSIF (_r.period_freeze) THEN
+        RAISE EXCEPTION 'Can not delete a G/L Transaction in a frozen period';
+    ELSIF (_r.period_closed) THEN
+        RAISE EXCEPTION 'Can not delete a G/L Transaction on account % in a closed period', formatGlAccount(_r.gltrans_accnt_id);
+    ELSIF (_r.gltrans_rec) THEN
+        RAISE EXCEPTION 'Can not delete a G/L Transaction that has been reconciled';
+    ELSIF (NOT _r.gltrans_posted) THEN
+        RAISE EXCEPTION 'Can not delete a G/L Transaction that has not been posted to Trial Balance';
+    END IF;
+
+  END LOOP;
+
+  RETURN true;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/deleteincident.sql b/foundation-database/public/functions/deleteincident.sql
new file mode 100644 (file)
index 0000000..fc38c21
--- /dev/null
@@ -0,0 +1,43 @@
+CREATE OR REPLACE FUNCTION deleteIncident(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pincdtid    ALIAS FOR $1;
+  _count      INTEGER := 0;
+  _incdtnbr   INTEGER := 0;
+BEGIN
+  SELECT COUNT(*) INTO _count
+  FROM todoitem
+  WHERE (todoitem_incdt_id=pincdtid);
+  IF (_count > 0) THEN
+    RETURN -1;
+  END IF;
+
+  DELETE FROM comment
+   WHERE((comment_source='INCDT')
+     AND (comment_source_id=pincdtid));
+
+  DELETE FROM incdthist
+   WHERE (incdthist_incdt_id=pincdtid);
+
+  DELETE FROM imageass
+  WHERE ((imageass_source='INCDT')
+     AND (imageass_source_id=pincdtid));
+
+  DELETE FROM url
+  WHERE ((url_source='INCDT')
+     AND (url_source_id=pincdtid));
+
+  SELECT incdt_number INTO _incdtnbr
+  FROM incdt
+  WHERE (incdt_id=pincdtid);
+
+  DELETE FROM incdt
+    WHERE (incdt_id=pincdtid);
+
+-- Incident #11538 needs to be fully resolved before release can be implemented
+--    PERFORM releaseIncidentNumber(_incdtnbr);
+
+  RETURN 0;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/deleteinvoice.sql b/foundation-database/public/functions/deleteinvoice.sql
new file mode 100644 (file)
index 0000000..0287028
--- /dev/null
@@ -0,0 +1,49 @@
+CREATE OR REPLACE FUNCTION deleteInvoice(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pInvcheadid ALIAS FOR $1;
+
+BEGIN
+  UPDATE shipitem SET shipitem_invoiced=FALSE, shipitem_invcitem_id=NULL
+  FROM invcitem
+  WHERE ((shipitem_invoiced)
+    AND  (shipitem_invcitem_id=invcitem_id)
+    AND  (invcitem_invchead_id=pInvcheadid));
+
+  UPDATE coitem SET coitem_status = 'O'
+  WHERE ((coitem_status = 'C')
+    AND  (coitem_id IN (SELECT cobill_coitem_id
+                       FROM cobill, invcitem
+                       WHERE ((cobill_invcitem_id=invcitem_id)
+                         AND  (invcitem_invchead_id=pInvcheadid)))));
+
+  UPDATE cobill SET cobill_invcnum=NULL, cobill_invcitem_id=NULL
+  FROM invcitem
+  WHERE ((cobill_invcitem_id=invcitem_id)
+    AND  (invcitem_invchead_id=pInvcheadid));
+
+  UPDATE invdetail SET invdetail_invcitem_id=NULL
+  FROM invcitem
+  WHERE ((invdetail_invcitem_id=invcitem_id)
+    AND  (invcitem_invchead_id=pInvcheadid));
+
+  UPDATE cobmisc SET cobmisc_invcnumber=NULL, cobmisc_invchead_id=NULL,
+                    cobmisc_posted=FALSE
+  WHERE (cobmisc_invchead_id=pInvcheadid);
+
+  DELETE FROM aropenalloc
+  WHERE (aropenalloc_doctype='I')
+    AND (aropenalloc_doc_id=pInvcheadid);
+
+  DELETE FROM invcitem
+  WHERE (invcitem_invchead_id=pInvcheadid);
+
+  DELETE FROM invchead
+  WHERE (invchead_id=pInvcheadid);
+
+  RETURN pInvcheadid;
+
+END;
+$$ LANGUAGE plpgsql;
+
diff --git a/foundation-database/public/functions/deleteipsitem.sql b/foundation-database/public/functions/deleteipsitem.sql
new file mode 100644 (file)
index 0000000..1f99772
--- /dev/null
@@ -0,0 +1,13 @@
+CREATE OR REPLACE FUNCTION deleteIpsItem(pIpsItemId INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+
+BEGIN
+
+  DELETE FROM ipsiteminfo WHERE ipsitem_id=pIpsItemId;
+
+  RETURN 1;
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/deleteipsprodcat.sql b/foundation-database/public/functions/deleteipsprodcat.sql
new file mode 100644 (file)
index 0000000..fc0e83e
--- /dev/null
@@ -0,0 +1,13 @@
+CREATE OR REPLACE FUNCTION deleteIpsProdCat(pIpsItemId INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+
+BEGIN
+
+  DELETE FROM ipsiteminfo WHERE ipsitem_id=pIpsItemId;
+  
+  RETURN 1;
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/deleteitem.sql b/foundation-database/public/functions/deleteitem.sql
new file mode 100644 (file)
index 0000000..081539e
--- /dev/null
@@ -0,0 +1,101 @@
+CREATE OR REPLACE FUNCTION deleteItem(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemid ALIAS FOR $1;
+  _result INTEGER;
+
+BEGIN
+
+  SELECT bomitem_id INTO _result
+  FROM bomitem
+  WHERE (bomitem_item_id=pItemid)
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -1;
+  END IF;
+
+  SELECT itemsite_id INTO _result
+  FROM itemsite
+  WHERE (itemsite_item_id=pItemid)
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -2;
+  END IF;
+
+  SELECT itemsub_id INTO _result
+  FROM itemsub
+  WHERE (itemsub_sub_item_id=pItemid)
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -3;
+  END IF;
+
+  IF (fetchmetricbool('RevControl')) THEN
+    SELECT rev_id INTO _result
+    FROM rev
+    WHERE ((rev_target_id=pItemid)
+    AND (rev_target_type = 'BOM'))
+    LIMIT 1;
+    IF (FOUND) THEN
+      RETURN -6;
+    END IF;
+  END IF;
+
+  DELETE FROM bomhead
+  WHERE (bomhead_item_id=pItemid);
+  DELETE FROM bomitem
+  WHERE (bomitem_item_id=pItemid);
+
+  DELETE FROM itemcost
+  WHERE (itemcost_item_id=pItemid);
+  DELETE FROM costhist
+  WHERE (costhist_item_id=pItemid);
+
+  DELETE FROM itemsub
+  WHERE (itemsub_parent_item_id=pItemid);
+  DELETE FROM itemsub
+  WHERE (itemsub_sub_item_id=pItemid);
+
+  DELETE FROM itemsrcp
+  WHERE (itemsrcp_itemsrc_id IN (SELECT itemsrc_id FROM itemsrc WHERE (itemsrc_item_id=pItemid)));
+  DELETE FROM itemsrc
+  WHERE (itemsrc_item_id=pItemid);
+
+  DELETE FROM itemalias
+  WHERE (itemalias_item_id=pItemid);
+
+  DELETE FROM itemgrpitem
+  WHERE (itemgrpitem_item_id=pItemid);
+
+  DELETE FROM ipsiteminfo
+  WHERE (ipsitem_item_id=pItemid);
+
+  DELETE FROM imageass
+  WHERE ( (imageass_source='I')
+    AND   (imageass_source_id=pItemid) );
+
+  DELETE FROM locitem
+  WHERE (locitem_item_id=pItemid);
+
+  DELETE FROM itemtax
+   WHERE(itemtax_item_id=pItemid);
+
+  DELETE FROM itemsite
+  WHERE (itemsite_item_id=pItemid);
+
+  DELETE FROM itemuom
+   WHERE(itemuom_itemuomconv_id IN (SELECT itemuomconv_id
+                                      FROM itemuomconv
+                                     WHERE(itemuomconv_item_id=pItemid)));
+
+  DELETE FROM itemuomconv
+   WHERE(itemuomconv_item_id=pItemid);
+
+  DELETE FROM item
+  WHERE (item_id=pItemid);
+
+  RETURN 0;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/deleteitemcost.sql b/foundation-database/public/functions/deleteitemcost.sql
new file mode 100644 (file)
index 0000000..a81803f
--- /dev/null
@@ -0,0 +1,36 @@
+CREATE OR REPLACE FUNCTION deleteItemCost(INTEGER, INTEGER) RETURNS INTEGER AS $BODY$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemId ALIAS FOR $1;
+  pCostElemId ALIAS FOR $2;
+  _itemcost_id INTEGER;
+  _postcost_return BOOLEAN;
+  _std_cost NUMERIC;
+
+BEGIN
+  SELECT itemcost_id INTO _itemcost_id
+  FROM itemcost
+  WHERE ( (itemcost_item_id = pItemId) AND (itemcost_costelem_id = pCostElemId) );
+
+  IF (NOT FOUND) THEN
+       RAISE EXCEPTION 'itemcost % not found for. ', pItemId || ' & ' || pCostElemId;
+  END IF;
+
+  SELECT itemcost_stdcost INTO _std_cost
+  FROM itemcost
+  WHERE (itemcost_id = _itemcost_id);
+
+  IF (_std_cost > 0) THEN
+--Actual Cost is updated to zero to ensure inventory is valued correctly
+    PERFORM updateCost(_itemcost_id, 0);
+  END IF;
+
+  DELETE FROM itemcost
+  WHERE (itemcost_id=_itemcost_id);
+  RETURN _itemcost_id; 
+
+END;
+$BODY$
+  LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/deleteitemsite.sql b/foundation-database/public/functions/deleteitemsite.sql
new file mode 100644 (file)
index 0000000..0365d3e
--- /dev/null
@@ -0,0 +1,213 @@
+CREATE OR REPLACE FUNCTION deleteItemSite(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  _result INTEGER;
+  _lotserial BOOLEAN;
+  _bbom BOOLEAN;
+  _mfg BOOLEAN;
+  _standard BOOLEAN;
+
+BEGIN
+
+  IF ( ( SELECT ( (itemsite_qtyonhand <> 0) OR (itemsite_nnqoh <> 0) )
+         FROM itemsite
+         WHERE (itemsite_id=pItemsiteid) ) ) THEN
+    RETURN -9;
+  END IF;
+
+  SELECT metric_value='t' INTO _bbom
+    FROM metric
+   WHERE (metric_name='BBOM');
+
+  SELECT metric_value='t' INTO _lotserial
+    FROM metric
+   WHERE (metric_name='LotSerialControl');
+
+  SELECT metric_value NOT IN ('PostBooks', 'Standard') INTO _mfg
+    FROM metric
+   WHERE (metric_name='Application');
+
+  SELECT metric_value='Standard' INTO _standard
+    FROM metric
+   WHERE (metric_name='Application');
+
+  SELECT invhist_id INTO _result
+  FROM invhist
+  WHERE (invhist_itemsite_id=pItemsiteid)
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -1;
+  END IF;
+
+  IF (_lotserial) THEN
+    SELECT lsdetail_id INTO _result
+    FROM lsdetail
+    WHERE (lsdetail_itemsite_id=pItemsiteid)
+    LIMIT 1;
+    IF (FOUND) THEN
+      RETURN -1;
+    END IF;
+  END IF;
+
+  SELECT wo_id INTO _result
+  FROM wo
+  WHERE (wo_itemsite_id=pItemsiteid)
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -2;
+  END IF;
+
+  SELECT womatl_id INTO _result
+  FROM womatl
+  WHERE (womatl_itemsite_id=pItemsiteid)
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -2;
+  END IF;
+
+  SELECT womatlvar_id INTO _result
+  FROM womatlvar
+  WHERE ( (womatlvar_parent_itemsite_id=pItemsiteid)
+   OR (womatlvar_component_itemsite_id=pItemsiteid) )
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -2;
+  END IF;
+
+  IF (_bbom) THEN
+    SELECT brdvar_id INTO _result
+    FROM xtmfg.brdvar
+    WHERE ( (brdvar_itemsite_id=pItemsiteid)
+     OR (brdvar_parent_itemsite_id=pItemsiteid) )
+    LIMIT 1;
+    IF (FOUND) THEN
+      RETURN -2;
+    END IF;
+  END IF;
+
+  SELECT coitem_id INTO _result
+  FROM coitem
+  WHERE (coitem_itemsite_id=pItemsiteid)
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -3;
+  END IF;
+
+  SELECT cohist_id INTO _result
+  FROM cohist
+  WHERE (cohist_itemsite_id=pItemsiteid)
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -3;
+  END IF;
+
+  SELECT quitem_id INTO _result
+  FROM quitem
+  WHERE (quitem_itemsite_id=pItemsiteid)
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -3;
+  END IF;
+
+  SELECT cmitem_id INTO _result
+  FROM cmitem
+  WHERE (cmitem_itemsite_id=pItemsiteid)
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -3;
+  END IF;
+
+
+  SELECT poitem_id INTO _result
+  FROM poitem
+  WHERE (poitem_itemsite_id=pItemsiteid)
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -4;
+  END IF;
+
+  SELECT recv_id INTO _result
+  FROM recv
+  WHERE (recv_itemsite_id=pItemsiteid)
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -4;
+  END IF;
+
+  SELECT poreject_id INTO _result
+  FROM poreject
+  WHERE (poreject_itemsite_id=pItemsiteid)
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -4;
+  END IF;
+
+  SELECT pr_id INTO _result
+  FROM pr
+  WHERE (pr_itemsite_id=pItemsiteid)
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -4;
+  END IF;
+
+  IF (_mfg OR _standard) THEN
+    SELECT planord_id INTO _result
+    FROM planord
+    WHERE (planord_itemsite_id=pItemsiteid)
+    LIMIT 1;
+    IF (FOUND) THEN
+      RETURN -5;
+    END IF;
+  END IF;
+
+  IF (_mfg) THEN
+    SELECT pschitem_id INTO _result
+    FROM xtmfg.pschitem
+    WHERE (pschitem_itemsite_id=pItemsiteid)
+    LIMIT 1;
+    IF (FOUND) THEN
+      RETURN -6;
+    END IF;
+
+    SELECT woopervar_id INTO _result
+    FROM xtmfg.woopervar
+    WHERE (woopervar_parent_itemsite_id=pItemsiteid)
+    LIMIT 1;
+    IF (FOUND) THEN
+      RETURN -2;
+    END IF;
+    
+  END IF;
+
+  IF (_mfg OR _standard) THEN
+    SELECT itemsite_id INTO _result
+    FROM itemsite
+    WHERE (itemsite_supply_itemsite_id=pItemsiteid)
+    LIMIT 1;
+    IF (FOUND) THEN
+      RETURN -7;
+    END IF;
+  END IF;
+
+  DELETE FROM invcnt
+  WHERE (invcnt_itemsite_id=pItemsiteid);
+
+  DELETE FROM itemloc
+  WHERE (itemloc_itemsite_id=pItemsiteid);
+  DELETE FROM itemlocdist
+  WHERE (itemlocdist_itemsite_id=pItemsiteid);
+
+  IF (_bbom) THEN
+    DELETE FROM xtmfg.brddist
+    WHERE (brddist_itemsite_id=pItemsiteid);
+  END IF;
+
+  DELETE FROM itemsite
+  WHERE (itemsite_id=pItemsiteid);
+
+  RETURN 0;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/deleteitemuom.sql b/foundation-database/public/functions/deleteitemuom.sql
new file mode 100644 (file)
index 0000000..6534c51
--- /dev/null
@@ -0,0 +1,12 @@
+CREATE OR REPLACE FUNCTION deleteItemUOM(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemuomid ALIAS FOR $1;
+
+BEGIN
+  DELETE FROM itemuom WHERE itemuom_id=pItemuomid;
+
+  RETURN 0;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/deleteitemuomconv.sql b/foundation-database/public/functions/deleteitemuomconv.sql
new file mode 100644 (file)
index 0000000..da09d02
--- /dev/null
@@ -0,0 +1,30 @@
+CREATE OR REPLACE FUNCTION deleteItemUOMConv(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemuomconvid ALIAS FOR $1;
+  _fromuomid     INTEGER;
+  _invuomid      INTEGER;
+  _itemid        INTEGER;
+  _touomid       INTEGER;
+
+BEGIN
+  SELECT itemuomconv_item_id, item_inv_uom_id,
+         itemuomconv_from_uom_id, itemuomconv_to_uom_id
+          INTO _itemid, _invuomid, _fromuomid, _touomid
+  FROM itemuomconv JOIN item ON (itemuomconv_item_id=item_id)
+  WHERE (itemuomconv_id=pItemuomconvid);
+
+  IF EXISTS(SELECT *
+            FROM uomusedforitem(_itemid)
+            WHERE ((uom_id IN (_fromuomid, _touomid))
+               AND (uom_id != _invuomid)) ) THEN
+    RETURN -1;
+  END IF;
+
+  DELETE FROM itemuom WHERE itemuom_itemuomconv_id=pItemuomconvid;
+  DELETE FROM itemuomconv WHERE itemuomconv_id=pItemuomconvid;
+
+  RETURN 0;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/deletelocation.sql b/foundation-database/public/functions/deletelocation.sql
new file mode 100644 (file)
index 0000000..98b5c4e
--- /dev/null
@@ -0,0 +1,58 @@
+CREATE OR REPLACE FUNCTION deleteLocation(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pLocationid ALIAS FOR $1;
+  _check INTEGER;
+
+BEGIN
+
+--  Check to see if any itemsite used the passed location as their default
+  SELECT itemsite_id INTO _check
+  FROM itemsite
+  WHERE (itemsite_location_id=pLocationid)
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -1;
+  END IF;
+
+--  Check to see if any inventory is currently stored at the passed location
+  SELECT itemloc_id INTO _check
+  FROM itemloc
+  WHERE (itemloc_location_id=pLocationid)
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -2;
+  END IF;
+
+--  Check to see if any undistributed inventory transactions are currently posted at the passed location
+  SELECT itemlocdist_id INTO _check
+  FROM itemlocdist
+  WHERE ( (itemlocdist_source_type=''L'')
+  AND (itemlocdist_source_id=pLocationid) )
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -3;
+  END IF;
+
+--  Check to see if the passed location has any Inventory Detail posted against it
+  SELECT invdetail_id INTO _check
+  FROM invdetail
+  WHERE (invdetail_location_id=pLocationid)
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -4;
+  END IF;
+
+--  Delete any associated locitem records
+  DELETE FROM locitem
+  WHERE (locitem_location_id=pLocationid);
+
+--  Delete the location record
+  DELETE FROM location
+  WHERE (location_id=pLocationid);
+
+  RETURN pLocationid;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/deletemetasql.sql b/foundation-database/public/functions/deletemetasql.sql
new file mode 100644 (file)
index 0000000..1337453
--- /dev/null
@@ -0,0 +1,10 @@
+CREATE OR REPLACE FUNCTION deleteMetaSQL(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pid ALIAS FOR $1;
+BEGIN
+  DELETE FROM metasql WHERE metasql_id = pid;
+  RETURN 0;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/deleteopenrecurringitems.sql b/foundation-database/public/functions/deleteopenrecurringitems.sql
new file mode 100644 (file)
index 0000000..132b9b0
--- /dev/null
@@ -0,0 +1,116 @@
+CREATE OR REPLACE FUNCTION deleteOpenRecurringItems(INTEGER, TEXT, TIMESTAMP WITH TIME ZONE, BOOLEAN) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pParentid     ALIAS FOR $1;
+  pType         TEXT                     := UPPER($2);
+  pDatetime     TIMESTAMP WITH TIME ZONE := COALESCE($3, startOfTime());
+  pInclParent   BOOLEAN                  := COALESCE($4, FALSE);
+
+  _count         INTEGER := 0;
+  _delchildstmt  TEXT;
+  _delparentstmt TEXT;
+  _rt            RECORD;
+  _tmp           INTEGER;
+BEGIN
+  RAISE DEBUG 'deleteOpenRecurringItems(%, %, %)', pParentid, pType, pDatetime;
+
+  IF (pParentid IS NULL) THEN
+    RETURN -11;
+  END IF;
+
+  SELECT * INTO _rt FROM recurtype WHERE (UPPER(recurtype_type)=pType);
+  GET DIAGNOSTICS _count = ROW_COUNT;
+  IF (_count <= 0) THEN
+    RETURN -10;
+  END IF;
+
+  -- 2 deletes avoid reparenting problems if the parent gets deleted first
+  IF (_rt.recurtype_delfunc IS NULL) THEN
+    _delchildstmt := 'DELETE FROM [fulltable] '
+                  || ' WHERE (NOT ([done])'
+                  || '    AND ([schedcol]>''$2'')'
+                  || '    AND ([table]_recurring_[table]_id=$1)'
+                  || '    AND ([table]_id!=$1));';
+
+    _delparentstmt := 'DELETE FROM [fulltable] USING recur'
+                   || ' WHERE (NOT ([done])'
+                   || '    AND ([schedcol]>''$2'')'
+                   || '    AND ([table]_recurring_[table]_id=$1)'
+                   || '    AND ([table]_id=$1));';
+
+  ELSE
+    _delchildstmt := 'SELECT [delfunc]([table]_id)'
+                  || '  FROM [fulltable] '
+                  || ' WHERE (NOT ([done])'
+                  || '    AND ([schedcol]>''$2'')'
+                  || '    AND ([table]_recurring_[table]_id=$1)'
+                  || '    AND ([table]_id!=$1));';
+    _delparentstmt := 'SELECT [delfunc]([table]_id)'
+                   || '  FROM [fulltable] '
+                   || ' WHERE (NOT ([done])'
+                   || '    AND ([schedcol]>''$2'')'
+                   || '    AND ([table]_recurring_[table]_id=$1)'
+                   || '    AND ([table]_id!=$1));';
+    _delchildstmt  := REPLACE(_delchildstmt,  '[delfunc]', _rt.recurtype_delfunc);
+    _delparentstmt := REPLACE(_delparentstmt, '[delfunc]', _rt.recurtype_delfunc);
+  END IF;
+
+  RAISE DEBUG '_delchildstmt has been set to %', _delchildstmt;
+
+  _delchildstmt := REPLACE(_delchildstmt, '[fulltable]', _rt.recurtype_table);
+  _delchildstmt := REPLACE(_delchildstmt, '[table]',
+                            REGEXP_REPLACE(_rt.recurtype_table, E'.*\\.', ''));
+  _delchildstmt := REPLACE(_delchildstmt, '[done]',  _rt.recurtype_donecheck);
+  _delchildstmt := REPLACE(_delchildstmt, '[schedcol]', _rt.recurtype_schedcol);
+
+  _delparentstmt := REPLACE(_delparentstmt, '[fulltable]', _rt.recurtype_table);
+  _delparentstmt := REPLACE(_delparentstmt, '[table]',
+                            REGEXP_REPLACE(_rt.recurtype_table, E'.*\\.', ''));
+  _delparentstmt := REPLACE(_delparentstmt, '[done]',  _rt.recurtype_donecheck);
+  _delparentstmt := REPLACE(_delparentstmt, '[schedcol]', _rt.recurtype_schedcol);
+
+  RAISE DEBUG 'substitutions changed _delchildstmt to %', _delchildstmt;
+
+  IF (_rt.recurtype_delfunc IS NULL) THEN
+    -- 8.4+: EXECUTE _delchildstmt  USING pDatetime, pType;
+    RAISE DEBUG '% with % and %', _delchildstmt, pType, pDatetime;
+    EXECUTE REPLACE(REPLACE(_delchildstmt, '$1', pParentid::TEXT),
+                                           '$2', pDatetime::TEXT);
+    GET DIAGNOSTICS _count = ROW_COUNT;
+
+    IF (pInclParent) THEN
+      -- 8.4+: EXECUTE _delparentstmt USING pDatetime, pType;
+      RAISE DEBUG '% with % and %', _delparentstmt, pType, pDatetime;
+      EXECUTE REPLACE(REPLACE(_delparentstmt, '$1', pParentid::TEXT),
+                                              '$2', pDatetime::TEXT);
+      GET DIAGNOSTICS _tmp   = ROW_COUNT;
+      _count := _count + _tmp;
+    END IF;
+
+  ELSE
+    -- 8.4+: FOR _tmp IN EXECUTE _delchildstmt USING pDatetime, pType LOOP
+    FOR _tmp IN EXECUTE REPLACE(REPLACE(_delchildstmt, '$1', pParentid::TEXT),
+                                                       '$2', pDatetime::TEXT)
+    LOOP
+      IF _tmp < 0 THEN
+        RETURN _tmp;
+      END IF;
+      _count := _count + 1;
+    END LOOP;
+
+    IF (pInclParent) THEN
+      -- 8.4+: EXECUTE _delparentstmt INTO _tmp USING pDatetime, pType;
+      EXECUTE REPLACE(REPLACE(_delparentstmt, '$1', pParentid::TEXT),
+                                              '$2', pDatetime::TEXT) INTO _tmp;
+      IF (_tmp < 0) THEN
+        RETURN _tmp;
+      END IF;
+      _count := _count + 1;
+    END IF;
+  END IF;
+
+  RAISE DEBUG 'deleteOpenrecurringItems() returning %', _count;
+  RETURN _count;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/deleteopportunity.sql b/foundation-database/public/functions/deleteopportunity.sql
new file mode 100644 (file)
index 0000000..cf75249
--- /dev/null
@@ -0,0 +1,50 @@
+
+CREATE OR REPLACE FUNCTION deleteOpportunity(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pOpheadid ALIAS FOR $1;
+  _test INTEGER;
+BEGIN
+
+  SELECT todoitem_id INTO _test
+    FROM todoitem
+   WHERE(todoitem_ophead_id=pOpheadid)
+   LIMIT 1;
+  IF(FOUND) THEN
+    RETURN -1;
+  END IF;
+
+  SELECT quhead_id INTO _test
+    FROM quhead
+   WHERE(quhead_ophead_id=pOpheadid)
+   LIMIT 1;
+  IF(FOUND) THEN
+    RETURN -2;
+  END IF;
+
+  SELECT cohead_id INTO _test
+    FROM cohead
+   WHERE(cohead_ophead_id=pOpheadid)
+   LIMIT 1;
+  IF(FOUND) THEN
+    RETURN -3;
+  END IF;
+
+  DELETE
+    FROM charass
+   WHERE((charass_target_type=''OPP'')
+     AND (charass_target_id=pOpheadid));
+
+  DELETE
+    FROM comment
+   WHERE((comment_source=''OPP'')
+     AND (comment_source_id=pOpheadid));
+
+  DELETE
+    FROM ophead
+   WHERE(ophead_id=pOpheadid);
+  
+  return 0;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/deletepackage.sql b/foundation-database/public/functions/deletepackage.sql
new file mode 100644 (file)
index 0000000..f7fd4f2
--- /dev/null
@@ -0,0 +1,40 @@
+CREATE OR REPLACE FUNCTION deletePackage(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  ppkgheadid    ALIAS FOR $1;
+  _i            INTEGER := 0;
+  _pkgname      TEXT;
+  _r            RECORD;
+  _tabs         TEXT[] := ARRAY['cmd',  'cmdarg', 'image',  'metasql',
+                                'priv', 'report', 'script', 'uiform'];
+  _debug        BOOL := false;
+
+BEGIN
+  IF (EXISTS(SELECT *
+             FROM pkgdep
+             WHERE (pkgdep_parent_pkghead_id=ppkgheadid))) THEN
+    RETURN -1;
+  END IF;
+
+  SELECT pkghead_name INTO _pkgname
+  FROM pkghead
+  WHERE (pkghead_id=ppkgheadid);
+  IF (NOT FOUND) THEN
+    RETURN -2;
+  END IF;
+
+  IF (LOWER(_pkgname) = 'public' OR LOWER(_pkgname) = 'api') THEN
+    RETURN -3;
+  END IF;
+
+  FOR _i IN ARRAY_LOWER(_tabs,1)..ARRAY_UPPER(_tabs,1) LOOP
+    EXECUTE 'ALTER TABLE ' || _pkgname || '.pkg' || _tabs[_i] ||
+            ' DISABLE TRIGGER pkg' || _tabs[_i] || 'altertrigger;';
+  END LOOP;
+
+  DELETE FROM pkghead WHERE pkghead_id=ppkgheadid;
+
+  RETURN ppkgheadid;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/deletepo.sql b/foundation-database/public/functions/deletepo.sql
new file mode 100644 (file)
index 0000000..5c9dbc4
--- /dev/null
@@ -0,0 +1,35 @@
+CREATE OR REPLACE FUNCTION deletePo(INTEGER) RETURNS BOOLEAN AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pPoheadid ALIAS FOR $1;
+  _poitemid INTEGER;
+
+BEGIN
+
+  IF ( ( SELECT pohead_status
+         FROM pohead
+         WHERE (pohead_id=pPoheadid) ) = 'U' ) THEN
+
+    -- Unlink from any Sales Orders
+    UPDATE coitem SET coitem_order_type=NULL,
+                      coitem_order_id=NULL
+    FROM poitem 
+    WHERE ( (coitem_order_type='P')
+      AND   (coitem_order_id=poitem_id)
+      AND   (poitem_pohead_id=pPoheadid) );
+
+    DELETE FROM poitem
+    WHERE (poitem_pohead_id=pPoheadid);
+
+    DELETE FROM pohead
+    WHERE (pohead_id=pPoheadid);
+
+    RETURN TRUE;
+
+  ELSE
+    RETURN FALSE;
+  END IF;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/deletepoitem.sql b/foundation-database/public/functions/deletepoitem.sql
new file mode 100644 (file)
index 0000000..1c72afe
--- /dev/null
@@ -0,0 +1,50 @@
+
+CREATE OR REPLACE FUNCTION deletepoitem(integer) RETURNS integer AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pPoitemid ALIAS FOR $1;
+  _poheadid INTEGER := -1;
+  _status CHARACTER;
+
+BEGIN
+  SELECT poitem_pohead_id, poitem_status INTO _poheadid, _status
+  FROM poitem
+  WHERE (poitem_id=pPoitemid);
+
+  IF NOT(FOUND) THEN
+    RETURN 0;
+  END IF;
+
+  IF ( _status = 'U' ) THEN
+    DELETE FROM poitem
+    WHERE (poitem_id=pPoitemid);
+  ELSE   
+    IF ( _status = 'O' ) THEN
+      PERFORM recv_id
+      FROM recv
+      WHERE ( (recv_order_type='PO')
+       AND (recv_orderitem_id=pPoitemid) );
+      IF (FOUND) THEN
+        RETURN -10;
+      ELSE
+        RETURN -20;
+      END IF;
+    ELSE
+      RETURN -10;
+    END IF;
+  END IF;
+
+  PERFORM poitem_id
+  FROM poitem
+  WHERE poitem_pohead_id = _poheadid;
+
+  IF NOT(FOUND) THEN
+    DELETE FROM pohead
+    WHERE (pohead_id = _poheadid);
+  END IF;
+  
+  RETURN 0;
+
+END;
+$$ LANGUAGE 'plpgsql' VOLATILE;
diff --git a/foundation-database/public/functions/deletepr.sql b/foundation-database/public/functions/deletepr.sql
new file mode 100644 (file)
index 0000000..b19bbeb
--- /dev/null
@@ -0,0 +1,36 @@
+CREATE OR REPLACE FUNCTION deletePr(CHAR, INTEGER) RETURNS BOOL AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pParentType ALIAS FOR $1;
+  pParentId ALIAS FOR $2;
+
+BEGIN
+
+  DELETE FROM pr
+  WHERE ((pr_status=''O'')
+   AND (pr_order_type=pParentType)
+   AND (pr_order_id=pParentId));
+
+  RETURN TRUE;
+
+END;
+' LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION deletePr(INTEGER) RETURNS BOOL AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pPrid ALIAS FOR $1;
+
+BEGIN
+
+  DELETE FROM pr
+  WHERE ( (pr_status=''O'')
+   AND (pr_id=pPrid) );
+
+  RETURN TRUE;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/deleteproductcategory.sql b/foundation-database/public/functions/deleteproductcategory.sql
new file mode 100644 (file)
index 0000000..bd5b889
--- /dev/null
@@ -0,0 +1,30 @@
+CREATE OR REPLACE FUNCTION deleteProductCategory(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pProdcatid ALIAS FOR $1;
+  _check INTEGER;
+
+BEGIN
+
+--  Check to see if any items are assigned to the passed classcode
+  SELECT item_id INTO _check
+  FROM item
+  WHERE (item_prodcat_id=pProdcatid)
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -1;
+  END IF;
+
+--  Delete any assocated records
+  DELETE FROM salesaccnt
+  WHERE (salesaccnt_prodcat_id=pProdcatid);
+
+--  Delete the passed prodcat
+  DELETE FROM prodcat
+  WHERE (prodcat_id=pProdcatid);
+
+  RETURN pProdcatid;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/deleteprofitcenter.sql b/foundation-database/public/functions/deleteprofitcenter.sql
new file mode 100644 (file)
index 0000000..f4c4edd
--- /dev/null
@@ -0,0 +1,22 @@
+CREATE OR REPLACE FUNCTION deleteProfitCenter(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pid ALIAS FOR $1;
+
+BEGIN
+  IF (EXISTS(SELECT accnt_id
+             FROM accnt, prftcntr
+             WHERE ((accnt_company=prftcntr_number)
+               AND  (prftcntr_id=pid))
+            )) THEN
+    RETURN -1;
+  END IF;
+
+  DELETE FROM prftcntr
+  WHERE (prftcntr_id=pid);
+
+  RETURN pid;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/deleteproject.sql b/foundation-database/public/functions/deleteproject.sql
new file mode 100644 (file)
index 0000000..34a198d
--- /dev/null
@@ -0,0 +1,81 @@
+
+CREATE OR REPLACE FUNCTION deleteProject(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pPrjid ALIAS FOR $1;
+  _result INTEGER;
+BEGIN
+
+  SELECT quhead_id INTO _result
+    FROM quhead
+   WHERE (quhead_prj_id=pPrjid)
+   LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -1;
+  END IF;
+
+  SELECT cohead_id INTO _result
+    FROM cohead
+   WHERE (cohead_prj_id=pPrjid)
+   LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -2;
+  END IF;
+
+  SELECT wo_id INTO _result
+    FROM wo
+   WHERE (wo_prj_id=pPrjid)
+   LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -3;
+  END IF;
+
+  SELECT pr_id INTO _result
+    FROM pr
+   WHERE (pr_prj_id=pPrjid)
+   LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -4;
+  END IF;
+
+  SELECT poitem_id INTO _result
+    FROM poitem
+   WHERE (poitem_prj_id=pPrjid)
+   LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -5;
+  END IF;
+
+  SELECT invchead_id INTO _result
+    FROM invchead
+   WHERE (invchead_prj_id=pPrjid)
+   LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -6;
+  END IF;
+
+  DELETE FROM comment
+  WHERE ((comment_source='J')
+  AND (comment_source_id=pPrjid));
+
+  DELETE FROM comment
+  WHERE ((comment_source='TA')
+  AND (comment_source_id IN (
+    SELECT prjtask_id
+    FROM prjtask
+    WHERE (prjtask_prj_id=pPrjId))));
+
+  DELETE FROM prjtask
+   WHERE (prjtask_prj_id=pPrjid);
+
+  UPDATE prj
+     SET prj_recurring_prj_id=null
+   WHERE(prj_recurring_prj_id=pPrjid);
+
+  DELETE FROM prj
+   WHERE (prj_id=pPrjid);
+  RETURN pPrjid;
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/deleteprojecttask.sql b/foundation-database/public/functions/deleteprojecttask.sql
new file mode 100644 (file)
index 0000000..336b5ec
--- /dev/null
@@ -0,0 +1,38 @@
+
+CREATE OR REPLACE FUNCTION deleteProjectTask(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pPrjtaskid ALIAS FOR $1;
+  _row RECORD;
+  _result INTEGER;
+BEGIN
+
+  SELECT * INTO _row
+    FROM prjtask
+   WHERE (prjtask_id=pPrjtaskid)
+   LIMIT 1;
+  IF (NOT FOUND) THEN
+    RETURN -1;
+  END IF;
+
+  IF (COALESCE(_row.prjtask_hours_actual, 0.0) > 0.0) THEN
+    RETURN -2;
+  END IF;
+
+  IF (COALESCE(_row.prjtask_exp_actual, 0.0) > 0.0) THEN
+    RETURN -3;
+  END IF;
+
+  DELETE FROM comment
+  WHERE ((comment_source='TA')
+  AND (comment_source_id=pPrjtaskid));
+
+  DELETE FROM prjtask
+   WHERE (prjtask_id=pPrjtaskid);
+
+  RETURN 0;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/deleteqryhead.sql b/foundation-database/public/functions/deleteqryhead.sql
new file mode 100644 (file)
index 0000000..577d33e
--- /dev/null
@@ -0,0 +1,14 @@
+CREATE OR REPLACE FUNCTION deleteQryhead(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pqryheadid    ALIAS FOR $1;
+
+BEGIN
+  DELETE FROM qryitem WHERE (qryitem_qryhead_id=pqryheadid);
+  DELETE FROM qryhead WHERE (qryhead_id=pqryheadid);
+
+  RETURN pqryheadid;
+END;
+$$
+LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/deletequote.sql b/foundation-database/public/functions/deletequote.sql
new file mode 100644 (file)
index 0000000..ab4972a
--- /dev/null
@@ -0,0 +1,72 @@
+SELECT dropIfExists('FUNCTION', 'deleteQuote(integer)', 'public');
+SELECT dropIfExists('FUNCTION', 'deleteQuote(integer, integer)', 'public');
+
+CREATE OR REPLACE FUNCTION deleteQuote(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pQuheadid ALIAS FOR $1;
+BEGIN
+  RETURN deleteQuote(pQuheadid, NULL::TEXT);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION deleteQuote(INTEGER, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pQuheadid ALIAS FOR $1;
+  pQuoteNumber ALIAS FOR $2;
+BEGIN
+  RETURN deleteQuote(pQuheadid, pQuoteNumber::TEXT);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION deleteQuote(INTEGER, TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pQuheadid    ALIAS FOR $1;
+  pQuoteNumber ALIAS FOR $2;
+
+  _quNumberScheme      TEXT;
+  _quoteNumber         TEXT;
+  _quitemid             INTEGER;
+  _result               INTEGER;
+
+BEGIN
+
+  SELECT fetchMetricText('QUNumberGeneration') INTO _quNumberScheme;
+
+  IF (pQuoteNumber IS NULL) THEN
+    SELECT quhead_number INTO _quoteNumber
+    FROM quhead
+    WHERE (quhead_id=pQuheadid);
+  ELSE
+    _quoteNumber := pQuoteNumber;
+  END IF;
+
+  DELETE FROM quitem
+  WHERE (quitem_quhead_id=pQuheadid);
+
+  DELETE FROM quhead
+  WHERE (quhead_id=pQuheadid);
+
+  IF (_quoteNumber IS NOT NULL) THEN
+    IF (_quNumberScheme IN ('A', 'O')) THEN
+      -- do not release quote # if quote converted to sales order
+      IF (NOT EXISTS (SELECT cohead_id
+                     FROM cohead
+                     WHERE (cohead_number=_quoteNumber))) THEN
+       _result = releaseQuNumber(_quoteNumber);
+      END IF;
+    ELSEIF (_quNumberScheme = 'S') THEN
+      _result = releaseSoNumber(_quoteNumber);
+    END IF;
+  END IF;
+
+  -- Don't care about result of release number
+  RETURN 0;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/deleterecvfororder.sql b/foundation-database/public/functions/deleterecvfororder.sql
new file mode 100644 (file)
index 0000000..a16a0c6
--- /dev/null
@@ -0,0 +1,20 @@
+CREATE OR REPLACE FUNCTION deleteRecvForOrder(TEXT, INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pordertype   ALIAS FOR $1;
+  porderid     ALIAS FOR $2;
+
+BEGIN
+  DELETE FROM recv
+  USING orderitem
+  WHERE ((recv_orderitem_id=orderitem_id)
+    AND  (recv_order_type=orderitem_orderhead_type)
+    AND  (NOT recv_posted)
+    AND  (orderitem_orderhead_id=porderid)
+    AND  (orderitem_orderhead_type=pordertype));
+
+  RETURN 0;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/deletesalescategory.sql b/foundation-database/public/functions/deletesalescategory.sql
new file mode 100644 (file)
index 0000000..592b87d
--- /dev/null
@@ -0,0 +1,41 @@
+CREATE OR REPLACE FUNCTION deleteSalesCategory(integer) RETURNS integer
+    AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSalescatid ALIAS FOR $1;
+
+BEGIN
+
+  PERFORM invcitem_salescat_id
+  FROM invchead, invcitem
+  WHERE ( (invcitem_invchead_id=invchead_id)
+   AND (NOT invchead_posted)
+   AND (invcitem_salescat_id=pSalescatid) );
+  IF (FOUND) THEN
+    RETURN -1;
+  END IF;
+
+  PERFORM invcitem_salescat_id
+  FROM invchead, invcitem
+  WHERE ( (invcitem_invchead_id=invchead_id)
+   AND (invchead_posted)
+   AND (invcitem_salescat_id=pSalescatid) );
+  IF (FOUND) THEN
+    RETURN -2;
+  END IF;
+
+  PERFORM aropen_salescat_id
+     FROM aropen
+    WHERE (aropen_salescat_id=pSalescatid);
+  IF (FOUND) THEN
+    RETURN -3;
+  END IF;
+
+  DELETE FROM salescat
+  WHERE (salescat_id=pSalescatid);
+
+  RETURN 0;
+
+END;
+' LANGUAGE plpgsql;
diff --git a/foundation-database/public/functions/deleteshippingcharge.sql b/foundation-database/public/functions/deleteshippingcharge.sql
new file mode 100644 (file)
index 0000000..cc48b51
--- /dev/null
@@ -0,0 +1,23 @@
+
+CREATE OR REPLACE FUNCTION deleteShippingCharge(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pShipchrgid ALIAS FOR $1;
+
+BEGIN
+
+  IF EXISTS(SELECT 1
+              FROM custinfo
+             WHERE (cust_shipchrg_id=pShipchrgid)) THEN
+    RETURN -1;
+  END IF;
+
+  DELETE FROM shipchrg
+  WHERE (shipchrg_id=pShipchrgid);
+
+  RETURN pShipchrgid;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/deleteshippingchargetype.sql b/foundation-database/public/functions/deleteshippingchargetype.sql
new file mode 100644 (file)
index 0000000..d3f1904
--- /dev/null
@@ -0,0 +1,62 @@
+CREATE OR REPLACE FUNCTION deleteShippingChargeType (integer) RETURNS integer AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pShipchrgid ALIAS FOR $1;
+  _check INTEGER;
+
+BEGIN
+
+--  Check to see if the passed shipchrg is used as a default for any customers
+  SELECT cust_id INTO _check
+  FROM custinfo
+  WHERE (cust_shipchrg_id=pShipchrgid)
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -1;
+  END IF;
+
+--  Check to see if the passed shipchrg is used as a default for any shiptos
+  SELECT shipto_id INTO _check
+  FROM shiptoinfo
+  WHERE (shipto_shipchrg_id=pShipchrgid)
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -2;
+  END IF;
+
+--  Check to see if the passed shipchrg is used on any sales orders
+  SELECT cohead_id INTO _check
+  FROM cohead
+  WHERE (cohead_shipchrg_id=pShipchrgid)
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -3;
+  END IF;
+
+--  Check to see if the passed shipchrg is used on any shippers
+  SELECT shiphead_id INTO _check
+  FROM shiphead
+  WHERE (shiphead_shipchrg_id=pShipchrgid)
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -4;
+  END IF;
+
+--  Check to see if the passed shipchrg is used on any invoices
+  SELECT invchead_id INTO _check
+  FROM invchead
+  WHERE (invchead_shipchrg_id=pShipchrgid)
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -5;
+  END IF;
+
+--  Delete the passed shipchrg
+  DELETE FROM shipchrg
+  WHERE (shipchrg_id=pShipchrgid);
+
+  RETURN pShipchrgid;
+
+END;
+$$ LANGUAGE plpgsql;
diff --git a/foundation-database/public/functions/deleteshipto.sql b/foundation-database/public/functions/deleteshipto.sql
new file mode 100644 (file)
index 0000000..ac2aeaf
--- /dev/null
@@ -0,0 +1,68 @@
+
+CREATE OR REPLACE FUNCTION deleteShipto(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pShiptoid ALIAS FOR $1;
+
+BEGIN
+
+  PERFORM asohist_id
+  FROM asohist
+  WHERE (asohist_shipto_id=pShiptoid)
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -1;
+  END IF;
+
+  PERFORM cohead_id
+  FROM cohead
+  WHERE (cohead_shipto_id=pShiptoid)
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -2;
+  END IF;
+
+  PERFORM cmhead_id
+  FROM cmhead
+  WHERE (cmhead_shipto_id=pShiptoid)
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -3;
+  END IF;
+
+  PERFORM cohist_id
+  FROM cohist
+  WHERE (cohist_shipto_id=pShiptoid)
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -4;
+  END IF;
+
+  PERFORM quhead_id
+  FROM quhead
+  WHERE (quhead_shipto_id=pShiptoid)
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -5;
+  END IF;
+
+  PERFORM invchead_id
+  FROM invchead
+  WHERE (invchead_shipto_id=pShiptoid)
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -6;
+  END IF;
+
+  DELETE FROM ipsass
+  WHERE (ipsass_shipto_id=pShiptoid);
+
+  DELETE FROM shiptoinfo
+  WHERE (shipto_id=pShiptoid);
+
+  RETURN 0;
+
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/deleteso.sql b/foundation-database/public/functions/deleteso.sql
new file mode 100644 (file)
index 0000000..e60f0d5
--- /dev/null
@@ -0,0 +1,96 @@
+
+CREATE OR REPLACE FUNCTION deleteSo(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSoheadid    ALIAS FOR $1;
+BEGIN
+  RETURN deleteSo(pSoheadid, NULL);
+END;
+$$ LANGUAGE plpgsql;
+
+CREATE OR REPLACE FUNCTION deleteSo(INTEGER, TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSoheadid    ALIAS FOR $1;
+  pSonumber    ALIAS FOR $2;
+
+  _r            RECORD;
+  _coitemid     INTEGER;
+  _result       INTEGER;
+  _poStatus     INTEGER := 0;
+
+BEGIN
+-- Get cohead
+  SELECT * INTO _r FROM cohead WHERE (cohead_id=pSoheadid);
+
+   IF (NOT FOUND) THEN
+     RETURN 0;
+   END IF;
+
+-- Cannot delete if credit card payments
+  IF (EXISTS(SELECT ccpay_id
+            FROM ccpay, payco
+            WHERE ((ccpay_status IN ('C'))
+              AND  (ccpay_id=payco_ccpay_id)
+              AND  (payco_cohead_id=pSoheadid)))) THEN
+    RETURN -1;
+  END IF;
+
+-- Cannot delete if credit card history
+  IF (EXISTS(SELECT ccpay_id
+            FROM ccpay, payco
+            WHERE ((ccpay_status != 'C')
+              AND  (ccpay_id=payco_ccpay_id)
+              AND  (payco_cohead_id=pSoheadid)))) THEN
+    RETURN -2;
+  END IF;
+
+-- Delete Sales Order Items
+  FOR _coitemid IN
+    SELECT coitem_id
+    FROM coitem
+    WHERE ( (coitem_cohead_id=pSoheadid)
+      AND   (coitem_subnumber=0) ) LOOP
+
+    SELECT deleteSoItem(_coitemid) INTO _result;
+    IF (_result < 0) THEN
+      IF (_result = -20) THEN
+        _poStatus := _poStatus - 1;
+      ELSE
+        RETURN _result;
+      END IF;
+    END IF;
+
+  END LOOP;
+
+  DELETE FROM pack
+  WHERE (pack_head_id=pSoheadid and pack_head_type = 'SO');
+
+  DELETE FROM cohead
+  WHERE (cohead_id=pSoheadid);
+
+  IF (fetchMetricBool('AutoCreateProjectsForOrders')) THEN
+    PERFORM deleteProject(_r.cohead_prj_id);
+  END IF;
+
+  DELETE FROM aropenalloc
+  WHERE ((aropenalloc_doctype='S')
+    AND  (aropenalloc_doc_id=pSoheadid));
+
+  IF (COALESCE(pSonumber,'') != '') THEN
+    _result = releaseSoNumber(pSonumber);
+  ELSEIF (_r.cohead_number IS NOT NULL) THEN
+    _result = releaseSoNumber(_r.cohead_number);
+  END IF;
+
+  IF (_poStatus < 0) THEN
+    RETURN -20;
+  ELSE
+    RETURN 0;
+  END IF;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/deletesoitem.sql b/foundation-database/public/functions/deletesoitem.sql
new file mode 100644 (file)
index 0000000..c22e910
--- /dev/null
@@ -0,0 +1,129 @@
+
+CREATE OR REPLACE FUNCTION deleteSoItem(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSoitemid    ALIAS FOR $1;
+
+  _r            RECORD;
+  _s            RECORD;
+  _result       INTEGER;
+  _deletePO     INTEGER := 0;
+  _recvId       INTEGER := -1;
+  _poStatus     TEXT;
+  _jobItem      BOOLEAN;
+
+BEGIN
+-- Get coitem
+   SELECT * INTO _r FROM coitem WHERE (coitem_id=pSoitemid);
+
+   IF (NOT FOUND) THEN
+     RETURN -999;
+   END IF;
+
+-- Cannot delete if shipped
+  IF (_r.coitem_qtyshipped > 0) THEN
+    RETURN -101;
+  END IF;
+
+-- Cannot delete if issued to shipping
+  SELECT shipitem_id INTO _result
+  FROM shipitem JOIN shiphead ON (shiphead_id=shipitem_shiphead_id AND shiphead_order_type='SO')
+  WHERE (shipitem_orderitem_id=pSoitemid)
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -102;
+  END IF;
+
+-- Cannot delete if returned
+  IF (fetchMetricBool('MultiWhs')) THEN
+    SELECT raitem_id INTO _result
+    FROM raitem
+    WHERE ( (raitem_orig_coitem_id=pSoitemid)
+       OR   (raitem_new_coitem_id=pSoitemid) )
+    LIMIT 1;
+    IF (FOUND) THEN
+      RETURN -103;
+    END IF;
+  END IF;
+
+-- Cannot delete if any inventory history
+  SELECT invhist_id INTO _result
+  FROM invhist
+  WHERE ( (invhist_ordnumber=formatSoNumber(pSoitemid))
+    AND   (invhist_ordtype='SO') )
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -105;
+  END IF;
+
+-- If Kit, check deletion of component items
+  IF (_r.coitem_subnumber = 0) THEN
+    FOR _s IN
+      SELECT *
+      FROM coitem
+      WHERE ((coitem_cohead_id = _r.coitem_cohead_id)
+        AND  (coitem_linenumber = _r.coitem_linenumber)
+        AND  (coitem_subnumber > 0))
+    LOOP
+      IF ((COALESCE(_s.coitem_order_id, -1) > 0)
+       AND (_s.coitem_order_type = 'P')) THEN
+        SELECT poitem_status, COALESCE(recv_id, -1)
+          INTO _poStatus, _recvId
+        FROM poitem LEFT OUTER JOIN recv
+               ON ((recv_orderitem_id=poitem_id)
+                 AND (recv_order_type='PO'))
+        WHERE (poitem_id = _s.coitem_order_id);
+
+        IF ((_recvId > 0) OR (_poStatus = 'C')) THEN
+          RETURN -10;
+        ELSIF ((_recvId = -1) AND (_poStatus = 'O')) THEN
+          _deletePO := _deletePO - 1;
+        END IF;
+      END IF;
+    END LOOP;
+  END IF;
+
+
+  SELECT (itemsite_costmethod='J') INTO _jobItem
+  FROM coitem JOIN itemsite ON (itemsite_id=coitem_itemsite_id)
+  WHERE (coitem_id=pSoitemid);
+
+  IF (_jobItem AND _r.coitem_order_type='W') THEN
+-- Delete associated Job Work Order
+    SELECT deleteWo(_r.coitem_order_id, TRUE, TRUE) INTO _result;
+    IF (_result < 0) THEN
+      RETURN -104;
+    END IF;
+  ELSIF (_r.coitem_order_type='W') THEN
+-- Delete associated Job Work Order
+    SELECT deleteWo(_r.coitem_order_id, TRUE) INTO _result;
+    IF (_result < 0) THEN
+      -- Cannot delete so break association
+      PERFORM changeWoProject(_r.coitem_order_id, -1, TRUE);
+    END IF;
+  ELSIF (_r.coitem_order_type='R') THEN
+-- Delete associated Purchase Request
+    PERFORM deletePr(_r.coitem_order_id);
+  ELSIF (_r.coitem_order_type='P') THEN
+-- Delete associated Purchase Order Item
+    SELECT deletepoitem(_r.coitem_order_id) INTO _result;
+    IF ((_result < 0) AND (_result <> -20)) THEN
+      RETURN _result;
+    ELSIF (_result = -20) THEN
+      _deletePO := _deletePO - 1;
+    END IF;
+  END IF;
+
+-- Delete the coitem
+  DELETE FROM coitem
+  WHERE (coitem_id=pSoitemid);
+
+  IF (_deletePO < 0) THEN
+    RETURN -20;
+  ELSE
+    RETURN 0;
+  END IF;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/deletestandardjournal.sql b/foundation-database/public/functions/deletestandardjournal.sql
new file mode 100644 (file)
index 0000000..3dffb34
--- /dev/null
@@ -0,0 +1,23 @@
+
+CREATE OR REPLACE FUNCTION deleteStandardJournal(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pStdjrnlid ALIAS FOR $1;
+
+BEGIN
+
+  DELETE FROM stdjrnlitem
+  WHERE (stdjrnlitem_stdjrnl_id=pStdjrnlid);
+
+  DELETE FROM stdjrnlgrpitem
+  WHERE (stdjrnlgrpitem_stdjrnl_id=pStdjrnlid);
+
+  DELETE FROM stdjrnl
+  WHERE (stdjrnl_id=pStdjrnlid);
+
+  RETURN 1;
+
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/deletestandardjournalgroup.sql b/foundation-database/public/functions/deletestandardjournalgroup.sql
new file mode 100644 (file)
index 0000000..527b729
--- /dev/null
@@ -0,0 +1,20 @@
+
+CREATE OR REPLACE FUNCTION deleteStandardJournalGroup(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pStdjrnlgrpid ALIAS FOR $1;
+
+BEGIN
+
+  DELETE FROM stdjrnlgrpitem
+  WHERE (stdjrnlgrpitem_stdjrnlgrp_id=pStdjrnlgrpid);
+
+  DELETE FROM stdjrnlgrp
+  WHERE (stdjrnlgrp_id=pStdjrnlgrpid);
+
+  RETURN 1;
+
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/deletesubaccount.sql b/foundation-database/public/functions/deletesubaccount.sql
new file mode 100644 (file)
index 0000000..d22c9ca
--- /dev/null
@@ -0,0 +1,22 @@
+CREATE OR REPLACE FUNCTION deleteSubaccount(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pid ALIAS FOR $1;
+
+BEGIN
+  IF (EXISTS(SELECT accnt_id
+             FROM accnt, subaccnt
+             WHERE ((accnt_company=subaccnt_number)
+               AND  (subaccnt_id=pid))
+            )) THEN
+    RETURN -1;
+  END IF;
+
+  DELETE FROM subaccnt
+  WHERE (subaccnt_id=pid);
+
+  RETURN pid;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/deletesubaccounttype.sql b/foundation-database/public/functions/deletesubaccounttype.sql
new file mode 100644 (file)
index 0000000..48da5f3
--- /dev/null
@@ -0,0 +1,30 @@
+
+CREATE OR REPLACE FUNCTION deleteSubAccountType (integer) RETURNS integer
+    AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSubAccntTypeid ALIAS FOR $1;
+  _check INTEGER;
+
+BEGIN
+
+--  Check to see if the passed subaccnttype is used in any accounts
+  SELECT accnt_id INTO _check
+  FROM accnt, subaccnttype
+  WHERE ( (accnt_subaccnttype_code=subaccnttype_code)
+   AND (subaccnttype_id=pSubAccntTypeid) )
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -1;
+  END IF;
+
+--  Delete the Sub Account Type
+  DELETE FROM subaccnttype
+  WHERE (subaccnttype_id=pSubAccntTypeid);
+
+  RETURN 0;
+
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/deletetax.sql b/foundation-database/public/functions/deletetax.sql
new file mode 100644 (file)
index 0000000..31be618
--- /dev/null
@@ -0,0 +1,24 @@
+CREATE OR REPLACE FUNCTION public.deletetax(integer)
+  RETURNS integer AS
+$$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  ptaxid       ALIAS FOR $1;
+BEGIN
+  -- these checks allow nice error reporting instead of throwing an SQL error
+  IF EXISTS(SELECT taxass_id FROM taxass WHERE (taxass_tax_id=ptaxid)) THEN
+    RETURN -10;
+  END IF;
+  IF EXISTS(SELECT taxhist_id FROM taxhist WHERE (taxhist_tax_id=ptaxid)) THEN
+    RETURN -20;
+  END IF;
+
+  DELETE FROM taxrate WHERE (taxrate_tax_id = ptaxid);
+  DELETE FROM tax WHERE (tax_id = ptaxid);
+
+  RETURN ptaxid;
+
+END;
+$$
+  LANGUAGE 'plpgsql' VOLATILE;
diff --git a/foundation-database/public/functions/deletetaxclass.sql b/foundation-database/public/functions/deletetaxclass.sql
new file mode 100644 (file)
index 0000000..8af284e
--- /dev/null
@@ -0,0 +1,27 @@
+CREATE OR REPLACE FUNCTION deletetaxclass(integer)
+  RETURNS integer AS
+$$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+pTaxclassid ALIAS FOR $1;
+_result INTEGER;
+
+BEGIN
+
+-- Check to find if the tax class is used in any tax code
+SELECT tax_id INTO _result
+FROM tax
+WHERE (tax_taxclass_id = pTaxclassid);
+IF (FOUND) THEN
+   RETURN -1;
+END IF;
+
+-- Delete the tax class if the above condition doesn't match
+DELETE FROM taxclass WHERE taxclass_id = pTaxclassid ;
+
+RETURN pTaxclassid;
+
+END;
+$$
+  LANGUAGE 'plpgsql' VOLATILE;
diff --git a/foundation-database/public/functions/deletetaxtype.sql b/foundation-database/public/functions/deletetaxtype.sql
new file mode 100644 (file)
index 0000000..4e22243
--- /dev/null
@@ -0,0 +1,41 @@
+CREATE OR REPLACE FUNCTION deleteTaxType(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pTaxtypeid ALIAS FOR $1;
+  _result INTEGER;
+BEGIN
+
+  SELECT taxtype_id
+    INTO _result
+    FROM taxtype
+   WHERE ((taxtype_sys)
+     AND  (taxtype_id=pTaxtypeid));
+  IF (FOUND) THEN
+    RETURN -1;
+  END IF;
+
+  SELECT taxass_id
+    INTO _result
+    FROM taxass
+   WHERE (taxass_taxtype_id=pTaxtypeid);
+  IF (FOUND) THEN
+    RETURN -2;
+  END IF;
+
+  SELECT taxhist_id
+    INTO _result
+    FROM taxhist
+   WHERE (taxhist_taxtype_id=pTaxtypeid);
+  IF (FOUND) THEN
+    RETURN -3;
+  END IF;
+
+  DELETE
+    FROM taxtype
+   WHERE (taxtype_id=pTaxtypeid);
+
+  RETURN pTaxtypeid;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/deletetaxzone.sql b/foundation-database/public/functions/deletetaxzone.sql
new file mode 100644 (file)
index 0000000..41c3145
--- /dev/null
@@ -0,0 +1,35 @@
+CREATE OR REPLACE FUNCTION deletetaxzone(integer)
+  RETURNS integer AS
+$$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+pTaxzoneid ALIAS FOR $1;
+_result INTEGER;
+
+BEGIN
+
+-- Check to find if the tax zone is used in any tax assignment
+SELECT taxass_id INTO _result
+FROM taxass
+WHERE (taxass_taxzone_id=pTaxzoneid);
+IF (FOUND) THEN
+   RETURN -1;
+END IF;
+
+-- Check to find if the tax zone has been referenced in any tax registration
+SELECT taxreg_id INTO _result 
+FROM taxreg
+WHERE (taxreg_taxzone_id=pTaxzoneid);
+IF (FOUND) THEN
+   RETURN -2;
+END IF;
+
+-- Delete the tax zone if none of the above conditions match
+DELETE FROM taxzone WHERE taxzone_id = pTaxzoneid ;
+
+RETURN pTaxzoneid;
+
+END;
+$$
+  LANGUAGE 'plpgsql' VOLATILE;
diff --git a/foundation-database/public/functions/deletetodoitem.sql b/foundation-database/public/functions/deletetodoitem.sql
new file mode 100644 (file)
index 0000000..a790e25
--- /dev/null
@@ -0,0 +1,13 @@
+
+CREATE OR REPLACE FUNCTION deleteTodoItem(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  ptodoItemId ALIAS FOR $1;
+BEGIN
+  DELETE FROM alarm WHERE ( (alarm_source=''TODO'') AND (alarm_source_id=ptodoItemId) );
+  DELETE FROM todoitem WHERE todoitem_id = ptodoItemId;
+  RETURN 0;
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/deleteunusedclasscodes.sql b/foundation-database/public/functions/deleteunusedclasscodes.sql
new file mode 100644 (file)
index 0000000..f3d2590
--- /dev/null
@@ -0,0 +1,12 @@
+CREATE OR REPLACE FUNCTION deleteUnusedClassCodes() RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+
+  DELETE FROM classcode
+  WHERE (classcode_id NOT IN (SELECT DISTINCT item_classcode_id FROM item));
+
+  RETURN 0;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/deleteunusedfreightclasses.sql b/foundation-database/public/functions/deleteunusedfreightclasses.sql
new file mode 100644 (file)
index 0000000..3e695cd
--- /dev/null
@@ -0,0 +1,12 @@
+CREATE OR REPLACE FUNCTION deleteUnusedFreightClasses() RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+
+  DELETE FROM freightclass
+  WHERE (freightclass_id NOT IN (SELECT DISTINCT COALESCE(item_freightclass_id, 0) FROM item));
+
+  RETURN 0;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/deleteunusedproductcategories.sql b/foundation-database/public/functions/deleteunusedproductcategories.sql
new file mode 100644 (file)
index 0000000..395779b
--- /dev/null
@@ -0,0 +1,17 @@
+CREATE OR REPLACE FUNCTION deleteUnusedProductCategories() RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+
+--  Delete any associated records
+  DELETE FROM salesaccnt
+  WHERE ( (salesaccnt_prodcat_id <> -1)
+   AND (salesaccnt_prodcat_id NOT IN (SELECT DISTINCT item_prodcat_id FROM item)) );
+
+  DELETE FROM prodcat
+  WHERE (prodcat_id NOT IN (SELECT DISTINCT item_prodcat_id FROM item));
+
+  RETURN 0;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/deleteuom.sql b/foundation-database/public/functions/deleteuom.sql
new file mode 100644 (file)
index 0000000..28d8b55
--- /dev/null
@@ -0,0 +1,15 @@
+CREATE OR REPLACE FUNCTION deleteUOM(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pUomid ALIAS FOR $1;
+
+BEGIN
+
+  DELETE FROM uomconv WHERE uomconv_from_uom_id=pUomid;
+  DELETE FROM uomconv WHERE uomconv_to_uom_id=pUomid;
+  DELETE FROM uom WHERE uom_id=pUomid;
+
+  RETURN 0;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/deleteuomconv.sql b/foundation-database/public/functions/deleteuomconv.sql
new file mode 100644 (file)
index 0000000..3f658ca
--- /dev/null
@@ -0,0 +1,12 @@
+CREATE OR REPLACE FUNCTION deleteUOMConv(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pUomconvid ALIAS FOR $1;
+
+BEGIN
+  DELETE FROM uomconv WHERE uomconv_id=pUomconvid;
+
+  RETURN 0;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/deleteurl.sql b/foundation-database/public/functions/deleteurl.sql
new file mode 100644 (file)
index 0000000..9c7aef0
--- /dev/null
@@ -0,0 +1,17 @@
+create or replace function deleteUrl(integer) returns boolean as $$
+declare
+  pId ALIAS FOR $1;
+begin
+  delete from urlinfo
+  where ( url_id in (
+    select url_id
+    from urlinfo
+      join docass on (docass_target_id=url_id)
+                 and (docass_target_type='URL')
+      where ( docass_id = pId ) ) );
+
+  delete from docass where docass_id = pId;
+
+  return true;
+end;
+$$ language 'plpgsql';
diff --git a/foundation-database/public/functions/deleteuserpreference.sql b/foundation-database/public/functions/deleteuserpreference.sql
new file mode 100644 (file)
index 0000000..bcdcf14
--- /dev/null
@@ -0,0 +1,33 @@
+CREATE OR REPLACE FUNCTION deleteUserPreference(TEXT) RETURNS BOOLEAN AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pPrefname ALIAS FOR $1;
+  _return BOOLEAN;
+
+BEGIN
+
+  SELECT deleteUserPreference(getEffectiveXtUser(), pPrefname) INTO _return;
+
+  RETURN _return;
+
+END;
+' LANGUAGE plpgsql;
+
+CREATE OR REPLACE FUNCTION deleteUserPreference(TEXT, TEXT) RETURNS BOOLEAN AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pUsername ALIAS FOR $1;
+  pPrefname ALIAS FOR $2;
+
+BEGIN
+
+  DELETE FROM usrpref
+  WHERE ( (usrpref_username=pUsername)
+   AND (usrpref_name=pPrefname) );
+
+  RETURN TRUE;
+
+END;
+' LANGUAGE plpgsql;
diff --git a/foundation-database/public/functions/deletevendoraddr.sql b/foundation-database/public/functions/deletevendoraddr.sql
new file mode 100644 (file)
index 0000000..94126b0
--- /dev/null
@@ -0,0 +1,26 @@
+CREATE OR REPLACE FUNCTION deleteVendorAddress(INTEGER) RETURNS INTEGER  AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pVendaddrid ALIAS FOR $1;
+  _test INTEGER;
+
+BEGIN
+
+--  Check to see if the passed vendor address is used in pohead
+  SELECT pohead_id INTO _test
+  FROM pohead
+  WHERE (pohead_vendaddr_id=pVendaddrid)
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -1;
+  END IF;
+
+--  Delete the passed vendor address
+  DELETE FROM vendaddrinfo
+  WHERE (vendaddr_id=pVendaddrid);
+
+  RETURN 0;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/deletevendortype.sql b/foundation-database/public/functions/deletevendortype.sql
new file mode 100644 (file)
index 0000000..b54a3aa
--- /dev/null
@@ -0,0 +1,26 @@
+CREATE OR REPLACE FUNCTION deleteVendorType(INTEGER) RETURNS INTEGER  AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pVendtypeid ALIAS FOR $1;
+  _test INTEGER;
+
+BEGIN
+
+--  Check to see if the passed vendor type is used in vendinfo
+  SELECT vend_id INTO _test
+  FROM vendinfo
+  WHERE (vend_vendtype_id=pVendtypeid)
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -1;
+  END IF;
+
+--  Delete the passed vendor type
+  DELETE FROM vendtype
+  WHERE (vendtype_id=pVendtypeid);
+
+  RETURN 0;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/deletewo.sql b/foundation-database/public/functions/deletewo.sql
new file mode 100644 (file)
index 0000000..caf10fb
--- /dev/null
@@ -0,0 +1,103 @@
+CREATE OR REPLACE FUNCTION deleteWo(INTEGER, BOOLEAN) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWoid ALIAS FOR $1;
+  deleteChildren ALIAS FOR $2;
+
+BEGIN
+  RETURN deleteWo(pWoid, deleteChildren, FALSE);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION deleteWo(INTEGER, BOOLEAN, BOOLEAN) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWoid ALIAS FOR $1;
+  deleteChildren ALIAS FOR $2;
+  deleteForce ALIAS FOR $3;
+  woStatus CHAR(1);
+  itemType CHAR(1);
+  ordtype CHAR(1);
+  ordid INTEGER;
+  returnCode INTEGER;
+  _wotcCnt     INTEGER;
+  _routings BOOLEAN;
+
+BEGIN
+  SELECT wo_status, wo_ordtype, wo_ordid, item_type
+  INTO   woStatus, ordtype, ordid, itemType
+  FROM wo JOIN itemsite ON (itemsite_id=wo_itemsite_id)
+          JOIN item ON (item_id=itemsite_item_id)
+  WHERE (wo_id=pWoid);
+
+  IF (NOT woStatus IN ('O', 'E', 'C')) THEN
+    RETURN -3;
+  END IF;
+
+  IF (NOT deleteForce) THEN
+    IF (itemType = 'J') THEN
+      RETURN -2;
+    END IF;
+  END IF;
+
+  SELECT fetchMetricBool('Routings') INTO _routings;
+
+  IF _routings THEN
+    SELECT count(*) INTO _wotcCnt
+    FROM xtmfg.wotc
+    WHERE (wotc_wo_id=pWoid);
+    IF (_wotcCnt > 0) THEN
+      RETURN -1;
+    END IF;
+  END IF;
+
+  IF (woStatus = 'R') THEN
+    INSERT INTO evntlog (evntlog_evnttime, evntlog_username, evntlog_evnttype_id,
+                         evntlog_ordtype, evntlog_ord_id, evntlog_warehous_id, evntlog_number)
+    SELECT CURRENT_TIMESTAMP, evntnot_username, evnttype_id,
+           'W', wo_id, itemsite_warehous_id, formatWoNumber(wo_id)
+    FROM evntnot, evnttype, itemsite, item, wo
+    WHERE ( (evntnot_evnttype_id=evnttype_id)
+     AND (evntnot_warehous_id=itemsite_warehous_id)
+     AND (wo_itemsite_id=itemsite_id)
+     AND (itemsite_item_id=item_id)
+     AND (evnttype_name='RWoRequestCancel')
+     AND (wo_id=pWoid) );
+
+     RETURN 0;
+  ELSE
+    IF (woStatus = 'E') THEN
+      returnCode := (SELECT implodeWo(pWoid, FALSE));
+    END IF;
+  END IF;
+
+  IF (woStatus IN ('O', 'E', 'C')) THEN
+    DELETE FROM womatl
+    WHERE (womatl_wo_id=pWoid);
+
+    IF _routings THEN
+      DELETE FROM xtmfg.wooper
+      WHERE (wooper_wo_id=pWoid);
+    END IF;
+
+    IF (ordtype = 'S') THEN
+      UPDATE coitem SET coitem_order_type=NULL, coitem_order_id=NULL
+      WHERE coitem_id=ordid;
+    END IF;
+
+    DELETE FROM wo
+    WHERE (wo_id=pWoid);
+  END IF;
+
+  IF (deleteChildren) THEN
+    returnCode := (SELECT MAX(deleteWo(wo_id, TRUE))
+                   FROM wo
+                   WHERE ((wo_ordtype='W')
+                    AND (wo_ordid=pWoid)));
+  END IF;
+
+  RETURN 0;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/deletewomaterial.sql b/foundation-database/public/functions/deletewomaterial.sql
new file mode 100644 (file)
index 0000000..9206224
--- /dev/null
@@ -0,0 +1,23 @@
+CREATE OR REPLACE FUNCTION deleteWoMaterial(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWomatlid ALIAS FOR $1;
+
+BEGIN
+
+  UPDATE wo
+  SET wo_adhoc=TRUE
+  FROM womatl
+  WHERE ((womatl_wo_id=wo_id)
+   AND (womatl_id=pWomatlid));
+
+--  Delete any created P/R for this Womatl
+  PERFORM deletePr(''W'', pWomatlid);
+
+  DELETE FROM womatl
+  WHERE (womatl_id=pWomatlid);
+
+  RETURN 0;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/detachccpayfromso.sql b/foundation-database/public/functions/detachccpayfromso.sql
new file mode 100644 (file)
index 0000000..00d191f
--- /dev/null
@@ -0,0 +1,16 @@
+CREATE OR REPLACE FUNCTION detachCCPayFromSO(INTEGER, INTEGER, INTEGER)
+  RETURNS INTEGER AS
+'
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pcoheadid            ALIAS FOR $1;
+  pwarehousid          ALIAS FOR $2;
+  pcustid              ALIAS FOR $3;
+
+BEGIN
+  RAISE NOTICE ''detachCCPayFromSO(INTEGER, INTEGER, INTEGER): deprecated'';
+  RETURN 0;
+END;
+'
+LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/detachcontact.sql b/foundation-database/public/functions/detachcontact.sql
new file mode 100644 (file)
index 0000000..e79fdba
--- /dev/null
@@ -0,0 +1,24 @@
+
+CREATE OR REPLACE FUNCTION detachContact(INTEGER, INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pcntctId    ALIAS FOR $1;
+  pcrmacctId  ALIAS FOR $2;
+BEGIN
+  UPDATE cntct SET cntct_crmacct_id = NULL
+  WHERE cntct_id = pcntctId
+    AND cntct_crmacct_id = pcrmacctId;
+
+  UPDATE crmacct SET crmacct_cntct_id_1 = NULL
+  WHERE crmacct_id = pcrmacctId
+    AND crmacct_cntct_id_1 = pcntctId;
+
+  UPDATE crmacct SET crmacct_cntct_id_2 = NULL
+  WHERE crmacct_id = pcrmacctId
+    AND crmacct_cntct_id_2 = pcntctId;
+
+  RETURN 0;
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/detag.sql b/foundation-database/public/functions/detag.sql
new file mode 100644 (file)
index 0000000..df497bf
--- /dev/null
@@ -0,0 +1,14 @@
+
+CREATE OR REPLACE FUNCTION detag(TEXT) RETURNS TEXT IMMUTABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSource ALIAS FOR $1;
+  _result TEXT := '';
+
+BEGIN
+  SELECT regexp_replace(pSource, E'<[^>]*>', '', 'g') INTO _result;
+  RETURN _result;
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/detailednnqoh.sql b/foundation-database/public/functions/detailednnqoh.sql
new file mode 100644 (file)
index 0000000..a0b60a1
--- /dev/null
@@ -0,0 +1,32 @@
+CREATE OR REPLACE FUNCTION detailedNNQOH(INTEGER, BOOLEAN) RETURNS NUMERIC AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  pABS ALIAS FOR $2;
+  _qoh NUMERIC;
+
+BEGIN
+
+  IF (pABS) THEN
+    SELECT SUM(noNeg(itemloc_qty)) INTO _qoh
+    FROM itemloc, location
+    WHERE ( (itemloc_location_id=location_id)
+     AND (NOT location_netable)
+     AND (itemloc_itemsite_id=pItemsiteid) );
+  ELSE
+    SELECT SUM(itemloc_qty) INTO _qoh
+    FROM itemloc, location
+    WHERE ( (itemloc_location_id=location_id)
+     AND (NOT location_netable)
+     AND (itemloc_itemsite_id=pItemsiteid) );
+  END IF;
+
+  IF (_qoh IS NULL) THEN
+    _qoh := 0;
+  END IF;
+
+  RETURN _qoh;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/detailedqoh.sql b/foundation-database/public/functions/detailedqoh.sql
new file mode 100644 (file)
index 0000000..4fda36f
--- /dev/null
@@ -0,0 +1,30 @@
+CREATE OR REPLACE FUNCTION detailedQOH(INTEGER, BOOLEAN) RETURNS NUMERIC AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  pABS ALIAS FOR $2;
+  _qoh NUMERIC;
+
+BEGIN
+
+  IF (pABS) THEN
+    SELECT SUM(noNeg(itemloc_qty)) INTO _qoh
+    FROM itemloc LEFT OUTER JOIN location ON (itemloc_location_id=location_id)
+    WHERE ( ( (location_id IS NULL) OR (location_netable) )
+     AND (itemloc_itemsite_id=pItemsiteid) );
+  ELSE
+    SELECT SUM(itemloc_qty) INTO _qoh
+    FROM itemloc LEFT OUTER JOIN location ON (itemloc_location_id=location_id)
+    WHERE ( ( (location_id IS NULL) OR (location_netable) )
+     AND (itemloc_itemsite_id=pItemsiteid) );
+  END IF;
+
+  IF (_qoh IS NULL) THEN
+    _qoh := 0;
+  END IF;
+
+  RETURN _qoh;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/determinediscountdate.sql b/foundation-database/public/functions/determinediscountdate.sql
new file mode 100644 (file)
index 0000000..b271356
--- /dev/null
@@ -0,0 +1,38 @@
+CREATE OR REPLACE FUNCTION determineDiscountDate(INTEGER, DATE) RETURNS DATE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pTermsid ALIAS FOR $1;
+  pSourceDate ALIAS FOR $2;
+  _discDate DATE;
+  _p RECORD;
+
+BEGIN
+
+  SELECT terms_type, terms_discdays, terms_cutoffday INTO _p
+  FROM terms
+  WHERE (terms_id=pTermsid);
+  IF (NOT FOUND) THEN
+    _discDate := pSourceDate;
+
+--  Handle type D terms
+  ELSIF (_p.terms_type = 'D') THEN
+    _discDate := (pSourceDate + _p.terms_discdays);
+
+--  Handle type P terms
+  ELSIF (_p.terms_type = 'P') THEN
+    IF (date_part('day', pSourceDate) <= _p.terms_cutoffday) THEN
+      _discDate := (DATE(date_trunc('month', pSourceDate)) + (_p.terms_discdays - 1));
+    ELSE
+      _discDate := (DATE(date_trunc('month', pSourceDate)) + (_p.terms_discdays - 1) + INTERVAL '1 month');
+    END IF;
+
+--  Handle unknown terms
+  ELSE
+    _discDate := pSourceDate;
+  END IF;
+
+  RETURN _discDate;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/determineduedate.sql b/foundation-database/public/functions/determineduedate.sql
new file mode 100644 (file)
index 0000000..51d607a
--- /dev/null
@@ -0,0 +1,38 @@
+CREATE OR REPLACE FUNCTION determineDueDate(INTEGER, DATE) RETURNS DATE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pTermsid ALIAS FOR $1;
+  pSourceDate ALIAS FOR $2;
+  _dueDate DATE;
+  _p RECORD;
+
+BEGIN
+
+  SELECT terms_type, terms_duedays, terms_cutoffday INTO _p
+  FROM terms
+  WHERE (terms_id=pTermsid);
+  IF (NOT FOUND) THEN
+    _dueDate := pSourceDate;
+
+--  Handle type D terms
+  ELSIF (_p.terms_type = 'D') THEN
+    _dueDate := (pSourceDate + _p.terms_duedays);
+
+--  Handle type P terms
+  ELSIF (_p.terms_type = 'P') THEN
+    IF (date_part('day', pSourceDate) <= _p.terms_cutoffday) THEN
+      _dueDate := (DATE(date_trunc('month', pSourceDate)) + (_p.terms_duedays - 1));
+    ELSE
+      _dueDate := (DATE(date_trunc('month', pSourceDate)) + (_p.terms_duedays - 1) + INTERVAL '1 month');
+    END IF;
+
+--  Handle unknown terms
+  ELSE
+    _dueDate := pSourceDate;
+  END IF;
+
+  RETURN _dueDate;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/disablepackage.sql b/foundation-database/public/functions/disablepackage.sql
new file mode 100644 (file)
index 0000000..d9793e7
--- /dev/null
@@ -0,0 +1,43 @@
+CREATE OR REPLACE FUNCTION disablePackage(TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  ppkgname ALIAS FOR $1;
+  _i       INTEGER := 0;
+  _tabs    TEXT[] := ARRAY['cmd',  'cmdarg', 'image',  'metasql',
+                           'priv', 'report', 'script', 'uiform'];
+
+BEGIN
+  IF (version() < 'PostgreSQL 8.2') THEN
+    RETURN -1;
+  END IF;
+
+  FOR _i IN ARRAY_LOWER(_tabs,1)..ARRAY_UPPER(_tabs,1) LOOP
+    EXECUTE 'ALTER TABLE ' || ppkgname || '.pkg' || _tabs[_i] ||
+            ' NO INHERIT public.' || _tabs[_i] || ';';
+  END LOOP;
+  
+  RETURN 0;
+END;
+$$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION disablePackage(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  ppkgheadid    ALIAS FOR $1;
+  _pkgname      TEXT;
+
+BEGIN
+  SELECT pkghead_name INTO _pkgname
+  FROM pkghead
+  WHERE (pkghead_id=ppkgheadid);
+  IF (NOT FOUND) THEN
+    RETURN -2;
+  END IF;
+
+  RETURN disablePackage(_pkgname);
+END;
+$$
+LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/distributeitemlocseries.sql b/foundation-database/public/functions/distributeitemlocseries.sql
new file mode 100644 (file)
index 0000000..66ab4dd
--- /dev/null
@@ -0,0 +1,200 @@
+CREATE OR REPLACE FUNCTION distributeItemlocSeries(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemlocSeries   ALIAS FOR $1;
+  _distCounter     INTEGER;
+  _itemlocdist     RECORD;
+  _itemlocid       INTEGER;
+  _invhistid       INTEGER;
+  _check           BOOLEAN;
+  _debug           BOOLEAN := true;
+BEGIN
+
+  IF (_debug) THEN
+    RAISE NOTICE 'distributeItemlocSeries, series=%', pItemlocSeries;
+  END IF;
+
+  _distCounter := 0;
+
+--  March through all of the itemlocdists for pItemlocSeries
+  FOR _itemlocdist IN SELECT itemlocdist_id AS itemlocdistid,
+                             itemlocdist_source_type AS type,
+                             itemlocdist_source_id AS sourceid,
+                             itemlocdist_qty AS qty,
+                             itemlocdist_itemsite_id AS itemsiteid,
+                             itemsite_freeze,
+                             itemlocdist_invhist_id AS invhistid,
+                             itemlocdist_ls_id AS lotserialid,
+                             itemlocdist_expiration AS expiration,
+                             itemlocdist_flush,
+                             itemlocdist_warranty AS warranty,
+                             itemlocdist_series AS series
+                      FROM itemlocdist, itemsite
+                      WHERE ( (itemlocdist_itemsite_id=itemsite_id)
+                       AND (itemlocdist_series=pItemlocSeries) )
+                      ORDER BY itemlocdist_flush DESC LOOP
+
+    _distCounter := _distCounter + 1;
+    IF (_debug) THEN
+      RAISE NOTICE 'itemlocdist loop %', _distCounter;
+      RAISE NOTICE 'itemlocdistid=%', _itemlocdist.itemlocdistid;
+      RAISE NOTICE 'type=%', _itemlocdist.type;
+      RAISE NOTICE 'sourceid=%', _itemlocdist.sourceid;
+      RAISE NOTICE 'qty=%', _itemlocdist.qty;
+      RAISE NOTICE 'itemsiteid=%', _itemlocdist.itemsiteid;
+      RAISE NOTICE 'freeze=%', _itemlocdist.itemsite_freeze;
+      RAISE NOTICE 'invhistid=%', _itemlocdist.invhistid;
+      RAISE NOTICE 'lotserialid=%', _itemlocdist.lotserialid;
+      RAISE NOTICE 'expiration=%', _itemlocdist.expiration;
+      RAISE NOTICE 'flush=%', _itemlocdist.itemlocdist_flush;
+      RAISE NOTICE 'warranty=%', _itemlocdist.warranty;
+    END IF;
+
+--  Commit invhist to itemsite
+    IF (NOT _itemlocdist.itemsite_freeze) THEN
+       PERFORM postInvHist(_itemlocdist.invhistid);
+    END IF;
+
+--  Mark the invhist tuple for the itemlocdist in question as having detail
+    UPDATE invhist
+    SET invhist_hasdetail=TRUE
+    WHERE ( (NOT invhist_hasdetail)
+     AND (invhist_id=_itemlocdist.invhistid) );
+
+--  If this itemlocdist is a flush, write a invdetail tuple that records the empty
+    IF (_itemlocdist.itemlocdist_flush) THEN
+      INSERT INTO invdetail
+      ( invdetail_invhist_id, invdetail_location_id, invdetail_ls_id,
+        invdetail_qty, invdetail_qty_before, invdetail_qty_after, invdetail_expiration,
+        invdetail_warrpurc )
+      SELECT _itemlocdist.invhistid, itemloc_location_id, itemloc_ls_id,
+             (itemloc_qty * -1), itemloc_qty, 0, itemloc_expiration, 
+             _itemlocdist.warranty
+      FROM itemloc
+      WHERE ( (itemloc_qty <> 0)
+       AND (itemloc_id=_itemlocdist.sourceid) );
+
+--  Delete the flushed itemloc if its parent itemsite is not frozen
+      IF (NOT _itemlocdist.itemsite_freeze) THEN
+        DELETE FROM itemloc
+        WHERE (itemloc_id=_itemlocdist.sourceid);
+      END IF;
+
+    ELSE
+--  If this is a location type distribution, check to see if the target itemloc
+--  already exists
+      IF (_itemlocdist.type = 'L') THEN
+        SELECT itemloc_id INTO _itemlocid
+        FROM itemloc
+        WHERE ( (itemloc_itemsite_id=_itemlocdist.itemsiteid)
+         AND (itemloc_location_id=_itemlocdist.sourceid)
+         AND (COALESCE(itemloc_ls_id,-1)=COALESCE(_itemlocdist.lotserialid,-1))
+         AND (COALESCE(itemloc_expiration,endOfTime())=COALESCE(_itemlocdist.expiration,endOfTime()))
+         AND (COALESCE(itemloc_warrpurc,endoftime())=COALESCE(_itemlocdist.warranty,endoftime())) );
+
+--  Nope, create it
+        IF (NOT FOUND) THEN
+          SELECT NEXTVAL('itemloc_itemloc_id_seq') INTO _itemlocid;
+
+          INSERT INTO itemloc
+          ( itemloc_id, itemloc_itemsite_id,
+            itemloc_location_id, itemloc_qty,
+            itemloc_ls_id, itemloc_expiration,
+            itemloc_warrpurc )
+          VALUES
+          ( _itemlocid, _itemlocdist.itemsiteid,
+            _itemlocdist.sourceid, 0,
+            _itemlocdist.lotserialid, _itemlocdist.expiration,
+            _itemlocdist.warranty );
+        END IF;
+
+      ELSE
+        _itemlocid = _itemlocdist.sourceid;
+
+        IF (_itemlocid IS NOT NULL AND (SELECT count(itemloc_id) = 0 FROM itemloc WHERE itemloc_id=_itemlocid)) THEN
+          RAISE EXCEPTION 'No record to distribute against. Someone else may have already distributed this record.';
+        END IF;
+      END IF;
+
+--  Record the invdetail
+      INSERT INTO invdetail
+      (invdetail_invhist_id, invdetail_location_id, invdetail_ls_id,
+       invdetail_qty, invdetail_qty_before, invdetail_qty_after, invdetail_expiration,
+       invdetail_warrpurc)
+      SELECT _itemlocdist.invhistid, itemloc_location_id, _itemlocdist.lotserialid,
+             _itemlocdist.qty, itemloc_qty, (itemloc_qty + _itemlocdist.qty),
+             itemloc_expiration,_itemlocdist.warranty
+      FROM itemloc
+      WHERE (itemloc_id=_itemlocid);
+
+--  Update the itemloc_qty if its parent itemsite is not frozen
+      IF (NOT _itemlocdist.itemsite_freeze) THEN
+        UPDATE itemloc
+        SET itemloc_qty = (itemloc_qty + _itemlocdist.qty)
+        WHERE (itemloc_id=_itemlocid);
+      END IF;
+
+--  Adjust QOH if this itemlocdist is to/from a non-netable location
+      IF ( SELECT (NOT location_netable)
+           FROM itemloc, location
+           WHERE ( (itemloc_location_id=location_id)
+            AND (itemloc_id=_itemlocid) ) ) THEN
+
+--  Record the netable->non-netable (or visaveras) invhist
+        SELECT NEXTVAL('invhist_invhist_id_seq') INTO _invhistid;
+        INSERT INTO invhist
+        ( invhist_id, invhist_itemsite_id, 
+          invhist_transtype, invhist_invqty,
+          invhist_qoh_before, invhist_qoh_after,
+          invhist_docnumber, invhist_comments,
+          invhist_invuom, invhist_unitcost,
+          invhist_costmethod, invhist_value_before, invhist_value_after,
+          invhist_series ) 
+        SELECT _invhistid, itemsite_id, 
+               'NN', (_itemlocdist.qty * -1),
+               itemsite_qtyonhand, (itemsite_qtyonhand - _itemlocdist.qty),
+               invhist_docnumber, invhist_comments,
+               uom_name, stdCost(item_id),
+               itemsite_costmethod, itemsite_value,
+               (itemsite_value + (_itemlocdist.qty * -1 * CASE WHEN(itemsite_costmethod='A') THEN avgcost(itemsite_id)
+                                                               ELSE stdCost(itemsite_item_id)
+                                                          END)),
+               _itemlocdist.series
+        FROM item, itemsite, invhist, uom
+        WHERE ((itemsite_item_id=item_id)
+         AND (item_inv_uom_id=uom_id)
+         AND (itemsite_controlmethod <> 'N')
+         AND (itemsite_id=_itemlocdist.itemsiteid)
+         AND (invhist_id=_itemlocdist.invhistid));
+
+--  Adjust the parent itemsite
+        IF (NOT _itemlocdist.itemsite_freeze) THEN
+          UPDATE itemsite
+          SET itemsite_qtyonhand = (itemsite_qtyonhand - _itemlocdist.qty),
+              itemsite_nnqoh = (itemsite_nnqoh + _itemlocdist.qty)
+          FROM itemloc
+          WHERE ((itemloc_itemsite_id=itemsite_id)
+           AND (itemloc_id=_itemlocid));
+        END IF;
+      END IF;
+
+    END IF;
+
+--  If, after the distribution, the target itemloc_qty = 0, delete the itemloc
+--  if its parent itemsite is not frozen
+    IF (NOT _itemlocdist.itemsite_freeze) THEN
+      DELETE FROM itemloc
+      WHERE ( (itemloc_qty=0)
+       AND (itemloc_id=_itemlocid) );
+    END IF;
+
+  END LOOP;
+
+  DELETE FROM itemlocdist
+  WHERE (itemlocdist_series=pItemlocSeries);
+
+  RETURN _distCounter;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/distributetodefault.sql b/foundation-database/public/functions/distributetodefault.sql
new file mode 100644 (file)
index 0000000..a68c72c
--- /dev/null
@@ -0,0 +1,84 @@
+CREATE OR REPLACE FUNCTION distributeToDefault(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemlocdistid ALIAS FOR $1;
+
+BEGIN
+
+  RETURN distributeToDefault(pItemlocdistid, 'O');
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION distributeToDefault(INTEGER, TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemlocdistid ALIAS FOR $1;
+  pTranstype ALIAS FOR $2;
+  _locationid INTEGER;
+  _itemlocdistid INTEGER;
+  _qty NUMERIC;
+
+BEGIN
+
+--  Make sure that the itemsite in question has a default location
+  SELECT CASE WHEN (pTranstype='R') THEN itemsite_recvlocation_id
+              WHEN (pTranstype='I') THEN itemsite_issuelocation_id
+              ELSE itemsite_location_id
+         END INTO _locationid
+  FROM itemlocdist, itemsite
+  WHERE ( (itemlocdist_itemsite_id=itemsite_id)
+   AND (itemlocdist_id=pItemlocdistid) );
+  IF ( (NOT FOUND) OR (_locationid = -1) ) THEN
+    RETURN -1;
+  END IF;
+
+--  Determine the remaining qty required to distribute
+  SELECT (p.itemlocdist_qty - COALESCE(SUM(c.itemlocdist_qty), 0)) INTO _qty
+  FROM itemlocdist AS p LEFT OUTER JOIN itemlocdist AS c ON (c.itemlocdist_itemlocdist_id=p.itemlocdist_id)
+  WHERE (p.itemlocdist_id=pItemlocdistid)
+  GROUP BY p.itemlocdist_qty;
+
+  IF (_qty = 0) THEN
+    RETURN -2;
+  END IF;
+
+--  Check to see if an itemlocdist with the correct location/lotserial/expiration already exists
+  SELECT target.itemlocdist_id INTO _itemlocdistid
+  FROM itemlocdist AS source, itemlocdist AS target, itemloc, itemsite
+  WHERE ( (target.itemlocdist_source_type='L')
+   AND (target.itemlocdist_source_id=_locationid)
+   AND (target.itemlocdist_itemsite_id=source.itemlocdist_itemsite_id)
+   AND (COALESCE(target.itemlocdist_ls_id)=COALESCE(source.itemlocdist_ls_id))
+   AND (target.itemlocdist_expiration=source.itemlocdist_expiration)
+   AND (target.itemlocdist_itemlocdist_id=source.itemlocdist_itemlocdist_id)
+   AND (target.itemlocdist_itemsite_id=itemsite_id)
+   AND (source.itemlocdist_id=pItemlocdistid) );
+
+  IF (FOUND) THEN
+    UPDATE itemlocdist
+    SET itemlocdist_qty = (itemlocdist_qty + _qty)
+    WHERE (itemlocdist_id=_itemlocdistid);
+
+    RETURN _itemlocdistid;
+  END IF;
+
+--  Create a new itemlocdist
+  SELECT NEXTVAL('itemlocdist_itemlocdist_id_seq') INTO _itemlocdistid;
+
+  INSERT INTO itemlocdist
+  ( itemlocdist_id, itemlocdist_itemlocdist_id, itemlocdist_source_type,
+    itemlocdist_ls_id, itemlocdist_expiration,
+    itemlocdist_source_id, itemlocdist_itemsite_id, itemlocdist_qty )
+  SELECT _itemlocdistid, pItemlocdistid, 'L',
+         itemlocdist_ls_id, itemlocdist_expiration,
+         _locationid, itemlocdist_itemsite_id, _qty
+  FROM itemlocdist
+  WHERE (itemlocdist_id=pItemlocdistid);
+
+  RETURN _itemlocdistid;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/distributetodefaultitemloc.sql b/foundation-database/public/functions/distributetodefaultitemloc.sql
new file mode 100644 (file)
index 0000000..01aa555
--- /dev/null
@@ -0,0 +1,83 @@
+CREATE OR REPLACE FUNCTION distributeToDefaultItemLoc(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemlocdistid ALIAS FOR $1;
+
+BEGIN
+
+  RETURN distributeToDefaultItemLoc(pItemlocdistid, 'O');
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION distributeToDefaultItemLoc(INTEGER, TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemlocdistid ALIAS FOR $1;
+  pTranstype ALIAS FOR $2;
+  _itemlocid INTEGER;
+  _itemlocdistid INTEGER;
+  _qty NUMERIC;
+
+BEGIN
+
+--  Make sure that the itemsite in question has a default location
+  SELECT itemloc_id INTO _itemlocid
+    FROM itemlocdist, itemsite, itemloc
+   WHERE ((itemlocdist_itemsite_id=itemsite_id)
+     AND ( (itemsite_location_id=itemloc_location_id AND pTranstype='O') OR
+           (itemsite_recvlocation_id=itemloc_location_id AND pTranstype='R') OR
+           (itemsite_issuelocation_id=itemloc_location_id AND pTranstype='I') )
+     AND (itemloc_itemsite_id=itemsite_id)
+     AND (itemlocdist_id=pItemlocdistid));
+  IF ( (NOT FOUND) OR (_itemlocid = -1) ) THEN
+    RETURN -1;
+  END IF;
+
+--  Determine the remaining qty required to distribute
+  SELECT (p.itemlocdist_qty - COALESCE(SUM(c.itemlocdist_qty), 0)) INTO _qty
+    FROM itemlocdist AS p LEFT OUTER JOIN itemlocdist AS c
+      ON (c.itemlocdist_itemlocdist_id=p.itemlocdist_id)
+   WHERE (p.itemlocdist_id=pItemlocdistid)
+   GROUP BY p.itemlocdist_qty;
+
+  IF (_qty = 0) THEN
+    RETURN -2;
+  END IF;
+
+--  Check to see if an itemlocdist with the correct location/lotserial/expiration already exists
+  SELECT target.itemlocdist_id INTO _itemlocdistid
+  FROM itemlocdist AS source, itemlocdist AS target
+  WHERE ( (target.itemlocdist_source_type='I')
+   AND (target.itemlocdist_source_id=_itemlocid)
+   AND (COALESCE(target.itemlocdist_ls_id,-1)=COALESCE(source.itemlocdist_ls_id,-1))
+   AND (target.itemlocdist_expiration=source.itemlocdist_expiration)
+   AND (target.itemlocdist_itemlocdist_id=source.itemlocdist_id)
+   AND (source.itemlocdist_id=pItemlocdistid) );
+
+  IF (FOUND) THEN
+    UPDATE itemlocdist
+    SET itemlocdist_qty = (itemlocdist_qty + _qty)
+    WHERE (itemlocdist_id=_itemlocdistid);
+
+    RETURN _itemlocdistid;
+  END IF;
+
+--  Create a new itemlocdist
+  SELECT NEXTVAL('itemlocdist_itemlocdist_id_seq') INTO _itemlocdistid;
+
+  INSERT INTO itemlocdist
+  ( itemlocdist_id, itemlocdist_itemlocdist_id,
+    itemlocdist_source_type, itemlocdist_source_id,
+    itemlocdist_qty, itemlocdist_expiration )
+  VALUES
+  ( _itemlocdistid, pItemlocdistid,
+    'I', _itemlocid,
+    _qty, endOfTime() );
+
+  RETURN _itemlocdistid;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/distributetolocations.sql b/foundation-database/public/functions/distributetolocations.sql
new file mode 100644 (file)
index 0000000..653dd94
--- /dev/null
@@ -0,0 +1,227 @@
+CREATE OR REPLACE FUNCTION distributeToLocations(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemlocdistid ALIAS FOR $1;
+  _distCounter INTEGER;
+  _itemlocdist RECORD;
+  _itemlocid INTEGER;
+  _runningQty NUMERIC;
+  _tmp RECORD;
+
+BEGIN
+
+  _distCounter := 0;
+  _runningQty  := 0;
+
+-- A scenario can occur where two people try to post distributions
+-- to the same itemsite against two or more lot/serial/mlc locations
+-- leading to a deadlock. This line tries to prevent that by locking
+-- ahead of time all the itemsites that the transaction will need
+-- before any of the other tables are locked individually.
+  SELECT itemsite_id
+    INTO _tmp
+    FROM itemsite
+   WHERE(itemsite_id in (SELECT DISTINCT itemlocdist_itemsite_id
+                           FROM itemlocdist
+                          WHERE(itemlocdist_id=pItemlocdistid)))
+     FOR UPDATE;
+
+--  March through all of the itemlocdist owned by the passed parent itemlocdist
+  FOR _itemlocdist IN SELECT c.itemlocdist_id AS itemlocdistid,
+                             c.itemlocdist_source_type AS type,
+                             c.itemlocdist_source_id AS sourceid,
+                             c.itemlocdist_qty AS qty,
+                             p.itemlocdist_itemsite_id AS itemsiteid,
+                             itemsite_freeze,
+                             p.itemlocdist_invhist_id AS invhistid,
+                             p.itemlocdist_ls_id AS lotserialid,
+                             p.itemlocdist_expiration AS expiration,
+                             p.itemlocdist_warranty AS warranty,
+                             p.itemlocdist_order_type AS ordertype,
+                             p.itemlocdist_order_id AS orderid,
+                             p.itemlocdist_series AS series
+                      FROM itemlocdist AS c, itemlocdist AS p, itemsite
+                      WHERE ( (c.itemlocdist_itemlocdist_id=p.itemlocdist_id)
+                       AND (p.itemlocdist_source_type='O')
+                       AND (p.itemlocdist_itemsite_id=itemsite_id)
+                       AND (p.itemlocdist_id=pItemlocdistid) ) LOOP
+
+    _distCounter := _distCounter + 1;
+
+--  If the target for this itemlocdist is a location, check to see if the
+--  required itemloc already exists
+    IF (_itemlocdist.type = 'L') THEN
+      SELECT itemloc_id INTO _itemlocid
+      FROM itemloc
+      WHERE ( (itemloc_itemsite_id=_itemlocdist.itemsiteid)
+       AND (itemloc_location_id=_itemlocdist.sourceid)
+       AND (COALESCE(itemloc_ls_id, -1)=COALESCE(_itemlocdist.lotserialid, -1))
+       AND (COALESCE(itemloc_expiration,endOfTime())=COALESCE(_itemlocdist.expiration,endOfTime()))
+       AND (COALESCE(itemloc_warrpurc,endoftime())=COALESCE(_itemlocdist.warranty,endoftime())) );
+
+--  Nope, make it
+      IF (NOT FOUND) THEN
+        SELECT NEXTVAL('itemloc_itemloc_id_seq') INTO _itemlocid;
+        INSERT INTO itemloc
+        ( itemloc_id, itemloc_itemsite_id,
+          itemloc_location_id, itemloc_qty,
+          itemloc_ls_id, itemloc_expiration,
+          itemloc_warrpurc )
+        VALUES
+        ( _itemlocid, _itemlocdist.itemsiteid,
+          _itemlocdist.sourceid, 0,
+          _itemlocdist.lotserialid, _itemlocdist.expiration,
+          _itemlocdist.warranty );
+      END IF;
+
+    ELSE
+--  Yep, cache it
+      _itemlocid = _itemlocdist.sourceid;
+
+      IF (_itemlocid IS NOT NULL AND (SELECT count(itemloc_id) = 0 FROM itemloc WHERE itemloc_id=_itemlocid)) THEN
+        RAISE EXCEPTION 'No record to distribute against. Someone else may have already distributed this record.';
+      END IF;
+    END IF;
+
+--  Record the invdetail for this itemlocdist
+    INSERT INTO invdetail
+    ( invdetail_invhist_id, invdetail_location_id, invdetail_ls_id,
+      invdetail_qty, invdetail_qty_before, invdetail_qty_after, invdetail_expiration, 
+      invdetail_warrpurc )
+    SELECT _itemlocdist.invhistid, itemloc_location_id, itemloc_ls_id,
+           _itemlocdist.qty, itemloc_qty, (itemloc_qty + _itemlocdist.qty),
+           itemloc_expiration,_itemlocdist.warranty
+    FROM itemloc
+    WHERE (itemloc_id=_itemlocid);
+
+--  Update the parent invhist to indicate that it has invdetail records
+    UPDATE invhist
+    SET invhist_hasdetail=TRUE
+    WHERE ((invhist_hasdetail=FALSE)
+     AND (invhist_id=_itemlocdist.invhistid));
+
+--  Update the itemloc_qty if its parent itemsite is not frozen
+    IF (NOT _itemlocdist.itemsite_freeze) THEN
+      UPDATE itemloc
+      SET itemloc_qty = (itemloc_qty + _itemlocdist.qty)
+      WHERE (itemloc_id=_itemlocid);
+
+      PERFORM postInvHist(_itemlocdist.invhistid);
+
+--  Handle reservation data
+      IF ( (SELECT fetchMetricBool('EnableSOReservationsByLocation')) AND
+           (_itemlocdist.qty < 0) ) THEN
+
+--  If a shipment on a sales order, record reservation change before updating
+--  so it can be reversed later if necessary
+        IF (_itemlocdist.ordertype = 'SO') THEN
+          INSERT INTO shipitemlocrsrv
+          SELECT nextval('shipitemlocrsrv_shipitemlocrsrv_id_seq'),
+            shipitem_id, itemloc_itemsite_id, itemloc_location_id,
+            itemloc_ls_id, itemloc_expiration, itemloc_warrpurc,
+            least(_itemlocdist.qty, itemlocrsrv_qty)
+          FROM shipitem, itemloc
+            JOIN itemlocrsrv ON (itemloc_id=itemlocrsrv_itemloc_id)
+          WHERE ( (shipitem_invhist_id=_itemlocdist.invhistid)
+            AND   (itemloc_id=_itemlocid)
+            AND   (itemlocrsrv_source=_itemlocdist.ordertype)
+            AND   (itemlocrsrv_source_id=_itemlocdist.orderid) );
+        END IF;
+
+--  Update the itemloc reservation
+        UPDATE itemlocrsrv
+        SET itemlocrsrv_qty = (itemlocrsrv_qty + _itemlocdist.qty)
+        WHERE ( (itemlocrsrv_itemloc_id=_itemlocid)
+          AND   (itemlocrsrv_source=_itemlocdist.ordertype)
+          AND   (itemlocrsrv_source_id=_itemlocdist.orderid) );
+          
+--  Delete reservation if fully distributed
+        DELETE FROM itemlocrsrv
+        WHERE ( (itemlocrsrv_itemloc_id=_itemlocid)
+          AND   (itemlocrsrv_source=_itemlocdist.ordertype)
+          AND   (itemlocrsrv_source_id=_itemlocdist.orderid)
+          AND   (itemlocrsrv_qty=0) );
+      END IF;
+    END IF;
+
+--  Adjust QOH if this itemlocdist is to/from a non-netable location
+    IF ( SELECT (NOT location_netable)
+         FROM itemloc, location
+         WHERE ((itemloc_location_id=location_id)
+          AND (itemloc_id=_itemlocid)) ) THEN
+
+--  Record the invhist record for the netable->non-netable (or visaversa)
+      INSERT INTO invhist
+      ( invhist_itemsite_id,
+        invhist_transtype, invhist_invqty,
+        invhist_qoh_before, invhist_qoh_after,
+        invhist_docnumber, invhist_comments,
+        invhist_invuom, invhist_unitcost,
+        invhist_costmethod, invhist_value_before, invhist_value_after,
+        invhist_series )
+      SELECT itemsite_id,
+             'NN', (_itemlocdist.qty * -1),
+             itemsite_qtyonhand, (itemsite_qtyonhand - _itemlocdist.qty),
+             invhist_docnumber, invhist_comments,
+             uom_name, stdCost(item_id),
+             itemsite_costmethod, itemsite_value,
+             (itemsite_value + (_itemlocdist.qty * -1 * CASE WHEN(itemsite_costmethod='A') THEN avgcost(itemsite_id)
+                                                             ELSE stdCost(itemsite_item_id)
+                                                        END)),
+             _itemlocdist.series
+      FROM item, itemsite, invhist, uom
+      WHERE ( (itemsite_item_id=item_id)
+       AND (item_inv_uom_id=uom_id)
+       AND (itemsite_controlmethod <> 'N')
+       AND (itemsite_id=_itemlocdist.itemsiteid)
+       AND (invhist_id=_itemlocdist.invhistid) );
+
+--  Update the itemsite_qoh
+      IF (NOT _itemlocdist.itemsite_freeze) THEN
+        UPDATE itemsite
+        SET itemsite_qtyonhand = (itemsite_qtyonhand - _itemlocdist.qty),
+            itemsite_nnqoh = (itemsite_nnqoh + _itemlocdist.qty)
+        FROM itemloc
+        WHERE ((itemloc_itemsite_id=itemsite_id)
+         AND (itemloc_id=_itemlocid));
+      END IF;
+    END IF;
+
+--  Cache the running qty.
+    _runningQty := _runningQty + _itemlocdist.qty;
+
+--  Dene with the child itemlocdist, so delete it
+    DELETE FROM itemlocdist
+    WHERE (itemlocdist_id=_itemlocdist.itemlocdistid);
+
+--  If the target itemloc is now at qty=0, delete it if its parent
+--  itemsite is not frozen
+    IF (NOT _itemlocdist.itemsite_freeze) THEN
+      DELETE FROM itemloc
+      WHERE ( (itemloc_qty=0)
+       AND (itemloc_id=_itemlocid) );
+    END IF;
+
+  END LOOP;
+
+--  If the running qty for the detailed distributions is the same as the
+--  total qty to distribute indicated by the parent itemlocdist, then the
+--  parent itemlocdist has been fully distributed and should be deleted.
+  IF ( ( SELECT itemlocdist_qty
+         FROM itemlocdist
+         WHERE (itemlocdist_id=pItemlocdistid) ) = _runningQty) THEN
+    DELETE FROM itemlocdist
+    WHERE (itemlocdist_id=pItemlocdistid);
+  ELSE
+--  There is still some more qty to distribute in the parent itemlocdist.
+--  Update the qty to distribute with the qty that has been distributed.
+    UPDATE itemlocdist
+    SET itemlocdist_qty = (itemlocdist_qty - _runningQty)
+    WHERE (itemlocdist_id=pItemlocdistid);
+  END IF;
+
+  RETURN _distCounter;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/distributevoucherline.sql b/foundation-database/public/functions/distributevoucherline.sql
new file mode 100644 (file)
index 0000000..03bcbe4
--- /dev/null
@@ -0,0 +1,170 @@
+CREATE OR REPLACE FUNCTION distributeVoucherLine(INTEGER, INTEGER, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pVoucherId ALIAS FOR $1;
+  pPoitemId ALIAS FOR $2;
+  pCurrId ALIAS FOR $3;
+  _count INTEGER;
+  _costelemId INTEGER;
+  _close BOOLEAN;
+  _r RECORD;
+  _qtyOrdered NUMERIC;
+  _voitemId INTEGER;
+  _taxtypeid INTEGER;
+
+BEGIN
+
+--  Make sure the P/O and Voucher are same currency
+
+  SELECT COALESCE(COUNT(*),0) INTO _count
+        FROM poitem JOIN pohead ON (pohead_id=poitem_pohead_id)
+        WHERE ((poitem_id=pPoitemid)
+        AND (pohead_curr_id=pCurrId));
+  IF (_count = 0) THEN
+        RETURN -3;
+  END IF;
+
+--  Validate and get cost element
+
+        SELECT COALESCE(COUNT(*),0) INTO _count
+                FROM itemcost, item, itemsite, poitem
+                WHERE ((itemcost_item_id=item_id)
+                AND (item_id=itemsite_item_id)
+                AND (itemsite_id=poitem_itemsite_id)
+                AND (poitem_id=pPoitemId));
+
+        IF (_count > 1) THEN
+                RETURN -5;
+        ELSEIF (_count = 1) THEN
+                SELECT itemcost_costelem_id INTO _costelemId
+                        FROM itemcost, item, itemsite, poitem
+                        WHERE ((itemcost_item_id=item_id)
+                        AND (item_id=itemsite_item_id)
+                        AND (itemsite_id=poitem_itemsite_id)
+                        AND (poitem_id=pPoitemId));
+        ELSE
+                SELECT costelem_id INTO _costelemId
+                        FROM costelem
+                        WHERE (costelem_type='Material');
+        END IF;
+
+
+--  Clear previous distributions
+
+        UPDATE recv SET recv_vohead_id=NULL, recv_voitem_id=NULL
+                WHERE ((recv_vohead_id=pVoucherId)
+                AND (recv_orderitem_id=pPoitemId)
+               AND (recv_order_type='PO'));
+        UPDATE poreject SET poreject_vohead_id=NULL, poreject_voitem_id=NULL
+                WHERE ((poreject_vohead_id=pVoucherId)
+                AND (poreject_poitem_id=pPoitemId));
+        DELETE FROM vodist
+                WHERE ((vodist_poitem_id=pPoitemId)
+                AND (vodist_vohead_id=pVoucherId));
+        DELETE FROM voitem
+                WHERE ((voitem_poitem_id=pPoitemId)
+                AND (voitem_vohead_id=pVoucherId));
+
+--  Determine Line balances
+
+        SELECT  COALESCE(SUM(qty_received),0) AS qty_received,
+                COALESCE(SUM(qty_rejected),0) AS qty_rejected,
+                COALESCE(SUM(qty_vouchered),0) AS qty_vouchered,
+                round(COALESCE(SUM(balance),0),2) AS balance,
+                round(COALESCE(SUM(freight),0),2) AS freight INTO _r
+         FROM   (
+                SELECT  recv_qty AS qty_received,
+                        0 AS qty_rejected,
+                        0 AS qty_vouchered,
+                        (recv_qty * COALESCE(recv_purchcost, poitem_unitprice)) AS balance,
+                        recv_freight AS freight
+                FROM poitem JOIN recv ON ((recv_orderitem_id=poitem_id) AND
+                                          (recv_order_type='PO'))
+                WHERE ( (recv_vohead_id IS NULL)
+                        AND (NOT recv_invoiced)
+                        AND (recv_posted)
+                        AND (poitem_id=pPoitemId) )
+
+                UNION ALL
+
+                SELECT  0 AS qty_received,
+                        (poreject_qty) AS qty_rejected,
+                        0 AS qty_vouchered,
+                        (poreject_qty * -1 * COALESCE(recv_purchcost, poitem_unitprice)) AS balance,
+                        0 AS freight
+                FROM poitem JOIN poreject ON (poreject_poitem_id=poitem_id)
+                            LEFT OUTER JOIN recv ON (recv_id=poreject_recv_id)
+                WHERE ( (poreject_posted)
+                        AND (poreject_vohead_id IS NULL)
+                        AND (NOT poreject_invoiced)
+                        AND (poitem_id=pPoitemId) )
+
+                UNION ALL
+
+                SELECT  0 AS qty_received,
+                        0 AS qty_rejected,
+                        voitem_qty AS qty_vouchered,
+                        0 AS balance,
+                        0 AS freight
+                FROM voitem, poitem
+                WHERE ( (voitem_poitem_id=pPoitemId)
+                        AND (poitem_id=voitem_poitem_id) )
+                ) AS data;
+
+                SELECT poitem_qty_ordered INTO _qtyOrdered
+                FROM poitem
+                WHERE (poitem_id=pPoitemId);
+
+        IF _r.balance < 0 THEN
+                RETURN -4;
+        ELSEIF ( ((_r.qty_received <> 0) OR (_r.qty_received <> 0)) AND (_r.qty_received - _r.qty_rejected = 0) ) THEN
+                RETURN -2;
+        ELSEIF ((_r.qty_received - _r.qty_rejected) = 0) THEN
+                RETURN 0;
+        END IF;
+
+-- Determine whether to close P/O item
+
+        IF (_r.qty_received -_r.qty_rejected + _r.qty_vouchered) >= _qtyOrdered THEN
+                _close:=True;
+        ELSE
+                _close:=False;
+        END IF;
+
+
+-- Create distribution
+
+        INSERT INTO vodist
+                (vodist_poitem_id,vodist_vohead_id,vodist_costelem_id,vodist_amount,vodist_qty,vodist_expcat_id)
+                VALUES (pPoitemId,pVoucherId,_costelemId,_r.balance,(_r.qty_received -_r.qty_rejected),-1);
+
+-- Create voucher item
+        SELECT poitem_taxtype_id INTO _taxtypeid
+        FROM poitem
+        WHERE (poitem_id=pPoitemId);
+
+        SELECT NEXTVAL('voitem_voitem_id_seq') INTO _voitemId;
+
+        INSERT INTO voitem (voitem_id,voitem_vohead_id,voitem_poitem_id,voitem_close,voitem_qty,voitem_freight, voitem_taxtype_id)
+                VALUES (_voitemId,pVoucherId,pPoitemId,_close,(_r.qty_received -_r.qty_rejected),_r.freight, _taxtypeid);
+
+-- Tag receipt records
+
+        UPDATE recv
+        SET recv_vohead_id=pVoucherId, recv_voitem_id=_voitemId
+        WHERE ((recv_orderitem_id=pPoitemId)
+         AND  (recv_order_type='PO')
+          AND  (recv_vohead_id IS NULL));
+
+        UPDATE poreject
+        SET poreject_vohead_id=pVoucherId,poreject_voitem_id=_voitemId
+        WHERE ((poreject_poitem_id=pPoitemId)
+        AND (NOT poreject_invoiced)
+        AND (poreject_vohead_id IS NULL));
+
+
+  RETURN 1;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/dopostcosts.sql b/foundation-database/public/functions/dopostcosts.sql
new file mode 100644 (file)
index 0000000..3c8e9cf
--- /dev/null
@@ -0,0 +1,139 @@
+CREATE OR REPLACE FUNCTION doPostCosts(BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+
+  pMaterial ALIAS FOR $1;
+  plowerMaterial ALIAS FOR $2;
+  pdirectLabor ALIAS FOR $3;
+  plowerDirectLabor ALIAS FOR $4;
+  poverhead ALIAS FOR $5;
+  plowerOverhead ALIAS FOR $6;
+  pmachOverhead ALIAS FOR $7;
+  plowerMachOverhead ALIAS FOR $8;
+  pUser ALIAS FOR $9;
+  plowerUser ALIAS FOR $10;
+  prollUp ALIAS FOR $11;
+  _item RECORD;
+  _result INTEGER := 0;
+
+BEGIN
+
+  PERFORM resetLowLevelCode(-1);
+
+  FOR _item IN SELECT costUpdate_item_id
+            FROM costUpdate
+            ORDER BY costUpdate_lowlevel_code DESC LOOP
+    PERFORM doPostCosts(_item.costUpdate_item_id, FALSE,
+                      pMaterial, plowerMaterial, pdirectLabor,
+                      plowerDirectLabor, poverhead, plowerOverhead,
+                      pmachOverhead, plowerMachOverhead,
+                      puser, plowerUser, prollUp);
+  END LOOP;
+
+  RETURN _result;
+
+END;
+ ' LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION doPostCosts(INTEGER, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+
+  pItemId ALIAS FOR $1;
+  pResetLowLevel ALIAS FOR $2;
+  pMaterial ALIAS FOR $3;
+  plowerMaterial ALIAS FOR $4;
+  pdirectLabor ALIAS FOR $5;
+  plowerDirectLabor ALIAS FOR $6;
+  poverhead ALIAS FOR $7;
+  plowerOverhead ALIAS FOR $8;
+  pmachOverhead ALIAS FOR $9;
+  plowerMachOverhead ALIAS FOR $10;
+  pUser ALIAS FOR $11;
+  plowerUser ALIAS FOR $12;
+  prollUp ALIAS FOR $13;
+  _itemcost RECORD;
+  _result INTEGER;
+
+BEGIN
+
+    IF (pResetLowLevel) THEN
+       PERFORM resetLowLevelCode(pItemId);
+    END IF;
+
+    FOR _itemcost IN SELECT itemcost_id, costelem_sys, costelem_type,
+                           itemcost_lowlevel,
+                           costUpdate_lowlevel_code, costUpdate_item_type
+            FROM itemcost, costelem, costUpdate
+            WHERE itemcost_item_id = pItemId
+             AND costUpdate_item_id = itemcost_item_id
+              AND itemcost_costelem_id = costelem_id LOOP
+      IF (NOT _itemcost.costelem_sys) THEN
+        IF ( (pUser) AND ( NOT _itemcost.itemcost_lowlevel) ) THEN
+          PERFORM postCost(_itemcost.itemcost_id);
+          _result := _result + 1;
+        END IF;
+        IF ( (plowerUser) AND ( _itemcost.itemcost_lowlevel) ) THEN
+          PERFORM postCost(_itemcost.itemcost_id);
+          _result := _result + 1;
+        END IF;
+      END IF;
+
+      IF (_itemcost.costelem_type = ''Material'') THEN
+        IF ( (pMaterial) AND ( NOT _itemcost.itemcost_lowlevel) ) THEN
+          PERFORM postCost(_itemcost.itemcost_id);
+          _result := _result + 1;
+        END IF;
+        IF ( (plowerMaterial) AND ( _itemcost.itemcost_lowlevel) ) THEN
+          PERFORM postCost(_itemcost.itemcost_id);
+          _result := _result + 1;
+        END IF;
+      END IF;
+
+      IF (_itemcost.costelem_type = ''Direct Labor'') THEN
+        IF ( (pdirectLabor) AND ( NOT _itemcost.itemcost_lowlevel) ) THEN
+          PERFORM postCost(_itemcost.itemcost_id);
+          _result := _result + 1;
+        END IF;
+        IF ( (plowerDirectLabor) AND ( _itemcost.itemcost_lowlevel) ) THEN
+          PERFORM postCost(_itemcost.itemcost_id);
+          _result := _result + 1;
+        END IF;
+      END IF;
+
+      IF (_itemcost.costelem_type = ''Overhead'') THEN
+        IF ( (poverhead) AND ( NOT _itemcost.itemcost_lowlevel) ) THEN
+          PERFORM postCost(_itemcost.itemcost_id);
+          _result := _result + 1;
+        END IF;
+        IF ( (plowerOverhead) AND ( _itemcost.itemcost_lowlevel) ) THEN
+          PERFORM postCost(_itemcost.itemcost_id);
+          _result := _result + 1;
+        END IF;
+      END IF;
+
+      IF (_itemcost.costelem_type = ''Machine Overhead'') THEN
+        IF ( (pmachOverhead) AND ( NOT _itemcost.itemcost_lowlevel) ) THEN
+          PERFORM postCost(_itemcost.itemcost_id);
+          _result := _result + 1;
+        END IF;
+        IF ( (plowerMachOverhead) AND ( _itemcost.itemcost_lowlevel) ) THEN
+          PERFORM postCost(_itemcost.itemcost_id);
+          _result := _result + 1;
+        END IF;
+      END IF;
+
+    END LOOP;
+
+    IF (prollUp) THEN    
+      PERFORM rollUpStandardCost(pItemId);
+      _result := _result + 1;
+    END IF;
+
+    RETURN _result;
+
+END;
+ ' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/doupdatecosts.sql b/foundation-database/public/functions/doupdatecosts.sql
new file mode 100644 (file)
index 0000000..83f606b
--- /dev/null
@@ -0,0 +1,161 @@
+CREATE OR REPLACE FUNCTION doUpdateCosts(BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+
+  plowerMaterial ALIAS FOR $1;
+  pdirectLabor ALIAS FOR $2;
+  plowerDirectLabor ALIAS FOR $3;
+  poverhead ALIAS FOR $4;
+  plowerOverhead ALIAS FOR $5;
+  pmachOverhead ALIAS FOR $6;
+  plowerMachOverhead ALIAS FOR $7;
+  plowerUser ALIAS FOR $8;
+  prollUp ALIAS FOR $9;
+
+BEGIN
+  RETURN doUpdateCosts(plowerMaterial, pdirectLabor, plowerDirectLabor,
+                      poverhead, plowerOverhead, pmachOverhead,
+                      plowerMachOverhead, plowerUser, prollUp, TRUE);
+END;
+' LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION doUpdateCosts(BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+
+  plowerMaterial ALIAS FOR $1;
+  pdirectLabor ALIAS FOR $2;
+  plowerDirectLabor ALIAS FOR $3;
+  poverhead ALIAS FOR $4;
+  plowerOverhead ALIAS FOR $5;
+  pmachOverhead ALIAS FOR $6;
+  plowerMachOverhead ALIAS FOR $7;
+  plowerUser ALIAS FOR $8;
+  prollUp ALIAS FOR $9;
+  pActual ALIAS FOR $10;
+  _item RECORD;
+  _bom RECORD;
+  _result INTEGER := 0;
+
+BEGIN
+
+  PERFORM resetLowLevelCode(-1);
+
+  FOR _item IN SELECT costUpdate_item_id
+            FROM costUpdate
+            ORDER BY costUpdate_lowlevel_code DESC LOOP
+    PERFORM doUpdateCosts(_item.costUpdate_item_id, false, plowerMaterial,
+                 pdirectLabor, plowerDirectLabor, poverhead, plowerOverhead,
+                 pmachOverhead, plowerMachOverhead, plowerUser, prollUp,
+                 pActual);
+  END LOOP;
+
+  RETURN _result;
+
+END;
+ ' LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION doUpdateCosts(INTEGER, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+
+  pItemId ALIAS FOR $1;
+  pResetLowLevel ALIAS FOR $2;
+  plowerMaterial ALIAS FOR $3;
+  pdirectLabor ALIAS FOR $4;
+  plowerDirectLabor ALIAS FOR $5;
+  poverhead ALIAS FOR $6;
+  plowerOverhead ALIAS FOR $7;
+  pmachOverhead ALIAS FOR $8;
+  plowerMachOverhead ALIAS FOR $9;
+  plowerUser ALIAS FOR $10;
+  prollUp ALIAS FOR $11;
+
+BEGIN
+    RETURN doUpdateCosts(pItemId, pResetLowLevel, plowerMaterial, pdirectLabor,
+                        plowerDirectLabor, poverhead, plowerOverhead,
+                        pmachOverhead, plowerMachOverhead, plowerUser, prollUp,
+                        TRUE);
+END;
+' LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION doUpdateCosts(INTEGER, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+
+  pItemId ALIAS FOR $1;
+  pResetLowLevel ALIAS FOR $2;
+  plowerMaterial ALIAS FOR $3;
+  pdirectLabor ALIAS FOR $4;
+  plowerDirectLabor ALIAS FOR $5;
+  poverhead ALIAS FOR $6;
+  plowerOverhead ALIAS FOR $7;
+  pmachOverhead ALIAS FOR $8;
+  plowerMachOverhead ALIAS FOR $9;
+  plowerUser ALIAS FOR $10;
+  prollUp ALIAS FOR $11;
+  pUpdateActual ALIAS FOR $12;
+  _item RECORD;
+  _bom RECORD;
+  _result INTEGER := 0;
+  _resultFromReset INTEGER;
+  _counterNum INTEGER;
+  _feedBackNum INTEGER;
+
+BEGIN
+
+    IF (pResetLowLevel) THEN
+       PERFORM resetLowLevelCode(pItemId);
+    END IF;
+
+    SELECT costUpdate_item_id AS item_id, costUpdate_item_type AS item_type
+       INTO _item
+    FROM costUpdate
+    WHERE costUpdate_item_id = pItemId;
+
+    IF ((plowerMaterial) AND ((_item.item_type <> ''P'') AND (_item.item_type <> ''O''))) THEN    
+      PERFORM updateSorACost(_item.item_id, ''Material'', TRUE, lowerCost(_item.item_id, ''Material'', pUpdateActual), pUpdateActual);
+    END IF;
+
+    IF (pdirectLabor) THEN    
+      PERFORM updateSorACost(_item.item_id, ''Direct Labor'', FALSE, xtmfg.directLaborCost(_item.item_id), pUpdateActual);
+    END IF;
+
+    IF (plowerDirectLabor) THEN    
+      PERFORM updateSorACost(_item.item_id, ''Direct Labor'', TRUE, lowerCost(_item.item_id, ''Direct Labor'', pUpdateActual), pUpdateActual);
+    END IF;
+
+    IF (poverhead) THEN    
+      PERFORM updateSorACost(_item.item_id, ''Overhead'', FALSE, xtmfg.overheadCost(_item.item_id), pUpdateActual);
+    END IF;
+
+    IF (plowerOverhead) THEN    
+      PERFORM updateSorACost(_item.item_id, ''Overhead'', TRUE, lowerCost(_item.item_id, ''Overhead'', pUpdateActual), pUpdateActual);
+    END IF;
+
+    IF (pmachOverhead) THEN    
+      PERFORM updateSorACost(_item.item_id, ''Machine Overhead'', FALSE, xtmfg.machineOverheadCost(_item.item_id), pUpdateActual);
+    END IF;
+
+    IF (plowerMachOverhead) THEN    
+      PERFORM updateSorACost(_item.item_id, ''Machine Overhead'', TRUE, lowerCost(_item.item_id, ''Machine Overhead'', pUpdateActual), pUpdateActual);
+    END IF;
+
+    IF (plowerUser) THEN    
+      PERFORM updateLowerUserCosts(_item.item_id, pUpdateActual);
+    END IF;
+
+    IF (prollUp) THEN    
+      PERFORM rollUpSorACost(_item.item_id, pUpdateActual);
+    END IF;
+
+    RETURN _result;
+
+END;
+ ' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/dropifexists.sql b/foundation-database/public/functions/dropifexists.sql
new file mode 100644 (file)
index 0000000..664ce7e
--- /dev/null
@@ -0,0 +1,194 @@
+CREATE OR REPLACE FUNCTION dropIfExists(TEXT, TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN dropIfExists($1, $2, 'public');
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION dropIfExists(TEXT, TEXT, TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN dropIfExists($1, $2, $3, false);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION dropIfExists(TEXT, TEXT, TEXT, BOOLEAN) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pType         ALIAS FOR $1;
+  pObject       ALIAS FOR $2;
+  pSchema       ALIAS FOR $3;
+  pCascade      ALIAS FOR $4;
+  _table       TEXT;
+  _query       TEXT;
+BEGIN
+  IF (UPPER(pType) = 'INDEX') THEN
+    _query = 'DROP INDEX ' || quote_ident(LOWER(pSchema)) || '.' || quote_ident(LOWER(pObject));
+    
+    BEGIN
+      EXECUTE _query;
+    EXCEPTION WHEN undefined_object OR invalid_schema_name THEN
+               RETURN 0;
+             WHEN OTHERS THEN RAISE EXCEPTION '% %', SQLSTATE, SQLERRM;
+    END;
+
+  ELSEIF (UPPER(pType) = 'TABLE') THEN
+    _query = 'DROP TABLE ' || quote_ident(LOWER(pSchema)) || '.' || quote_ident(LOWER(pObject)); 
+    
+    IF (pCascade) THEN
+      _query = _query || ' CASCADE';
+    END IF;
+
+    BEGIN
+      EXECUTE _query;
+    EXCEPTION WHEN undefined_table OR invalid_schema_name THEN
+               RETURN 0;
+             WHEN OTHERS THEN RAISE EXCEPTION '% %', SQLSTATE, SQLERRM;
+    END;
+
+  ELSIF (UPPER(pType) = 'VIEW') THEN
+    _query = 'DROP VIEW ' || quote_ident(LOWER(pSchema)) || '.' || quote_ident(LOWER(pObject));
+
+    IF (pCascade) THEN
+      _query = _query || ' CASCADE';
+    END IF;
+    
+    BEGIN
+      EXECUTE _query;
+    EXCEPTION WHEN undefined_table OR invalid_schema_name THEN
+               RETURN 0;
+             WHEN OTHERS THEN RAISE EXCEPTION '% %', SQLSTATE, SQLERRM;
+    END;
+
+  ELSIF (UPPER(pType) = 'TRIGGER') THEN
+    SELECT relname INTO _table
+    FROM pg_trigger, pg_class
+    WHERE ((tgrelid=pg_class.oid)
+      AND  (UPPER(tgname)=UPPER(pObject)));
+    IF (NOT FOUND) THEN
+      _table := '[no table]';
+    END IF;
+
+    _query = 'DROP TRIGGER ' || quote_ident(LOWER(pObject)) ||
+            ' ON ' || quote_ident(LOWER(pSchema)) || '.' || quote_ident(LOWER(_table));
+    BEGIN
+      EXECUTE _query;
+    EXCEPTION WHEN undefined_object THEN
+               RETURN 0;
+             WHEN undefined_table OR invalid_schema_name THEN
+               RETURN 0;
+             WHEN OTHERS THEN RAISE EXCEPTION '% %', SQLSTATE, SQLERRM;
+    END;
+
+  ELSIF (UPPER(pType) = 'FUNCTION') THEN
+    _query = 'DROP FUNCTION ' || (LOWER(pSchema)) || '.' ||
+                                   (LOWER(pObject));
+    BEGIN
+      EXECUTE _query;
+    EXCEPTION WHEN undefined_object OR undefined_function OR invalid_schema_name THEN
+               RETURN 0;
+             WHEN OTHERS THEN RAISE EXCEPTION '% %', SQLSTATE, SQLERRM;
+    END;
+
+  ELSIF (UPPER(pType) = 'CONSTRAINT') THEN
+    IF( (SELECT count(*)
+           FROM pg_constraint, pg_class, pg_namespace
+          WHERE((conrelid=pg_class.oid)
+            AND (connamespace=pg_namespace.oid)
+            AND (conname=pObject)
+            AND (nspname=pSchema))
+         ) > 1 ) THEN
+      RAISE EXCEPTION 'dropIfExists called on constraint name that matches more than 1 constraint.';
+    END IF;
+    SELECT relname INTO _table
+    FROM pg_constraint, pg_class, pg_namespace
+    WHERE ((conrelid=pg_class.oid)
+      AND  (connamespace=pg_namespace.oid)
+      AND  (conname=pObject)
+      AND  (nspname=pSchema));
+    IF (NOT FOUND) THEN
+      RETURN 0;
+    END IF;
+    _query = 'ALTER TABLE ' || quote_ident(LOWER(pSchema)) || '.' || quote_ident(LOWER(_table))
+             || ' DROP CONSTRAINT ' || quote_ident(LOWER(pObject));
+    BEGIN
+      EXECUTE _query;
+    EXCEPTION WHEN undefined_table OR invalid_schema_name THEN
+               RETURN 0;
+             WHEN OTHERS THEN RAISE EXCEPTION '% %', SQLSTATE, SQLERRM;
+    END;
+
+  ELSIF (UPPER(pType) = 'SCHEMA') THEN
+    _query = 'DROP SCHEMA ' || quote_ident(LOWER(pObject));
+    BEGIN
+      EXECUTE _query;
+    EXCEPTION WHEN invalid_schema_name THEN
+                RETURN 0;
+              WHEN OTHERS THEN RAISE EXCEPTION '% %', SQLSTATE, SQLERRM;
+    END;
+
+  ELSIF (UPPER(pType) = 'TYPE') THEN
+    _query = 'DROP TYPE ' || quote_ident(LOWER(pSchema)) || '.' ||
+                               quote_ident(LOWER(pObject));
+    IF (pCascade) THEN
+      _query = _query || ' CASCADE';
+    END IF;
+    
+    BEGIN
+      EXECUTE _query;
+    EXCEPTION WHEN undefined_object OR invalid_schema_name THEN
+                RETURN 0;
+              WHEN OTHERS THEN RAISE EXCEPTION '% %', SQLSTATE, SQLERRM;
+    END;
+
+  ELSE
+    RAISE EXCEPTION 'dropIfExists(%, %): unknown pType %', pType, pObject, pType;
+  END IF;
+
+  RETURN 1;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION dropIfExists(TEXT, TEXT, TEXT, TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pType         ALIAS FOR $1;
+  pObject       ALIAS FOR $2;
+  pSchema       ALIAS FOR $3;
+  pRelation     ALIAS FOR $4;
+  _table        TEXT;
+  _query       TEXT;
+BEGIN
+  IF (UPPER(pType) = 'CONSTRAINT') THEN
+    SELECT relname INTO _table
+    FROM pg_constraint, pg_class, pg_namespace
+    WHERE ((conrelid=pg_class.oid)
+      AND  (connamespace=pg_namespace.oid)
+      AND  (conname=pObject)
+      AND  (relname=pRelation)
+      AND  (nspname=pSchema));
+    IF (NOT FOUND) THEN
+      RETURN 0;
+    END IF;
+    _query = 'ALTER TABLE ' || quote_ident(LOWER(pSchema)) || '.' || quote_ident(LOWER(pRelation))
+             || ' DROP CONSTRAINT ' || quote_ident(LOWER(pObject));
+    BEGIN
+      EXECUTE _query;
+    EXCEPTION WHEN undefined_table OR invalid_schema_name THEN
+               RETURN 0;
+             WHEN OTHERS THEN RAISE EXCEPTION '% %', SQLSTATE, SQLERRM;
+    END;
+
+  ELSE
+    RAISE EXCEPTION 'dropIfExists(%, %, %, %): pType % is not supported when relation is specified', pType, pObject, pSchema, pRelation, pType;
+  END IF;
+
+  RETURN 1;
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/editccnumber.sql b/foundation-database/public/functions/editccnumber.sql
new file mode 100644 (file)
index 0000000..76467c9
--- /dev/null
@@ -0,0 +1,103 @@
+CREATE OR REPLACE FUNCTION editccnumber(text, text)
+  RETURNS text AS
+'
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCcardnum ALIAS FOR $1;
+  pCcardtype ALIAS FOR $2;
+  card_length INTEGER;
+  card_valid boolean := false;
+  starting_digits TEXT;
+  _sum INTEGER := 0;
+  _digit INTEGER := 0;
+  _timesTwo BOOLEAN := false;
+BEGIN
+
+-- Check the card type
+  IF (pCcardtype NOT IN (''M'', ''V'', ''A'', ''D'')) THEN
+-- Unknown Card Type
+   RETURN -1;
+  END IF;
+
+  card_length := length(pCcardnum);
+
+-- Process Master Card Checking length
+-- Process Master Card Starting digits
+  IF (pCcardtype = ''M'') THEN
+    IF (card_length != 16) THEN
+-- Bad Card Length Card Type
+      RETURN -2;
+    END IF;
+    starting_digits := substr(pCcardnum, 1, 2);
+    IF (starting_digits < ''51'' OR starting_digits > ''55'') THEN
+-- Bad Starting digits
+      RETURN -6;
+    END IF;
+  END IF;
+
+-- Process Visa Card Checking length
+-- Process Visa Card Starting digits
+  IF (pCcardtype = ''V'') THEN
+    IF (card_length != 13 AND card_length != 16) THEN
+-- Bad Card Length Card Type
+      RETURN -3;
+    END IF;
+    starting_digits := substr(pCcardnum, 1, 1);
+    IF (starting_digits != ''4'') THEN
+-- Bad Starting digits
+      RETURN -7;
+    END IF;
+  END IF;
+
+-- Process American Express Card Checking length
+-- Process American Express Card Starting digits
+  IF (pCcardtype = ''A'') THEN
+    IF (card_length != 15) THEN
+-- Bad Card Length Card Type
+      RETURN -4;
+    END IF;
+    starting_digits := substr(pCcardnum, 1, 2);
+    IF (starting_digits != ''34'' AND starting_digits != ''37'') THEN
+-- Bad Starting digits
+      RETURN -8;
+    END IF;
+  END IF;
+
+-- Process Discover Card Checking length
+-- Process Discover Card Starting digits
+  IF (pCcardtype = ''D'') THEN
+    IF (card_length != 16) THEN
+-- Bad Card Length Card Type
+      RETURN -5;
+    END IF;
+    starting_digits := substr(pCcardnum, 1, 4);
+    IF (starting_digits != ''6011'') THEN
+-- Bad Starting digits
+      RETURN -9;
+    END IF;
+  END IF;
+
+-- Now comes the fun part of doing the "check" for the check sum
+-- perform a luhn checksum
+  FOR i IN REVERSE card_length .. 1 LOOP
+    _digit := int4(substr(pCcardnum, i, 1));
+    IF (_timesTwo) THEN
+      _digit := _digit * 2;
+      IF (_digit > 9) THEN
+        _digit := _digit - 9;
+      END IF;
+    END IF;
+    _sum := _sum + _digit;
+    _timesTwo := NOT _timesTwo;
+  END LOOP;
+
+  IF (mod(_sum, 10) != 0) THEN
+    RETURN -10;
+  END IF;
+
+  RETURN 0; -- No Error
+
+END;
+'
+  LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/enablepackage.sql b/foundation-database/public/functions/enablepackage.sql
new file mode 100644 (file)
index 0000000..1f655c3
--- /dev/null
@@ -0,0 +1,43 @@
+CREATE OR REPLACE FUNCTION enablePackage(TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  ppkgname  ALIAS FOR $1;
+  _i        INTEGER := 0;
+  _tabs     TEXT[] := ARRAY['cmd',  'cmdarg', 'image',  'metasql',
+                            'priv', 'report', 'script', 'uiform'];
+
+BEGIN
+  IF (version() < 'PostgreSQL 8.2') THEN
+    RETURN -1;
+  END IF;
+
+  FOR _i IN ARRAY_LOWER(_tabs,1)..ARRAY_UPPER(_tabs,1) LOOP
+    EXECUTE 'ALTER TABLE ' || ppkgname || '.pkg' || _tabs[_i] ||
+            ' INHERIT public.' || _tabs[_i] || ';';
+  END LOOP;
+
+  RETURN 0;
+END;
+$$
+LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION enablePackage(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  ppkgheadid    ALIAS FOR $1;
+  _pkgname      TEXT;
+
+BEGIN
+  SELECT pkghead_name INTO _pkgname
+  FROM pkghead
+  WHERE (pkghead_id=ppkgheadid);
+  IF (NOT FOUND) THEN
+    RETURN -2;
+  END IF;
+
+  RETURN enablePackage(_pkgname);
+END;
+$$
+LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/endoftime.sql b/foundation-database/public/functions/endoftime.sql
new file mode 100644 (file)
index 0000000..ece4047
--- /dev/null
@@ -0,0 +1,7 @@
+
+CREATE OR REPLACE FUNCTION endoftime() RETURNS DATE IMMUTABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+SELECT DATE('2100-01-01') as result;
+$$ LANGUAGE sql;
+
diff --git a/foundation-database/public/functions/entercount.sql b/foundation-database/public/functions/entercount.sql
new file mode 100644 (file)
index 0000000..f0e86fc
--- /dev/null
@@ -0,0 +1,23 @@
+CREATE OR REPLACE FUNCTION enterCount(int, numeric, text) RETURNS integer AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pInvcntid ALIAS FOR $1;
+  pQty ALIAS FOR $2;
+  pComments ALIAS FOR $3;
+BEGIN
+
+  UPDATE invcnt
+  SET invcnt_qoh_after = pQty,
+      invcnt_comments = CASE WHEN ( (LENGTH(invcnt_comments) = 0) AND
+                                    (LENGTH(pComments) > 0) ) THEN pComments
+                             WHEN (LENGTH(pComments) > 0) THEN (invcnt_comments || E'\n' || pComments)
+                             ELSE invcnt_comments
+                        END,
+      invcnt_cntdate = CURRENT_TIMESTAMP,
+      invcnt_cnt_username = getEffectiveXtUser()
+  WHERE (invcnt_id=pInvcntid);
+
+  RETURN 0;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/enterporeceipt.sql b/foundation-database/public/functions/enterporeceipt.sql
new file mode 100644 (file)
index 0000000..2a8c3a2
--- /dev/null
@@ -0,0 +1,32 @@
+CREATE OR REPLACE FUNCTION enterPoReceipt(INTEGER, NUMERIC) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN enterReceipt(''PO'', $1, $2, 0.0, '''', NULL, NULL);
+END;
+' LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION enterPoReceipt(INTEGER, NUMERIC, TEXT) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN enterPoReceipt(''PO'', $1, $2, 0.0, $3, NULL, NULL);
+END;
+' LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION enterPoReceipt(INTEGER, NUMERIC, NUMERIC, TEXT) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN enterPoReceipt(''PO'', $1, $2, $3, $4, NULL, NULL);
+END;
+' LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION enterPoReceipt(INTEGER, NUMERIC, NUMERIC, TEXT, INTEGER, DATE) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN enterReceipt(''PO'', $1, $2, $3, $4, $5, $6);
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/enterporeturn.sql b/foundation-database/public/functions/enterporeturn.sql
new file mode 100644 (file)
index 0000000..5eec0b1
--- /dev/null
@@ -0,0 +1,38 @@
+CREATE OR REPLACE FUNCTION enterPoReturn(INTEGER, NUMERIC, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN enterPoReturn($1, $2, $3, NULL);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION enterPoReturn(INTEGER, NUMERIC, INTEGER, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pPoitemid ALIAS FOR $1;
+  pQty ALIAS FOR $2;
+  pRjctcodeid ALIAS FOR $3;
+  pRecvid ALIAS FOR $4;
+  _porejectid INTEGER;
+
+BEGIN
+
+  SELECT NEXTVAL('poreject_poreject_id_seq') INTO _porejectid;
+
+  INSERT INTO poreject
+  ( poreject_id, poreject_date, poreject_ponumber, poreject_poitem_id, poreject_trans_username,
+    poreject_agent_username, poreject_itemsite_id,
+    poreject_vend_id, poreject_vend_item_number, poreject_vend_item_descrip, poreject_vend_uom,
+    poreject_qty, poreject_rjctcode_id, poreject_posted, poreject_invoiced, poreject_recv_id )
+  SELECT _porejectid, CURRENT_TIMESTAMP, pohead_number, poitem_id, getEffectiveXtUser(),
+         pohead_agent_username, poitem_itemsite_id,
+         pohead_vend_id, poitem_vend_item_number, poitem_vend_item_descrip, poitem_vend_uom,
+         pQty, pRjctcodeid, FALSE, FALSE, pRecvid
+  FROM poitem JOIN pohead ON (pohead_id=poitem_pohead_id)
+  WHERE (poitem_id=pPoitemid);
+
+  RETURN _porejectid;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/enterreceipt.sql b/foundation-database/public/functions/enterreceipt.sql
new file mode 100644 (file)
index 0000000..2ff1414
--- /dev/null
@@ -0,0 +1,155 @@
+CREATE OR REPLACE FUNCTION enterReceipt(TEXT, INTEGER, NUMERIC, NUMERIC, TEXT, INTEGER, DATE) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN enterReceipt($1, $2, $3, $4, $5, $6, $7, NULL);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION enterReceipt(TEXT, INTEGER, NUMERIC, NUMERIC, TEXT, INTEGER, DATE, NUMERIC) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pordertype   ALIAS FOR $1;
+  porderitemid ALIAS FOR $2;
+  pQty         ALIAS FOR $3;
+  pFreight     ALIAS FOR $4;
+  pNotes       ALIAS FOR $5;
+  pcurrid      ALIAS FOR $6;   -- NULL is handled by SELECT ... INTO _o
+  precvdate    ALIAS FOR $7;   -- NULL is handled by INSERT INTO recv
+  pRecvCost    ALIAS FOR $8;
+  _timestamp    TIMESTAMP;
+  _o           RECORD;
+  _recvid      INTEGER;
+  _warehouseid         INTEGER;
+  _recvcost    NUMERIC;
+
+BEGIN
+  IF(precvdate IS NULL OR precvdate = CURRENT_DATE) THEN
+    _timestamp := CURRENT_TIMESTAMP;
+  ELSE
+    _timestamp := precvdate;
+  END IF;
+  SELECT NEXTVAL('recv_recv_id_seq') INTO _recvid;
+
+  DELETE FROM recv
+  WHERE ((NOT recv_posted)
+    AND  (recv_order_type=pordertype)
+    AND  (recv_orderitem_id=porderitemid) );
+
+  IF (pQty > 0) THEN
+    IF (pordertype='PO') THEN
+      SELECT pohead_number AS orderhead_number,
+          poitem_id AS orderitem_id,
+          pohead_agent_username AS orderhead_agent_username,
+          CASE WHEN (poitem_itemsite_id = -1) THEN NULL
+               ELSE poitem_itemsite_id
+          END AS itemsite_id,
+          vend_id,
+          COALESCE(poitem_vend_item_number, '') AS vend_item_number,
+          COALESCE(poitem_vend_item_descrip, '') AS vend_item_descrip,
+          COALESCE(poitem_vend_uom, '') AS vend_uom,
+          poitem_duedate AS duedate,
+          poitem_unitprice AS orderitem_unitcost,
+          pohead_curr_id AS orderitem_unitcost_curr_id,
+          pohead_curr_id AS freight_curr_id,
+          poitem_rlsd_duedate AS rlsd_duedate INTO _o
+        FROM pohead
+          JOIN poitem ON (pohead_id=poitem_pohead_id)
+          JOIN vendinfo ON (pohead_vend_id=vend_id)
+        WHERE (poitem_id=porderitemid);
+        
+    ELSIF (pordertype='RA') THEN
+       SELECT rahead_number AS orderhead_number,
+          raitem_id AS orderitem_id,
+          ''::text AS orderhead_agent_username,
+          raitem_itemsite_id AS itemsite_id,
+          NULL::integer AS vend_id,
+          ''::text AS vend_item_number,
+          ''::text AS vend_item_descrip,
+          ''::text AS vend_uom,
+          raitem_scheddate AS duedate,
+          raitem_unitprice AS orderitem_unitcost,
+          rahead_curr_id AS orderitem_unitcost_curr_id,
+          rahead_curr_id AS freight_curr_id,
+           raitem_scheddate AS rlsd_duedate INTO _o
+        FROM rahead
+          JOIN raitem ON (rahead_id=raitem_rahead_id)
+        WHERE (raitem_id=porderitemid);
+        
+    ELSIF (pordertype='TO') THEN
+         SELECT tohead_number AS orderhead_number,
+          toitem_id AS orderitem_id,
+          tohead_agent_username AS orderhead_agent_username,
+          itemsite_id,
+          NULL::integer AS vend_id,
+          ''::text AS vend_item_number,
+          ''::text AS vend_item_descrip,
+          ''::text AS vend_uom,
+          toitem_duedate AS duedate,
+          toitem_stdcost AS orderitem_unitcost,
+          baseCurrId() AS orderitem_unitcost_curr_id,
+          toitem_freight_curr_id AS freight_curr_id,
+           toitem_duedate AS rlsd_duedate INTO _o
+        FROM itemsite, tohead
+          JOIN toitem ON (tohead_id=toitem_tohead_id)
+        WHERE ((toitem_id=porderitemid)
+          AND  (tohead_dest_warehous_id=itemsite_warehous_id)
+          AND  (toitem_item_id=itemsite_item_id));
+    END IF;
+
+    --Make sure user has site privileges
+     IF ((FOUND) AND (_o.itemsite_id IS NOT NULL)) THEN
+       SELECT warehous_id INTO _warehouseid
+       FROM itemsite,site()
+       WHERE ((itemsite_id=_o.itemsite_id)
+         AND (warehous_id=itemsite_warehous_id));
+          
+       IF (NOT FOUND) THEN
+         RETURN 0;
+        END IF;
+      END IF;   
+
+    --Make sure we aren't trying to receive a Kit
+    IF ((FOUND) AND (_o.itemsite_id IS NOT NULL)) THEN
+      IF (SELECT (item_type='K')
+          FROM itemsite, item
+          WHERE ((itemsite_id=_o.itemsite_id)
+            AND  (item_id=itemsite_item_id))) THEN
+        RETURN 0;
+      END IF;
+    END IF;   
+
+    IF (NOT FOUND) THEN
+      RETURN -1;
+    END IF;
+
+    -- default to orderitem_unitcost if recv_purchcost is not specified
+    IF(pRecvCost IS NULL) THEN
+      _recvcost := _o.orderitem_unitcost;
+    ELSE
+      _recvcost := pRecvCost;
+    END IF;
+
+    INSERT INTO recv
+    ( recv_id, recv_date,
+      recv_order_number, recv_order_type, recv_orderitem_id,
+      recv_trans_usr_name, recv_agent_username, recv_itemsite_id,
+      recv_vend_id, recv_vend_item_number, recv_vend_item_descrip,
+      recv_vend_uom, recv_qty, recv_duedate,
+      recv_purchcost, recv_purchcost_curr_id,
+      recv_notes, recv_freight, recv_freight_curr_id, recv_rlsd_duedate
+    ) VALUES (
+      _recvid, _timestamp,
+      _o.orderhead_number, pordertype, _o.orderitem_id::INTEGER,
+      getEffectiveXtUser(), _o.orderhead_agent_username, _o.itemsite_id::INTEGER,
+      _o.vend_id::INTEGER, _o.vend_item_number, _o.vend_item_descrip,
+      _o.vend_uom, pQty, _o.duedate,
+      _recvcost, _o.orderitem_unitcost_curr_id::INTEGER,
+      pNotes, pFreight, _o.freight_curr_id::INTEGER, _o.rlsd_duedate);
+  END IF;
+
+  RETURN _recvid;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/expirecreditcard.sql b/foundation-database/public/functions/expirecreditcard.sql
new file mode 100644 (file)
index 0000000..d3a6235
--- /dev/null
@@ -0,0 +1,53 @@
+CREATE OR REPLACE FUNCTION expireCreditCard(integer, bytea)
+  RETURNS text AS
+'
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCust ALIAS FOR $1;
+  pKey ALIAS FOR $2;
+  num_updated INTEGER;
+  cc_year INTEGER;
+  cc_month INTEGER;
+  cc_year_t TEXT;
+  cc_month_t TEXT;
+  _dr RECORD;
+  _cc RECORD;
+  bf TEXT;
+
+BEGIN
+
+  num_updated := 0;
+  bf := ''bf'';
+
+  select cast(date_part(''year'', CURRENT_DATE) AS INTEGER) AS check_year, cast(date_part(''month'', CURRENT_DATE) AS INTEGER) AS check_month INTO _dr;
+
+  FOR _cc IN SELECT ccard_id, 
+                    decrypt(setbytea(ccard_month_expired), setbytea(pKey), ''bf'') AS ccard_month_expired,
+                    decrypt(setbytea(ccard_year_expired), setbytea(pKey), ''bf'') AS ccard_year_expired
+      FROM ccard
+      WHERE ( (ccard_cust_id=pCust)
+        AND   (ccard_active) ) LOOP
+
+      SELECT formatbytea(_cc.ccard_month_expired) INTO cc_month_t;
+      SELECT formatbytea(_cc.ccard_year_expired) INTO cc_year_t;
+      SELECT cast(cc_month_t AS INTEGER) INTO cc_month;
+      SELECT cast(cc_year_t AS INTEGER) INTO cc_year;
+
+      IF (cc_year < _dr.check_year) THEN
+--  We have an expired card
+        UPDATE ccard set ccard_active = FALSE where ccard_id = _cc.ccard_id;
+        num_updated := num_updated + 1;
+      ELSIF (cc_year = _dr.check_year AND cc_month < _dr.check_month) THEN
+--  We have an expired card
+        UPDATE ccard set ccard_active = FALSE where ccard_id = _cc.ccard_id;
+        num_updated := num_updated + 1;
+      END IF;
+
+  END LOOP;
+
+  RETURN num_updated;
+
+END;
+'
+  LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/explodebom.sql b/foundation-database/public/functions/explodebom.sql
new file mode 100644 (file)
index 0000000..7447656
--- /dev/null
@@ -0,0 +1,87 @@
+CREATE OR REPLACE FUNCTION explodeBOM(INTEGER, INTEGER, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemid ALIAS FOR $1;
+  pParentid ALIAS FOR $2;
+  pLevel ALIAS FOR $3;
+  _revid INTEGER;
+
+BEGIN
+
+  SELECT getActiveRevId('BOM',pItemid) INTO _revid;
+
+  RETURN explodeBOM(pItemid, _revid, pParentid, pLevel);
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION explodeBOM(INTEGER, INTEGER, INTEGER, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemid ALIAS FOR $1;
+  pRevisionid ALIAS FOR $2;
+  pParentid ALIAS FOR $3;
+  pLevel ALIAS FOR $4;
+  _bomworkid INTEGER;
+  _level INTEGER;
+  _p RECORD;
+  _r RECORD;
+  _temp TEXT;
+
+BEGIN
+
+  _level := (pLevel + 1);
+
+--  Cache some parameters about the parent
+  SELECT bomwork_item_id, bomwork_set_id, bomwork_qtyreq,
+         bomwork_seqnumber, bomwork_effective, bomwork_expires INTO _p
+  FROM bomwork
+  WHERE (bomwork_id=pParentid);
+
+--  Step through all of the components of the parent component
+  FOR _r IN SELECT bomitem.*,
+                   item_id,
+                   (itemuomtouomratio(bomitem_item_id, bomitem_uom_id, NULL) * bomitem_qtyfxd) AS qtyfxd,
+                   (itemuomtouomratio(bomitem_item_id, bomitem_uom_id, NULL) * bomitem_qtyper) AS qtyper,
+                   CASE WHEN (_p.bomwork_effective > bomitem_effective) THEN _p.bomwork_effective
+                        ELSE bomitem_effective
+                   END AS effective,
+                   CASE WHEN (_p.bomwork_expires < bomitem_expires) THEN _p.bomwork_expires
+                        ELSE bomitem_expires
+                   END AS expires,
+                   stdcost(item_id, bomitem_id) AS standardcost, actcost(item_id, bomitem_id) AS actualcost
+  FROM bomitem(pItemid, pRevisionid), item
+  WHERE ( (bomitem_item_id=item_id)
+  AND (bomitem_expires > _p.bomwork_effective) ) LOOP
+
+--  Insert the current component and some bomitem parameters into the bomwork set
+    SELECT NEXTVAL('bomwork_bomwork_id_seq') INTO _bomworkid;
+    INSERT INTO bomwork
+    ( bomwork_id, bomwork_set_id, bomwork_parent_id, bomwork_level,
+      bomwork_parent_seqnumber, bomwork_seqnumber,
+      bomwork_item_id, bomwork_createwo, bomwork_qtyreq,
+      bomwork_qtyfxd, bomwork_qtyper, bomwork_scrap, bomwork_issuemethod,
+      bomwork_effective, bomwork_expires,
+      bomwork_stdunitcost, bomwork_actunitcost, 
+      bomwork_notes, bomwork_ref,
+      bomwork_bomitem_id, bomwork_ecn )
+    VALUES
+    ( _bomworkid, _p.bomwork_set_id, pParentid, _level,
+      _p.bomwork_seqnumber, _r.bomitem_seqnumber,
+      _r.item_id, _r.bomitem_createwo, (_p.bomwork_qtyreq * _r.qtyper + _r.qtyfxd),
+      _r.qtyfxd, _r.qtyper, _r.bomitem_scrap, _r.bomitem_issuemethod,
+      _r.effective, _r.expires,
+      _r.standardcost, _r.actualcost,
+      _r.bomitem_notes, _r.bomitem_ref,
+      _r.bomitem_id, _r.bomitem_ecn );
+
+--  Recursively repeat for this component's components
+    PERFORM explodeBOM(_r.item_id, _bomworkid, _level);
+  END LOOP;
+
+  RETURN 1;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/explodekit.sql b/foundation-database/public/functions/explodekit.sql
new file mode 100644 (file)
index 0000000..fbeb3fc
--- /dev/null
@@ -0,0 +1,159 @@
+
+CREATE OR REPLACE FUNCTION explodeKit(INTEGER, INTEGER, INTEGER, INTEGER, NUMERIC) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSoheadid ALIAS FOR $1;
+  pLinenumber ALIAS FOR $2;
+  pSubnumber ALIAS FOR $3;
+  pItemsiteid ALIAS FOR $4;
+  pQty ALIAS FOR $5;
+BEGIN
+  RETURN explodeKit(pSoheadid, pLinenumber, pSubnumber, pItemsiteid, pQty, CURRENT_DATE, NULL);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION explodeKit(INTEGER, INTEGER, INTEGER, INTEGER, NUMERIC, DATE, DATE) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSoheadid ALIAS FOR $1;
+  pLinenumber ALIAS FOR $2;
+  pSubnumber ALIAS FOR $3;
+  pItemsiteid ALIAS FOR $4;
+  pQty ALIAS FOR $5;
+  pScheddate ALIAS FOR $6;
+  pPromdate ALIAS FOR $7;
+BEGIN
+  RETURN explodeKit(pSoheadid, pLinenumber, pSubnumber, pItemsiteid, pQty, CURRENT_DATE, NULL, '');
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION explodeKit(INTEGER, INTEGER, INTEGER, INTEGER, NUMERIC, DATE, DATE, TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSoheadid ALIAS FOR $1;
+  pLinenumber ALIAS FOR $2;
+  pSubnumber ALIAS FOR $3;
+  pItemsiteid ALIAS FOR $4;
+  pQty ALIAS FOR $5;
+  pScheddate ALIAS FOR $6;
+  pPromdate ALIAS FOR $7;
+  pMemo ALIAS FOR $8;
+  _subnumber INTEGER := COALESCE(pSubnumber,0);
+  _revid INTEGER;
+  _itemid INTEGER;
+  _warehousid INTEGER;
+  _item RECORD;
+  _type TEXT;
+  _coitemid INTEGER;
+  _count INTEGER;
+  _orderid INTEGER := 0;
+  _itemsrcid INTEGER;
+BEGIN
+
+  SELECT getActiveRevId('BOM',itemsite_item_id), itemsite_warehous_id, itemsite_item_id
+    INTO _revid, _warehousid, _itemid
+    FROM itemsite
+   WHERE(itemsite_id=pItemsiteid);
+  IF(NOT FOUND) THEN
+    RAISE EXCEPTION 'No Item Site for the specified line was found.';
+  END IF;
+
+  FOR _item IN
+  SELECT bomitem_id, 
+         itemsite_id,
+         itemsite_warehous_id,
+         COALESCE((itemsite_active AND item_active), false) AS active,
+         COALESCE((itemsite_sold AND item_sold), false) AS sold,
+         item_id,
+         item_type,
+         item_price_uom_id,
+         itemsite_createsopr,itemsite_createwo,itemsite_createsopo, itemsite_dropship,
+         bomitem_uom_id,
+         itemuomtouomratio(item_id, bomitem_uom_id, item_inv_uom_id) AS invuomratio,
+         roundQty(itemuomfractionalbyuom(bomitem_item_id, bomitem_uom_id),(bomitem_qtyfxd + bomitem_qtyper * pQty) * (1 + bomitem_scrap)) AS qty
+    FROM bomitem JOIN item ON (item_id=bomitem_item_id)
+                  LEFT OUTER JOIN itemsite ON ((itemsite_item_id=item_id) AND (itemsite_warehous_id=_warehousid))
+   WHERE((bomitem_parent_item_id=_itemid)
+     AND (bomitem_rev_id=_revid)
+     AND (CURRENT_DATE BETWEEN bomitem_effective AND (bomitem_expires - 1)))
+   ORDER BY bomitem_seqnumber LOOP
+    IF (NOT _item.active) THEN
+      RAISE EXCEPTION 'One or more of the components for the kit is inactive for the selected item site.';
+    ELSIF (NOT _item.sold) THEN
+      RAISE EXCEPTION 'One or more of the components for the kit is not sold for the selected item site.';
+    ELSIF (_item.item_type='F') THEN
+      SELECT explodeKit(pSoheadid, pLinenumber, _subnumber, _item.itemsite_id, _item.qty)
+        INTO _subnumber;
+    ELSE
+      IF (_item.itemsite_createsopr) THEN
+        _type := 'R';
+      ELSIF (_item.itemsite_createsopo) THEN
+        _type := 'P';
+      ELSIF (_item.itemsite_createwo) THEN
+        _type := 'W';
+      ELSE
+        _type := NULL;
+      END IF;
+      _subnumber := _subnumber + 1;
+      _coitemid = nextval('coitem_coitem_id_seq');
+      raise notice 'coitem id: %',_coitemid;
+      INSERT INTO coitem
+            (coitem_id, coitem_cohead_id,
+             coitem_linenumber, coitem_subnumber,
+             coitem_itemsite_id, coitem_status,
+             coitem_scheddate, coitem_promdate,
+             coitem_qtyord, coitem_qty_uom_id, coitem_qty_invuomratio,
+             coitem_qtyshipped, coitem_qtyreturned,
+             coitem_unitcost, coitem_custprice,
+             coitem_price, coitem_price_uom_id, coitem_price_invuomratio,
+             coitem_order_type, coitem_order_id,
+             coitem_custpn, coitem_memo,
+             coitem_prcost)
+      VALUES (_coitemid, pSoheadid,
+             pLinenumber, _subnumber,
+             _item.itemsite_id, 'O',
+             pScheddate, pPromdate,
+             _item.qty, _item.bomitem_uom_id, _item.invuomratio,
+             0, 0,
+             stdCost(_item.item_id), 0,
+             0, _item.item_price_uom_id, 1,
+             _type, -1,
+             '', pMemo,
+             0);
+
+      IF (_item.itemsite_createsopr) THEN
+        SELECT createPR(cohead_number::INTEGER, 'S', _coitemid) INTO _orderid
+        FROM cohead
+        WHERE (cohead_id=pSoheadid);
+        IF (_orderid > 0) THEN
+          UPDATE coitem SET coitem_order_id=_orderid
+          WHERE (coitem_id=_coitemid);
+        ELSE
+          RAISE EXCEPTION 'Could not explode kit. CreatePR failed, result=%', _orderid; 
+        END IF;
+      END IF;
+
+      IF (_item.itemsite_createsopo) THEN
+        SELECT itemsrc_id INTO _itemsrcid
+        FROM itemsrc
+        WHERE ((itemsrc_item_id=_item.item_id)
+        AND (itemsrc_default));
+
+        GET DIAGNOSTICS _count = ROW_COUNT;
+        IF (_count > 0) THEN
+          PERFORM createPurchaseToSale(_coitemid, _itemsrcid, _item.itemsite_dropship);
+        ELSE
+          RAISE WARNING 'One or more Kit items are flagged as purchase-to-order for this site, but no default item source is defined.';
+        END IF;
+      END IF;
+     
+    END IF;
+  END LOOP;
+
+  RETURN _subnumber;
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/explodephantomorder.sql b/foundation-database/public/functions/explodephantomorder.sql
new file mode 100644 (file)
index 0000000..bc4ec52
--- /dev/null
@@ -0,0 +1,51 @@
+
+CREATE OR REPLACE FUNCTION explodePhantomOrder(INTEGER, INTEGER, NUMERIC) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pPlanordid ALIAS FOR $1;
+  pPhantomid ALIAS FOR $2;
+  pQty       ALIAS FOR $3;
+  _b RECORD;
+
+BEGIN
+  FOR _b IN SELECT planord_number, c.itemsite_id AS componentsiteid,
+                   calculatenextworkingdate(c.itemsite_warehous_id, planord_startdate, (c.itemsite_leadtime * -1)) AS startdate,
+                   planord_startdate AS duedate,
+                   bomitem_createwo, c.itemsite_planning_type AS planningtype,
+                   (itemuomtouom(bomitem_item_id, bomitem_uom_id, NULL, (bomitem_qtyfxd + pQty * bomitem_qtyper) * (1 + bomitem_scrap))) AS qtyreq,
+                   item_type
+              FROM bomitem, planord, itemsite AS p, itemsite AS c, item
+             WHERE ((bomitem_parent_item_id=p.itemsite_item_id)
+               AND (bomitem_rev_id=getActiveRevId('BOM',bomitem_parent_item_id))
+               AND (bomitem_item_id=c.itemsite_item_id)
+               AND (p.itemsite_warehous_id=c.itemsite_warehous_id)
+               AND (c.itemsite_item_id=item_id)
+               AND (woEffectiveDate(planord_startdate) BETWEEN bomitem_effective AND (bomitem_expires - 1))
+               AND (p.itemsite_id=pPhantomid)
+               AND (planord_id=pPlanordid)) LOOP
+
+    IF (_b.item_type = 'F') THEN
+      PERFORM explodePhantomOrder(pPlanordid, _b.componentsiteid, _b.qtyreq);
+    ELSE
+--  Create the Planned Requirement
+      INSERT INTO planreq
+      ( planreq_source, planreq_source_id,
+        planreq_itemsite_id, planreq_qty )
+      VALUES
+      ( 'P', pPlanordid,
+        _b.componentsiteid, _b.qtyreq );
+
+      IF (_b.bomitem_createwo AND _b.planningtype != 'N') THEN
+        PERFORM createPlannedOrder( pPlanordid, _b.planord_number, _b.componentsiteid,
+                                    _b.qtyreq, _b.startdate, _b.duedate,
+                                    FALSE, FALSE, NULL, NULL);
+      END IF;
+    END IF;
+
+  END LOOP;
+
+  RETURN pPlanordid;
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/explodewo.sql b/foundation-database/public/functions/explodewo.sql
new file mode 100644 (file)
index 0000000..b36c020
--- /dev/null
@@ -0,0 +1,309 @@
+
+CREATE OR REPLACE FUNCTION explodeWo(INTEGER, BOOLEAN) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWoid ALIAS FOR $1;
+  pExplodeChildren ALIAS FOR $2;
+  resultCode INTEGER;
+  newWo RECORD;
+  _newwoid INTEGER;
+  _p RECORD;
+  _r RECORD;
+  _bbom BOOLEAN;
+
+BEGIN
+-- Find out if Breeder BOMs are enabled
+  SELECT metric_value='t' INTO _bbom
+         FROM metric
+         WHERE (metric_name='BBOM');
+
+--  Make sure that this W/O is Open
+  SELECT wo_id INTO resultCode
+  FROM wo
+  WHERE ((wo_status='O')
+   AND (wo_id=pWoid));
+  IF (NOT FOUND) THEN
+    RETURN -4;
+  END IF;
+
+--  Make sure that all Component Item Sites exist and are valid
+--  Item Sites must be active and not Job Costed
+  SELECT bomitem_id INTO resultCode
+  FROM wo, bomitem, itemsite
+  WHERE ( (wo_itemsite_id=itemsite_id)
+   AND (itemsite_item_id=bomitem_parent_item_id)
+   AND (woEffectiveDate(wo_startdate) BETWEEN bomitem_effective AND (bomitem_expires - 1))
+   AND (wo_id=pWoid)
+   AND (bomitem_rev_id=wo_bom_rev_id)
+   AND (bomitem_item_id NOT IN
+        ( SELECT component.itemsite_item_id
+          FROM itemsite AS component, itemsite AS parent
+          WHERE ( (wo_itemsite_id=parent.itemsite_id)
+           AND (parent.itemsite_item_id=bomitem_parent_item_id)
+           AND (bomitem_item_id=component.itemsite_item_id)
+           AND (woEffectiveDate(wo_startdate) BETWEEN bomitem_effective AND (bomitem_expires - 1))
+           AND (component.itemsite_active)
+           AND (component.itemsite_warehous_id=parent.itemsite_warehous_id) ) ) ) )
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -2;
+  END IF;
+
+--  If the Parent Item is a Breeder, make sure that all the
+--  Co-Product/By-Product Item Sites exist
+  IF (_bbom) THEN
+
+    IF ( ( SELECT (item_type='B')
+           FROM wo, itemsite, item
+           WHERE ( (wo_itemsite_id=itemsite_id)
+            AND (itemsite_item_id=item_id)
+            AND (wo_id=pWoid) ) ) ) THEN
+      SELECT bbomitem_id INTO resultCode
+      FROM wo, xtmfg.bbomitem, itemsite
+      WHERE ( (wo_itemsite_id=itemsite_id)
+       AND (itemsite_item_id=bbomitem_parent_item_id)
+       AND (woEffectiveDate(wo_startdate) BETWEEN bbomitem_effective AND (bbomitem_expires - 1))
+       AND (wo_id=pWoid)
+       AND (bbomitem_item_id NOT IN
+            ( SELECT component.itemsite_item_id
+              FROM itemsite AS component, itemsite AS parent
+              WHERE ( (wo_itemsite_id=parent.itemsite_id)
+               AND (parent.itemsite_item_id=bbomitem_parent_item_id)
+               AND (bbomitem_item_id=component.itemsite_item_id)
+               AND (woEffectiveDate(wo_startdate) BETWEEN bbomitem_effective AND (bbomitem_expires - 1))
+               AND (component.itemsite_active)
+               AND (component.itemsite_warehous_id=parent.itemsite_warehous_id) ) ) ) )
+      LIMIT 1;
+      IF (FOUND) THEN
+        RETURN -3;
+      END IF;
+    END IF;
+  END IF;
+
+--  Create the W/O Material Requirements
+  INSERT INTO womatl
+  ( womatl_wo_id, womatl_bomitem_id, womatl_wooper_id, womatl_schedatwooper,
+    womatl_itemsite_id,
+    womatl_duedate,
+    womatl_uom_id, womatl_qtyfxd, womatl_qtyper, womatl_scrap,
+    womatl_qtyreq,
+    womatl_qtyiss, womatl_qtywipscrap,
+    womatl_lastissue, womatl_lastreturn, womatl_cost,
+    womatl_picklist, womatl_createwo, womatl_issuewo,
+    womatl_issuemethod, womatl_notes, womatl_ref )
+  SELECT wo_id, bomitem_id, bomitem_booitem_seq_id, bomitem_schedatwooper,
+         cs.itemsite_id,
+         CASE WHEN bomitem_schedatwooper THEN COALESCE(calcWooperStartStub(wo_id,bomitem_booitem_seq_id), wo_startdate)
+              ELSE wo_startdate
+         END,
+         bomitem_uom_id, bomitem_qtyfxd, bomitem_qtyper, bomitem_scrap,
+         roundQty(itemuomfractionalbyuom(bomitem_item_id, bomitem_uom_id), (bomitem_qtyfxd + bomitem_qtyper * wo_qtyord) * (1 + bomitem_scrap)),
+         0, 0,
+         startOfTime(), startOfTime(), 0,
+         item_picklist, ( (item_type='M') AND (bomitem_createwo) ),
+         CASE WHEN ( (item_type='M') AND (bomitem_issuewo) ) THEN TRUE
+              WHEN (cs.itemsite_costmethod='J') THEN TRUE
+              ELSE FALSE
+         END,
+         bomitem_issuemethod, bomitem_notes, bomitem_ref 
+  FROM bomitem, wo, itemsite AS ps, itemsite AS cs, item
+  WHERE ( (wo_itemsite_id=ps.itemsite_id)
+   AND (bomitem_parent_item_id=ps.itemsite_item_id)
+   AND (bomitem_item_id=cs.itemsite_item_id)
+   AND (bomitem_rev_id=wo_bom_rev_id)
+   AND (ps.itemsite_warehous_id=cs.itemsite_warehous_id)
+   AND (cs.itemsite_item_id=item_id)
+   AND (woEffectiveDate(wo_startdate) BETWEEN bomitem_effective AND (bomitem_expires - 1))
+   AND (wo_id=pWoid)
+       AND ((bomitem_char_id IS NULL)
+       OR  EXISTS (
+         SELECT charass_id
+         FROM coitem,charass
+         WHERE ((charass_target_type='SI')
+          AND  (charass_target_id=coitem_id)
+          AND  (charass_char_id=bomitem_char_id)
+          AND  (charass_value=bomitem_value)
+          AND  (wo_ordtype='S')
+          AND  (coitem_id=wo_ordid)))) );
+
+--  Update any created P/R's the have the project id as the parent WO.
+  UPDATE pr SET pr_prj_id=wo_prj_id
+    FROM womatl, wo
+   WHERE ((wo_id=pWoid)
+     AND  (womatl_wo_id=wo_id)
+     AND  (pr_order_type='W')
+     AND  (pr_order_id=womatl_id));
+
+--  If the parent Item is a Breeder, create the brddist
+--  records for the Co-Products and By-Products
+  IF (_bbom) THEN
+
+    INSERT INTO xtmfg.brddist
+    ( brddist_wo_id, brddist_wo_qty, brddist_itemsite_id,
+      brddist_stdqtyper, brddist_qty, brddist_posted )
+    SELECT wo_id, 0, cs.itemsite_id,
+           bbomitem_qtyper, 0, FALSE
+    FROM wo, xtmfg.bbomitem,
+         itemsite AS ps, itemsite AS cs, item
+    WHERE ( (bbomitem_parent_item_id=ps.itemsite_item_id)
+     AND (wo_itemsite_id=ps.itemsite_id)
+     AND (ps.itemsite_item_id=item_id)
+     AND (item_type='B')
+     AND (bbomitem_item_id=cs.itemsite_item_id)
+     AND (cs.itemsite_warehous_id=ps.itemsite_warehous_id)
+     AND (wo_id=pWoid) );
+
+  END IF;
+
+--  Insert the W/O Operations if routings enabled
+  IF ( ( SELECT (metric_value='t')
+         FROM metric
+         WHERE (metric_name='Routings') ) ) THEN
+
+    INSERT INTO xtmfg.wooper
+    ( wooper_wo_id, wooper_booitem_id, wooper_seqnumber,
+      wooper_wrkcnt_id, wooper_stdopn_id,
+      wooper_descrip1, wooper_descrip2, wooper_toolref,
+      wooper_sutime, wooper_sucosttype, wooper_surpt,
+      wooper_rntime, wooper_rncosttype, wooper_rnrpt,
+      wooper_rnqtyper,
+      wooper_produom, wooper_invproduomratio,
+      wooper_issuecomp, wooper_rcvinv,
+      wooper_suconsumed, wooper_sucomplete,
+      wooper_rnconsumed, wooper_rncomplete,
+      wooper_qtyrcv, wooper_instruc, wooper_scheduled,
+      wooper_wip_location_id )
+    SELECT wo_id, booitem_id, booitem_seqnumber,
+           booitem_wrkcnt_id, booitem_stdopn_id,
+           booitem_descrip1, booitem_descrip2, booitem_toolref,
+           CASE WHEN (booitem_surpt) THEN booitem_sutime
+                ELSE 0
+           END, booitem_sucosttype, booitem_surpt,
+           CASE WHEN ((booitem_rnqtyper = 0) OR (booitem_invproduomratio = 0)) THEN 0
+                WHEN (NOT booitem_rnrpt) THEN 0
+                ELSE ( ( booitem_rntime /
+                         booitem_rnqtyper /
+                         booitem_invproduomratio ) * wo_qtyord )
+           END, booitem_rncosttype, booitem_rnrpt,
+           CASE WHEN (booitem_rnqtyper = 0) THEN 0
+                WHEN (NOT booitem_rnrpt) THEN 0
+                ELSE (booitem_rntime / booitem_rnqtyper)
+           END,
+           booitem_produom, booitem_invproduomratio,
+           booitem_issuecomp, booitem_rcvinv,
+           0::NUMERIC, FALSE,
+           0::NUMERIC, FALSE,
+           0::NUMERIC, booitem_instruc,
+           calculatenextworkingdate(itemsite_warehous_id,wo_startdate,booitem_execday-1),
+           booitem_wip_location_id
+    FROM xtmfg.booitem, wo, itemsite
+    WHERE ((wo_itemsite_id=itemsite_id)
+     AND (itemsite_item_id=booitem_item_id)
+     AND (booitem_rev_id=wo_boo_rev_id)
+     AND (woEffectiveDate(wo_startdate) BETWEEN booitem_effective AND (booitem_expires - 1))
+     AND (wo_id=pWoid));
+
+--  Update womatls item to link to wooper items when the respective
+--  bomitem record indicates a booitem issue link.
+    UPDATE womatl
+    SET womatl_wooper_id=wooper_id
+    FROM wo,xtmfg.wooper,xtmfg.booitem
+    WHERE ((womatl_wooper_id=booitem_seq_id)
+     AND (wooper_booitem_id=booitem_id)
+     AND (womatl_wo_id=wo_id)
+     AND (wooper_wo_id=wo_id)
+     AND (wo_boo_rev_id=booitem_rev_id)
+     AND (wo_id=pWoid));
+    END IF;
+
+-- Handle all of the Phantom material requirements
+  WHILE ( ( SELECT COUNT(*)
+            FROM womatl, itemsite, item
+            WHERE ( (womatl_itemsite_id=itemsite_id)
+             AND (itemsite_item_id=item_id)
+             AND (womatl_wo_id=pWoid)
+             AND (item_type='F') ) ) > 0 ) LOOP
+
+    FOR _p IN SELECT wo_qtyord, wo_startdate, womatl_id, womatl_wooper_id
+              FROM wo, womatl, itemsite, item
+              WHERE ( (womatl_itemsite_id=itemsite_id)
+               AND (itemsite_item_id=item_id)
+               AND (item_type='F')
+               AND (womatl_wo_id=wo_id)
+               AND (wo_id=pWoid) ) LOOP
+
+      INSERT INTO womatl
+      ( womatl_wo_id, womatl_itemsite_id, womatl_wooper_id,
+        womatl_schedatwooper, womatl_duedate,
+        womatl_uom_id, womatl_qtyfxd, womatl_qtyper, womatl_scrap,
+        womatl_qtyreq,
+        womatl_qtyiss, womatl_qtywipscrap,
+        womatl_lastissue, womatl_lastreturn,
+        womatl_cost, womatl_picklist,
+        womatl_createwo, womatl_issuewo,
+        womatl_issuemethod, womatl_notes, womatl_ref )
+      SELECT pWoid, cs.itemsite_id, _p.womatl_wooper_id,
+             womatl_schedatwooper, womatl_duedate,
+             bomitem_uom_id, bomitem_qtyfxd, (bomitem_qtyper * womatl_qtyper), bomitem_scrap,
+             roundQty(itemuomfractionalbyuom(bomitem_item_id, bomitem_uom_id), 
+                     (bomitem_qtyfxd + _p.wo_qtyord * bomitem_qtyper * womatl_qtyper) * (1 + bomitem_scrap)),
+             0, 0,
+             startOfTime(), startOfTime(),
+             0, ci.item_picklist,
+             ( (ci.item_type='M') AND (bomitem_createwo) ), ( (ci.item_type='M') AND (bomitem_issuewo) ),
+             bomitem_issuemethod, bomitem_notes, bomitem_ref
+      FROM womatl JOIN wo ON (wo_id=womatl_wo_id)
+                  JOIN itemsite ps ON (ps.itemsite_id=womatl_itemsite_id)
+                  JOIN item pi ON (pi.item_id=ps.itemsite_item_id)
+                  JOIN bomitem ON ( (bomitem_parent_item_id=pi.item_id) AND
+                                    (woEffectiveDate(_p.wo_startdate) BETWEEN bomitem_effective AND (bomitem_expires - 1)) AND
+                                    (bomitem_rev_id=getActiveRevId('BOM', pi.item_id)) )
+                  JOIN item ci ON (ci.item_id=bomitem.bomitem_item_id)
+                  JOIN itemsite cs ON ( (cs.itemsite_item_id=ci.item_id) AND
+                                        (cs.itemsite_warehous_id=ps.itemsite_warehous_id) )
+      WHERE (womatl_id=_p.womatl_id);
+
+      DELETE FROM womatl
+      WHERE (womatl_id=_p.womatl_id);
+
+    END LOOP;
+  END LOOP;
+
+--  Create W/Os for manufactured component items
+  FOR newWo IN SELECT wo_number, nextWoSubnumber(wo_number) AS nextSubnumber,
+                      itemsite_id, itemsite_leadtime, womatl_duedate,
+                      womatl_wo_id, womatl_qtyreq, womatl_uom_id, wo_prj_id,
+                      item_id, item_inv_uom_id, womatl_id
+               FROM womatl, wo, itemsite, item
+               WHERE ( (womatl_wo_id=wo_id)
+                AND (womatl_itemsite_id=itemsite_id)
+                AND (womatl_createwo)
+                AND (itemsite_wosupply)
+                AND (itemsite_item_id=item_id)
+                AND (wo_id=pWoid) )
+               ORDER BY womatl_id LOOP
+
+    SELECT createWo( newWo.wo_number, newWo.itemsite_id, 1, 
+                     itemuomtouom(newWo.item_id,newWo.womatl_uom_id,newWo.item_inv_uom_id,newWo.womatl_qtyreq),
+                      newWo.itemsite_leadtime, newWo.womatl_duedate, '',
+                      'W', newWo.womatl_wo_id, newWo.wo_prj_id ) INTO _newwoid;
+
+    UPDATE wo SET wo_womatl_id = newWo.womatl_id WHERE wo_id=_newwoid;
+
+  END LOOP;
+
+  UPDATE wo
+  SET wo_status='E', wo_adhoc=FALSE
+  WHERE (wo_id=pWoid);
+
+  IF (pExplodeChildren) THEN
+    SELECT MAX(explodeWo(wo_id, TRUE)) INTO resultCode
+    FROM wo
+    WHERE ( (wo_ordtype='W')
+     AND (wo_ordid=pWoid) );
+  END IF;
+
+  RETURN pWoid;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/explodewoeffective.sql b/foundation-database/public/functions/explodewoeffective.sql
new file mode 100644 (file)
index 0000000..9690f42
--- /dev/null
@@ -0,0 +1,16 @@
+CREATE OR REPLACE FUNCTION explodeWoEffective() RETURNS TEXT AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _value TEXT;
+
+BEGIN
+
+  SELECT metric_value INTO _value
+  FROM metric
+  WHERE (metric_name=''ExplodeWOEffective'');
+
+  RETURN _value;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/fetchapmemonumber.sql b/foundation-database/public/functions/fetchapmemonumber.sql
new file mode 100644 (file)
index 0000000..bc536d4
--- /dev/null
@@ -0,0 +1,5 @@
+CREATE OR REPLACE FUNCTION fetchAPMemoNumber() RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+  SELECT fetchNextNumber('APMemoNumber')::INTEGER;
+$$ LANGUAGE 'sql';
diff --git a/foundation-database/public/functions/fetcharmemonumber.sql b/foundation-database/public/functions/fetcharmemonumber.sql
new file mode 100644 (file)
index 0000000..1842582
--- /dev/null
@@ -0,0 +1,7 @@
+
+CREATE OR REPLACE FUNCTION fetchARMemoNumber() RETURNS TEXT AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+  SELECT fetchNextNumber('ARMemoNumber');
+$$ LANGUAGE 'sql';
+
diff --git a/foundation-database/public/functions/fetchcashrcptnumber.sql b/foundation-database/public/functions/fetchcashrcptnumber.sql
new file mode 100644 (file)
index 0000000..d775702
--- /dev/null
@@ -0,0 +1,6 @@
+CREATE OR REPLACE FUNCTION fetchCashRcptNumber() RETURNS TEXT AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+  SELECT fetchNextNumber('CashRcptNumber');
+$$ LANGUAGE 'sql';
+
diff --git a/foundation-database/public/functions/fetchcmnumber.sql b/foundation-database/public/functions/fetchcmnumber.sql
new file mode 100644 (file)
index 0000000..1bd54f3
--- /dev/null
@@ -0,0 +1,5 @@
+CREATE OR REPLACE FUNCTION fetchCMNumber() RETURNS text AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+  SELECT fetchNextNumber('CmNumber');
+$$ LANGUAGE sql;
diff --git a/foundation-database/public/functions/fetchcrmaccountnumber.sql b/foundation-database/public/functions/fetchcrmaccountnumber.sql
new file mode 100644 (file)
index 0000000..ba8395f
--- /dev/null
@@ -0,0 +1,6 @@
+CREATE OR REPLACE FUNCTION fetchCRMAccountNumber() RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+  SELECT fetchNextNumber('CRMAccountNumber')::INTEGER;
+$$ LANGUAGE 'sql';
+
diff --git a/foundation-database/public/functions/fetchdefaultfob.sql b/foundation-database/public/functions/fetchdefaultfob.sql
new file mode 100644 (file)
index 0000000..f385d48
--- /dev/null
@@ -0,0 +1,7 @@
+CREATE OR REPLACE FUNCTION FetchDefaultFob(pWarehousId INTEGER) RETURNS TEXT AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+  SELECT warehous_fob
+    FROM whsinfo
+   WHERE (warehous_id=$1);
+$$ LANGUAGE SQL;
diff --git a/foundation-database/public/functions/fetchdefaultshipvia.sql b/foundation-database/public/functions/fetchdefaultshipvia.sql
new file mode 100644 (file)
index 0000000..4875fa8
--- /dev/null
@@ -0,0 +1,15 @@
+CREATE OR REPLACE FUNCTION FetchDefaultShipVia() RETURNS TEXT AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _returnVal TEXT;
+BEGIN
+  SELECT shipvia_code INTO _returnVal
+  FROM shipvia
+  WHERE shipvia_id=
+       (SELECT CAST(metric_value AS integer)
+       FROM metric
+       WHERE metric_name = ''DefaultShipViaId'');
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/fetchglsequence.sql b/foundation-database/public/functions/fetchglsequence.sql
new file mode 100644 (file)
index 0000000..c2d9c9b
--- /dev/null
@@ -0,0 +1,15 @@
+
+CREATE OR REPLACE FUNCTION fetchGLSequence() RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _sequence INTEGER;
+
+BEGIN
+
+    SELECT NEXTVAL(''gltrans_sequence_seq'') INTO _sequence;
+    RETURN _sequence;
+
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/fetchincidentnumber.sql b/foundation-database/public/functions/fetchincidentnumber.sql
new file mode 100644 (file)
index 0000000..c7e7a23
--- /dev/null
@@ -0,0 +1,7 @@
+
+CREATE OR REPLACE FUNCTION fetchIncidentNumber() RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+  SELECT fetchNextNumber('IncidentNumber')::integer;
+$$ LANGUAGE 'sql';
+
diff --git a/foundation-database/public/functions/fetchinvcnumber.sql b/foundation-database/public/functions/fetchinvcnumber.sql
new file mode 100644 (file)
index 0000000..233d6bd
--- /dev/null
@@ -0,0 +1,5 @@
+CREATE OR REPLACE FUNCTION fetchInvcNumber() RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+  SELECT fetchNextNumber('InvcNumber')::integer;
+$$ LANGUAGE 'sql';
diff --git a/foundation-database/public/functions/fetchitemuomconvtypes.sql b/foundation-database/public/functions/fetchitemuomconvtypes.sql
new file mode 100644 (file)
index 0000000..473577b
--- /dev/null
@@ -0,0 +1,28 @@
+CREATE OR REPLACE FUNCTION fetchItemUomConvTypes(integer)
+  RETURNS text[] AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemUomConvId ALIAS FOR $1;
+  _p RECORD;
+  _result text[];
+  _cnt INTEGER;
+
+BEGIN
+
+  _cnt := 0;
+
+  FOR _p IN SELECT
+    uomtype_name
+  FROM itemuomconv, itemuom, uomtype
+  WHERE ((itemuomconv_id=pItemUomConvId)
+  AND (itemuomconv_id=itemuom_itemuomconv_id)
+  AND (itemuom_uomtype_id=uomtype_id))
+  LOOP
+    _result[_cnt] := _p.uomtype_name; 
+    _cnt := _cnt + 1;
+  END LOOP;
+
+  RETURN _result;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/fetchjournalnumber.sql b/foundation-database/public/functions/fetchjournalnumber.sql
new file mode 100644 (file)
index 0000000..1474419
--- /dev/null
@@ -0,0 +1,21 @@
+CREATE OR REPLACE FUNCTION fetchjournalnumber(TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pUse ALIAS FOR $1;
+  _number INTEGER;
+
+BEGIN
+
+  SELECT nextval('journal_number_seq') INTO _number;
+
+  INSERT INTO jrnluse
+  (jrnluse_date, jrnluse_number, jrnluse_use)
+  VALUES
+  (CURRENT_TIMESTAMP, _number, pUse);
+
+  RETURN _number;
+  
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/fetchmetricbool.sql b/foundation-database/public/functions/fetchmetricbool.sql
new file mode 100644 (file)
index 0000000..8483b97
--- /dev/null
@@ -0,0 +1,18 @@
+CREATE OR REPLACE FUNCTION FetchMetricBool(text) RETURNS BOOLEAN STABLE AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _pMetricName ALIAS FOR $1;
+  _returnVal BOOLEAN;
+BEGIN
+  SELECT CASE 
+    WHEN MIN(metric_value) = ''t'' THEN
+     true
+    ELSE
+     false
+    END INTO _returnVal
+    FROM metric
+   WHERE metric_name = _pMetricName;
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/fetchmetrictext.sql b/foundation-database/public/functions/fetchmetrictext.sql
new file mode 100644 (file)
index 0000000..eb21619
--- /dev/null
@@ -0,0 +1,13 @@
+CREATE OR REPLACE FUNCTION FetchMetricText(text) RETURNS TEXT STABLE AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _pMetricName ALIAS FOR $1;
+  _returnVal TEXT;
+BEGIN
+  SELECT metric_value::TEXT INTO _returnVal
+    FROM metric
+   WHERE metric_name = _pMetricName;
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/fetchmetricvalue.sql b/foundation-database/public/functions/fetchmetricvalue.sql
new file mode 100644 (file)
index 0000000..f6ad67f
--- /dev/null
@@ -0,0 +1,15 @@
+CREATE OR REPLACE FUNCTION FetchMetricValue(text) RETURNS NUMERIC STABLE AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _pMetricName ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  SELECT CASE WHEN (isNumeric(metric_value)) THEN metric_value::INTEGER
+              ELSE NULL
+         END INTO _returnVal
+    FROM metric
+   WHERE metric_name = _pMetricName;
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/fetchnextchecknumber.sql b/foundation-database/public/functions/fetchnextchecknumber.sql
new file mode 100644 (file)
index 0000000..5ce14b9
--- /dev/null
@@ -0,0 +1,23 @@
+
+CREATE OR REPLACE FUNCTION fetchNextCheckNumber(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pBankaccntid ALIAS FOR $1;
+  _nextChkNumber INTEGER;
+
+BEGIN
+
+  SELECT bankaccnt_nextchknum INTO _nextChkNumber
+  FROM bankaccnt
+  WHERE (bankaccnt_id=pBankaccntid);
+
+  UPDATE bankaccnt
+  SET bankaccnt_nextchknum = (bankaccnt_nextchknum + 1)
+  WHERE (bankaccnt_id=pBankaccntid);
+
+  RETURN _nextChkNumber;
+
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/fetchnextnumber.sql b/foundation-database/public/functions/fetchnextnumber.sql
new file mode 100644 (file)
index 0000000..c20b5b9
--- /dev/null
@@ -0,0 +1,66 @@
+CREATE OR REPLACE FUNCTION fetchNextNumber(TEXT) RETURNS TEXT AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  psequence    ALIAS FOR $1;
+  _number      TEXT;
+  _numcol      TEXT;
+  _select      TEXT;
+  _table       TEXT;
+  _test                TEXT;
+  _nextnum     INTEGER;
+  _seqiss       seqiss;
+  __seqiss      seqiss[];
+  _not_issued       BOOLEAN;
+
+BEGIN
+  SELECT CAST(orderseq_number AS text), orderseq_number, orderseq_table, orderseq_numcol, COALESCE(orderseq_seqiss, ARRAY[]::seqiss[])
+    INTO _number, _nextnum, _table, _numcol, __seqiss
+  FROM orderseq
+  WHERE (orderseq_name=psequence) FOR UPDATE;
+
+  IF (NOT FOUND) THEN
+    RAISE EXCEPTION 'Invalid orderseq_name %', psequence;
+  END IF;
+  
+  LOOP
+
+    _seqiss := (_nextnum, now());
+
+    SELECT count(*) = 0 INTO _not_issued
+    FROM (SELECT UNNEST(__seqiss) AS issued) data
+    WHERE (issued).seqiss_number = _nextnum;
+
+    _nextnum := _nextnum + 1;
+
+    -- Test if the number has been issued, but not committed
+    IF (_not_issued) THEN
+
+      -- Test if the number has been committed
+      _select := 'SELECT ' || quote_ident(_numcol) ||
+                ' FROM '  || quote_ident(_table) ||
+                ' WHERE (' || quote_ident(_numcol) || '=' ||
+                 quote_literal(_number) || ');';
+
+      EXECUTE _select INTO _test;
+
+      IF (_test IS NULL OR NOT FOUND) THEN
+        EXIT;
+      END IF;
+
+    END IF;
+
+    -- Number in use, try again
+    _number = _nextnum::text;
+
+  END LOOP;
+
+  UPDATE orderseq SET 
+    orderseq_number = _nextnum,
+    orderseq_seqiss = orderseq_seqiss || _seqiss
+  WHERE (orderseq_name=psequence);
+
+  RETURN _number;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/fetchponumber.sql b/foundation-database/public/functions/fetchponumber.sql
new file mode 100644 (file)
index 0000000..764a43b
--- /dev/null
@@ -0,0 +1,5 @@
+CREATE OR REPLACE FUNCTION fetchPoNumber() RETURNS TEXT AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+  SELECT fetchNextNumber('PoNumber');
+$$ LANGUAGE 'sql';
diff --git a/foundation-database/public/functions/fetchprefwarehousid.sql b/foundation-database/public/functions/fetchprefwarehousid.sql
new file mode 100644 (file)
index 0000000..ac86024
--- /dev/null
@@ -0,0 +1,14 @@
+CREATE OR REPLACE FUNCTION FetchPrefWarehousId() RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _result INTEGER;
+BEGIN
+    SELECT CAST(usrpref_value AS INTEGER) INTO _result
+    FROM usrpref
+    WHERE ((usrpref_username=getEffectiveXtUser())
+    AND (usrpref_name=''PreferredWarehouse''));
+
+    RETURN _result;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/fetchprnumber.sql b/foundation-database/public/functions/fetchprnumber.sql
new file mode 100644 (file)
index 0000000..87dcd26
--- /dev/null
@@ -0,0 +1,5 @@
+CREATE OR REPLACE FUNCTION fetchPrNumber() RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+  SELECT fetchNextNumber('PrNumber')::INTEGER;
+$$ LANGUAGE 'sql';
diff --git a/foundation-database/public/functions/fetchqunumber.sql b/foundation-database/public/functions/fetchqunumber.sql
new file mode 100644 (file)
index 0000000..e9442be
--- /dev/null
@@ -0,0 +1,5 @@
+CREATE OR REPLACE FUNCTION fetchQuNumber() RETURNS TEXT AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+  SELECT fetchNextNumber('QuNumber');
+$$ LANGUAGE 'sql';
diff --git a/foundation-database/public/functions/fetchshipmentnumber.sql b/foundation-database/public/functions/fetchshipmentnumber.sql
new file mode 100644 (file)
index 0000000..f430d11
--- /dev/null
@@ -0,0 +1,26 @@
+CREATE OR REPLACE FUNCTION fetchshipmentnumber() RETURNS TEXT AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _number              TEXT;
+  _test                        INTEGER;
+
+BEGIN
+  LOOP
+
+    SELECT CAST(nextval('shipment_number_seq') AS TEXT) INTO _number;
+    
+    SELECT shiphead_id INTO _test
+      FROM shiphead
+     WHERE (shiphead_number=_number);
+    IF (NOT FOUND) THEN
+      EXIT;
+    END IF;
+
+  END LOOP;
+
+  RETURN _number;
+  
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/fetchsonumber.sql b/foundation-database/public/functions/fetchsonumber.sql
new file mode 100644 (file)
index 0000000..7ad66fa
--- /dev/null
@@ -0,0 +1,5 @@
+CREATE OR REPLACE FUNCTION fetchSoNumber() RETURNS TEXT AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+  SELECT fetchNextNumber('SoNumber');
+$$ LANGUAGE 'sql';
diff --git a/foundation-database/public/functions/fetchtonumber.sql b/foundation-database/public/functions/fetchtonumber.sql
new file mode 100644 (file)
index 0000000..f9b2712
--- /dev/null
@@ -0,0 +1,5 @@
+CREATE OR REPLACE FUNCTION fetchToNumber() RETURNS TEXT AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+  SELECT fetchNextNumber('ToNumber');
+$$ LANGUAGE 'sql';
diff --git a/foundation-database/public/functions/fetchusrprefbool.sql b/foundation-database/public/functions/fetchusrprefbool.sql
new file mode 100644 (file)
index 0000000..92810ac
--- /dev/null
@@ -0,0 +1,19 @@
+CREATE OR REPLACE FUNCTION FetchUsrPrefBool(text) RETURNS BOOLEAN AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _pPrefName ALIAS FOR $1;
+  _returnVal BOOLEAN;
+BEGIN
+  SELECT CASE 
+    WHEN MIN(usrpref_value) = ''t'' THEN
+     true
+    ELSE
+     false
+    END INTO _returnVal
+  FROM usrpref
+  WHERE ( (usrpref_username=getEffectiveXtUser())
+    AND   (usrpref_name=_pPrefName) );
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/fetchvonumber.sql b/foundation-database/public/functions/fetchvonumber.sql
new file mode 100644 (file)
index 0000000..a6914a2
--- /dev/null
@@ -0,0 +1,5 @@
+CREATE OR REPLACE FUNCTION fetchVoNumber() RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+  SELECT fetchNextNumber('VcNumber')::INTEGER;
+$$ LANGUAGE 'sql';
diff --git a/foundation-database/public/functions/fetchwonumber.sql b/foundation-database/public/functions/fetchwonumber.sql
new file mode 100644 (file)
index 0000000..4c485d4
--- /dev/null
@@ -0,0 +1,5 @@
+CREATE OR REPLACE FUNCTION fetchWoNumber() RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+  SELECT fetchNextNumber('WoNumber')::INTEGER;
+$$ LANGUAGE 'sql';
diff --git a/foundation-database/public/functions/financialreport.sql b/foundation-database/public/functions/financialreport.sql
new file mode 100644 (file)
index 0000000..98f2569
--- /dev/null
@@ -0,0 +1,1479 @@
+SELECT dropIfExists('FUNCTION','financialreport(INTEGER, INTEGER)');
+SELECT dropIfExists('FUNCTION','financialreport(INTEGER, INTEGER, bpchar)');
+SELECT dropIfExists('FUNCTION','financialreport(INTEGER, INTEGER, bool, bool)');
+SELECT dropIfExists('FUNCTION','financialreport(INTEGER,_int4,bpchar,bool)');
+
+CREATE OR REPLACE FUNCTION financialreport(INTEGER, INTEGER, INTEGER) RETURNS bool AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pFlheadid ALIAS FOR $1;
+  pPeriodid ALIAS FOR $2;
+  pPrjid    ALIAS FOR $3;
+  _result bool;
+
+BEGIN
+
+  SELECT financialreport(pFlheadid,pPeriodid,'M', pPrjid) INTO _result;
+
+  RETURN _result;
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION financialreport(INTEGER, INTEGER, bpchar, INTEGER)
+  RETURNS bool AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pFlheadid ALIAS FOR $1;
+  pPeriodid ALIAS FOR $2;
+  pInterval ALIAS FOR $3;
+  pPrjid    ALIAS FOR $4;
+
+  _r RECORD;
+  _t RECORD;
+  _s RECORD;
+
+BEGIN
+
+-- Validate Interval
+   IF pInterval <> 'M' AND pInterval <> 'Q' AND pInterval <> 'Y' THEN
+     RAISE EXCEPTION 'Invalid Interval --> %', pInterval;
+   END IF;
+
+-- Get rid of any old reporting done by this user for the specified criteria
+  DELETE FROM flrpt
+   WHERE ((flrpt_flhead_id=pFlheadid)
+     AND  (flrpt_period_id=pPeriodId)
+     AND  (flrpt_interval=pInterval)
+     AND  (flrpt_username=getEffectiveXtUser()));
+
+-- Find out if we need to show a Grand Total and which if any of the values
+-- we want to show in that grand total.
+  SELECT flhead_showtotal,
+         CASE WHEN(flhead_showstart) THEN 0.00
+              ELSE NULL
+         END AS beginning,
+         CASE WHEN(flhead_showend) THEN 0.00
+              ELSE NULL
+         END AS ending,
+         CASE WHEN(flhead_showdelta) THEN 0.00
+              ELSE NULL
+         END AS debits,
+         CASE WHEN(flhead_showdelta) THEN 0.00
+              ELSE NULL
+         END AS credits,
+         CASE WHEN(flhead_showbudget) THEN 0.00
+              ELSE NULL
+         END AS budget,
+         CASE WHEN(flhead_showdiff) THEN 0.00
+              ELSE NULL
+         END AS diff,
+         CASE WHEN(flhead_showcustom) THEN 0.00
+              ELSE NULL
+         END AS custom,
+         CASE WHEN(flhead_usealttotal) THEN flhead_alttotal
+              ELSE NULL
+         END AS altname INTO _r
+    FROM flhead
+   WHERE (flhead_id=pFlheadid);
+  IF (NOT FOUND) THEN
+    return FALSE;
+  END IF;
+
+-- If showing a Grand Total then create a record as a Group which acts
+-- as a parent to the whole report. This allows the code to update as
+-- it would for normal group total values.
+  IF (_r.flhead_showtotal) THEN
+    INSERT INTO flrpt
+           (flrpt_flhead_id, flrpt_period_id, flrpt_username,
+            flrpt_order, flrpt_level, flrpt_type, flrpt_type_id,
+            flrpt_beginning, flrpt_ending,
+            flrpt_debits, flrpt_credits, flrpt_budget, flrpt_diff,
+            flrpt_custom, flrpt_altname, flrpt_interval )
+    VALUES (pFlheadid, pPeriodid, getEffectiveXtUser(),
+            0, -1, 'G', -1,
+            _r.beginning, _r.ending,
+            _r.debits, _r.credits, _r.budget, _r.diff,
+            _r.custom, _r.altname, pInterval );
+  END IF;
+
+  PERFORM insertFlGroup(pFlheadid, pPeriodid, -1, 0, FALSE, pInterval, pPrjid);
+
+-- go through the list of records that need percentages calculated and perform
+-- those calculations.
+  FOR _t IN SELECT flrpt_order, CASE WHEN(flgrp_prcnt_flgrp_id = -1) THEN flgrp_flgrp_id ELSE flgrp_prcnt_flgrp_id END AS flgrp_id
+              FROM flrpt, flgrp
+             WHERE ((flrpt_flhead_id=pFlheadid)
+               AND  (flrpt_period_id=pPeriodid)
+               AND  (flrpt_interval=pInterval)
+               AND  (flrpt_username=getEffectiveXtUser())
+               AND  (flrpt_type='G')
+               AND  (flrpt_type_id=flgrp_id))
+             UNION
+            SELECT flrpt_order, CASE WHEN(flitem_prcnt_flgrp_id = -1) THEN flitem_flgrp_id ELSE flitem_prcnt_flgrp_id END AS flgrp_id
+              FROM flrpt, flitem
+             WHERE ((flrpt_flhead_id=pFlheadid)
+               AND  (flrpt_period_id=pPeriodid)
+               AND  (flrpt_interval=pInterval)
+               AND  (flrpt_username=getEffectiveXtUser())
+               AND  (flrpt_type='I')
+               AND  (flrpt_type_id=flitem_id))
+             UNION
+            SELECT flrpt_order, CASE WHEN(flspec_prcnt_flgrp_id = -1) THEN flspec_flgrp_id ELSE flspec_prcnt_flgrp_id END AS flgrp_id
+              FROM flrpt, flspec
+             WHERE ((flrpt_flhead_id=pFlheadid)
+               AND  (flrpt_period_id=pPeriodid)
+               AND  (flrpt_interval=pInterval)
+               AND  (flrpt_username=getEffectiveXtUser())
+               AND  (flrpt_type='S')
+               AND  (flrpt_type_id=flspec_id)) LOOP
+
+    IF( (_t.flgrp_id=-1) OR (NOT (SELECT flgrp_summarize FROM flgrp WHERE flgrp_id=_t.flgrp_id)) ) THEN
+      SELECT COALESCE(SUM(flrpt_beginning),0) AS beginningTotal,
+             COALESCE(SUM(flrpt_ending),0) AS endingTotal,
+             COALESCE(SUM(flrpt_debits),0) AS debitsTotal,
+             COALESCE(SUM(flrpt_credits),0) AS creditsTotal,
+             COALESCE(SUM(flrpt_budget),0) AS budgetTotal,
+             COALESCE(SUM(flrpt_diff), 0) AS diffTotal,
+             COALESCE(SUM(flrpt_custom), 0) AS customTotal INTO _s
+        FROM flrpt
+       WHERE ((flrpt_flhead_id=pFlheadid)
+         AND  (flrpt_period_id=pPeriodid)
+         AND  (flrpt_interval=pInterval)
+         AND  (flrpt_username=getEffectiveXtUser())
+         AND  (flrpt_type != 'T')
+         AND  (flrpt_parent_id=_t.flgrp_id));
+    ELSE
+      SELECT COALESCE(SUM(flrpt_beginning),0) AS beginningTotal,
+             COALESCE(SUM(flrpt_ending),0) AS endingTotal,
+             COALESCE(SUM(flrpt_debits),0) AS debitsTotal,
+             COALESCE(SUM(flrpt_credits),0) AS creditsTotal,
+             COALESCE(SUM(flrpt_budget),0) AS budgetTotal,
+             COALESCE(SUM(flrpt_diff), 0) AS diffTotal,
+             COALESCE(SUM(flrpt_custom), 0) AS customTotal INTO _s
+        FROM flrpt
+       WHERE ((flrpt_flhead_id=pFlheadid)
+         AND  (flrpt_period_id=pPeriodid)
+         AND  (flrpt_interval=pInterval)
+         AND  (flrpt_username=getEffectiveXtUser())
+         AND  (flrpt_type = 'G')
+         AND  (flrpt_type_id=_t.flgrp_id));
+    END IF;
+
+    UPDATE flrpt SET flrpt_beginningprcnt = flrpt_beginningprcnt + flrpt_beginning / CASE WHEN (_s.beginningTotal=0) THEN 1 ELSE _s.beginningTotal END,
+                     flrpt_endingprcnt = flrpt_endingprcnt + flrpt_ending / CASE WHEN (_s.endingTotal=0) THEN 1 ELSE _s.endingTotal END,
+                     flrpt_debitsprcnt = flrpt_debitsprcnt + flrpt_debits / CASE WHEN (_s.debitsTotal=0) THEN 1 ELSE _s.debitsTotal END,
+                     flrpt_creditsprcnt = flrpt_creditsprcnt + flrpt_credits / CASE WHEN (_s.creditsTotal=0) THEN 1 ELSE _s.creditsTotal END,
+                     flrpt_budgetprcnt = flrpt_budgetprcnt + flrpt_budget / CASE WHEN (_s.budgetTotal=0) THEN 1 ELSE _s.budgetTotal END,
+                     flrpt_diffprcnt = flrpt_diffprcnt + flrpt_diff / CASE WHEN (_s.diffTotal=0) THEN 1 ELSE _s.diffTotal END,
+                     flrpt_customprcnt = flrpt_customprcnt + flrpt_custom / CASE WHEN (_s.customTotal=0) THEN 1 ELSE _s.customTotal END
+     WHERE ((flrpt_flhead_id=pFlheadid)
+       AND  (flrpt_period_id=pPeriodid)
+       AND  (flrpt_interval=pInterval)
+       AND  (flrpt_username=getEffectiveXtUser())
+       AND  (flrpt_order=_t.flrpt_order));
+  END LOOP;
+
+
+-- Update any subtotal records to reflect the percentage values of the parents
+-- since those are calculated after the subtotal records were created.
+  FOR _t IN SELECT a.flrpt_order AS flrpt_order,
+                   b.flrpt_beginningprcnt AS flrpt_beginningprcnt,
+                   b.flrpt_endingprcnt AS flrpt_endingprcnt,
+                   b.flrpt_debitsprcnt AS flrpt_debitsprcnt,
+                   b.flrpt_creditsprcnt AS flrpt_creditsprcnt,
+                   b.flrpt_budgetprcnt AS flrpt_budgetprcnt,
+                   b.flrpt_diffprcnt AS flrpt_diffprcnt,
+                   b.flrpt_customprcnt AS flrpt_customprcnt
+              FROM flrpt AS a, flrpt AS b
+             WHERE ((a.flrpt_flhead_id=pFlheadid)
+               AND  (a.flrpt_period_id=pPeriodid)
+               AND  (a.flrpt_interval=pInterval)
+               AND  (a.flrpt_username=getEffectiveXtUser())
+               AND  (a.flrpt_type='T')
+               AND  (b.flrpt_flhead_id=a.flrpt_flhead_id)
+               AND  (b.flrpt_period_id=a.flrpt_period_id)
+               AND  (b.flrpt_interval=pInterval)
+               AND  (b.flrpt_username=a.flrpt_username)
+               AND  (b.flrpt_type='G')
+               AND  (b.flrpt_type_id=a.flrpt_parent_id)) LOOP
+    UPDATE flrpt SET flrpt_beginningprcnt=flrpt_beginningprcnt + _t.flrpt_beginningprcnt,
+                     flrpt_endingprcnt=flrpt_endingprcnt + _t.flrpt_endingprcnt,
+                     flrpt_debitsprcnt=flrpt_debitsprcnt + _t.flrpt_debitsprcnt,
+                     flrpt_creditsprcnt=flrpt_creditsprcnt + _t.flrpt_creditsprcnt,
+                     flrpt_budgetprcnt=flrpt_budgetprcnt + _t.flrpt_budgetprcnt,
+                     flrpt_diffprcnt=flrpt_diffprcnt + _t.flrpt_diffprcnt,
+                     flrpt_customprcnt=flrpt_customprcnt + _t.flrpt_customprcnt
+               WHERE ((flrpt_flhead_id=pFlheadid)
+                 AND  (flrpt_period_id=pPeriodid)
+                 AND  (flrpt_interval=pInterval)
+                 AND  (flrpt_username=getEffectiveXtUser())
+                 AND  (flrpt_order=_t.flrpt_order));
+  END LOOP;
+
+-- If showing a Grand total then move the record we created early to the
+-- end of the report and marked as a Total record.
+  IF (_r.flhead_showtotal) THEN
+    UPDATE flrpt
+       SET flrpt_order = COALESCE((SELECT MAX(flrpt_order)
+                                     FROM flrpt
+                                    WHERE ((flrpt_flhead_id=pFlheadid)
+                                      AND  (flrpt_period_id=pPeriodid)
+                                      AND  (flrpt_interval=pInterval)
+                                      AND  (flrpt_username=getEffectiveXtUser()))
+                                  ), 0) + 1,
+           flrpt_level = 0,
+           flrpt_type = 'T'
+     WHERE ((flrpt_flhead_id=pFlheadid)
+       AND  (flrpt_period_id=pPeriodid)
+       AND  (flrpt_interval=pInterval)
+       AND  (flrpt_username=getEffectiveXtUser())
+       AND  (flrpt_order=0)
+       AND  (flrpt_level = -1)
+       AND  (flrpt_type = 'G')
+       AND  (flrpt_type_id=-1));
+  END IF;
+
+  return TRUE;
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION financialreport(INTEGER, INTEGER, bool, bool, INTEGER)
+  RETURNS SETOF flstmtitem AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pFlcolid ALIAS FOR $1;
+  pPeriodid ALIAS FOR $2;
+  pShowNumbers ALIAS FOR $3;
+  pIndentName ALIAS FOR $4;
+  pPrjid ALIAS FOR $5;
+  _row flstmtitem%ROWTYPE;
+  _p RECORD;
+  _x RECORD;
+  _priorMoPeriodId INTEGER;
+  _priorQtPeriodId INTEGER;
+  _priorYrPeriodId INTEGER;
+  _first BOOLEAN;
+  _prevlevel INTEGER;
+  _subgrp INTEGER;
+  _qtrInterval TEXT;
+  _yrInterval TEXT;
+
+BEGIN
+
+  _priorMoPeriodId := -1;
+  _priorQtPeriodId := -1;
+  _priorYrPeriodId := -1;
+  _first := TRUE;
+  _prevlevel :=0;
+  _subgrp := 0;
+
+--Get Layout Data
+  SELECT flhead_id,flhead_type,
+        flcol_month,flcol_quarter,flcol_year,flcol_priortype,
+        flcol_priormonth,flcol_priorquarter,flcol_prioryear,
+        flcol_priordiff,flcol_priordiffprcnt,flcol_priorprcnt,
+        flcol_budget,flcol_budgetdiff,flcol_budgetdiffprcnt,
+        flcol_budgetprcnt INTO _p
+  FROM flhead,flcol
+  WHERE ((flcol_id=pFlcolid)
+  AND (flhead_id=flcol_flhead_id));
+
+  IF (_p.flhead_type='B') THEN
+    _qtrInterval := 'M';
+    _yrInterval := 'M';
+  ELSE
+    _qtrInterval := 'Q';
+    _yrInterval := 'Y';
+  END IF;
+
+--Delete old data from all periods
+  DELETE FROM flrpt
+  WHERE ((flrpt_username=getEffectiveXtUser())
+  AND (flrpt_flhead_id=_p.flhead_id));
+
+--Populate report data...
+--...for Month
+        IF (_p.flcol_month) THEN
+
+        PERFORM financialreport(_p.flhead_id,pPeriodid,'M',pPrjid);
+
+                IF ((_p.flcol_priortype = 'P') AND (_p.flcol_priormonth)) THEN
+
+                        SELECT COALESCE(pp.period_id,-1) INTO _priorMoPeriodId
+                        FROM period cp, period pp
+                        WHERE ((cp.period_id=pPeriodId)
+                        AND (cp.period_start > pp.period_start))
+                        ORDER BY pp.period_start DESC LIMIT 1;
+
+                        IF (_priorMoPeriodId IS NOT NULL) THEN
+                                PERFORM financialreport(_p.flhead_id,_priorMoPeriodId,'M',pPrjid);
+                        END IF;
+
+                        ELSE IF ((_p.flcol_priortype='Y')AND (_p.flcol_priormonth)) THEN
+
+                                SELECT COALESCE(pp.period_id,-1) INTO _priorMoPeriodId
+                                FROM period cp, period pp
+                                WHERE ((cp.period_id=pPeriodId)
+                                AND (cp.period_id != pp.period_id)
+                                AND (cp.period_start > pp.period_start)
+                                AND (cp.period_number = pp.period_number))
+                                ORDER BY pp.period_start DESC LIMIT 1;
+
+                                IF (_priorMoPeriodId IS NOT NULL) THEN
+                                        PERFORM financialreport(_p.flhead_id,_priorMoPeriodId,'M',pPrjid);
+                                END IF;
+
+                        END IF;
+
+                END IF;
+        END IF;
+
+--...for Quarter
+        IF (_p.flcol_quarter) THEN
+
+        PERFORM financialreport(_p.flhead_id,pPeriodid,'Q',pPrjid);
+
+        END IF;
+
+        IF ((_p.flcol_priortype='P') AND (_p.flcol_priorquarter)) THEN
+
+                SELECT COALESCE(pp.period_id,-1) INTO _priorQtPeriodId
+                FROM period cp, period pp
+                WHERE ((cp.period_id=pPeriodId)
+                AND (cp.period_start > pp.period_start)
+                AND (pp.period_quarter=
+                        CASE WHEN cp.period_quarter > 1 THEN
+                                cp.period_quarter - 1
+                        ELSE 4 END)
+                AND (pp.period_start >= cp.period_start - interval '1 year'))
+                ORDER BY pp.period_start DESC LIMIT 1;
+
+                IF (_priorQtPeriodId IS NOT NULL) THEN
+                        PERFORM financialreport(_p.flhead_id,_priorQtPeriodId,'Q',pPrjid);
+                END IF;
+
+                ELSE IF ((_p.flcol_priortype='Y')AND (_p.flcol_priorquarter)) THEN
+
+                        SELECT pp.period_id INTO _priorQtPeriodId
+                        FROM period cp, period pp, yearperiod cy, yearperiod py
+                        WHERE ((cp.period_id=pPeriodId)
+                        AND (cp.period_yearperiod_id=cy.yearperiod_id)
+                        AND (pp.period_yearperiod_id=py.yearperiod_id)
+                        AND (cp.period_quarter=pp.period_quarter)
+                        AND (cy.yearperiod_start > py.yearperiod_start))
+                        ORDER BY py.yearperiod_start DESC, pp.period_start DESC LIMIT 1;
+
+                        IF (_priorQtPeriodId IS NOT NULL) THEN
+                                PERFORM financialreport(_p.flhead_id,_priorQtPeriodId,'Q',pPrjid);
+                        END IF;
+
+                END IF;
+        END IF;
+
+--...for Year
+        IF (_p.flcol_year) THEN
+
+                PERFORM financialreport(_p.flhead_id,pPeriodid,'Y',pPrjid);
+
+        END IF;
+
+        IF (_p.flcol_prioryear='D') THEN
+
+                SELECT COALESCE(pp.period_id,-1) INTO _priorYrPeriodId
+                FROM period cp, period pp
+                WHERE ((cp.period_id = pPeriodId)
+                AND (cp.period_number = pp.period_number)
+                AND (cp.period_start > pp.period_start))
+                ORDER BY pp.period_start DESC LIMIT 1;
+
+                IF (_priorYrPeriodId IS NOT NULL) THEN
+                        PERFORM financialreport(_p.flhead_id,_priorYrPeriodId,'Y',pPrjid);
+                END IF;
+
+                ELSE IF (_p.flcol_prioryear='F') THEN
+
+                        SELECT pp.period_id INTO _priorYrPeriodId
+                        FROM period cp, period pp, yearperiod cy, yearperiod py
+                        WHERE ((cp.period_id=pPeriodId)
+                        AND (cp.period_yearperiod_id=cy.yearperiod_id)
+                        AND (pp.period_yearperiod_id=py.yearperiod_id)
+                        AND (cy.yearperiod_start > py.yearperiod_start))
+                        ORDER BY pp.period_start DESC LIMIT 1;
+
+                        IF (_priorYrPeriodId IS NOT NULL) THEN
+                                PERFORM financialreport(_p.flhead_id,_priorYrPeriodId,'Y',pPrjid);
+                        END IF;
+
+                END IF;
+        END IF;
+
+--Return the data
+  FOR _x IN
+        SELECT
+        flrpt.flrpt_flhead_id AS flstmtitem_flhead_id,
+        flrpt.flrpt_period_id AS flstmtitem_period_id,
+        flrpt.flrpt_username AS flstmtitem_username,
+        flrpt.flrpt_order AS flstmtitem_order,
+        flrpt.flrpt_level AS flstmtitem_level,
+        flrpt.flrpt_type AS flstmtitem_type,
+        flrpt.flrpt_type_id AS flstmtitem_type_id,
+        flrpt.flrpt_parent_id AS flstmtitem_parent_id,
+        NULL AS flstmtitem_accnt_id,
+        CASE
+                WHEN (pIndentName) THEN
+                        formatindent(flgrp.flgrp_name,flrpt.flrpt_level)
+                ELSE flgrp.flgrp_name
+        END AS flstmtitem_name,
+        CASE
+                WHEN (flgrp_summarize AND (flhead_type IN ('I','C'))) THEN
+                        (COALESCE(flrptmo.flrpt_diff,0))
+                WHEN (flgrp_summarize AND (flhead_type = 'B')) THEN
+                        (COALESCE(flrptmo.flrpt_ending,0))
+                ELSE NULL
+        END AS flstmtitem_month,
+        CASE
+                WHEN (flgrp_summarize) THEN
+                        (COALESCE(flrptmo.flrpt_debits,0))
+                ELSE NULL
+        END AS flstmtitem_monthdb,
+        CASE
+                WHEN (flgrp_summarize) THEN
+                        (COALESCE(flrptmo.flrpt_credits,0))
+                ELSE NULL
+        END AS flstmtitem_monthcr,
+        CASE
+                WHEN (flgrp_summarize AND flgrp_showdiffprcnt) THEN
+                        (COALESCE(flrptmo.flrpt_diffprcnt,0))
+                WHEN (flgrp_summarize AND flgrp_showendprcnt) THEN
+                        (COALESCE(flrptmo.flrpt_endingprcnt,0))
+                ELSE NULL
+        END AS flstmtitem_monthprcnt,
+        CASE
+                WHEN (flgrp_summarize) THEN
+                        (flrptmo.flrpt_budget)
+                ELSE NULL
+        END AS flstmtitem_monthbudget,
+        CASE
+                WHEN (flgrp_summarize) THEN
+                        (flrptmo.flrpt_budgetprcnt)
+                ELSE NULL
+        END AS flstmtitem_monthbudgetprcnt,
+        CASE
+                WHEN (flgrp_summarize AND flhead_type IN ('I','C')) THEN
+                        (COALESCE((flrptmo.flrpt_diff-flrptmo.flrpt_budget),0))
+                WHEN (flgrp_summarize AND flhead_type = 'B') THEN
+                        (COALESCE((flrptmo.flrpt_ending-flrptmo.flrpt_budget),0))
+                ELSE NULL
+        END AS flstmtitem_monthbudgetdiff,
+        CASE
+                WHEN (flgrp_summarize AND (flhead_type IN ('I','C')) AND (flrptmo.flrpt_budget > 0)) THEN
+                        (COALESCE(((flrptmo.flrpt_diff-flrptmo.flrpt_budget)/flrptmo.flrpt_budget),0))
+                WHEN (flgrp_summarize AND (flhead_type='B') AND (flrptmo.flrpt_budget > 0)) THEN
+                        (COALESCE(((flrptmo.flrpt_ending-flrptmo.flrpt_budget)/flrptmo.flrpt_budget),0))
+                WHEN (flgrp_summarize AND (flrptmo.flrpt_budget = 0)) THEN
+                        NULL
+                ELSE NULL
+        END AS flstmtitem_monthbudgetdiffprcnt,
+        CASE
+                WHEN (flgrp_summarize AND flhead_type IN ('I','C')) THEN
+                        (COALESCE(flrptqt.flrpt_diff,0))
+                WHEN (flgrp_summarize AND flhead_type = 'B') THEN
+                        (COALESCE(flrptqt.flrpt_ending,0))
+                ELSE NULL
+        END AS flstmtitem_qtr,
+        CASE
+                WHEN (flgrp_summarize) THEN
+                        (COALESCE(flrptqt.flrpt_debits,0))
+                ELSE NULL
+        END AS flstmtitem_qtrdb,
+        CASE
+                WHEN (flgrp_summarize) THEN
+                        (COALESCE(flrptqt.flrpt_credits,0))
+                ELSE NULL
+        END AS flstmtitem_qtrcr,
+        CASE
+                WHEN (flgrp_summarize AND flgrp_showdiffprcnt) THEN
+                        (flrptqt.flrpt_diffprcnt)
+                WHEN (flgrp_summarize AND flgrp_showendprcnt) THEN
+                        (flrptqt.flrpt_endingprcnt)
+                ELSE NULL
+        END AS flstmtitem_qtrprcnt,
+        CASE
+                WHEN (flgrp_summarize) THEN
+                        (COALESCE(flrptqt.flrpt_budget,0))
+                ELSE NULL
+        END AS flstmtitem_qtrbudget,
+        CASE
+                WHEN (flgrp_summarize) THEN
+                        (flrptqt.flrpt_budgetprcnt)
+                ELSE NULL
+        END AS flstmtitem_qtrbudgetprcnt,
+        CASE
+                WHEN (flgrp_summarize AND flhead_type IN ('I','C')) THEN
+                        (COALESCE((flrptqt.flrpt_diff-flrptqt.flrpt_budget),0))
+                WHEN (flgrp_summarize AND flhead_type = 'B') THEN
+                        (COALESCE((flrptqt.flrpt_ending-flrptqt.flrpt_budget),0))
+                ELSE NULL
+        END AS flstmtitem_qtrbudgetdiff,
+        CASE
+                WHEN (flgrp_summarize AND (flhead_type IN ('I','C')) AND (flrptqt.flrpt_budget > 0)) THEN
+                        (COALESCE(((flrptqt.flrpt_diff-flrptqt.flrpt_budget)/flrptqt.flrpt_budget),0))
+                WHEN (flgrp_summarize AND (flhead_type='B') AND (flrptqt.flrpt_budget > 0)) THEN
+                        (COALESCE(((flrptqt.flrpt_ending-flrptqt.flrpt_budget)/flrptqt.flrpt_budget),0))
+                ELSE NULL
+        END AS flstmtitem_qtrbudgetdiffprcnt,
+        CASE
+                WHEN (flgrp_summarize AND flhead_type IN ('I','C')) THEN
+                        (COALESCE(flrptyr.flrpt_diff,0))
+                WHEN (flgrp_summarize AND flhead_type = 'B') THEN
+                        (COALESCE(flrptyr.flrpt_ending,0))
+                ELSE NULL
+        END AS flstmtitem_year,
+        CASE
+                WHEN (flgrp_summarize) THEN
+                        (COALESCE(flrptyr.flrpt_debits,0))
+                ELSE NULL
+        END AS flstmtitem_yeardb,
+        CASE
+                WHEN (flgrp_summarize) THEN
+                        (COALESCE(flrptyr.flrpt_credits,0))
+                ELSE NULL
+        END AS flstmtitem_yearcr,
+        CASE
+                WHEN (flgrp_summarize AND flgrp_showdiffprcnt) THEN
+                        (COALESCE(flrptyr.flrpt_diffprcnt,0))
+                WHEN (flgrp_summarize AND flgrp_showendprcnt) THEN
+                        (COALESCE(flrptyr.flrpt_endingprcnt,0))
+                ELSE NULL
+        END AS flstmtitem_yearprcnt,
+        CASE
+                WHEN (flgrp_summarize) THEN
+                        (flrptyr.flrpt_budget)
+                ELSE NULL
+        END AS  flstmtitem_yearbudget,
+        CASE
+                WHEN (flgrp_summarize) THEN
+                        (flrptyr.flrpt_budgetprcnt)
+                ELSE NULL
+        END AS flstmtitem_yearbudgetprcnt,
+        CASE
+                WHEN (flgrp_summarize AND flhead_type IN ('I','C')) THEN
+                        (COALESCE((flrptyr.flrpt_diff-flrptyr.flrpt_budget),0))
+                WHEN (flgrp_summarize AND flhead_type = 'B') THEN
+                        (COALESCE((flrptyr.flrpt_ending-flrptyr.flrpt_budget),0))
+                ELSE NULL
+        END AS flstmtitem_yearbudgetdiff,
+        CASE
+                WHEN (flgrp_summarize AND (flhead_type IN ('I','C')) AND (flrptyr.flrpt_budget > 0)) THEN
+                        (COALESCE(((flrptyr.flrpt_diff-flrptyr.flrpt_budget)/flrptyr.flrpt_budget),0))
+                WHEN (flgrp_summarize AND (flhead_type = 'B') AND (flrptyr.flrpt_budget > 0)) THEN
+                        (COALESCE(((flrptyr.flrpt_ending-flrptyr.flrpt_budget)/flrptyr.flrpt_budget),0))
+                WHEN (flgrp_summarize AND (flrptyr.flrpt_budget = 0)) THEN
+                        NULL
+                ELSE NULL
+        END AS flstmtitem_yearbudgetdiffprcnt,
+        CASE
+                WHEN (flgrp_summarize AND flhead_type IN ('I','C')) THEN
+                        (COALESCE(flrptprmo.flrpt_diff,0))
+                WHEN (flgrp_summarize AND flhead_type = 'B') THEN
+                        (COALESCE(flrptprmo.flrpt_ending,0))
+                ELSE NULL
+        END AS flstmtitem_prmonth,
+        CASE
+                WHEN (flgrp_summarize AND flgrp_showdiffprcnt) THEN
+                        (flrptprmo.flrpt_diffprcnt)
+                WHEN (flgrp_summarize AND flgrp_showendprcnt) THEN
+                        (flrptprmo.flrpt_endingprcnt)
+                ELSE NULL
+        END AS flstmtitem_prmonthprcnt,
+        CASE
+                WHEN (flgrp_summarize AND flhead_type IN ('I','C')) THEN
+                        (COALESCE(flrptmo.flrpt_diff-flrptprmo.flrpt_diff,0))
+                WHEN (flgrp_summarize AND flhead_type = 'B') THEN
+                        (COALESCE(flrptmo.flrpt_ending-flrptprmo.flrpt_ending,0))
+                ELSE NULL
+        END AS flstmtitem_prmonthdiff,
+        CASE
+                WHEN (flgrp_summarize AND (flhead_type IN ('I','C')) AND (flrptprmo.flrpt_diff > 0)) THEN
+                        (COALESCE((flrptmo.flrpt_diff-flrptprmo.flrpt_diff)/flrptprmo.flrpt_diff,0))
+                WHEN (flgrp_summarize AND (flhead_type = 'B') AND (flrptprmo.flrpt_ending > 0)) THEN
+                        (COALESCE((flrptmo.flrpt_ending-flrptprmo.flrpt_ending)/flrptprmo.flrpt_ending,0))
+                WHEN (flgrp_summarize AND (flrptprmo.flrpt_ending = 0)) THEN
+                        NULL
+                ELSE NULL
+        END AS flstmtitem_prmonthdiffprcnt,
+        CASE
+                WHEN (flgrp_summarize AND flhead_type IN ('I','C')) THEN
+                        (COALESCE(flrptprqt.flrpt_diff,0))
+                WHEN (flgrp_summarize AND flhead_type = 'B') THEN
+                        (COALESCE(flrptprqt.flrpt_ending,0))
+                ELSE NULL
+        END AS flstmtitem_prqtr,
+        CASE
+                WHEN (flgrp_summarize AND flgrp_showdiffprcnt) THEN
+                        (flrptprqt.flrpt_diffprcnt)
+                WHEN (flgrp_summarize AND flgrp_showendprcnt) THEN
+                        (flrptprqt.flrpt_endingprcnt)
+                ELSE NULL
+        END AS flstmtitem_prqtrprcnt,
+        CASE
+                WHEN (flgrp_summarize AND flhead_type IN ('I','C')) THEN
+                        (COALESCE(flrptqt.flrpt_diff-flrptprqt.flrpt_diff,0))
+                WHEN (flgrp_summarize AND flhead_type = 'B') THEN
+                        (COALESCE(flrptqt.flrpt_ending-flrptprqt.flrpt_ending,0))
+                ELSE NULL
+        END AS flstmtitem_prqtrdiff,
+        CASE
+                WHEN (flgrp_summarize AND (flhead_type IN ('I','C')) AND (flrptprqt.flrpt_diff > 0)) THEN
+                        (COALESCE((flrptqt.flrpt_diff-flrptprqt.flrpt_diff)/flrptprqt.flrpt_diff,0))
+                WHEN (flgrp_summarize AND (flhead_type = 'B') AND (flrptprqt.flrpt_ending > 0)) THEN
+                        (COALESCE((flrptqt.flrpt_ending-flrptprqt.flrpt_ending)/flrptprqt.flrpt_ending,0))
+                WHEN (flgrp_summarize AND (flrptprqt.flrpt_ending = 0)) THEN
+                        NULL
+                ELSE NULL
+        END AS flstmtitem_prqtrdiffprcnt,
+        CASE
+                WHEN (flgrp_summarize AND flhead_type IN ('I','C')) THEN
+                        (COALESCE(flrptpryr.flrpt_diff,0))
+                WHEN (flgrp_summarize AND flhead_type = 'B') THEN
+                        (COALESCE(flrptpryr.flrpt_ending,0))
+                ELSE NULL
+        END AS flstmtitem_pryear,
+        CASE
+                WHEN (flgrp_summarize AND flgrp_showdiffprcnt) THEN
+                        (flrptpryr.flrpt_diffprcnt)
+                WHEN (flgrp_summarize AND flgrp_showendprcnt) THEN
+                        (flrptpryr.flrpt_endingprcnt)
+                ELSE NULL
+        END AS flstmtitem_pryearprcnt,
+        CASE
+                WHEN (flgrp_summarize AND flhead_type IN ('I','C')) THEN
+                        (COALESCE(flrptyr.flrpt_diff-flrptpryr.flrpt_diff,0))
+                WHEN (flgrp_summarize AND flhead_type = 'B') THEN
+                        (COALESCE(flrptyr.flrpt_ending-flrptpryr.flrpt_ending,0))
+                ELSE NULL
+        END AS flstmtitem_pryeardiff,
+        CASE
+                WHEN (flgrp_summarize AND (flhead_type IN ('I','C')) AND (flrptpryr.flrpt_diff > 0)) THEN
+                        (COALESCE((flrptyr.flrpt_diff-flrptpryr.flrpt_diff)/flrptpryr.flrpt_diff,0))
+                WHEN (flgrp_summarize AND (flhead_type = 'B' ) AND (flrptpryr.flrpt_ending > 0)) THEN
+                        (COALESCE((flrptyr.flrpt_ending-flrptpryr.flrpt_ending)/flrptpryr.flrpt_ending,0))
+                WHEN (flgrp_summarize AND (flrptpryr.flrpt_ending = 0)) THEN
+                        NULL
+                ELSE NULL
+        END AS flstmtitem_pryeardiffprcnt
+        FROM flgrp,flhead,
+                (SELECT DISTINCT
+                        flrpt_flhead_id,
+                        flrpt_period_id,
+                        flrpt_username,
+                        flrpt_order,
+                        flrpt_level,
+                        flrpt_type,
+                        flrpt_type_id,
+                        flrpt_parent_id
+                FROM flrpt
+                WHERE ((flrpt_type='G')
+                AND (flrpt_flhead_id=_p.flhead_id)
+                AND (flrpt_period_id=pPeriodId)
+                AND (flrpt_username=getEffectiveXtUser()))) AS flrpt
+                        LEFT OUTER JOIN flrpt flrptmo
+                                ON ((flrptmo.flrpt_type=flrpt.flrpt_type)
+                                AND (flrptmo.flrpt_type_id=flrpt.flrpt_type_id)
+                                AND (flrptmo.flrpt_flhead_id=flrpt.flrpt_flhead_id)
+                                AND (flrptmo.flrpt_period_id=flrpt.flrpt_period_id)
+                                AND (flrptmo.flrpt_interval='M')
+                                AND (flrptmo.flrpt_username=flrpt.flrpt_username)
+                                AND (flrptmo.flrpt_order=flrpt.flrpt_order))
+                        LEFT OUTER JOIN flrpt flrptqt
+                                ON ((flrptqt.flrpt_type=flrpt.flrpt_type)
+                                AND (flrptqt.flrpt_type_id=flrpt.flrpt_type_id)
+                                AND (flrptqt.flrpt_flhead_id=flrpt.flrpt_flhead_id)
+                                AND (flrptqt.flrpt_period_id=flrpt.flrpt_period_id)
+                                AND (flrptqt.flrpt_interval=_qtrInterval)
+                                AND (flrptqt.flrpt_username=flrpt.flrpt_username)
+                                AND (flrptqt.flrpt_order=flrpt.flrpt_order))
+                        LEFT OUTER JOIN flrpt flrptyr
+                                ON ((flrptyr.flrpt_type=flrpt.flrpt_type)
+                                AND (flrptyr.flrpt_type_id=flrpt.flrpt_type_id)
+                                AND (flrptyr.flrpt_flhead_id=flrpt.flrpt_flhead_id)
+                                AND (flrptyr.flrpt_period_id=flrpt.flrpt_period_id)
+                                AND (flrptyr.flrpt_interval=_yrInterval)
+                                AND (flrptyr.flrpt_username=flrpt.flrpt_username)
+                                AND (flrptyr.flrpt_order=flrpt.flrpt_order))
+                        LEFT OUTER JOIN flrpt flrptprmo
+                                ON ((flrptprmo.flrpt_type=flrpt.flrpt_type)
+                                AND (flrptprmo.flrpt_type_id=flrpt.flrpt_type_id)
+                                AND (flrptprmo.flrpt_flhead_id=flrpt.flrpt_flhead_id)
+                                AND (flrptprmo.flrpt_period_id=_priorMoPeriodId)
+                                AND (flrptprmo.flrpt_interval='M')
+                                AND (flrptprmo.flrpt_username=flrpt.flrpt_username)
+                                AND (flrptprmo.flrpt_order=flrpt.flrpt_order))
+                        LEFT OUTER JOIN flrpt flrptprqt
+                                ON ((flrptprqt.flrpt_type=flrpt.flrpt_type)
+                                AND (flrptprqt.flrpt_type_id=flrpt.flrpt_type_id)
+                                AND (flrptprqt.flrpt_flhead_id=flrpt.flrpt_flhead_id)
+                                AND (flrptprqt.flrpt_period_id=_priorQtPeriodId)
+                                AND (flrptprqt.flrpt_interval='Q')
+                                AND (flrptprqt.flrpt_username=flrpt.flrpt_username)
+                                AND (flrptprqt.flrpt_order=flrpt.flrpt_order))
+                        LEFT OUTER JOIN flrpt flrptpryr
+                                ON ((flrptpryr.flrpt_type=flrpt.flrpt_type)
+                                AND (flrptpryr.flrpt_type_id=flrpt.flrpt_type_id)
+                                AND (flrptpryr.flrpt_flhead_id=flrpt.flrpt_flhead_id)
+                                AND (flrptpryr.flrpt_period_id=_priorYrPeriodId)
+                                AND (flrptpryr.flrpt_interval='Y')
+                                AND (flrptpryr.flrpt_username=flrpt.flrpt_username)
+                                AND (flrptpryr.flrpt_order=flrpt.flrpt_order))
+        WHERE ((flgrp_id = flrpt.flrpt_type_id)
+        AND (flhead_id = flgrp_flhead_id))
+        UNION
+        SELECT
+        flrpt.flrpt_flhead_id AS flstmtitem_flhead_id,
+        flrpt.flrpt_period_id AS flstmtitem_period_id,
+        flrpt.flrpt_username AS flstmtitem_username,
+        flrpt.flrpt_order AS flstmtitem_order,
+        flrpt.flrpt_level AS flstmtitem_level,
+        flrpt.flrpt_type AS flstmtitem_type,
+        flrpt.flrpt_type_id AS flstmtitem_type_id,
+        flrpt.flrpt_parent_id AS flstmtitem_parent_id,
+        flrpt.flrpt_accnt_id AS flstmtitem_accnt_id,
+        CASE
+                WHEN (pIndentName) THEN
+                        formatindent(flrpt.flrpt_name,flrpt.flrpt_level)
+                ELSE flrpt.flrpt_name
+        END AS flstmtitem_name,
+        CASE
+                WHEN (flhead_type IN ('I','C')) THEN
+                        (COALESCE(flrptmo.flrpt_diff,0))
+                ELSE (COALESCE(flrptmo.flrpt_ending,0))
+        END AS flstmtitem_month,
+        (COALESCE(flrptmo.flrpt_debits,0)) AS flstmtitem_monthdb,
+        (COALESCE(flrptmo.flrpt_credits,0)) AS flstmtitem_monthcr,
+        CASE
+                WHEN (flitem_showdiffprcnt) THEN
+                        (flrptmo.flrpt_diffprcnt)
+                WHEN (flitem_showendprcnt) THEN
+                        (flrptmo.flrpt_endingprcnt)
+                ELSE NULL
+        END AS flstmtitem_monthprcnt,
+        (COALESCE(flrptmo.flrpt_budget,0)) AS flstmtitem_monthbudget,
+        (flrptmo.flrpt_budgetprcnt) AS flstmtitem_monthbudgetprcnt,
+        CASE
+                WHEN (flhead_type IN ('I','C')) THEN
+                        (COALESCE((flrptmo.flrpt_diff-flrptmo.flrpt_budget),0))
+                ELSE (COALESCE((flrptmo.flrpt_ending-flrptmo.flrpt_budget),0))
+        END AS flstmtitem_monthbudgetdiff,
+        CASE
+                WHEN ((flhead_type IN ('I','C')) AND (flrptmo.flrpt_budget > 0)) THEN
+                        (COALESCE(((flrptmo.flrpt_diff-flrptmo.flrpt_budget)/flrptmo.flrpt_budget),0))
+                WHEN ((flhead_type='B') AND (flrptmo.flrpt_budget > 0)) THEN
+                        (COALESCE(((flrptmo.flrpt_ending-flrptmo.flrpt_budget)/flrptmo.flrpt_budget),0))
+                ELSE NULL
+        END AS flstmtitem_monthbudgetdiffprcnt,
+        CASE
+                WHEN (flhead_type IN ('I','C')) THEN
+                        (COALESCE(flrptqt.flrpt_diff,0))
+                ELSE (COALESCE(flrptqt.flrpt_ending,0))
+        END AS flstmtitem_qtr,
+        (COALESCE(flrptqt.flrpt_debits,0)) AS flstmtitem_qtrdb,
+        (COALESCE(flrptqt.flrpt_credits,0)) AS flstmtitem_qtrcr,
+        CASE
+                WHEN (flitem_showdiffprcnt) THEN
+                        (COALESCE(flrptqt.flrpt_diffprcnt,0))
+                WHEN (flitem_showendprcnt) THEN
+                        (COALESCE(flrptqt.flrpt_endingprcnt,0))
+                ELSE NULL
+        END AS flstmtitem_qtrprcnt,
+        (COALESCE(flrptqt.flrpt_budget,0)) AS flstmtitem_qtrbudget,
+        (flrptqt.flrpt_budgetprcnt) AS flstmtitem_qtrbudgetprcnt,
+        CASE
+                WHEN (flhead_type IN ('I','C')) THEN
+                        (COALESCE((flrptqt.flrpt_diff-flrptqt.flrpt_budget),0))
+                ELSE (COALESCE((flrptqt.flrpt_ending-flrptqt.flrpt_budget),0))
+        END AS flstmtitem_qtrbudgetdiff,
+        CASE
+                WHEN ((flhead_type IN ('I','C')) AND (flrptqt.flrpt_budget > 0)) THEN
+                        (COALESCE(((flrptqt.flrpt_diff-flrptqt.flrpt_budget)/flrptqt.flrpt_budget),0))
+                WHEN ((flhead_type='B') AND (flrptqt.flrpt_budget > 0)) THEN
+                        (COALESCE(((flrptqt.flrpt_ending-flrptqt.flrpt_budget)/flrptqt.flrpt_budget),0))
+                ELSE NULL
+        END AS flstmtitem_qtrbudgetdiffprcnt,
+        CASE
+                WHEN (flhead_type IN ('I','C')) THEN
+                        (COALESCE(flrptyr.flrpt_diff,0))
+                ELSE (COALESCE(flrptyr.flrpt_ending,0))
+        END AS flstmtitem_year,
+        (COALESCE(flrptyr.flrpt_debits,0)) AS flstmtitem_yeardb,
+        (COALESCE(flrptyr.flrpt_credits,0)) AS flstmtitem_yearcr,
+        CASE
+                WHEN (flitem_showdiffprcnt) THEN
+                        (flrptyr.flrpt_diffprcnt)
+                WHEN (flitem_showendprcnt) THEN
+                        (flrptyr.flrpt_endingprcnt)
+                ELSE NULL
+        END AS flstmtitem_yearprcnt,
+        (COALESCE(flrptyr.flrpt_budget,0)) AS  flstmtitem_yearbudget,
+        (flrptyr.flrpt_budgetprcnt) AS flstmtitem_yearbudgetprcnt,
+        CASE
+                WHEN (flhead_type IN ('I','C')) THEN
+                        (COALESCE((flrptyr.flrpt_diff-flrptyr.flrpt_budget),0))
+                ELSE (COALESCE((flrptyr.flrpt_ending-flrptyr.flrpt_budget),0))
+        END AS flstmtitem_yearbudgetdiff,
+        CASE
+                WHEN ((flhead_type IN ('I','C')) AND (flrptyr.flrpt_budget > 0)) THEN
+                        (COALESCE(((flrptyr.flrpt_diff-flrptyr.flrpt_budget)/flrptyr.flrpt_budget),0))
+                WHEN ((flhead_type = 'B') AND (flrptyr.flrpt_budget > 0)) THEN
+                        (COALESCE(((flrptyr.flrpt_ending-flrptyr.flrpt_budget)/flrptyr.flrpt_budget),0))
+                ELSE NULL
+        END AS flstmtitem_yearbudgetdiffprcnt,
+        CASE
+                WHEN (flhead_type IN ('I','C')) THEN
+                        (COALESCE(flrptprmo.flrpt_diff,0))
+                ELSE (COALESCE(flrptprmo.flrpt_ending,0))
+        END AS flstmtitem_prmonth,
+        CASE
+                WHEN (flitem_showdiffprcnt) THEN
+                        (flrptprmo.flrpt_diffprcnt)
+                WHEN (flitem_showendprcnt) THEN
+                        (flrptprmo.flrpt_endingprcnt)
+                ELSE NULL
+        END AS flstmtitem_prmonthprcnt,
+        CASE
+                WHEN (flhead_type IN ('I','C')) THEN
+                        (COALESCE(flrptmo.flrpt_diff-flrptprmo.flrpt_diff,0))
+                ELSE (COALESCE(flrptmo.flrpt_ending-flrptprmo.flrpt_ending,0))
+        END AS flstmtitem_prmonthdiff,
+        CASE
+                WHEN ((flhead_type IN ('I','C')) AND (flrptprmo.flrpt_diff > 0)) THEN
+                        (COALESCE((flrptmo.flrpt_diff-flrptprmo.flrpt_diff)/flrptprmo.flrpt_diff,0))
+                WHEN ((flhead_type = 'B') AND (flrptprmo.flrpt_ending > 0)) THEN
+                        (COALESCE((flrptmo.flrpt_ending-flrptprmo.flrpt_ending)/flrptprmo.flrpt_ending,0))
+                ELSE NULL
+        END AS flstmtitem_prmonthdiffprcnt,
+        CASE
+                WHEN (flhead_type IN ('I','C')) THEN
+                        (COALESCE(flrptprqt.flrpt_diff,0))
+                ELSE (COALESCE(flrptprqt.flrpt_ending,0))
+        END AS flstmtitem_prqtr,
+        CASE
+                WHEN (flitem_showdiffprcnt) THEN
+                        (flrptprqt.flrpt_diffprcnt)
+                WHEN (flitem_showendprcnt) THEN
+                        (flrptprqt.flrpt_endingprcnt)
+                ELSE NULL
+        END AS flstmtitem_prqtrprcnt,
+        CASE
+                WHEN (flhead_type IN ('I','C')) THEN
+                        (COALESCE(flrptqt.flrpt_diff-flrptprqt.flrpt_diff,0))
+                ELSE (COALESCE(flrptqt.flrpt_ending-flrptprqt.flrpt_ending,0))
+        END AS flstmtitem_prqtrdiff,
+        CASE
+                WHEN ((flhead_type IN ('I','C')) AND (flrptprqt.flrpt_diff > 0)) THEN
+                        (COALESCE((flrptqt.flrpt_diff-flrptprqt.flrpt_diff)/flrptprqt.flrpt_diff,0))
+                WHEN ((flhead_type = 'B') AND (flrptprqt.flrpt_ending > 0)) THEN
+                        (COALESCE((flrptqt.flrpt_ending-flrptprqt.flrpt_ending)/flrptprqt.flrpt_ending,0))
+                ELSE NULL
+        END AS flstmtitem_prqtrdiffprcnt,
+        CASE
+                WHEN (flhead_type IN ('I','C')) THEN
+                        (COALESCE(flrptpryr.flrpt_diff,0))
+                ELSE (COALESCE(flrptpryr.flrpt_ending,0))
+        END AS flstmtitem_pryear,
+        CASE
+                WHEN (flitem_showdiffprcnt) THEN
+                        (flrptpryr.flrpt_diffprcnt)
+                WHEN (flitem_showendprcnt) THEN
+                        (flrptpryr.flrpt_endingprcnt)
+                ELSE NULL
+        END AS flstmtitem_pryearprcnt,
+        CASE
+                WHEN (flhead_type IN ('I','C')) THEN
+                        (COALESCE((flrptyr.flrpt_diff-flrptpryr.flrpt_diff),0))
+                ELSE (COALESCE((flrptyr.flrpt_ending-flrptpryr.flrpt_ending),0))
+        END AS flstmtitem_pryeardiff,
+        CASE
+                WHEN ((flhead_type IN ('I','C')) AND (flrptpryr.flrpt_diff > 0)) THEN
+                        (COALESCE((flrptyr.flrpt_diff-flrptpryr.flrpt_diff)/flrptpryr.flrpt_diff,0))
+                WHEN ((flhead_type = 'B' ) AND (flrptpryr.flrpt_ending > 0)) THEN
+                        (COALESCE((flrptyr.flrpt_ending-flrptpryr.flrpt_ending)/flrptpryr.flrpt_ending,0))
+                ELSE NULL
+        END AS flstmtitem_pryeardiffprcnt
+        FROM flitem,flhead,
+                (SELECT DISTINCT
+                        flrpt_flhead_id,
+                        flrpt_period_id,
+                        flrpt_username,
+                        flrpt_order,
+                        flrpt_level,
+                        flrpt_type,
+                        flrpt_type_id,
+                        flrpt_parent_id,
+                        accnt_id AS flrpt_accnt_id,
+                        CASE WHEN (pShowNumbers) THEN
+                                (formatGLAccount(accnt_id) || '-' || accnt_descrip)
+                        ELSE accnt_descrip END AS flrpt_name
+                FROM flrpt,accnt
+                WHERE ((flrpt_type='I')
+                AND (flrpt_flhead_id=_p.flhead_id)
+                AND (flrpt_period_id=pPeriodid)
+                AND (flrpt_username=getEffectiveXtUser())
+                AND (accnt_id=flrpt_accnt_id))) AS flrpt
+                        LEFT OUTER JOIN flrpt flrptmo
+                                ON ((flrptmo.flrpt_type=flrpt.flrpt_type)
+                                AND (flrptmo.flrpt_type_id=flrpt.flrpt_type_id)
+                                AND (flrptmo.flrpt_flhead_id=flrpt.flrpt_flhead_id)
+                                AND (flrptmo.flrpt_period_id=flrpt.flrpt_period_id)
+                                AND (flrptmo.flrpt_interval='M')
+                                AND (flrptmo.flrpt_username=flrpt.flrpt_username)
+                                AND (flrptmo.flrpt_order=flrpt.flrpt_order))
+                        LEFT OUTER JOIN flrpt flrptqt
+                                ON ((flrptqt.flrpt_type=flrpt.flrpt_type)
+                                AND (flrptqt.flrpt_type_id=flrpt.flrpt_type_id)
+                                AND (flrptqt.flrpt_flhead_id=flrpt.flrpt_flhead_id)
+                                AND (flrptqt.flrpt_period_id=flrpt.flrpt_period_id)
+                                AND (flrptqt.flrpt_interval=_qtrInterval)
+                                AND (flrptqt.flrpt_username=flrpt.flrpt_username)
+                                AND (flrptqt.flrpt_order=flrpt.flrpt_order))
+                        LEFT OUTER JOIN flrpt flrptyr
+                                ON ((flrptyr.flrpt_type=flrpt.flrpt_type)
+                                AND (flrptyr.flrpt_type_id=flrpt.flrpt_type_id)
+                                AND (flrptyr.flrpt_flhead_id=flrpt.flrpt_flhead_id)
+                                AND (flrptyr.flrpt_period_id=flrpt.flrpt_period_id)
+                                AND (flrptyr.flrpt_interval=_yrInterval)
+                                AND (flrptyr.flrpt_username=flrpt.flrpt_username)
+                                AND (flrptyr.flrpt_order=flrpt.flrpt_order))
+                        LEFT OUTER JOIN flrpt flrptprmo
+                                ON ((flrptprmo.flrpt_type=flrpt.flrpt_type)
+                                AND (flrptprmo.flrpt_type_id=flrpt.flrpt_type_id)
+                                AND (flrptprmo.flrpt_flhead_id=flrpt.flrpt_flhead_id)
+                                AND (flrptprmo.flrpt_period_id=_priorMoPeriodId)
+                                AND (flrptprmo.flrpt_interval='M')
+                                AND (flrptprmo.flrpt_username=flrpt.flrpt_username)
+                                AND (flrptprmo.flrpt_order=flrpt.flrpt_order))
+                        LEFT OUTER JOIN flrpt flrptprqt
+                                ON ((flrptprqt.flrpt_type=flrpt.flrpt_type)
+                                AND (flrptprqt.flrpt_type_id=flrpt.flrpt_type_id)
+                                AND (flrptprqt.flrpt_flhead_id=flrpt.flrpt_flhead_id)
+                                AND (flrptprqt.flrpt_period_id=_priorQtPeriodId)
+                                AND (flrptprqt.flrpt_interval='Q')
+                                AND (flrptprqt.flrpt_username=flrpt.flrpt_username)
+                                AND (flrptprqt.flrpt_order=flrpt.flrpt_order))
+                        LEFT OUTER JOIN flrpt flrptpryr
+                                ON ((flrptpryr.flrpt_type=flrpt.flrpt_type)
+                                AND (flrptpryr.flrpt_type_id=flrpt.flrpt_type_id)
+                                AND (flrptpryr.flrpt_flhead_id=flrpt.flrpt_flhead_id)
+                                AND (flrptpryr.flrpt_period_id=_priorYrPeriodId)
+                                AND (flrptpryr.flrpt_interval='Y')
+                                AND (flrptpryr.flrpt_username=flrpt.flrpt_username)
+                                AND (flrptpryr.flrpt_order=flrpt.flrpt_order) )
+        WHERE ((flitem_id = flrpt.flrpt_type_id)
+        AND (flhead_id = flitem_flhead_id))
+        UNION
+        SELECT
+        flrpt.flrpt_flhead_id AS flstmtitem_flhead_id,
+        flrpt.flrpt_period_id AS flstmtitem_period_id,
+        flrpt.flrpt_username AS flstmtitem_username,
+        flrpt.flrpt_order AS flstmtitem_order,
+        flrpt.flrpt_level AS flstmtitem_level,
+        flrpt.flrpt_type AS flstmtitem_type,
+        flrpt.flrpt_type_id AS flstmtitem_type_id,
+        flrpt.flrpt_parent_id AS flstmtitem_parent_id,
+        NULL AS flstmtitem_accnt_id,
+        CASE WHEN(flrpt.flrpt_type='T' AND flrpt.flrpt_level=0) THEN
+                        COALESCE(flrpt.flrpt_altname, 'Total')
+                WHEN(flrpt.flrpt_type='T') THEN
+                        formatindent(COALESCE(flrpt.flrpt_altname, 'Subtotal') ,
+                        (CASE WHEN pIndentName THEN flrpt.flrpt_level ELSE 0 END))
+                ELSE formatindent(('Type ' || flrpt.flrpt_type || ' ' || text(flrpt.flrpt_type_id)),
+                        (CASE WHEN pIndentName THEN flrpt.flrpt_level ELSE 0 END))
+                END AS flstmtitem_name,
+        CASE
+                WHEN (flhead_type IN ('I','C')) THEN
+                        (COALESCE(flrptmo.flrpt_diff,0))
+                ELSE (COALESCE(flrptmo.flrpt_ending,0))
+        END AS flstmtitem_month,
+        (COALESCE(flrptmo.flrpt_debits,0)) AS flstmtitem_monthdb,
+        (COALESCE(flrptmo.flrpt_credits,0)) AS flstmtitem_monthcr,
+        CASE
+                WHEN (flhead_type IN ('I','C')) THEN
+                        (flrptmo.flrpt_diffprcnt)
+                ELSE (flrptmo.flrpt_endingprcnt)
+        END AS flstmtitem_monthprcnt,
+        (COALESCE(flrptmo.flrpt_budget,0)) AS flstmtitem_monthbudget,
+        (flrptmo.flrpt_budgetprcnt) AS flstmtitem_monthbudgetprcnt,
+        CASE
+                WHEN (flhead_type IN ('I','C')) THEN
+                        (COALESCE((flrptmo.flrpt_diff-flrptmo.flrpt_budget),0))
+                ELSE (COALESCE((flrptmo.flrpt_ending-flrptmo.flrpt_budget),0))
+        END AS flstmtitem_monthbudgetdiff,
+        CASE
+                WHEN ((flhead_type IN ('I','C')) AND (flrptmo.flrpt_budget > 0)) THEN
+                        (COALESCE(((flrptmo.flrpt_diff-flrptmo.flrpt_budget)/flrptmo.flrpt_budget),0))
+                WHEN ((flhead_type='B') AND (flrptmo.flrpt_budget > 0)) THEN
+                        (COALESCE(((flrptmo.flrpt_ending-flrptmo.flrpt_budget)/flrptmo.flrpt_budget),0))
+                ELSE NULL
+        END AS flstmtitem_monthbudgetdiffprcnt,
+        CASE
+                WHEN (flhead_type IN ('I','C')) THEN
+                        (COALESCE(flrptqt.flrpt_diff,0))
+                ELSE (COALESCE(flrptqt.flrpt_ending,0))
+        END AS flstmtitem_qtr,
+        (COALESCE(flrptqt.flrpt_debits,0)) AS flstmtitem_qtrdb,
+        (COALESCE(flrptqt.flrpt_credits,0)) AS flstmtitem_qtrcr,
+        CASE
+                WHEN (flhead_type IN ('I','C')) THEN
+                        (flrptqt.flrpt_diffprcnt)
+                ELSE (flrptqt.flrpt_endingprcnt)
+        END AS flstmtitem_qtrprcnt,
+        (COALESCE(flrptqt.flrpt_budget,0)) AS flstmtitem_qtrbudget,
+        (flrptqt.flrpt_budgetprcnt) AS flstmtitem_qtrbudgetprcnt,
+        CASE
+                WHEN (flhead_type IN ('I','C')) THEN
+                        (COALESCE((flrptqt.flrpt_diff-flrptqt.flrpt_budget),0))
+                ELSE (COALESCE((flrptqt.flrpt_ending-flrptqt.flrpt_budget),0))
+        END AS flstmtitem_qtrbudgetdiff,
+        CASE
+                WHEN ((flhead_type IN ('I','C')) AND (flrptqt.flrpt_budget > 0)) THEN
+                        (COALESCE(((flrptqt.flrpt_diff-flrptqt.flrpt_budget)/flrptqt.flrpt_budget),0))
+                WHEN ((flhead_type='B') AND (flrptqt.flrpt_budget > 0)) THEN
+                        (COALESCE(((flrptqt.flrpt_ending-flrptqt.flrpt_budget)/flrptqt.flrpt_budget),0))
+                ELSE NULL
+        END AS flstmtitem_qtrbudgetdiffprcnt,
+        CASE
+                WHEN (flhead_type IN ('I','C')) THEN
+                        (COALESCE(flrptyr.flrpt_diff,0))
+                ELSE (COALESCE(flrptyr.flrpt_ending,0))
+        END AS flstmtitem_year,
+        (COALESCE(flrptyr.flrpt_debits,0)) AS flstmtitem_yeardb,
+        (COALESCE(flrptyr.flrpt_credits,0)) AS flstmtitem_yearcr,
+        CASE
+                WHEN (flhead_type IN ('I','C')) THEN
+                        (flrptyr.flrpt_diffprcnt)
+                ELSE (flrptyr.flrpt_endingprcnt)
+        END AS flstmtitem_yearprcnt,
+        (COALESCE(flrptyr.flrpt_budget,0)) AS  flstmtitem_yearbudget,
+        (flrptyr.flrpt_budgetprcnt) AS flstmtitem_yearbudgetprcnt,
+        CASE
+                WHEN (flhead_type IN ('I','C')) THEN
+                        (COALESCE((flrptyr.flrpt_diff-flrptyr.flrpt_budget),0))
+                ELSE (COALESCE((flrptyr.flrpt_ending-flrptyr.flrpt_budget),0))
+        END AS flstmtitem_yearbudgetdiff,
+        CASE
+                WHEN ((flhead_type IN ('I','C')) AND (flrptyr.flrpt_budget > 0)) THEN
+                        (COALESCE(((flrptyr.flrpt_diff-flrptyr.flrpt_budget)/flrptyr.flrpt_budget),0))
+                WHEN ((flhead_type = 'B') AND (flrptyr.flrpt_budget > 0)) THEN
+                        (COALESCE(((flrptyr.flrpt_ending-flrptyr.flrpt_budget)/flrptyr.flrpt_budget),0))
+                ELSE NULL
+        END AS flstmtitem_yearbudgetdiffprcnt,
+        CASE
+                WHEN (flhead_type IN ('I','C')) THEN
+                        (COALESCE(flrptprmo.flrpt_diff,0))
+                ELSE (COALESCE(flrptprmo.flrpt_ending,0))
+        END AS flstmtitem_prmonth,
+        CASE
+                WHEN (flhead_type IN ('I','C')) THEN
+                        (flrptprmo.flrpt_diffprcnt)
+                ELSE (flrptprmo.flrpt_endingprcnt)
+        END AS flstmtitem_prmonthprcnt,
+        CASE
+                WHEN (flhead_type IN ('I','C')) THEN
+                        (COALESCE(flrptmo.flrpt_diff-flrptprmo.flrpt_diff,0))
+                ELSE (COALESCE(flrptmo.flrpt_ending-flrptprmo.flrpt_ending,0))
+        END AS flstmtitem_prmonthdiff,
+        CASE
+                WHEN ((flhead_type IN ('I','C')) AND (flrptprmo.flrpt_diff > 0)) THEN
+                        (COALESCE((flrptmo.flrpt_diff-flrptprmo.flrpt_diff)/flrptprmo.flrpt_diff,0))
+                WHEN ((flhead_type = 'B') AND (flrptprmo.flrpt_ending > 0)) THEN
+                        (COALESCE((flrptmo.flrpt_ending-flrptprmo.flrpt_ending)/flrptprmo.flrpt_ending,0))
+                ELSE NULL
+        END AS flstmtitem_prmonthdiffprcnt,
+        CASE
+                WHEN (flhead_type IN ('I','C')) THEN
+                        (COALESCE(flrptprqt.flrpt_diff,0))
+                ELSE (COALESCE(flrptprqt.flrpt_ending,0))
+        END AS flstmtitem_prqtr,
+        CASE
+                WHEN (flhead_type IN ('I','C')) THEN
+                        (flrptprqt.flrpt_diffprcnt)
+                ELSE (flrptprqt.flrpt_endingprcnt)
+        END AS flstmtitem_prqtrprcnt,
+        CASE
+                WHEN (flhead_type IN ('I','C')) THEN
+                        (COALESCE(flrptqt.flrpt_diff-flrptprqt.flrpt_diff,0))
+                ELSE (COALESCE(flrptqt.flrpt_ending-flrptprqt.flrpt_ending,0))
+        END AS flstmtitem_prqtrdiff,
+        CASE
+                WHEN ((flhead_type IN ('I','C')) AND (flrptprqt.flrpt_diff > 0)) THEN
+                        (COALESCE((flrptqt.flrpt_diff-flrptprqt.flrpt_diff)/flrptprqt.flrpt_diff,0))
+                WHEN ((flhead_type = 'B') AND (flrptprqt.flrpt_ending > 0)) THEN
+                        (COALESCE((flrptqt.flrpt_ending-flrptprqt.flrpt_ending)/flrptprqt.flrpt_ending,0))
+                ELSE NULL
+        END AS flstmtitem_prqtrdiffprcnt,
+        CASE
+                WHEN (flhead_type IN ('I','C')) THEN
+                        (COALESCE(flrptpryr.flrpt_diff,0))
+                ELSE (COALESCE(flrptpryr.flrpt_ending,0))
+        END AS flstmtitem_pryear,
+        CASE
+                WHEN (flhead_type IN ('I','C')) THEN
+                        (flrptpryr.flrpt_diffprcnt)
+                ELSE (flrptpryr.flrpt_endingprcnt)
+        END AS flstmtitem_pryearprcnt,
+        CASE
+                WHEN (flhead_type IN ('I','C')) THEN
+                        (COALESCE(flrptyr.flrpt_diff-flrptpryr.flrpt_diff,0))
+                ELSE (COALESCE(flrptyr.flrpt_ending-flrptpryr.flrpt_ending,0))
+        END AS flstmtitem_pryeardiff,
+        CASE
+                WHEN ((flhead_type IN ('I','C')) AND (flrptpryr.flrpt_diff > 0)) THEN
+                        (COALESCE((flrptyr.flrpt_diff-flrptpryr.flrpt_diff)/flrptpryr.flrpt_diff,0))
+                WHEN ((flhead_type = 'B' ) AND (flrptpryr.flrpt_ending > 0)) THEN
+                        (COALESCE((flrptyr.flrpt_ending-flrptpryr.flrpt_ending)/flrptpryr.flrpt_ending,0))
+                ELSE NULL
+        END AS flstmtitem_pryeardiffprcnt
+        FROM flhead CROSS JOIN (SELECT DISTINCT
+                        flrpt_flhead_id,
+                        flrpt_period_id,
+                        flrpt_username,
+                        flrpt_order,
+                        flrpt_level,
+                        flrpt_type,
+                        flrpt_type_id,
+                        flrpt_parent_id,
+                        flrpt_altname
+                FROM flrpt
+                WHERE ((NOT (flrpt_type IN ('G','I','S')))
+                AND (flrpt_flhead_id=_p.flhead_id)
+                AND (flrpt_period_id=pPeriodId)
+                AND (flrpt_username=getEffectiveXtUser()))) AS flrpt
+                        LEFT OUTER JOIN flrpt flrptmo
+                                ON ((flrptmo.flrpt_type=flrpt.flrpt_type)
+                                AND (flrptmo.flrpt_order=flrpt.flrpt_order)
+                                AND (flrptmo.flrpt_flhead_id=flrpt.flrpt_flhead_id)
+                                AND (flrptmo.flrpt_period_id=flrpt.flrpt_period_id)
+                                AND (flrptmo.flrpt_interval='M')
+                                AND (flrptmo.flrpt_username=flrpt.flrpt_username))
+                        LEFT OUTER JOIN flrpt flrptqt
+                                ON ((flrptqt.flrpt_type=flrpt.flrpt_type)
+                                AND (flrptqt.flrpt_order=flrpt.flrpt_order)
+                                AND (flrptqt.flrpt_flhead_id=flrpt.flrpt_flhead_id)
+                                AND (flrptqt.flrpt_period_id=flrpt.flrpt_period_id)
+                                AND (flrptqt.flrpt_interval=_qtrInterval)
+                                AND (flrptqt.flrpt_username=flrpt.flrpt_username))
+                        LEFT OUTER JOIN flrpt flrptyr
+                                ON ((flrptyr.flrpt_type=flrpt.flrpt_type)
+                                AND (flrptyr.flrpt_order=flrpt.flrpt_order)
+                                AND (flrptyr.flrpt_flhead_id=flrpt.flrpt_flhead_id)
+                                AND (flrptyr.flrpt_period_id=flrpt.flrpt_period_id)
+                                AND (flrptyr.flrpt_interval=_yrInterval)
+                                AND (flrptyr.flrpt_username=flrpt.flrpt_username))
+                        LEFT OUTER JOIN flrpt flrptprmo
+                                ON ((flrptprmo.flrpt_type=flrpt.flrpt_type)
+                                AND (flrptprmo.flrpt_order=flrpt.flrpt_order)
+                                AND (flrptprmo.flrpt_flhead_id=flrpt.flrpt_flhead_id)
+                                AND (flrptprmo.flrpt_period_id=_priorMoPeriodId)
+                                AND (flrptprmo.flrpt_interval='M')
+                                AND (flrptprmo.flrpt_username=flrpt.flrpt_username))
+                        LEFT OUTER JOIN flrpt flrptprqt
+                                ON ((flrptprqt.flrpt_type=flrpt.flrpt_type)
+                                AND (flrptprqt.flrpt_order=flrpt.flrpt_order)
+                                AND (flrptprqt.flrpt_flhead_id=flrpt.flrpt_flhead_id)
+                                AND (flrptprqt.flrpt_period_id=_priorQtPeriodId)
+                                AND (flrptprqt.flrpt_interval='Q')
+                                AND (flrptprqt.flrpt_username=flrpt.flrpt_username))
+                        LEFT OUTER JOIN flrpt flrptpryr
+                                ON ((flrptpryr.flrpt_type=flrpt.flrpt_type)
+                                AND (flrptpryr.flrpt_order=flrpt.flrpt_order)
+                                AND (flrptpryr.flrpt_flhead_id=flrpt.flrpt_flhead_id)
+                                AND (flrptpryr.flrpt_period_id=_priorYrPeriodId)
+                                AND (flrptpryr.flrpt_interval='Y')
+                                AND (flrptpryr.flrpt_username=flrpt.flrpt_username))
+        WHERE (flhead_id=flrpt.flrpt_flhead_id)
+        ORDER BY flstmtitem_order
+        LOOP
+                IF _prevlevel > _x.flstmtitem_level THEN
+                        _subgrp := _subgrp+1;
+                END IF;
+                _prevlevel:=_x.flstmtitem_level;
+                _row.flstmtitem_subgrp := _subgrp;
+
+                IF NOT _first THEN
+                        RETURN NEXT _row;
+                END IF;
+
+                _first := FALSE;
+
+                _row.flstmtitem_flhead_id := _x.flstmtitem_flhead_id;
+                _row.flstmtitem_period_id := _x.flstmtitem_period_id;
+                _row.flstmtitem_username := _x.flstmtitem_username;
+                _row.flstmtitem_order := _x.flstmtitem_order;
+                _row.flstmtitem_level := _x.flstmtitem_level;
+                _row.flstmtitem_type := _x.flstmtitem_type;
+                _row.flstmtitem_type_id := _x.flstmtitem_type_id;
+                _row.flstmtitem_parent_id := _x.flstmtitem_parent_id;
+                _row.flstmtitem_accnt_id := _x.flstmtitem_accnt_id;
+                _row.flstmtitem_name := _x.flstmtitem_name;
+                _row.flstmtitem_month := _x.flstmtitem_month;
+                _row.flstmtitem_monthdb := _x.flstmtitem_monthdb;
+                _row.flstmtitem_monthcr := _x.flstmtitem_monthcr;
+                _row.flstmtitem_monthprcnt := _x.flstmtitem_monthprcnt;
+                _row.flstmtitem_monthbudget := _x.flstmtitem_monthbudget;
+                _row.flstmtitem_monthbudgetprcnt := _x.flstmtitem_monthbudgetprcnt;
+                _row.flstmtitem_monthbudgetdiff := _x.flstmtitem_monthbudgetdiff;
+                _row.flstmtitem_monthbudgetdiffprcnt := _x.flstmtitem_monthbudgetdiffprcnt;
+                _row.flstmtitem_qtr := _x.flstmtitem_qtr;
+                _row.flstmtitem_qtrdb := _x.flstmtitem_qtrdb;
+                _row.flstmtitem_qtrcr := _x.flstmtitem_qtrcr;
+                _row.flstmtitem_qtrprcnt := _x.flstmtitem_qtrprcnt;
+                _row.flstmtitem_qtrbudget := _x.flstmtitem_qtrbudget;
+                _row.flstmtitem_qtrbudgetprcnt := _x.flstmtitem_qtrbudgetprcnt;
+                _row.flstmtitem_qtrbudgetdiff := _x.flstmtitem_qtrbudgetdiff;
+                _row.flstmtitem_qtrbudgetdiffprcnt := _x.flstmtitem_qtrbudgetdiffprcnt;
+                _row.flstmtitem_year := _x.flstmtitem_year;
+                _row.flstmtitem_yeardb := _x.flstmtitem_yeardb;
+                _row.flstmtitem_yearcr := _x.flstmtitem_yearcr;
+                _row.flstmtitem_yearprcnt := _x.flstmtitem_yearprcnt;
+                _row.flstmtitem_yearbudget := _x.flstmtitem_yearbudget;
+                _row.flstmtitem_yearbudgetprcnt := _x.flstmtitem_yearbudgetprcnt;
+                _row.flstmtitem_yearbudgetdiff := _x.flstmtitem_yearbudgetdiff;
+                _row.flstmtitem_yearbudgetdiffprcnt := _x.flstmtitem_yearbudgetdiffprcnt;
+                _row.flstmtitem_prmonth := _x.flstmtitem_prmonth;
+                _row.flstmtitem_prmonthprcnt := _x.flstmtitem_prmonthprcnt;
+                _row.flstmtitem_prmonthdiff := _x.flstmtitem_prmonthdiff;
+                _row.flstmtitem_prmonthdiffprcnt := _x.flstmtitem_prmonthdiffprcnt;
+                _row.flstmtitem_prqtr := _x.flstmtitem_prqtr;
+                _row.flstmtitem_prqtrprcnt := _x.flstmtitem_prqtrprcnt;
+                _row.flstmtitem_prqtrdiff := _x.flstmtitem_prqtrdiff;
+                _row.flstmtitem_prqtrdiffprcnt := _x.flstmtitem_prqtrdiffprcnt;
+                _row.flstmtitem_pryear := _x.flstmtitem_pryear;
+                _row.flstmtitem_pryearprcnt := _x.flstmtitem_pryearprcnt;
+                _row.flstmtitem_pryeardiff := _x.flstmtitem_pryeardiff;
+                _row.flstmtitem_pryeardiffprcnt := _x.flstmtitem_pryeardiffprcnt;
+
+        END LOOP;
+
+        _row.flstmtitem_subgrp := _subgrp + 1;
+        RETURN NEXT _row;
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION financialreport(INTEGER,_int4,bpchar,bool,INTEGER)
+  RETURNS SETOF fltrenditem AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pFlheadId ALIAS FOR $1;
+  pPeriodIds ALIAS FOR $2;
+  pInterval ALIAS FOR $3;
+  pShowNumbers ALIAS FOR $4;
+  pPrjid ALIAS FOR $5;
+  _row fltrenditem%ROWTYPE;
+  _type CHAR;
+  _p RECORD;
+  _count INTEGER;
+  _n NUMERIC;
+  _fld NUMERIC[];
+  _grndttl NUMERIC;
+  _i INTEGER;
+  _first BOOLEAN;
+  _prevlevel INTEGER;
+  _subgrp INTEGER;
+
+BEGIN
+        _first := true;
+        _subgrp := 0;
+
+        IF ARRAY_UPPER(pPeriodIds,1) <= 12 THEN
+                _count := ARRAY_UPPER(pPeriodIds,1);
+        ELSE
+                _count := 12;
+        END IF;
+
+        --Get Type
+        SELECT flhead_type FROM flhead INTO _type WHERE flhead_id=pFlheadId;
+
+        --Build Financial Data
+        FOR _i IN 1.._count
+        LOOP
+                PERFORM financialreport(pFlheadId,pPeriodIds[_i],pInterval,pPrjid);
+        END LOOP;
+
+        --Get Row Data
+        FOR _p IN
+        SELECT flrpt_flhead_id,
+                flrpt_username,
+                flrpt_order,
+                flrpt_level,
+                flrpt_type,
+                flrpt_type_id,
+                flrpt_parent_id,
+                flrpt_accnt_id,
+                formatindent(flgrp.flgrp_name,flrpt.flrpt_level) AS flrpt_name,
+                CASE
+                        WHEN (flgrp_summarize AND (_type IN ('I','C'))) THEN
+                                (COALESCE(flrpt_diff,0))
+                        WHEN (flgrp_summarize AND (_type = 'B')) THEN
+                                (COALESCE(flrpt_ending,0))
+                        ELSE NULL
+                END AS f_fld1,
+                flgrp_summarize AS display
+        FROM flrpt,flgrp
+        WHERE ((flrpt_flhead_id=pFlheadId)
+        AND (flgrp_id=flrpt_type_id)
+        AND (flrpt_type='G')
+        AND (flrpt_period_id=pPeriodIds[1])
+        AND (flrpt_interval=pInterval)
+        AND (flrpt_username=getEffectiveXtUser()))
+        UNION
+        SELECT flrpt_flhead_id,
+                flrpt_username,
+                flrpt_order,
+                flrpt_level,
+                flrpt_type,
+                flrpt_type_id,
+                flrpt_parent_id,
+                flrpt_accnt_id,
+                formatindent(accnt_descrip,flrpt.flrpt_level) AS flrpt_name,
+                CASE
+                        WHEN (_type IN ('I','C')) THEN
+                                (COALESCE(flrpt_diff,0))
+                        WHEN (_type = 'B') THEN
+                                (COALESCE(flrpt_ending,0))
+                        ELSE NULL
+                END AS f_fld1,
+                true AS display
+        FROM flrpt,flitem,accnt
+        WHERE ((flrpt_flhead_id=pFlheadId)
+        AND (flrpt_accnt_id=accnt_id)
+        AND (flitem_id=flrpt_type_id)
+        AND (flrpt_type='I')
+        AND (flrpt_period_id=pPeriodIds[1])
+        AND (flrpt_interval=pInterval)
+        AND (flrpt_username=getEffectiveXtUser()))
+        UNION
+        SELECT flrpt_flhead_id,
+                flrpt_username,
+                flrpt_order,
+                flrpt_level,
+                flrpt_type,
+                flrpt_type_id,
+                flrpt_parent_id,
+                flrpt_accnt_id,
+                CASE
+                        WHEN (flrpt.flrpt_type='T' AND flrpt.flrpt_level=0) THEN
+                                COALESCE(flrpt.flrpt_altname, 'Total')
+                        WHEN (flrpt.flrpt_type='T') THEN
+                                formatindent(COALESCE(flrpt.flrpt_altname, 'Subtotal') ,flrpt.flrpt_level) 
+
+                        ELSE
+                                formatindent(('Type ' || flrpt.flrpt_type || ' ' || text(flrpt.flrpt_type_id)), flrpt.flrpt_level)
+                END AS flstmtitem_name,
+                CASE
+                        WHEN (_type IN ('I','C')) THEN
+                                (COALESCE(flrpt_diff,0))
+                        WHEN (_type = 'B') THEN
+                                (COALESCE(flrpt_ending,0))
+                        ELSE NULL
+                END AS f_fld1,
+                true AS display
+        FROM flrpt
+        WHERE ((flrpt_flhead_id=pFlheadId)
+        AND (flrpt_type NOT IN ('I','S','G'))
+        AND (flrpt_period_id=pPeriodIds[1])
+        AND (flrpt_interval=pInterval)
+        AND (flrpt_username=getEffectiveXtUser()))
+        ORDER BY flrpt_order
+        LOOP
+
+                IF _type IN ('I','C') THEN
+                        _grndttl := _p.f_fld1;
+                END IF;
+
+                --Loop through and calculate period column values
+                IF (_p.display) THEN
+                        FOR _i IN 2.._count
+                        LOOP
+                                SELECT
+                                CASE
+                                        WHEN (_type IN ('I','C')) THEN
+                                                COALESCE(flrpt_diff,0)
+                                        WHEN (_type = 'B') THEN
+                                                COALESCE(flrpt_ending,0)
+                                        ELSE NULL
+                                END INTO _n
+                                FROM flrpt
+                                WHERE ((flrpt_flhead_id=pFlheadId)
+                                AND (flrpt_period_id=pPeriodIds[_i])
+                                AND (flrpt_interval=pInterval)
+                                AND (flrpt_username=getEffectiveXtUser())
+                                AND (flrpt_order=_p.flrpt_order));
+                                _fld[_i-1] := _n;
+                                IF _type IN ('I','C') THEN
+                                        _grndttl := _grndttl+_n;
+                                END IF;
+                        END LOOP;
+                END IF;
+
+                --Send it all back to the caller
+                IF _prevlevel > _p.flrpt_level THEN
+                        _subgrp := _subgrp+1;
+                END IF;
+                _prevlevel:=_p.flrpt_level;
+                _row.fltrenditem_subgrp := _subgrp;
+
+                IF NOT _first THEN
+                        RETURN NEXT _row;
+                END IF;
+
+                _first := FALSE;
+
+                _row.fltrenditem_flhead_id := _p.flrpt_flhead_id;
+                _row.fltrenditem_username := _p.flrpt_username;
+                _row.fltrenditem_order := _p.flrpt_order;
+                _row.fltrenditem_level := _p.flrpt_level;
+                _row.fltrenditem_type := _p.flrpt_type;
+                _row.fltrenditem_type_id := _p.flrpt_type_id;
+                _row.fltrenditem_parent_id := _p.flrpt_parent_id;
+                _row.fltrenditem_accnt_id := _p.flrpt_accnt_id;
+                _row.fltrenditem_name := _p.flrpt_name;
+                IF (_p.display) THEN
+                        _row.fltrenditem_fld1 := (_p.f_fld1);
+                        _row.fltrenditem_fld2 := (_fld[1]);
+                        _row.fltrenditem_fld3 := (_fld[2]);
+                        _row.fltrenditem_fld4 := (_fld[3]);
+                        _row.fltrenditem_fld5 := (_fld[4]);
+                        _row.fltrenditem_fld6 := (_fld[5]);
+                        _row.fltrenditem_fld7 := (_fld[6]);
+                        _row.fltrenditem_fld8 := (_fld[7]);
+                        _row.fltrenditem_fld9 := (_fld[8]);
+                        _row.fltrenditem_fld10 := (_fld[9]);
+                        _row.fltrenditem_fld11 := (_fld[10]);
+                        _row.fltrenditem_fld12 := (_fld[11]);
+                        _row.fltrenditem_grndttl := (_grndttl);
+                ELSE
+                        _row.fltrenditem_fld1 := NULL;
+                        _row.fltrenditem_fld2 := NULL;
+                        _row.fltrenditem_fld3 := NULL;
+                        _row.fltrenditem_fld4 := NULL;
+                        _row.fltrenditem_fld5 := NULL;
+                        _row.fltrenditem_fld6 := NULL;
+                        _row.fltrenditem_fld7 := NULL;
+                        _row.fltrenditem_fld8 := NULL;
+                        _row.fltrenditem_fld9 := NULL;
+                        _row.fltrenditem_fld10 := NULL;
+                        _row.fltrenditem_fld11 := NULL;
+                        _row.fltrenditem_fld12 := NULL;
+                        _row.fltrenditem_grndttl := NULL;
+                END IF;
+
+        END LOOP;
+
+        _row.fltrenditem_subgrp := _subgrp + 1;
+        RETURN NEXT _row;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/findapaccount.sql b/foundation-database/public/functions/findapaccount.sql
new file mode 100644 (file)
index 0000000..456cb73
--- /dev/null
@@ -0,0 +1,35 @@
+CREATE OR REPLACE FUNCTION findAPAccount(INTEGER) RETURNS INTEGER STABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pVendid ALIAS FOR $1;
+  _accntid INTEGER;
+
+BEGIN
+
+  IF (NOT fetchMetricBool('InterfaceAPToGL')) THEN
+    RETURN 0;
+  END IF;
+
+  SELECT apaccnt_ap_accnt_id INTO _accntid
+    FROM apaccnt
+    JOIN vendinfo ON (apaccnt_vendtype_id=vend_vendtype_id)
+  WHERE (vend_id=pVendid);
+  IF (FOUND) THEN
+    RETURN _accntid;
+  END IF;
+
+  SELECT apaccnt_ap_accnt_id INTO _accntid
+    FROM apaccnt
+    JOIN vendtype ON (vendtype_code ~ apaccnt_vendtype)
+    JOIN vendinfo ON (vend_vendtype_id=vendtype_id)
+  WHERE ((apaccnt_vendtype_id=-1)
+     AND (vend_id=pVendid));
+  IF (FOUND) THEN
+    RETURN _accntid;
+  END IF;
+
+  RETURN -1;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/findapdiscountaccount.sql b/foundation-database/public/functions/findapdiscountaccount.sql
new file mode 100644 (file)
index 0000000..c5320a2
--- /dev/null
@@ -0,0 +1,35 @@
+CREATE OR REPLACE FUNCTION findAPDiscountAccount(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pVendid ALIAS FOR $1;
+  _accntid INTEGER;
+
+BEGIN
+
+  IF (NOT fetchMetricBool('InterfaceAPToGL')) THEN
+    RETURN 0;
+  END IF;
+
+  SELECT apaccnt_discount_accnt_id INTO _accntid
+    FROM apaccnt
+    JOIN vendinfo ON (apaccnt_vendtype_id=vend_vendtype_id)
+  WHERE (vend_id=pVendid);
+  IF (FOUND) THEN
+    RETURN _accntid;
+  END IF;
+
+  SELECT apaccnt_discount_accnt_id INTO _accntid
+    FROM apaccnt
+    JOIN vendtype ON (vendtype_code ~ apaccnt_vendtype)
+    JOIN vendinfo ON (vend_vendtype_id=vendtype_id)
+  WHERE ((apaccnt_vendtype_id=-1)
+     AND (vend_id=pVendid));
+  IF (FOUND) THEN
+    RETURN _accntid;
+  END IF;
+
+  RETURN -1;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/findapprepaidaccount.sql b/foundation-database/public/functions/findapprepaidaccount.sql
new file mode 100644 (file)
index 0000000..12ee6d8
--- /dev/null
@@ -0,0 +1,37 @@
+CREATE OR REPLACE FUNCTION findAPPrepaidAccount(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pVendid ALIAS FOR $1;
+  _accntid INTEGER;
+
+BEGIN
+
+  IF (NOT fetchMetricBool('InterfaceAPToGL')) THEN
+    RETURN 0;
+  END IF;
+
+--  Check for a Vendor Type specific Account
+  SELECT apaccnt_prepaid_accnt_id INTO _accntid
+    FROM apaccnt
+    JOIN vendinfo ON (apaccnt_vendtype_id=vend_vendtype_id)
+  WHERE (vend_id=pVendid);
+  IF (FOUND) THEN
+    RETURN _accntid;
+  END IF;
+
+--  Check for a Vendor Type pattern
+  SELECT apaccnt_prepaid_accnt_id INTO _accntid
+    FROM apaccnt
+    JOIN vendtype ON (vendtype_code ~ apaccnt_vendtype)
+    JOIN vendinfo ON (vend_vendtype_id=vendtype_id)
+  WHERE ((apaccnt_vendtype_id=-1)
+     AND (vend_id=pVendid));
+  IF (FOUND) THEN
+    RETURN _accntid;
+  END IF;
+
+  RETURN -1;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/findaraccount.sql b/foundation-database/public/functions/findaraccount.sql
new file mode 100644 (file)
index 0000000..a2ae24c
--- /dev/null
@@ -0,0 +1,37 @@
+CREATE OR REPLACE FUNCTION findARAccount(INTEGER) RETURNS INTEGER STABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCustid ALIAS FOR $1;
+  _accntid INTEGER;
+
+BEGIN
+
+  IF (NOT fetchMetricBool('InterfaceARToGL')) THEN
+    RETURN 0;
+  END IF;
+
+--  Check for a Customer Type specific Account
+  SELECT araccnt_ar_accnt_id INTO _accntid
+  FROM araccnt, custinfo
+  WHERE ( (araccnt_custtype_id=cust_custtype_id)
+   AND (cust_id=pCustid) );
+  IF (FOUND) THEN
+    RETURN _accntid;
+  END IF;
+
+--  Check for a Customer Type pattern
+  SELECT araccnt_ar_accnt_id INTO _accntid
+  FROM araccnt, custinfo, custtype
+  WHERE ( (custtype_code ~ araccnt_custtype)
+   AND (araccnt_custtype_id=-1)
+   AND (cust_custtype_id=custtype_id)
+   AND (cust_id=pCustid) );
+  IF (FOUND) THEN
+    RETURN _accntid;
+  END IF;
+
+  RETURN -1;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/findardiscountaccount.sql b/foundation-database/public/functions/findardiscountaccount.sql
new file mode 100644 (file)
index 0000000..8b788c0
--- /dev/null
@@ -0,0 +1,37 @@
+CREATE OR REPLACE FUNCTION findardiscountaccount(integer) RETURNS integer AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCustid ALIAS FOR $1;
+  _accntid INTEGER;
+
+BEGIN
+
+  IF (NOT fetchMetricBool('InterfaceARToGL')) THEN
+    RETURN 0;
+  END IF;
+
+--  Check for a Customer Type specific Account
+  SELECT araccnt_discount_accnt_id INTO _accntid
+  FROM araccnt, custinfo
+  WHERE ( (araccnt_custtype_id=cust_custtype_id)
+   AND (cust_id=pCustid) );
+  IF (FOUND) THEN
+    RETURN _accntid;
+  END IF;
+
+--  Check for a Customer Type pattern
+  SELECT araccnt_discount_accnt_id INTO _accntid
+  FROM araccnt, custinfo, custtype
+  WHERE ( (custtype_code ~ araccnt_custtype)
+   AND (cust_custtype_id=custtype_id)
+   AND (araccnt_custtype_id=-1)
+   AND (cust_id=pCustid) );
+  IF (FOUND) THEN
+    RETURN _accntid;
+  END IF;
+
+  RETURN -1;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/findcalendarorigin.sql b/foundation-database/public/functions/findcalendarorigin.sql
new file mode 100644 (file)
index 0000000..21314aa
--- /dev/null
@@ -0,0 +1,54 @@
+
+CREATE OR REPLACE FUNCTION findCalendarOrigin(INTEGER) RETURNS DATE AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCalheadid ALIAS FOR $1;
+  _originType CHAR(1);
+  _origin DATE;
+
+BEGIN
+
+  SELECT calhead_origin INTO _originType
+  FROM calhead
+  WHERE (calhead_id=pCalheadid);
+
+  IF (NOT FOUND) THEN
+    _origin := NULL;
+
+  ELSIF (_originType = ''D'') THEN
+    _origin := CURRENT_DATE;
+
+  ELSIF (_originType = ''E'') THEN
+    _origin := (CURRENT_DATE + 1);
+
+  ELSIF (_originType = ''W'') THEN
+    _origin := (CURRENT_DATE - EXTRACT(DOW FROM CURRENT_DATE)::INTEGER);
+
+  ELSIF (_originType = ''X'') THEN
+    _origin := ((CURRENT_DATE - EXTRACT(DOW FROM CURRENT_DATE)::INTEGER) + INTERVAL ''1 week'');
+
+  ELSIF (_originType = ''M'') THEN
+    _origin := date_trunc(''month'', CURRENT_DATE);
+
+  ELSIF (_originType = ''N'') THEN
+    _origin := (date_trunc(''month'', CURRENT_DATE) + INTERVAL ''1 month'');
+
+  ELSIF (_originType = ''L'') THEN
+    _origin := (date_trunc(''year'', CURRENT_DATE) - INTERVAL ''1 year'');
+
+  ELSIF (_originType = ''Y'') THEN
+    _origin := date_trunc(''year'', CURRENT_DATE);
+
+  ELSIF (_originType = ''Z'') THEN
+    _origin := (date_trunc(''year'', CURRENT_DATE) + INTERVAL ''1 year'');
+
+  ELSE
+    _origin := NULL;
+  END IF;
+
+  RETURN _origin;
+
+  END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/findcustomerform.sql b/foundation-database/public/functions/findcustomerform.sql
new file mode 100644 (file)
index 0000000..0c0b3e9
--- /dev/null
@@ -0,0 +1,73 @@
+CREATE OR REPLACE FUNCTION findCustomerForm(INTEGER, CHARACTER(1)) RETURNS TEXT AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCustid ALIAS FOR $1;
+  pFormtype ALIAS FOR $2;
+  _f RECORD;
+  _found BOOLEAN;
+
+BEGIN
+
+--  Check for a Customer Type specific Form
+  SELECT custform.* INTO _f
+    FROM custform
+    JOIN custinfo ON (custform_custtype_id=cust_custtype_id)
+  WHERE (cust_id=pCustid);
+
+  IF (FOUND) THEN
+    _found := TRUE;
+  ELSE
+--  Check for a Customer Type pattern
+    SELECT custform.* INTO _f
+      FROM custform
+      JOIN custtype ON (custtype_code ~ custform_custtype)
+      JOIN custinfo ON (cust_custtype_id=custtype_id)
+    WHERE ((custform_custtype_id=-1)
+       AND (cust_id=pCustid));
+
+    IF (FOUND) THEN
+      _found := TRUE;
+    ELSE
+      _found := FALSE;
+    END IF;
+  END IF;
+
+  IF (_found) THEN
+    IF ( (pFormType = 'I') AND (_f.custform_invoice_report_name IS NOT NULL) ) THEN
+      RETURN _f.custform_invoice_report_name;
+
+    ELSIF ( (pFormType = 'C') AND (_f.custform_creditmemo_report_name IS NOT NULL) ) THEN
+      RETURN _f.custform_creditmemo_report_name;
+
+    ELSIF ( (pFormType = 'S') AND (_f.custform_statement_report_name IS NOT NULL) ) THEN
+      RETURN _f.custform_statement_report_name;
+
+    ELSIF ( (pFormType = 'Q') AND (_f.custform_quote_report_name IS NOT NULL) ) THEN
+      RETURN _f.custform_quote_report_name;
+
+    ELSIF ( (pFormType = 'P') AND (_f.custform_packinglist_report_name IS NOT NULL) ) THEN
+      RETURN _f.custform_packinglist_report_name;
+
+    ELSIF ( (pFormType = 'L') AND (_f.custform_sopicklist_report_name IS NOT NULL) ) THEN
+      RETURN _f.custform_sopicklist_report_name;
+    END IF;
+
+  END IF;
+
+  IF (pFormType = 'I') THEN
+    RETURN 'Invoice';
+  ELSIF (pFormType = 'C') THEN
+    RETURN 'CreditMemo';
+  ELSIF (pFormType = 'S') THEN
+    RETURN 'Statement';
+  ELSIF (pFormType = 'Q') THEN
+    RETURN 'Quote';
+  ELSIF (pFormType = 'P') THEN
+    RETURN 'PackingList-Shipment';
+  ELSIF (pFormType = 'L') THEN
+    RETURN 'PackingList';
+  END IF;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/finddeferredaccount.sql b/foundation-database/public/functions/finddeferredaccount.sql
new file mode 100644 (file)
index 0000000..d0baf89
--- /dev/null
@@ -0,0 +1,39 @@
+
+CREATE OR REPLACE FUNCTION findDeferredAccount(INTEGER) RETURNS INTEGER STABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCustid ALIAS FOR $1;
+  _accntid INTEGER;
+
+BEGIN
+
+  IF (NOT fetchMetricBool('InterfaceARToGL')) THEN
+    RETURN 0;
+  END IF;
+
+--  Check for a Customer Type specific Account
+  SELECT araccnt_deferred_accnt_id INTO _accntid
+    FROM araccnt
+    JOIN custinfo ON (araccnt_custtype_id=cust_custtype_id)
+  WHERE (cust_id=pCustid);
+  IF (FOUND) THEN
+    RETURN _accntid;
+  END IF;
+
+--  Check for a Customer Type pattern
+  SELECT araccnt_deferred_accnt_id INTO _accntid
+    FROM araccnt
+    JOIN custtype ON (custtype_code ~ araccnt_custtype)
+    JOIN custinfo ON (cust_custtype_id=custtype_id)
+  WHERE ((araccnt_custtype_id=-1)
+     AND (cust_id=pCustid));
+  IF (FOUND) THEN
+    RETURN _accntid;
+  END IF;
+
+  RETURN -1;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/findfreightaccount.sql b/foundation-database/public/functions/findfreightaccount.sql
new file mode 100644 (file)
index 0000000..24e9e7c
--- /dev/null
@@ -0,0 +1,45 @@
+CREATE OR REPLACE FUNCTION findFreightAccount(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCustid ALIAS FOR $1;
+  _accntid INTEGER;
+
+BEGIN
+
+  IF (NOT fetchMetricBool('InterfaceARToGL')) THEN
+    RETURN 0;
+  END IF;
+
+--  Check for a Customer Type specific Account
+  SELECT araccnt_freight_accnt_id INTO _accntid
+  FROM araccnt, custinfo
+  WHERE ( (araccnt_custtype_id=cust_custtype_id)
+   AND (cust_id=pCustid) );
+  IF (FOUND) THEN
+    RETURN _accntid;
+  END IF;
+
+--  Check for a Customer Type pattern
+  SELECT araccnt_freight_accnt_id INTO _accntid
+  FROM araccnt, custinfo, custtype
+  WHERE ( (custtype_code ~ araccnt_custtype)
+   AND (cust_custtype_id=custtype_id)
+   AND (araccnt_custtype_id=-1)
+   AND (cust_id=pCustid) );
+  IF (FOUND) THEN
+    RETURN _accntid;
+  END IF;
+
+--  Find the default
+  SELECT metric_value::INTEGER INTO _accntid
+  FROM metric
+  WHERE (metric_name='FreightAccount');
+  IF (FOUND) THEN
+    RETURN _accntid;
+  END IF;
+
+  RETURN -1;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/findperiodend.sql b/foundation-database/public/functions/findperiodend.sql
new file mode 100644 (file)
index 0000000..fce5fe5
--- /dev/null
@@ -0,0 +1,100 @@
+
+CREATE OR REPLACE FUNCTION findPeriodEnd(INTEGER) RETURNS DATE STABLE AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCalitemid ALIAS FOR $1;
+  _calType CHAR(1);
+  _calItem RECORD;
+  _start DATE;
+  _loop INTEGER;
+
+BEGIN
+
+  SELECT calhead_type INTO _calType
+  FROM calhead, acalitem
+  WHERE ((acalitem_calhead_id=calhead_id)
+   AND (acalitem_id=pCalitemid));
+
+  IF (NOT FOUND) THEN
+    SELECT calhead_type INTO _calType
+    FROM calhead, rcalitem
+    WHERE ((rcalitem_calhead_id=calhead_id)
+     AND (rcalitem_id=pCalitemid));
+
+    IF (NOT FOUND) THEN
+      RETURN NULL;
+    END IF;
+  END IF;
+
+  IF (_calType = ''A'') THEN
+    RETURN ( SELECT (findPeriodStart(acalitem_id) + acalitem_periodlength - 1)
+             FROM acalitem
+             WHERE (acalitem_id=pCalitemid) );
+
+  ELSIF (_calType = ''R'') THEN
+
+--  Grab the relative calitem''s particulars
+    SELECT rcalitem_periodtype, rcalitem_periodcount INTO _calitem
+    FROM rcalitem
+    WHERE (rcalitem_id=pCalitemid);
+
+    IF (NOT FOUND) THEN
+      RETURN NULL;
+    END If;
+
+--  Grab the origin of the calitem
+    SELECT findPeriodStart(pCalitemid) INTO _start;
+
+    IF (_start IS NULL) THEN
+
+--  If days...
+    ELSIF (_calitem.rcalitem_periodtype = ''D'') THEN
+      _start := (_start + _calitem.rcalitem_periodcount - 1);
+
+--  If weeks... (gotta be a better way)
+    ELSIF (_calitem.rcalitem_periodtype = ''W'') THEN
+      _loop := _calitem.rcalitem_periodcount;
+
+      WHILE (_loop > 0) LOOP
+        _start := (_start + INTERVAL ''1 week'');
+        _loop := (_loop - 1);
+      END LOOP;
+
+      _start := (_start - 1);
+
+--  If months... (gotta be a better way)
+    ELSIF (_calitem.rcalitem_periodtype = ''M'') THEN
+      _loop := _calitem.rcalitem_periodcount;
+
+      WHILE (_loop > 0) LOOP
+        _start := (_start + INTERVAL ''1 month'');
+        _loop := (_loop - 1);
+      END LOOP;
+
+      _start := (_start - 1);
+
+--  If years... (gotta be a better way)
+    ELSIF (_calitem.rcalitem_periodtype = ''Y'') THEN
+      _loop := _calitem.rcalitem_periodcount;
+
+      WHILE (_loop > 0) LOOP
+        _start := (_start + INTERVAL ''1 year'');
+        _loop := (_loop - 1);
+      END LOOP;
+
+      _start := (_start - 1);
+
+    ELSE
+      _start := NULL;
+    END IF;
+
+  ELSE
+    _start := NULL;
+  END IF;
+
+  RETURN _start;
+
+END;
+'LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/findperiodstart.sql b/foundation-database/public/functions/findperiodstart.sql
new file mode 100644 (file)
index 0000000..7b592b4
--- /dev/null
@@ -0,0 +1,104 @@
+
+CREATE OR REPLACE FUNCTION findPeriodStart(INTEGER) RETURNS DATE STABLE AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCalitemid ALIAS FOR $1;
+  _calType CHAR(1);
+  _calItem RECORD;
+  _start DATE;
+  _loop INTEGER;
+
+BEGIN
+
+  SELECT calhead_type INTO _calType
+  FROM calhead, acalitem
+  WHERE ((acalitem_calhead_id=calhead_id)
+   AND (acalitem_id=pCalitemid));
+
+  IF (NOT FOUND) THEN
+    SELECT calhead_type INTO _calType
+    FROM calhead, rcalitem
+    WHERE ((rcalitem_calhead_id=calhead_id)
+     AND (rcalitem_id=pCalitemid));
+
+    IF (NOT FOUND) THEN
+      RETURN NULL;
+    END IF;
+  END IF;
+
+  IF (_calType = ''A'') THEN
+    RETURN ( SELECT acalitem_periodstart
+             FROM acalitem
+             WHERE (acalitem_id=pCalitemid) );
+
+  ELSIF (_calType = ''R'') THEN
+
+--  Grab the relative calitem''s particulars
+    SELECT rcalitem_offsettype, rcalitem_offsetcount INTO _calitem
+    FROM rcalitem
+    WHERE (rcalitem_id=pCalitemid);
+
+    IF (NOT FOUND) THEN
+      RETURN NULL;
+    END If;
+
+--  Grab the origin of the calitem''s parend calhead
+    SELECT findCalendarOrigin(calhead_id) INTO _start
+    FROM calhead, rcalitem
+    WHERE ((rcalitem_calhead_id=calhead_id)
+     AND (rcalitem_id=pCalitemid));
+
+--  If days...
+    IF (_calitem.rcalitem_offsettype = ''D'') THEN
+      _start := (_start + _calitem.rcalitem_offsetcount);
+
+--  If weeks...
+    ELSIF (_calitem.rcalitem_offsettype = ''W'') THEN
+      _start := (_start + (_calitem.rcalitem_offsetcount * 7));
+
+--  If months... (gotta be a better way)
+    ELSIF (_calitem.rcalitem_offsettype = ''M'') THEN
+      _loop := _calitem.rcalitem_offsetcount;
+
+      IF (_loop > 0) THEN
+        WHILE (_loop > 0) LOOP
+          _start := (_start + INTERVAL ''1 month'');
+          _loop := _loop - 1;
+        END LOOP;
+      ELSE
+        WHILE (_loop < 0) LOOP
+          _start := (_start - INTERVAL ''1 month'');
+          _loop := _loop + 1;
+        END LOOP;
+      END IF;
+
+--  If years... (gotta be a better way)
+    ELSIF (_calitem.rcalitem_offsettype = ''Y'') THEN
+      _loop := _calitem.rcalitem_offsetcount;
+
+      IF (_loop > 0) THEN
+        WHILE (_loop > 0) LOOP
+          _start := (_start + INTERVAL ''1 year'');
+          _loop := _loop - 1;
+        END LOOP;
+      ELSE
+        WHILE (_loop < 0) LOOP
+          _start := (_start - INTERVAL ''1 year'');
+          _loop := _loop + 1;
+        END LOOP;
+      END IF;
+
+    ELSE
+      _start := NULL;
+    END IF;
+
+  ELSE
+    _start := NULL;
+  END IF;
+
+  RETURN _start;
+
+END;
+'LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/findprepaidaccount.sql b/foundation-database/public/functions/findprepaidaccount.sql
new file mode 100644 (file)
index 0000000..26033ee
--- /dev/null
@@ -0,0 +1,39 @@
+
+CREATE OR REPLACE FUNCTION findPrepaidAccount(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCustid ALIAS FOR $1;
+  _accntid INTEGER;
+
+BEGIN
+
+  IF (NOT fetchMetricBool('InterfaceARToGL')) THEN
+    RETURN 0;
+  END IF;
+
+--  Check for a Customer Type specific Account
+  SELECT araccnt_prepaid_accnt_id INTO _accntid
+    FROM araccnt
+    JOIN custinfo ON (araccnt_custtype_id=cust_custtype_id)
+  WHERE (cust_id=pCustid);
+  IF (FOUND) THEN
+    RETURN _accntid;
+  END IF;
+
+--  Check for a Customer Type pattern
+  SELECT araccnt_prepaid_accnt_id INTO _accntid
+    FROM araccnt
+    JOIN custtype ON (custtype_code ~ araccnt_custtype)
+    JOIN custinfo ON (cust_custtype_id=custtype_id)
+  WHERE ((araccnt_custtype_id=-1)
+     AND (cust_id=pCustid) );
+  IF (FOUND) THEN
+    RETURN _accntid;
+  END IF;
+
+  RETURN -1;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/findsalesaccnt.sql b/foundation-database/public/functions/findsalesaccnt.sql
new file mode 100644 (file)
index 0000000..65a37a4
--- /dev/null
@@ -0,0 +1,109 @@
+CREATE OR REPLACE FUNCTION findSalesAccnt(INTEGER, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN findSalesAccnt($1, 'IS', $2, NULL, NULL);
+END;
+$$ LANGUAGE plpgsql;
+
+CREATE OR REPLACE FUNCTION findSalesAccnt(INTEGER, TEXT, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN findSalesAccnt($1, $2, $3, NULL, NULL);
+END;
+$$ LANGUAGE plpgsql;
+
+CREATE OR REPLACE FUNCTION findSalesAccnt(pid INTEGER,
+                                          pidType TEXT,
+                                          pCustid INTEGER,
+                                          pSaletypeid INTEGER,
+                                          pShipzoneid INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _s RECORD;
+
+BEGIN
+
+  IF (pidType = 'I') THEN
+    --  Check for a custtype specific rule
+    SELECT salesaccnt_id,
+           CASE WHEN (salesaccnt_warehous_id<>-1) THEN 1 ELSE 0 END +
+           CASE WHEN (salesaccnt_custtype_id<>-1) THEN 2 ELSE 0 END +
+           CASE WHEN (salesaccnt_prodcat_id<>-1) THEN 3 ELSE 0 END +
+           CASE WHEN (salesaccnt_shipzone_id<>-1) THEN 4 ELSE 0 END +
+           CASE WHEN (salesaccnt_saletype_id<>-1) THEN 5 ELSE 0 END AS orderby
+    INTO _s
+    FROM salesaccnt, item, prodcat, custinfo, custtype
+    WHERE ( (salesaccnt_warehous_id=-1)
+      AND  (item_prodcat_id=prodcat_id)
+      AND  (cust_custtype_id=custtype_id)
+      AND  ( (salesaccnt_prodcat='.*') OR
+           ( (salesaccnt_prodcat_id=-1) AND
+             (salesaccnt_prodcat<>'') AND
+             (prodcat_code ~ salesaccnt_prodcat) ) OR
+           ( (salesaccnt_prodcat_id=prodcat_id) ) )
+      AND  ( (salesaccnt_custtype='.*') OR
+           ( (salesaccnt_custtype_id=-1) AND
+             (salesaccnt_custtype<>'') AND
+             (custtype_code ~ salesaccnt_custtype) ) OR
+           ( (salesaccnt_custtype_id=custtype_id) ) )
+      AND  ( (salesaccnt_shipzone_id=-1) OR
+            (salesaccnt_shipzone_id=pShipzoneid) )
+      AND  ( (salesaccnt_saletype_id=-1) OR
+            (salesaccnt_saletype_id=pSaletypeid) )
+      AND (item_id=pid)
+      AND (cust_id=pCustid) )
+    ORDER BY orderby DESC, salesaccnt_custtype DESC, salesaccnt_prodcat DESC,
+             salesaccnt_saletype_id DESC, salesaccnt_shipzone_id DESC
+     LIMIT 1;
+
+  ELSIF (pidType = 'IS') THEN
+    --  Check for a custtype specific rule
+    SELECT salesaccnt_id,
+           CASE WHEN (salesaccnt_warehous_id<>-1) THEN 1 ELSE 0 END +
+           CASE WHEN (salesaccnt_custtype_id<>-1) THEN 2 ELSE 0 END +
+           CASE WHEN (salesaccnt_prodcat_id<>-1) THEN 3 ELSE 0 END +
+           CASE WHEN (salesaccnt_shipzone_id<>-1) THEN 4 ELSE 0 END +
+           CASE WHEN (salesaccnt_saletype_id<>-1) THEN 5 ELSE 0 END AS orderby
+    INTO _s
+    FROM salesaccnt, itemsite, item, prodcat, custinfo, custtype
+    WHERE ( ( (salesaccnt_warehous_id=-1) OR
+             (salesaccnt_warehous_id=itemsite_warehous_id) )
+     AND (itemsite_item_id=item_id)
+     AND (item_prodcat_id=prodcat_id)
+     AND (cust_custtype_id=custtype_id)
+     AND ( (salesaccnt_prodcat='.*') OR
+          ( (salesaccnt_prodcat_id=-1) AND
+            (salesaccnt_prodcat<>'') AND
+            (prodcat_code ~ salesaccnt_prodcat) ) OR
+          ( (salesaccnt_prodcat_id=prodcat_id) ) )
+     AND ( (salesaccnt_custtype='.*') OR
+          ( (salesaccnt_custtype_id=-1) AND
+            (salesaccnt_custtype<>'') AND
+            (custtype_code ~ salesaccnt_custtype) ) OR
+          ( (salesaccnt_custtype_id=custtype_id) ) )
+     AND  ( (salesaccnt_shipzone_id=-1) OR
+            (salesaccnt_shipzone_id=pShipzoneid) )
+     AND  ( (salesaccnt_saletype_id=-1) OR
+            (salesaccnt_saletype_id=pSaletypeid) )
+     AND (itemsite_id=pid)
+     AND (cust_id=pCustid) ) 
+    ORDER BY orderby DESC, salesaccnt_custtype DESC, salesaccnt_prodcat DESC, salesaccnt_warehous_id DESC,
+             salesaccnt_saletype_id DESC, salesaccnt_shipzone_id DESC
+    LIMIT 1;
+
+  ELSE
+    RETURN -2; -- invalid pidType
+  END IF;
+
+  IF (FOUND) THEN
+    RETURN _s.salesaccnt_id;
+  END IF;
+
+  RETURN -1;
+
+END;
+$$
+    LANGUAGE plpgsql;
diff --git a/foundation-database/public/functions/findspecialfinancial.sql b/foundation-database/public/functions/findspecialfinancial.sql
new file mode 100644 (file)
index 0000000..7ad797d
--- /dev/null
@@ -0,0 +1,184 @@
+
+CREATE OR REPLACE FUNCTION findSpecialFinancial(TEXT, TEXT, INTEGER) RETURNS NUMERIC AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pUnit ALIAS FOR $1;
+  pType ALIAS FOR $2;
+  pPeriodid ALIAS FOR $3;
+
+  _value NUMERIC;
+BEGIN
+
+  _value := 0.00;
+
+  IF (''OpenAR'' = pType) THEN
+    IF ( pUnit IN (''D'',''E'') ) THEN
+      SELECT SUM( CASE WHEN (aropen_doctype IN (''C'', ''R'')) THEN ((aropen_amount - aropen_paid) * -1)
+                       ELSE (aropen_amount - aropen_paid) END ) INTO _value
+        FROM aropen, period
+       WHERE ((aropen_open)
+         AND  (aropen_duedate BETWEEN period_start AND period_end)
+         AND  (period_id=pPeriodid));
+
+      IF (''E'' = pUnit) THEN
+        _value := 0.00 - _value;
+      END IF;
+    END IF;
+  END IF;
+
+  IF (''OpenAP'' = pType) THEN
+    IF ( pUnit IN (''C'',''E'') ) THEN
+      SELECT SUM( CASE WHEN (apopen_doctype=''C'') THEN ((apopen_amount - apopen_paid) * -1)
+                       ELSE (apopen_amount - apopen_paid) END ) INTO _value
+        FROM apopen, period
+       WHERE ((apopen_open)
+         AND  (apopen_duedate BETWEEN period_start AND period_end)
+         AND  (period_id=pPeriodid));
+    END IF;
+  END IF;
+
+  RETURN _value;
+
+END;
+' LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION copyFinancialLayout(INTEGER, TEXT) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSourceFlheadid ALIAS FOR $1;
+  pDestName ALIAS FOR $2;
+
+  _flheadid INTEGER;
+  _tblName TEXT;
+
+BEGIN
+
+-- Check for the flhead to be copy that it exists
+  PERFORM flhead_id
+     FROM flhead
+    WHERE (flhead_id=pSourceFlheadid);
+  IF (NOT FOUND) THEN
+    RETURN -1;
+  END IF;
+
+-- Check that the name is valid
+  IF (pDestName IS NULL OR pDestName = '''') THEN
+    RETURN -2;
+  END IF;
+
+-- Check for the name to copy to does not exist
+  PERFORM flhead_id
+     FROM flhead
+    WHERE (flhead_name=pDestName);
+  IF (FOUND) THEN
+    RETURN -3;
+  END IF;
+
+-- Copy the flhead record
+  SELECT nextval(''flhead_flhead_id_seq'') INTO _flheadid;
+  INSERT INTO flhead
+         (flhead_id, flhead_name, flhead_descrip,
+          flhead_showtotal, flhead_showstart,
+          flhead_showend, flhead_showdelta, flhead_showbudget,
+          flhead_showdiff, flhead_showcustom,
+          flhead_custom_label,
+          flhead_usealttotal, flhead_alttotal,
+          flhead_usealtbegin, flhead_altbegin,
+          flhead_usealtend, flhead_altend,
+          flhead_usealtdebits, flhead_altdebits,
+          flhead_usealtcredits, flhead_altcredits,
+          flhead_usealtbudget, flhead_altbudget,
+          flhead_usealtdiff, flhead_altdiff,
+          flhead_type, flhead_active, flhead_sys
+)
+  SELECT _flheadid, pDestName, flhead_descrip,
+         flhead_showtotal, flhead_showstart,
+         flhead_showend, flhead_showdelta, flhead_showbudget,
+         flhead_showdiff, flhead_showcustom,
+         flhead_custom_label,
+         flhead_usealttotal, flhead_alttotal,
+         flhead_usealtbegin, flhead_altbegin,
+         flhead_usealtend, flhead_altend,
+         flhead_usealtdebits, flhead_altdebits,
+         flhead_usealtcredits, flhead_altcredits,
+         flhead_usealtbudget, flhead_altbudget,
+         flhead_usealtdiff, flhead_altdiff,
+         flhead_type, flhead_active, false
+    FROM flhead
+   WHERE (flhead_id=pSourceFlheadid);
+
+-- Create temporary table so old and new group ids can be stored
+ SELECT relname FROM pg_class INTO _tblName
+ WHERE relname = ''tmp_flgrpxref'';
+ IF (_tblName IS NULL) THEN
+  EXECUTE ''CREATE TEMPORARY TABLE tmp_flgrpxref'' || getEffectiveXtUser() || '' 
+  (
+       flgrpxref_oldid int4,
+       flgrpxref_newid int4
+  ) ON COMMIT DROP;'';
+  END IF;
+
+-- Copy the top level groups
+  PERFORM copyFinancialGroup(flgrp_id, _flheadid, -1)
+     FROM flgrp
+    WHERE ((flgrp_flhead_id=pSourceFlheadid)
+      AND  (flgrp_flgrp_id=-1));
+
+-- Update Group Percent settings
+  EXECUTE ''UPDATE flgrp
+  SET flgrp_prcnt_flgrp_id=flgrpxref_newid
+  FROM tmp_flgrpxref'' || getEffectiveXtUser() || '' 
+  WHERE ((flgrp_flhead_id='' || _flheadid || '')
+  AND (flgrp_prcnt_flgrp_id=flgrpxref_oldid));'';
+
+  EXECUTE ''UPDATE flitem
+  SET flitem_prcnt_flgrp_id=flgrpxref_newid
+  FROM tmp_flgrpxref'' || getEffectiveXtUser() || '' 
+  WHERE ((flitem_flhead_id='' || _flheadid || '')
+  AND (flitem_prcnt_flgrp_id=flgrpxref_oldid));'';
+
+  EXECUTE ''UPDATE flspec
+  SET flspec_prcnt_flgrp_id=flgrpxref_newid
+  FROM tmp_flgrpxref'' || getEffectiveXtUser() || '' 
+  WHERE ((flspec_flhead_id='' || _flheadid || '')
+  AND (flspec_prcnt_flgrp_id=flgrpxref_oldid));'';
+
+-- Copy Column Layounts
+  INSERT INTO flcol
+        (flcol_flhead_id,
+        flcol_name,
+        flcol_descrip,
+        flcol_report_id,
+        flcol_month,
+        flcol_quarter,
+        flcol_year,
+        flcol_showdb,
+        flcol_prcnt,
+        flcol_priortype,
+        flcol_priormonth,
+        flcol_priorquarter,
+        flcol_prioryear,
+        flcol_priorprcnt,
+        flcol_priordiff,
+        flcol_priordiffprcnt,
+        flcol_budget,
+        flcol_budgetprcnt,
+        flcol_budgetdiff,
+        flcol_budgetdiffprcnt
+)
+SELECT
+        _flheadid,flcol_name,flcol_descrip,
+        flcol_report_id,flcol_month,flcol_quarter,
+        flcol_year,flcol_showdb,flcol_prcnt,
+        flcol_priortype,flcol_priormonth,flcol_priorquarter,
+        flcol_prioryear,flcol_priorprcnt,flcol_priordiff,
+        flcol_priordiffprcnt,flcol_budget,flcol_budgetprcnt,
+        flcol_budgetdiff,flcol_budgetdiffprcnt
+FROM flcol
+WHERE (flcol_flhead_id=pSourceFlheadid);
+
+  RETURN _flheadid;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/first_agg.sql b/foundation-database/public/functions/first_agg.sql
new file mode 100644 (file)
index 0000000..d5724be
--- /dev/null
@@ -0,0 +1,15 @@
+
+-- Create a function that always returns the first non-NULL item
+CREATE OR REPLACE FUNCTION public.first_agg ( anyelement, anyelement )
+RETURNS anyelement AS $$
+  SELECT CASE WHEN $1 IS NULL THEN $2 ELSE $1 END;
+$$ LANGUAGE SQL STABLE;
+
+-- And then wrap an aggreagate around it
+DROP AGGREGATE public.first (anyelement);
+CREATE AGGREGATE public.first (
+        sfunc    = public.first_agg,
+        basetype = anyelement,
+        stype    = anyelement
+);
+
diff --git a/foundation-database/public/functions/firstline.sql b/foundation-database/public/functions/firstline.sql
new file mode 100644 (file)
index 0000000..268b3fe
--- /dev/null
@@ -0,0 +1,14 @@
+
+CREATE OR REPLACE FUNCTION firstLine(TEXT) RETURNS TEXT IMMUTABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSource ALIAS FOR $1;
+  _result TEXT := '';
+
+BEGIN
+  SELECT regexp_replace(pSource, E'^(\r?\n)*([^\r\n]*)\r?\n.*', E'\\2') INTO _result;
+  RETURN _result;
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/fixacl.sql b/foundation-database/public/functions/fixacl.sql
new file mode 100644 (file)
index 0000000..4b10459
--- /dev/null
@@ -0,0 +1,62 @@
+CREATE OR REPLACE FUNCTION fixACL() RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _r        RECORD;
+  _count    INTEGER := 0;
+  _oldgrp   BOOLEAN := false;
+  _objtype  TEXT;
+  _table    TEXT;
+  _schema   TEXT;
+
+BEGIN
+  IF EXISTS(SELECT 1 FROM pg_group WHERE groname = 'openmfg') THEN
+    _oldgrp := true;
+  END IF;
+  
+  FOR _r IN SELECT relname, nspname, relkind,
+                   CASE relkind WHEN 'r' THEN 1
+                                WHEN 'v' THEN 2
+                                WHEN 'S' THEN 3
+                                ELSE 4
+                   END AS seq
+            FROM pg_catalog.pg_class c, pg_namespace n
+            WHERE ((n.oid=c.relnamespace)
+              AND  (nspname in ('public', 'api') OR
+                    nspname in (SELECT pkghead_name FROM pkghead))
+              AND  (relkind in ('S', 'r', 'v')))
+            ORDER BY seq
+  LOOP
+    _schema := quote_ident(_r.nspname);
+    _table  := quote_ident(_r.relname);
+
+    RAISE DEBUG '%.%', _schema, _table;
+    
+    IF (_oldgrp) THEN
+      EXECUTE 'REVOKE ALL ON ' || _schema || '.' || _table || ' FROM openmfg;';
+    END IF;
+    EXECUTE 'REVOKE ALL ON ' || _schema || '.' || _table || ' FROM PUBLIC;';
+    EXECUTE 'GRANT ALL ON '  || _schema || '.' || _table || ' TO GROUP xtrole;';
+    _count := _count + 1;
+
+    _objtype := CASE _r.relkind WHEN 'S' THEN 'SEQUENCE'
+                                WHEN 'r' THEN 'TABLE'
+                                WHEN 'v' THEN 'VIEW'
+                                ELSE NULL
+                END;
+    IF (_objtype IS NOT NULL) THEN
+      BEGIN
+        EXECUTE 'ALTER ' || _objtype || ' ' ||
+                _schema || '.' || _table || ' OWNER TO admin;';
+      EXCEPTION WHEN OTHERS THEN
+        RAISE WARNING 'Could not change ownership of %.% to admin',
+                      _schema, _table;
+      END;
+    END IF;
+
+  END LOOP;
+
+  RETURN _count;
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/formatabachecks.sql b/foundation-database/public/functions/formatabachecks.sql
new file mode 100644 (file)
index 0000000..fc9f930
--- /dev/null
@@ -0,0 +1,260 @@
+-- Function: formatabachecks(integer, integer, text)
+
+CREATE OR REPLACE FUNCTION formatabachecks(integer, integer, text)
+  RETURNS SETOF achline AS
+$BODY$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pbankaccntid     ALIAS FOR $1;   -- all unprinted checks for this bankaccnt
+  pcheckheadid     ALIAS FOR $2;   -- but if 2nd arg not null then just 1 check
+  penckey          ALIAS FOR $3;
+  _bank            RECORD;
+  _batchcount      INTEGER := 0;
+  _batchdate       DATE;
+  _check           RECORD;
+  _vendnumber      TEXT;
+  _vendname        TEXT;
+  _filenum         TEXT;
+  _prevsec         TEXT;
+  _row             achline%ROWTYPE;
+  _totalcr         NUMERIC := 0;
+  _totaldb         NUMERIC := 0;
+  _detailcount     INTEGER := 0;     -- count of type 1 entries
+  _vendbsb         TEXT;
+
+BEGIN
+  -- General notes:
+  -- Numeric values are formatted using RPAD(TO_CHAR(#, '0..0SG', #)).
+  --    TO_CHAR(#, ...) (at least in the default server locale) puts a space at
+  --    the beginning of the string for numbers >= 0 and '-' for numbers < 0.
+  --    'SG' pushes the sign char to the end, then RPAD cuts it off.
+  -- This whole thing is for Australian bank transactions only, and generates entries for an ABA file.
+  -- Currently restricted to checks to Vendor; there's no support for checks to
+  --    customers or tax authorities, or for debits or corrections.
+  -- This function has been adapted from the US-centric ACH formatACHChecks function.
+
+  IF (NOT fetchMetricBool('ACHEnabled')) THEN
+    RAISE EXCEPTION 'Cannot format the ABA file because the system is not configured for ABA file generation.';
+  END IF;
+  IF (LENGTH(COALESCE(penckey, '')) <= 0) THEN
+    RAISE EXCEPTION 'Cannot format the ABA file because there is no encryption key.';
+  END IF;
+
+  SELECT * INTO _bank
+  FROM bankaccnt
+  WHERE (bankaccnt_id=pbankaccntid);
+
+  IF (NOT FOUND) THEN
+    RAISE EXCEPTION 'Could not find the bank information to create the ABA file.';
+  ELSIF (NOT _bank.bankaccnt_ach_enabled) THEN
+    RAISE EXCEPTION 'Cannot format the ABA file because the Bank Account % is not configured for ABA transactions.',
+      _bank.bankaccnt_name;
+  ELSIF (LENGTH(COALESCE(_bank.bankaccnt_routing, '')) <= 0) THEN 
+    RAISE EXCEPTION 'Cannot format the ABA file because the Bank Account % has no BSB number.', _bank.bankaccnt_name;
+  END IF;
+
+  -- Check the BSB number is in the right format and then re-format for output.
+  -- Valid format is \d{3}-\d{3}|\d{6}000
+  IF (_bank.bankaccnt_routing ~ E'^(\\d{3})(?:-(?=\\d{3}$)|(?=\\d{3}0{3}$))(\\d{3})(0{3})?$') THEN
+    _bank.bankaccnt_routing := regexp_replace(
+      _bank.bankaccnt_routing,
+      E'^(\\d{3})(?:-(?=\\d{3}$)|(?=\\d{3}0{3}$))(\\d{3})(0{3})?$', E'\\1-\\2'
+    );
+  ELSE RAISE EXCEPTION 'Cannot format the ABA file because the Bank Account % has an invalid BSB number.',
+    _bank.bankaccnt_name;
+  END IF;
+
+
+  _filenum := LPAD(fetchNextNumber('ACHBatch'), 8, '0');
+
+  IF (COALESCE(_bank.bankaccnt_ach_lastdate,startOfTime()) < CURRENT_DATE
+    OR _bank.bankaccnt_ach_lastfileid IS NULL) THEN
+    _bank.bankaccnt_ach_lastfileid = '0';
+  ELSIF (_bank.bankaccnt_ach_lastfileid = '9') THEN
+    _bank.bankaccnt_ach_lastfileid = 'A';
+  ELSIF (_bank.bankaccnt_ach_lastfileid = 'Z') THEN
+    RAISE EXCEPTION 'Cannot write % check % to an ABA file because too many files have been written for this bank already today.',
+      _bank.bankaccnt_name, _check.checkhead_number;
+  ELSE
+    _bank.bankaccnt_ach_lastfileid = CHR(ASCII(_bank.bankaccnt_ach_lastfileid) + 1);
+  END IF;
+  
+
+  _row.achline_checkhead_id := NULL;
+  _row.achline_batch := _filenum;
+  _row.achline_type := 'HEADER';
+  _row.achline_value := RPAD(
+    RPAD('0',18)                                    -- Record Type 0 blank filled with 17 spaces
+    || '01'                                         -- Reel sequence number 
+    || RPAD(_bank.bankaccnt_bankname,3)             -- Approved financial instition abbreviation.
+    || RPAD('',7)                                   -- blank filled
+    || RPAD(fetchMetricText('ACHCompanyName'), 26)  -- Name of user supplying ABA file
+    || LPAD(fetchMetricText('ACHCompanyId'),6)      -- User identification number APCA issued
+    || RPAD('PAYMENT',12)                           -- description of entries on file
+                                                    --  currently only use payment description
+    || TO_CHAR(CURRENT_DATE,      'DDMMYY'),        -- date to be processed
+    120                                             -- blank filled to 120 characters
+  );
+  RETURN NEXT _row;
+
+  FOR _check IN SELECT *
+    FROM checkhead
+    JOIN vendinfo ON (checkhead_recip_type='V'
+      AND checkhead_recip_id=vend_id
+      AND vend_ach_enabled)
+    JOIN curr_symbol ON (checkhead_curr_id=curr_id)
+    LEFT OUTER JOIN crmacct ON (crmacct_vend_id=vend_id)
+    WHERE ((checkhead_bankaccnt_id=pbankaccntid)
+      AND (checkhead_amount > 0)
+      AND (checkhead_id=pcheckheadid OR pcheckheadid IS NULL)
+      AND NOT checkhead_posted
+      AND NOT checkhead_replaced
+      AND NOT checkhead_deleted
+      AND NOT checkhead_void
+      AND NOT checkhead_printed
+      AND (LENGTH(COALESCE(checkhead_ach_batch,'')) <= 0)
+      AND (curr_abbr='AUD'))
+    ORDER BY checkhead_checkdate, vend_name LOOP
+
+    IF (COALESCE(_check.checkhead_number, -1) <= 0
+      AND _bank.bankaccnt_ach_genchecknum) THEN
+        _check.checkhead_number := fetchNextCheckNumber(_check.checkhead_bankaccnt_id);
+    END IF;
+
+    -- Although a crmacct record is not required for used in this function
+    -- this code is retained for consistancy with the original formatachchecks function.
+    IF (_check.crmacct_id IS NULL) THEN
+      RAISE NOTICE 'Vendor % does not have a corresponding crmacct record.',
+        _check.checkhead_recip_id;
+    ELSIF (_check.crmacct_type IS NULL) THEN
+      RAISE NOTICE 'crmacct for vendor % does not have a valid crmacct_type.',
+         _check.checkhead_recip_id;
+    END IF;
+
+    _vendnumber := CASE WHEN _check.vend_ach_use_vendinfo THEN _check.vend_number
+      ELSE _check.vend_ach_indiv_number
+      END;
+    _vendname := CASE WHEN _check.vend_ach_use_vendinfo THEN _check.vend_name
+      ELSE _check.vend_ach_indiv_name
+      END;
+
+    IF (COALESCE(_check.vend_ach_routingnumber, '') = '') THEN
+      RAISE EXCEPTION 'Cannot write % check % to an ABA file because the BSB number for % has not been supplied.',
+        _bank.bankaccnt_name, _check.checkhead_number, _vendnumber;
+    ELSIF (COALESCE(_check.vend_ach_accntnumber, '') = '') THEN
+      RAISE EXCEPTION 'Cannot write % check % to an ABA file because the account number for % has not been supplied.',
+        _bank.bankaccnt_name, _check.checkhead_number, _vendnumber;
+    END IF;
+    _check.vend_ach_routingnumber := decrypt(setbytea(_check.vend_ach_routingnumber),
+      setbytea(penckey), 'bf');
+    _check.vend_ach_accntnumber   := decrypt(setbytea(_check.vend_ach_accntnumber),
+      setbytea(penckey), 'bf');
+    
+    -- Check the BSB number is in the right format and then re-format for output.
+    -- Valid format is \d{3}-\d{3}|\d{6}000
+    IF (formatbytea(_check.vend_ach_routingnumber) ~ E'^(\\d{3})(?:-(?=\\d{3}$)|(?=\\d{3}0{3}$))(\\d{3})(0{3})?$') THEN
+      _vendbsb := regexp_replace(
+        formatbytea(_check.vend_ach_routingnumber),
+        E'^(\\d{3})(?:-(?=\\d{3}$)|(?=\\d{3}0{3}$))(\\d{3})(0{3})?$', E'\\1-\\2'
+      );
+    ELSE RAISE EXCEPTION 'Cannot write % check % to an ABA file because the BSB number for % is not valid.',
+      _bank.bankaccnt_name, _check.checkhead_number, _vendnumber;
+    END IF;
+
+    _row.achline_checkhead_id := _check.checkhead_id;
+    _row.achline_batch        := _filenum;
+    _row.achline_type         := 'DETAIL';
+
+    _totaldb      := _totaldb + _check.checkhead_amount;                -- Total debits for balancing entry
+    _detailcount  := _detailcount + 1;                                  -- Detail record counter (type 1)
+    _totalcr      := _totalcr + _check.checkhead_amount;                -- Total credits from payments
+                                                                        
+    _row.achline_value := RPAD('1'                                      -- record type 1
+      || _vendbsb                                                       -- vendor BSB #
+      || LPAD(formatbytea(_check.vend_ach_accntnumber), 9)              -- vendor account no.
+      ||' '                                                             -- withholding tax indicator
+      ||'50'                                                            -- transaction code, this should be calculated.
+      || LPAD(to_char(_check.checkhead_amount,'FM99999999V99'),10,'0')  -- amount
+      || RPAD(_vendname,   32)                                          -- vendor name
+      || RPAD('Deposit',8) || RPAD('#'  , 2) || LPAD (_filenum,8,' ')   -- Lodgement Reference
+      || _bank.bankaccnt_routing                                        -- BSB #
+      || RPAD(_bank.bankaccnt_accntnumber, 9)                           -- company account number
+      || RPAD(fetchMetricText('ACHCompanyName'), 16)                    -- company account name
+      || LPAD('', 8, '0'),                                              -- Witholding Tax Amount
+      120                                                               -- line width
+    );
+    RETURN NEXT _row;
+
+    UPDATE checkhead
+    SET checkhead_ach_batch=_filenum,
+      checkhead_number=_check.checkhead_number
+    WHERE (checkhead_id=_check.checkhead_id);
+
+  END LOOP;
+
+  IF (NOT FOUND) THEN
+    RAISE EXCEPTION 'Cannot write an ABA file for % because there are no checks pending in AUD for EFT-enabled Vendors.',
+      _bank.bankaccnt_name;
+  END IF;
+
+  -- Place a final balancing detail record.
+  -- Check that the balancing record actually balances.
+  IF (_totalcr != _totaldb) THEN
+    RAISE EXCEPTION 'Cannot write an ABA file for % because the total credits: % does not equal the total debits: %, file will not balance.',
+    _bank.bankaccnt_name, _totalcr, _totaldb;
+  END IF;
+  
+
+  _detailcount := _detailcount + 1;
+  _row.achline_checkhead_id := NULL;
+  _row.achline_batch := _filenum;
+  _row.achline_type := 'BALANCING';
+  -- keep in sync with the other batchcontrol record format above
+  -- THE FOLLOWING IS THE DEBIT BALANCING RECORD
+  _row.achline_value := RPAD('1'                          -- record type 1
+    || _bank.bankaccnt_routing                            -- Austalian BSB #
+    || LPAD(_bank.bankaccnt_accntnumber, 9)               -- company account no.
+    || ' '                                                -- withholding tax indicator
+    || '13'                                               -- transaction code
+    || to_char(_totaldb,'FM09999999V99')                  -- the balancing amount
+    || RPAD(fetchMetricText('ACHCompanyName'),   32)      -- company name
+    || RPAD('DIRECT DEPOSIT',18)                          
+    || _bank.bankaccnt_routing                            -- Austalian BSB #
+    || RPAD(_bank.bankaccnt_accntnumber, 9)               -- company account number
+    || RPAD(fetchMetricText('ACHCompanyName'), 16)        -- company account name
+    || LPAD('', 8, '0'),                                  -- Witholding Tax Amount
+    120                                                   -- line width
+  );
+  RETURN NEXT _row;
+
+  RAISE DEBUG 'formatABAChecks building TRAILER with _totaldb %, _totalcr %, _detailcount %',
+               _totaldb, _totalcr, _detailcount;
+  -- file control record
+  _row.achline_checkhead_id := NULL;
+  _row.achline_batch := _filenum;
+  _row.achline_type := 'TRAILER';
+  _row.achline_value := RPAD('7'                                    -- record type 7
+    || RPAD('999-999',   7)                                         -- BSB format filler
+    || RPAD('' , 12)                                                -- blank
+    || LPAD(to_char((_totaldb - _totalcr),'FM09999999V99'),10,'0')  -- net total amount
+    || LPAD(to_char(_totalcr, 'FM09999999V99'),10,'0')              -- total credit amount
+    || LPAD(to_char(_totaldb, 'FM09999999V99'),10,'0')              -- total debit amount
+    || RPAD('', 24)                                                 -- blank
+    || RPAD(to_char(_detailcount, 'FM000000'), 6,'0'),              -- count of type 1 records
+    120                                                             -- blank fill
+  );
+
+  RETURN NEXT _row;
+
+
+  UPDATE bankaccnt
+  SET bankaccnt_ach_lastdate=CURRENT_DATE,
+    bankaccnt_ach_lastfileid=_bank.bankaccnt_ach_lastfileid
+  WHERE (bankaccnt_id=_bank.bankaccnt_id);
+
+  RETURN;
+
+END;
+$BODY$
+  LANGUAGE 'plpgsql' VOLATILE;
diff --git a/foundation-database/public/functions/formatachchecks.sql b/foundation-database/public/functions/formatachchecks.sql
new file mode 100644 (file)
index 0000000..672ec46
--- /dev/null
@@ -0,0 +1,358 @@
+CREATE OR REPLACE FUNCTION formatACHChecks(INTEGER, INTEGER, TEXT) RETURNS SETOF achline AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pbankaccntid     ALIAS FOR $1;   -- all unprinted checks for this bankaccnt
+  pcheckheadid     ALIAS FOR $2;   -- but if 2nd arg not null then just 1 check
+  penckey          ALIAS FOR $3;
+  _bank            RECORD;
+  _batchcount      INTEGER := 0;
+  _batchcr         NUMERIC := 0;
+  _batchdate       DATE;
+  _batchdb         NUMERIC := 0;
+  _batchhash       INTEGER := 0;
+  _check           RECORD;
+  _ccdnumber       TEXT;
+  _ccdname         TEXT;
+  _entrycount      INTEGER := 0;
+  _filenum         TEXT;
+  _prevsec         TEXT;
+  _row             achline%ROWTYPE;
+  _rowcount        INTEGER := 0;
+  _sec             TEXT;
+  _serviceclass    TEXT := '200';    -- 220 = credits, 225 = debits, 200 = mixed
+  _totalcr         NUMERIC := 0;
+  _totaldb         NUMERIC := 0;
+  _totalentrycnt   INTEGER := 0;
+  _totalhash       INTEGER := 0;
+  _transactionprefix TEXT;
+
+BEGIN
+  -- General notes:
+  -- Numeric values are formatted using RPAD(TO_CHAR(#, '0..0SG', #)).
+  --    TO_CHAR(#, ...) (at least in the default server locale) puts a space at
+  --    the beginning of the string for numbers >= 0 and '-' for numbers < 0.
+  --    'SG' pushes the sign char to the end, then RPAD cuts it off.
+  -- This whole thing is US-centric, as that's where the NACHA is.
+  -- Currently restricted to checks to Vendor; there's no support for checks to
+  --    customers or tax authorities, or for debits or corrections.
+
+  IF (NOT fetchMetricBool('ACHEnabled')) THEN
+    RAISE EXCEPTION 'Cannot format the ACH file because the system is not configured for ACH file generation.';
+  END IF;
+  IF (LENGTH(COALESCE(penckey, '')) <= 0) THEN
+    RAISE EXCEPTION 'Cannot format the ACH file because there is no encryption key.';
+  END IF;
+
+  SELECT * INTO _bank
+  FROM bankaccnt
+  WHERE (bankaccnt_id=pbankaccntid);
+
+  IF (NOT FOUND) THEN
+    RAISE EXCEPTION 'Could not find the bank information to create the ACH file.';
+  ELSIF (NOT _bank.bankaccnt_ach_enabled) THEN
+    RAISE EXCEPTION 'Cannot format the ACH file because the Bank Account % is not configured for ACH transactions.',
+      _bank.bankaccnt_name;
+  ELSIF (LENGTH(COALESCE(_bank.bankaccnt_routing, '')) <= 0) THEN 
+    RAISE EXCEPTION 'Cannot format the ACH file because the Bank Account % has no routing number.',
+      _bank.bankaccnt_name;
+  END IF;
+
+  _filenum := LPAD(fetchNextNumber('ACHBatch'), 8, '0');
+
+  IF (COALESCE(_bank.bankaccnt_ach_lastdate,startOfTime()) < CURRENT_DATE
+      OR _bank.bankaccnt_ach_lastfileid IS NULL) THEN
+    _bank.bankaccnt_ach_lastfileid = '0';
+  ELSIF (_bank.bankaccnt_ach_lastfileid = '9') THEN
+    _bank.bankaccnt_ach_lastfileid = 'A';
+  ELSIF (_bank.bankaccnt_ach_lastfileid = 'Z') THEN
+    RAISE EXCEPTION 'Cannot write % check % to an ACH file because too many files have been written for this bank already today.',
+                  _bank.bankaccnt_name, _check.checkhead_number;
+  ELSE
+    _bank.bankaccnt_ach_lastfileid = CHR(ASCII(_bank.bankaccnt_ach_lastfileid) + 1);
+  END IF;
+
+  _rowcount := _rowcount + 1;
+  _row.achline_checkhead_id := NULL;
+  _row.achline_batch := _filenum;
+  _row.achline_type := 'FILEHEADER';
+  _row.achline_value := RPAD('1'
+                          || '01'
+                          || RPAD(CASE WHEN _bank.bankaccnt_ach_desttype = 'B' THEN ' ' || _bank.bankaccnt_routing
+                                       WHEN _bank.bankaccnt_ach_desttype = 'F' THEN ' ' || _bank.bankaccnt_ach_fed_dest
+                                       ELSE _bank.bankaccnt_ach_dest END, 10)
+                          || RPAD(CASE WHEN _bank.bankaccnt_ach_origintype = 'B' THEN ' ' || _bank.bankaccnt_routing
+                                       WHEN _bank.bankaccnt_ach_origintype = 'I' THEN formatAchCompanyId()
+                                       ELSE _bank.bankaccnt_ach_origin END, 10)
+                          || TO_CHAR(CURRENT_DATE,      'YYMMDD')
+                          || TO_CHAR(CURRENT_TIMESTAMP, 'HH24MM')
+                          || UPPER(_bank.bankaccnt_ach_lastfileid)
+                          || '094'
+                          || '10'
+                          || '1'
+                          || RPAD(CASE WHEN _bank.bankaccnt_ach_desttype = 'B' THEN _bank.bankaccnt_bankname
+                                       WHEN _bank.bankaccnt_ach_desttype = 'F' THEN 'Federal Reserve'
+                                       ELSE _bank.bankaccnt_ach_destname END, 23)
+                          || RPAD(CASE WHEN _bank.bankaccnt_ach_origintype = 'B' THEN ' ' || _bank.bankaccnt_bankname
+                                       WHEN _bank.bankaccnt_ach_origintype = 'I' THEN fetchMetricText('ACHCompanyName')
+                                       ELSE _bank.bankaccnt_ach_originname END, 23)
+                          || RPAD(_filenum, 8),
+                          94);
+  RETURN NEXT _row;
+
+  FOR _check IN SELECT *
+                FROM checkhead
+                JOIN vendinfo ON (checkhead_recip_type='V'
+                              AND checkhead_recip_id=vend_id
+                              AND vend_ach_enabled)
+                JOIN curr_symbol ON (checkhead_curr_id=curr_id)
+                LEFT OUTER JOIN crmacct ON (crmacct_vend_id=vend_id)
+                WHERE ((checkhead_bankaccnt_id=pbankaccntid)
+                   AND (checkhead_amount > 0)
+                   AND (checkhead_id=pcheckheadid OR pcheckheadid IS NULL)
+                   AND NOT checkhead_posted
+                   AND NOT checkhead_replaced
+                   AND NOT checkhead_deleted
+                   AND NOT checkhead_void
+                   AND NOT checkhead_printed
+                   AND (LENGTH(COALESCE(checkhead_ach_batch,'')) <= 0)
+                   AND (curr_abbr='USD'))
+                ORDER BY checkhead_checkdate, vend_name LOOP
+
+    IF (COALESCE(_check.checkhead_number, -1) <= 0
+        AND _bank.bankaccnt_ach_genchecknum) THEN
+      _check.checkhead_number := fetchNextCheckNumber(_check.checkhead_bankaccnt_id);
+    END IF;
+
+    _prevsec := _sec;
+
+    IF (_check.crmacct_type = 'I') THEN
+      _sec := 'PPD';
+    ELSE
+      _sec := 'CCD';
+      IF (_check.crmacct_id IS NULL) THEN
+        RAISE NOTICE 'Vendor % does not have a corresponding crmacct record.',
+                     _check.checkhead_recip_id;
+      ELSIF (_check.crmacct_type IS NULL) THEN
+        RAISE NOTICE 'crmacct for vendor % does not have a valid crmacct_type.',
+                     _check.checkhead_recip_id;
+      END IF;
+    END IF;
+
+    _ccdnumber := CASE WHEN _check.vend_ach_use_vendinfo THEN _check.vend_number
+                       ELSE _check.vend_ach_indiv_number
+                  END;
+    _ccdname := CASE WHEN _check.vend_ach_use_vendinfo THEN _check.vend_name
+                     ELSE _check.vend_ach_indiv_name
+                END;
+
+    IF (COALESCE(_check.vend_ach_routingnumber, '') = '') THEN
+      RAISE EXCEPTION 'Cannot write % check % to an ACH file because the routing number for % has not been supplied.',
+                  _bank.bankaccnt_name, _check.checkhead_number, _ccdnumber;
+    ELSIF (COALESCE(_check.vend_ach_accntnumber, '') = '') THEN
+      RAISE EXCEPTION 'Cannot write % check % to an ACH file because the account number for % has not been supplied.',
+                  _bank.bankaccnt_name, _check.checkhead_number, _ccdnumber;
+    END IF;
+    _check.vend_ach_routingnumber := decrypt(setbytea(_check.vend_ach_routingnumber),
+                                         setbytea(penckey), 'bf');
+    _check.vend_ach_accntnumber   := decrypt(setbytea(_check.vend_ach_accntnumber),
+                                         setbytea(penckey), 'bf');
+    _transactionprefix := CASE WHEN (_check.vend_ach_accnttype = 'K') THEN '2'
+                               WHEN (_check.vend_ach_accnttype = 'C') THEN '3'
+                          END;
+
+    -- create separate batches for each check date and for PPD vs CCD
+    IF (COALESCE(_batchdate, startOfTime()) != _check.checkhead_checkdate
+        OR (_prevsec != _sec)) THEN
+      IF (_batchcount > 0) THEN
+        _rowcount := _rowcount + 1;
+        _row.achline_checkhead_id := NULL;
+        _row.achline_batch := _filenum;
+        _row.achline_type := 'BATCHCONTROL';
+        -- keep in sync with the other batchcontrol record format below
+        _row.achline_value := RPAD('8'
+                                || _serviceclass
+                                || RPAD(TO_CHAR(_entrycount, '000000SG'), 6)
+                                || RPAD(TO_CHAR(_batchhash % 10000000000,
+                                                '0000000000SG'), 10)
+                                || RPAD(TO_CHAR(_batchdb, '0000000000V99SG'), 12)
+                                || RPAD(TO_CHAR(_batchcr, '0000000000V99SG'), 12)
+                                || RPAD(formatAchCompanyId(), 10)
+                                || RPAD(' ', 19)
+                                || RPAD(' ',  6)
+                                || RPAD(_bank.bankaccnt_routing, 8)
+                                || RPAD(TO_CHAR(_batchcount, '0000000SG'), 7),
+                                94);
+        RETURN NEXT _row;
+      END IF;
+
+      _batchhash     := 0;
+      _batchcr       := 0;
+      _batchdb       := 0;
+      _batchdate     := _check.checkhead_checkdate;
+      _entrycount    := 0;
+      _rowcount      := _rowcount + 1;
+      _batchcount    := _batchcount + 1;
+      _row.achline_checkhead_id := NULL;
+      _row.achline_batch := _filenum;
+      _row.achline_type := 'BATCHHEADER';
+
+      -- effective entry date = 1 or 2 banking days after the banking day
+      -- of processing (the following accounts for weekends but not holidays)
+
+      _row.achline_value := RPAD('5'
+                              || _serviceclass
+                              || RPAD(fetchMetricText('ACHCompanyName'), 16)
+                              || RPAD('', 20)   -- TODO: find a use
+                              || RPAD(formatAchCompanyId(), 10)
+                              || _sec
+                              || RPAD('xTuple ERP', 10)
+                              || TO_CHAR(_check.checkhead_checkdate, 'YYMMDD')
+                              || TO_CHAR(CURRENT_DATE +
+                                         COALESCE(_bank.bankaccnt_ach_leadtime,1) +
+                                         CASE WHEN EXTRACT(DOW FROM CURRENT_DATE) = 5 THEN 2
+                                              WHEN EXTRACT(DOW FROM CURRENT_DATE) = 6 THEN 1
+                                              ELSE 0 END,
+                                         'YYMMDD')
+                              || RPAD('', 3)
+                              || '1'
+                              || RPAD(_bank.bankaccnt_routing, 8)
+                              || RPAD(TO_CHAR(_batchcount, '0000000SG'), 7),
+                              94);
+      RETURN NEXT _row;
+    END IF;
+
+    _row.achline_checkhead_id := _check.checkhead_id;
+    _row.achline_batch        := _filenum;
+    _row.achline_type         := _sec;
+
+    IF (_sec = 'CCD' OR _sec = 'PPD') THEN
+      _rowcount      := _rowcount + 1;
+      _entrycount    := _entrycount + 1;
+      _totalentrycnt := _totalentrycnt + 1;
+      _batchhash     := _batchhash + CAST(SUBSTRING(_bank.bankaccnt_routing FOR 8) AS INTEGER);
+      _totalhash     := _totalhash + CAST(SUBSTRING(_bank.bankaccnt_routing FOR 8) AS INTEGER);
+      _batchdb       := _batchdb + _check.checkhead_amount;
+      _totaldb       := _totaldb + _check.checkhead_amount;
+
+      _row.achline_value := RPAD('6'
+                              || _transactionprefix || '7'              -- debit
+                              || RPAD(_bank.bankaccnt_routing,      9)  -- 2 fields
+                              || RPAD(_bank.bankaccnt_accntnumber, 17)
+                              || RPAD(TO_CHAR(_check.checkhead_amount,
+                                              '00000000V99SG'), 10)
+                              || RPAD(fetchMetricText('ACHCompanyId'),   15)
+                              || RPAD(fetchMetricText('ACHCompanyName'), 22)
+                              || RPAD(TO_CHAR(_check.checkhead_id % 100, '00SG'),
+                                      2)        -- last 2 digits of checkhead_id
+                              || '0'
+                              || RPAD(_bank.bankaccnt_routing, 9)  -- split field
+                              || RPAD(TO_CHAR(_entrycount, '000000SG'), 15-9),
+                              94);
+      RETURN NEXT _row;
+
+      _rowcount      := _rowcount + 1;
+      _entrycount    := _entrycount + 1;
+      _totalentrycnt := _totalentrycnt + 1;
+      _batchhash     := _batchhash + CAST(SUBSTRING(formatbytea(_check.vend_ach_routingnumber) FOR 8) AS INTEGER);
+      _totalhash     := _totalhash + CAST(SUBSTRING(formatbytea(_check.vend_ach_routingnumber) FOR 8) AS INTEGER);
+      _totalcr       := _totalcr + _check.checkhead_amount;
+      _batchcr       := _batchcr + _check.checkhead_amount;
+      _row.achline_value := RPAD('6'
+                              || _transactionprefix || '2'              -- credit
+                              || RPAD(formatbytea(_check.vend_ach_routingnumber), 9)   -- 2 fields
+                              || RPAD(formatbytea(_check.vend_ach_accntnumber), 17)
+                              || RPAD(TO_CHAR(_check.checkhead_amount,
+                                              '00000000V99SG'), 10)
+                              || RPAD(_ccdnumber, 15)
+                              || RPAD(_ccdname,   22)
+                              || RPAD(TO_CHAR(_check.checkhead_id % 100, '00SG'),
+                                      2)        -- last 2 digits of checkhead_id
+                              || '0'
+                              || RPAD(_bank.bankaccnt_routing, 9)  -- split field
+                              || RPAD(TO_CHAR(_entrycount, '000000SG'), 15-9),
+                              94);
+      RETURN NEXT _row;
+
+    ELSE
+      RAISE EXCEPTION 'Cannot write % check % to an ACH file because % is not a supported SEC code.',
+                    _bank.bankaccnt_name, _check.checkhead_number, _sec;
+    END IF;
+
+    UPDATE checkhead
+    SET checkhead_ach_batch=_filenum,
+        checkhead_number=_check.checkhead_number
+    WHERE (checkhead_id=_check.checkhead_id);
+
+  END LOOP;
+
+  IF (NOT FOUND) THEN
+    RAISE EXCEPTION 'Cannot write an ACH file for % because there are no checks pending in USD for ACH-enabled Vendors.',
+                    _bank.bankaccnt_name;
+  END IF;
+
+  -- place a final batch control record
+  IF (_batchcount > 0) THEN
+    _rowcount := _rowcount + 1;
+    _row.achline_checkhead_id := NULL;
+    _row.achline_batch := _filenum;
+    _row.achline_type := 'BATCHCONTROL';
+    -- keep in sync with the other batchcontrol record format above
+    _row.achline_value := RPAD('8'
+                            || _serviceclass
+                            || RPAD(TO_CHAR(_entrycount, '000000SG'), 6)
+                            || RPAD(TO_CHAR(_batchhash % 10000000000,
+                                            '0000000000SG'), 10)
+                            || RPAD(TO_CHAR(_batchdb, '0000000000V99SG'), 12)
+                            || RPAD(TO_CHAR(_batchcr, '0000000000V99SG'), 12)
+                            || RPAD(formatAchCompanyId(), 10)
+                            || RPAD(' ', 19)
+                            || RPAD(' ',  6)
+                            || RPAD(_bank.bankaccnt_routing, 8)
+                            || RPAD(TO_CHAR(_batchcount, '0000000SG'), 7),
+                            94);
+    RETURN NEXT _row;
+  END IF;
+
+  -- and end with a file control record
+  _rowcount := _rowcount + 1;
+  _row.achline_checkhead_id := NULL;
+  _row.achline_batch := _filenum;
+  _row.achline_type := 'FILECONTROL';
+  _row.achline_value := RPAD('9'
+                          || RPAD(TO_CHAR(_batchcount,    '000000SG'),   6)
+                          || RPAD(TO_CHAR(_rowcount,      '000000SG'),   6)
+                          || RPAD(TO_CHAR(_totalentrycnt, '00000000SG'), 8)
+                          || RPAD(TO_CHAR(_totalhash % 10000000000,
+                                          '0000000000SG'), 10)
+                          || RPAD(TO_CHAR(_totaldb, '0000000000V99SG'), 12)
+                          || RPAD(TO_CHAR(_totalcr, '0000000000V99SG'), 12)
+                          || RPAD('', 39),
+                          94);
+
+  RETURN NEXT _row;
+
+  -- file must be a multiple of 10 lines long
+  _row.achline_checkhead_id := NULL;
+  _row.achline_batch := _filenum;
+  _row.achline_type := 'BLOCKFILL';
+  WHILE (_rowcount % 10 > 0) LOOP
+    _rowcount := _rowcount + 1;
+    _row.achline_value := RPAD('99999999999999999999'
+                            || '99999999999999999999'
+                            || '99999999999999999999'
+                            || '99999999999999999999'
+                            || '99999999999999999999', 94);
+    RETURN NEXT _row;
+  END LOOP;
+
+  UPDATE bankaccnt
+  SET bankaccnt_ach_lastdate=CURRENT_DATE,
+      bankaccnt_ach_lastfileid=_bank.bankaccnt_ach_lastfileid
+  WHERE (bankaccnt_id=_bank.bankaccnt_id);
+
+  RETURN;
+
+END;
+$$
+LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/formatachcompanyid.sql b/foundation-database/public/functions/formatachcompanyid.sql
new file mode 100644 (file)
index 0000000..b88ee9f
--- /dev/null
@@ -0,0 +1,16 @@
+CREATE OR REPLACE FUNCTION formatACHCompanyId() RETURNS TEXT AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN CASE WHEN fetchMetricText('ACHCompanyIdType') = 'D' THEN '3'
+              WHEN fetchMetricText('ACHCompanyIdType') = 'E' THEN '1'
+              WHEN fetchMetricText('ACHCompanyIdType') = 'O' THEN '9'
+         END ||
+         CASE WHEN fetchMetricText('ACHCompanyIdType') = 'D' OR
+                   fetchMetricText('ACHCompanyIdType') = 'E' THEN
+                 REPLACE(fetchMetricText('ACHCompanyId'), '-', '')
+              ELSE fetchMetricText('ACHCompanyId')
+         END;
+END;
+$$
+LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/formataddr.sql b/foundation-database/public/functions/formataddr.sql
new file mode 100644 (file)
index 0000000..1ae19a2
--- /dev/null
@@ -0,0 +1,145 @@
+
+CREATE OR REPLACE FUNCTION formatAddr(INTEGER) RETURNS TEXT AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pAddrId       ALIAS FOR $1;
+  _return       TEXT;
+
+BEGIN
+  -- US conventions
+  SELECT formatAddr(addr_line1, addr_line2, addr_line3,
+                    addr_city || ', ' || addr_state || ' ' || addr_postalcode,
+                    addr_country) INTO _return
+  FROM addr
+  WHERE (addr_id=pAddrId);
+
+  RETURN _return;
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION formatAddr(TEXT, TEXT, TEXT, TEXT, INTEGER) RETURNS TEXT AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  f_addr1 ALIAS FOR $1;
+  f_addr2 ALIAS FOR $2;
+  f_addr3 ALIAS FOR $3;
+  csz     ALIAS FOR $4;
+  line    ALIAS FOR $5;
+
+BEGIN
+  RETURN formatAddr(f_addr1, f_addr2, f_addr3, csz, '', line);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION formatAddr(TEXT, TEXT, TEXT, TEXT, TEXT, INTEGER) RETURNS TEXT AS $$ 
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  f_addr1 ALIAS FOR $1;
+  f_addr2 ALIAS FOR $2;
+  f_addr3 ALIAS FOR $3;
+  csz     ALIAS FOR $4;
+  country ALIAS FOR $5;
+  line    ALIAS FOR $6;
+
+  i int:=0;
+
+BEGIN
+
+  IF (LENGTH(TRIM(both from f_addr1)) > 0) THEN
+    i:=i+1;
+  END IF;
+
+  IF (i=line) THEN
+    RETURN f_addr1;
+  END IF;
+
+  IF (LENGTH(TRIM(both from f_addr2)) > 0)  THEN
+    i:=i+1;
+  END IF;
+
+  IF (i=line) THEN
+    RETURN f_addr2;
+  END IF;
+
+  IF (LENGTH(TRIM(both from f_addr3)) > 0) THEN
+    i:=i+1;
+  END IF;
+
+  IF (i=line) THEN
+    RETURN f_addr3;
+  END IF;
+
+  IF (LENGTH(TRIM(both from csz)) > 0) THEN
+    i:=i+1;
+  END IF;
+
+  IF (i=line) THEN
+    RETURN csz;
+  END IF;
+
+  IF (LENGTH(TRIM(both from country)) > 0) THEN
+    i:=i+1;
+  END IF;
+
+  IF (i=line) THEN
+    RETURN country;
+  END IF;
+
+  RETURN ' ';
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION formatAddr(TEXT, TEXT, TEXT, TEXT, TEXT) RETURNS TEXT AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  f_addr1 ALIAS FOR $1;
+  f_addr2 ALIAS FOR $2;
+  f_addr3 ALIAS FOR $3;
+  csz     ALIAS FOR $4;
+  country ALIAS FOR $5;
+  addr TEXT:='';
+
+BEGIN
+
+  IF (LENGTH(TRIM(both from f_addr1)) > 0) THEN
+    addr:=f_addr1;
+  END IF;
+
+  IF (LENGTH(TRIM(both from f_addr2)) > 0)  THEN
+        IF (LENGTH(TRIM(both from addr)) > 0) THEN
+                addr:=addr || E'\n';
+        END IF;
+    addr:=addr || f_addr2;
+  END IF;
+
+  IF (LENGTH(TRIM(both from f_addr3)) > 0)  THEN
+        IF (LENGTH(TRIM(both from addr)) > 0) THEN
+                addr:=addr || E'\n';
+        END IF;
+    addr:=addr || f_addr3;
+  END IF;
+
+  IF (LENGTH(TRIM(both from csz)) > 0)  THEN
+        IF (LENGTH(TRIM(both from addr)) > 0) THEN
+                addr:=addr || E'\n';
+        END IF;
+    addr:=addr || csz;
+  END IF;
+
+  IF (LENGTH(TRIM(both from country)) > 0)  THEN
+        IF (LENGTH(TRIM(both from addr)) > 0) THEN
+                addr:=addr || E'\n';
+        END IF;
+    addr:=addr || country;
+  END IF;
+
+  RETURN addr;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/formatboolyn.sql b/foundation-database/public/functions/formatboolyn.sql
new file mode 100644 (file)
index 0000000..58d8977
--- /dev/null
@@ -0,0 +1,14 @@
+
+CREATE OR REPLACE FUNCTION formatBoolYN(BOOLEAN) RETURNS TEXT IMMUTABLE AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE pBool ALIAS FOR $1;
+BEGIN
+  IF (pBool) THEN
+    RETURN ''Yes'';
+  ELSE
+    RETURN ''No'';
+  END IF;
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/formatbooseq.sql b/foundation-database/public/functions/formatbooseq.sql
new file mode 100644 (file)
index 0000000..4c84694
--- /dev/null
@@ -0,0 +1,24 @@
+
+CREATE OR REPLACE FUNCTION formatBooSeq(INTEGER, INTEGER) RETURNS TEXT AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemid ALIAS FOR $1;
+  pBooitemSeqId ALIAS FOR $2;
+  _result TEXT;
+  
+BEGIN
+
+  IF (fetchMetricBool('Routings')) THEN
+    SELECT booitem_seqnumber::text INTO _result
+    FROM xtmfg.booitem(pItemid)
+    WHERE (booitem_seq_id=pBooitemSeqId);
+
+    RETURN _result;
+  ELSE
+    RETURN NULL;
+  END IF;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/formatbytea.sql b/foundation-database/public/functions/formatbytea.sql
new file mode 100644 (file)
index 0000000..161915f
--- /dev/null
@@ -0,0 +1,18 @@
+CREATE OR REPLACE FUNCTION formatbytea(bytea)
+  RETURNS text AS
+'
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pField ALIAS FOR $1;
+  output_field TEXT;
+
+BEGIN
+
+  output_field := pField;
+
+  RETURN output_field;
+
+END;
+'
+  LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/formatccdashes.sql b/foundation-database/public/functions/formatccdashes.sql
new file mode 100644 (file)
index 0000000..aa4ec79
--- /dev/null
@@ -0,0 +1,31 @@
+CREATE OR REPLACE FUNCTION formatCCdashes(text, text)
+  RETURNS text AS
+'
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCCard ALIAS FOR $1;
+  pCCardType ALIAS FOR $2;
+  _returnCard text;
+  card_length integer;
+
+BEGIN
+
+  IF (pCCardType = ''A'')  THEN
+    _returnCard := pCCard;
+    RETURN _returnCard;
+  END IF;
+
+  card_length := length(pCcard);
+
+  if (card_length = 16) THEN
+    _returnCard := substr(pCCard, 1, 4) || ''-'' || substr(pCCard, 5, 4) || ''-'' || substr(pCCard, 9, 4) || ''-'' || substr(pCCard, 13, 4);
+  ELSE
+    _returnCard := substr(pCCard, 1, 4) || ''-'' || substr(pCCard, 5, 4) || ''-'' || substr(pCCard, 9, 4) || ''-'' || substr(pCCard, 13, 1);
+  END IF;
+
+  RETURN _returnCard;
+
+END;
+'
+  LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/formatccnumber.sql b/foundation-database/public/functions/formatccnumber.sql
new file mode 100644 (file)
index 0000000..8b817c4
--- /dev/null
@@ -0,0 +1,72 @@
+CREATE OR REPLACE FUNCTION formatccnumber(text)
+  RETURNS text AS
+'
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCcardnum ALIAS FOR $1;
+  card_length INTEGER;
+  output_cardnum TEXT;
+
+BEGIN
+
+  card_length := length(pCcardnum);
+
+  IF (card_length = 13) THEN
+    output_cardnum := ''*********'' || substr(pCcardnum, 10, 4);  
+  END IF;
+
+  IF (card_length = 14) THEN
+    output_cardnum := ''**********'' || substr(pCcardnum, 11, 4);  
+  END IF;
+
+  IF (card_length = 15) THEN
+    output_cardnum := ''***********'' || substr(pCcardnum, 12, 4);  
+  END IF;
+
+  IF (card_length = 16) THEN
+    output_cardnum := ''************'' || substr(pCcardnum, 13, 4);  
+  END IF;
+
+  RETURN output_cardnum;
+
+END;
+'
+  LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION formatccnumber(bytea)
+  RETURNS text AS
+'
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCcardnum ALIAS FOR $1;
+  card_length INTEGER;
+  output_cardnum TEXT;
+
+BEGIN
+
+  card_length := length(pCcardnum);
+
+  IF (card_length = 13) THEN
+    output_cardnum := ''*********'' || substr(pCcardnum, 10, 4);  
+  END IF;
+
+  IF (card_length = 14) THEN
+    output_cardnum := ''**********'' || substr(pCcardnum, 11, 4);  
+  END IF;
+
+  IF (card_length = 15) THEN
+    output_cardnum := ''***********'' || substr(pCcardnum, 12, 4);  
+  END IF;
+
+  IF (card_length = 16) THEN
+    output_cardnum := ''************'' || substr(pCcardnum, 13, 4);  
+  END IF;
+
+  RETURN output_cardnum;
+
+END;
+'
+  LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/formatcntctname.sql b/foundation-database/public/functions/formatcntctname.sql
new file mode 100644 (file)
index 0000000..c8aa61d
--- /dev/null
@@ -0,0 +1,83 @@
+CREATE OR REPLACE FUNCTION formatCntctName(INTEGER) RETURNS TEXT AS $$ 
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCntctId ALIAS FOR $1;
+  _r RECORD;
+  _rows NUMERIC;
+
+BEGIN
+
+  SELECT cntct_honorific, cntct_first_name, cntct_middle, 
+    cntct_last_name, cntct_suffix INTO _r
+  FROM cntct
+  WHERE (cntct_id=pCntctId);
+
+  GET DIAGNOSTICS _rows = ROW_COUNT;
+  IF (_rows = 0) THEN
+    RETURN '';
+  END IF;
+
+  RETURN formatCntctName(_r.cntct_honorific, _r.cntct_first_name, _r.cntct_middle, _r.cntct_last_name, _r.cntct_suffix);
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION formatCntctName(TEXT, TEXT, TEXT, TEXT, TEXT) RETURNS TEXT AS $$ 
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pHonorific ALIAS FOR $1;
+  pFirstName ALIAS FOR $2;
+  pMiddle ALIAS FOR $3;
+  pLastName ALIAS FOR $4;
+  pSuffix ALIAS FOR $5;
+  _name TEXT := '';
+
+BEGIN
+
+  IF (LENGTH(TRIM(both from COALESCE(pHonorific,''))) > 0) THEN
+    IF (POSITION('.' IN COALESCE(pHonorific, '')) > 0) THEN
+      _name:= pHonorific;
+    ELSE
+      _name:= pHonorific || '.';
+    END IF;
+  END IF;
+
+  IF (LENGTH(TRIM(both from COALESCE(pFirstName,''))) > 0)  THEN
+        IF (LENGTH(TRIM(both from _name)) > 0) THEN
+                _name:=_name || ' ';
+        END IF;
+    _name:=_name || pFirstName;
+  END IF;
+
+  IF (LENGTH(TRIM(both from COALESCE(pMiddle,''))) > 0)  THEN
+        IF (LENGTH(TRIM(both from _name)) > 0) THEN
+                _name:=_name || ' ';
+        END IF;
+    IF (POSITION('.' IN COALESCE(pHonorific, '')) > 0) THEN
+      _name:=_name || pMiddle;
+    ELSE
+      _name:=_name || pMiddle || '.';
+    END IF;
+  END IF;
+
+  IF (LENGTH(TRIM(both from COALESCE(pLastName,''))) > 0)  THEN
+        IF (LENGTH(TRIM(both from _name)) > 0) THEN
+                _name:=_name || ' ';
+        END IF;
+    _name:=_name || pLastName;
+  END IF;
+
+  IF (LENGTH(TRIM(both from COALESCE(pSuffix,''))) > 0)  THEN
+        IF (LENGTH(TRIM(both from _name)) > 0) THEN
+                _name:=_name || ' ';
+        END IF;
+    _name:=_name || pSuffix;
+  END IF;
+
+  RETURN _name;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/formatcost.sql b/foundation-database/public/functions/formatcost.sql
new file mode 100644 (file)
index 0000000..4f8072a
--- /dev/null
@@ -0,0 +1,7 @@
+CREATE OR REPLACE FUNCTION formatCost(NUMERIC) RETURNS TEXT IMMUTABLE AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN formatNumeric($1, ''cost'');
+END;'
+LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/formatcounttagbarcode.sql b/foundation-database/public/functions/formatcounttagbarcode.sql
new file mode 100644 (file)
index 0000000..dd0fb3f
--- /dev/null
@@ -0,0 +1,19 @@
+
+CREATE OR REPLACE FUNCTION formatCountTagBarcode(INTEGER) RETURNS TEXT IMMUTABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCnttagid ALIAS FOR $1;
+  _barcode TEXT;
+BEGIN
+
+  SELECT ( E'\138CTXX' ||
+           LTRIM(TO_CHAR(LENGTH(invcnt_tagnumber), '00')) || invcnt_tagnumber ) INTO _barcode
+  FROM invcnt
+  WHERE (invcnt_id=pCnttagid);
+
+  RETURN _barcode;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/formatcreditmemonumber.sql b/foundation-database/public/functions/formatcreditmemonumber.sql
new file mode 100644 (file)
index 0000000..22a8ba3
--- /dev/null
@@ -0,0 +1,14 @@
+
+CREATE OR REPLACE FUNCTION formatCreditMemoNumber(INTEGER) RETURNS TEXT IMMUTABLE AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCmheadid ALIAS FOR $1;
+
+BEGIN
+  RETURN ( SELECT COALESCE(cmhead_number::TEXT, '''')
+           FROM cmhead
+           WHERE (cmhead_id=pCmheadid) );
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/formatdate.sql b/foundation-database/public/functions/formatdate.sql
new file mode 100644 (file)
index 0000000..53c263d
--- /dev/null
@@ -0,0 +1,42 @@
+
+CREATE OR REPLACE FUNCTION formatDate(TIMESTAMP WITH TIME ZONE) RETURNS TEXT IMMUTABLE AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+SELECT TO_CHAR($1, COALESCE((SELECT locale_dateformat
+                             FROM locale, usr
+                             WHERE ((usr_locale_id=locale_id)
+                              AND (usr_username=getEffectiveXtUser())) ),
+                            ''yyyy-mm-dd'' )) AS result
+' LANGUAGE 'sql';
+
+
+CREATE OR REPLACE FUNCTION formatDate(DATE) RETURNS TEXT IMMUTABLE AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+SELECT TO_CHAR($1, COALESCE((SELECT locale_dateformat
+                             FROM locale, usr
+                             WHERE ((usr_locale_id=locale_id)
+                              AND (usr_username=getEffectiveXtUser())) ),
+                            ''yyyy-mm-dd'') ) AS result
+' LANGUAGE 'sql';
+
+CREATE OR REPLACE FUNCTION formatDate(DATE, TEXT) RETURNS TEXT IMMUTABLE AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pDate ALIAS FOR $1;
+  pString ALIAS FOR $2;
+
+BEGIN
+
+  IF ( (pDate = startOfTime()) OR
+       (pDate = endOfTime()) OR
+       (pDate IS NULL) ) THEN
+    RETURN pString;
+  ELSE
+    RETURN formatDate(pDate);
+  END IF;
+
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/formatdatetime.sql b/foundation-database/public/functions/formatdatetime.sql
new file mode 100644 (file)
index 0000000..45c590f
--- /dev/null
@@ -0,0 +1,21 @@
+
+CREATE OR REPLACE FUNCTION formatDateTime(TIMESTAMP WITHOUT TIME ZONE) RETURNS TEXT IMMUTABLE AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+SELECT TO_CHAR($1, COALESCE((SELECT locale_timestampformat
+                             FROM locale, usr
+                             WHERE ((usr_locale_id=locale_id)
+                              AND (usr_username=getEffectiveXtUser())) ),
+                            ''yyyy-mm-dd HH24:MI:SS'')) AS result
+' LANGUAGE 'sql';
+
+CREATE OR REPLACE FUNCTION formatDateTime(TIMESTAMP WITH TIME ZONE) RETURNS TEXT IMMUTABLE AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+SELECT TO_CHAR($1, COALESCE((SELECT locale_timestampformat
+                             FROM locale, usr
+                             WHERE ((usr_locale_id=locale_id)
+                              AND (usr_username=getEffectiveXtUser())) ),
+                            ''yyyy-mm-dd HH24:MI:SS'')) AS result
+' LANGUAGE 'sql';
+
diff --git a/foundation-database/public/functions/formatextprice.sql b/foundation-database/public/functions/formatextprice.sql
new file mode 100644 (file)
index 0000000..561031d
--- /dev/null
@@ -0,0 +1,7 @@
+CREATE OR REPLACE FUNCTION formatExtPrice(NUMERIC) RETURNS TEXT IMMUTABLE AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN formatNumeric($1, ''extprice'');
+END;'
+LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/formatflitemdescrip.sql b/foundation-database/public/functions/formatflitemdescrip.sql
new file mode 100644 (file)
index 0000000..d9f1943
--- /dev/null
@@ -0,0 +1,81 @@
+CREATE OR REPLACE FUNCTION formatflitemdescrip(int4)
+  RETURNS text AS  '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pFlitemId ALIAS FOR $1;
+  _x RECORD;
+  _descrip TEXT;
+
+BEGIN
+  SELECT flitem_accnt_id, flitem_company, flitem_profit, flitem_number,
+        flitem_sub, flitem_type, flitem_subaccnttype_code,
+        accnt_id, accnt_descrip INTO _x
+  FROM flitem LEFT OUTER JOIN accnt
+        ON flitem_accnt_id=accnt_id
+  WHERE flitem_id=pFlitemId;
+
+  IF _x.flitem_accnt_id > -1 THEN
+
+    SELECT (formatGLAccount(_x.accnt_id) || ''-'' || _x.accnt_descrip) INTO _descrip;
+
+  ELSE
+
+    _descrip:='''';
+
+    IF _x.flitem_type = ''A'' THEN
+      _descrip:=''Type='' || ''Asset'';
+      ELSE IF _x.flitem_type=''L'' THEN
+        _descrip:=''Type='' || ''Liability'';
+        ELSE IF _x.flitem_type=''R'' THEN
+          _descrip:=''Type='' || ''Revenue'';
+          ELSE IF _x.flitem_type=''E'' THEN
+            _descrip:=''Type='' || ''Expense'';
+            ELSE IF _x.flitem_type=''Q'' THEN
+              _descrip:=''Type='' || ''Equity'';
+            END IF;
+          END IF;
+        END IF;
+      END IF;
+    END IF;
+
+    IF _x.flitem_subaccnttype_code <> ''All'' THEN
+      IF (LENGTH(TRIM(both from _descrip)) > 0) THEN
+        _descrip:=_descrip || '', '';
+      END IF;
+      _descrip:=_descrip || ''Sub Accnt Type='' || _x.flitem_subaccnttype_code;
+    END IF;
+
+    IF _x.flitem_company <> ''All'' THEN
+      IF (LENGTH(TRIM(both from _descrip)) > 0) THEN
+        _descrip:=_descrip || '', '';
+      END IF;
+      _descrip:=_descrip || ''Company='' || _x.flitem_company;
+    END IF;
+
+    IF _x.flitem_profit <> ''All'' THEN
+      IF (LENGTH(TRIM(both from _descrip)) > 0) THEN
+        _descrip:=_descrip || '', '';
+      END IF;
+      _descrip:=_descrip || ''Profit='' || _x.flitem_profit;
+    END IF;
+
+    IF _x.flitem_number <> ''All'' THEN
+      IF (LENGTH(TRIM(both from _descrip)) > 0) THEN
+        _descrip:=_descrip || '', '';
+      END IF;
+      _descrip:=_descrip || ''Number='' || _x.flitem_number;
+    END IF;
+
+    IF _x.flitem_sub <> ''All'' THEN
+      IF (LENGTH(TRIM(both from _descrip)) > 0) THEN
+        _descrip:=_descrip || '', '';
+      END IF;
+      _descrip:=_descrip || ''Sub Accnt='' || _x.flitem_sub;
+    END IF;
+  END IF;
+
+  RETURN _descrip;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/formatglaccount.sql b/foundation-database/public/functions/formatglaccount.sql
new file mode 100644 (file)
index 0000000..fc31686
--- /dev/null
@@ -0,0 +1,63 @@
+
+CREATE OR REPLACE FUNCTION formatGLAccount(INTEGER) RETURNS TEXT IMMUTABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pAccntid ALIAS FOR $1;
+  _accnt RECORD;
+
+BEGIN
+
+  SELECT COALESCE(accnt_company, '') AS accnt_company,
+         COALESCE(accnt_profit, '') AS accnt_profit,
+         accnt_number,
+         COALESCE(accnt_sub, '') AS accnt_sub INTO _accnt
+  FROM accnt
+  WHERE (accnt_id=pAccntid);
+
+  IF (NOT FOUND) THEN
+    RETURN 'Error';
+  END IF;
+
+  RETURN formatGlAccount(_accnt.accnt_company, _accnt.accnt_profit, _accnt.accnt_number, _accnt.accnt_sub);
+
+END;
+$$      LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION formatGLAccount(TEXT, TEXT, TEXT, TEXT) RETURNS TEXT IMMUTABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCompany ALIAS FOR $1;
+  pProfit ALIAS FOR $2;
+  pNumber ALIAS FOR $3;
+  pSub    ALIAS FOR $4;
+  _number TEXT := '';
+
+BEGIN
+
+  IF ( ( SELECT metric_value::INTEGER
+         FROM metric
+         WHERE (metric_name='GLCompanySize') ) > 0 ) THEN
+    _number := pCompany || '-';
+  END IF;
+
+  IF ( ( SELECT metric_value::INTEGER
+         FROM metric
+         WHERE (metric_name='GLProfitSize') ) > 0 ) THEN
+    _number := _number || pProfit || '-';
+  END IF;
+
+  _number := _number || pNumber;
+
+  IF ( ( SELECT metric_value::INTEGER
+         FROM metric
+         WHERE (metric_name='GLSubaccountSize') ) > 0 ) THEN
+    _number := _number || '-' || pSub;
+  END IF;
+
+  RETURN _number;
+
+END;
+$$      LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/formatglaccountlong.sql b/foundation-database/public/functions/formatglaccountlong.sql
new file mode 100644 (file)
index 0000000..c4121eb
--- /dev/null
@@ -0,0 +1,19 @@
+
+CREATE OR REPLACE FUNCTION formatGLAccountLong(INTEGER) RETURNS TEXT IMMUTABLE AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pAccntid ALIAS FOR $1;
+  _result TEXT;
+
+BEGIN
+
+  SELECT (formatGLAccount(accnt_id) || ''-'' || accnt_descrip) INTO _result
+  FROM accnt
+  WHERE (accnt_id=pAccntid);
+
+  RETURN _result;
+
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/formatindent.sql b/foundation-database/public/functions/formatindent.sql
new file mode 100644 (file)
index 0000000..2fbb9b6
--- /dev/null
@@ -0,0 +1,25 @@
+
+CREATE OR REPLACE FUNCTION formatindent(text,int4)
+  RETURNS text AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pText ALIAS FOR $1;
+  pIndent ALIAS FOR $2;
+  _i INTEGER;
+  _result TEXT;
+
+BEGIN
+  _result := '''';
+  _i := 0;
+
+  WHILE _i < pIndent LOOP
+    _result := _result || ''  '';
+    _i := _i + 1;
+  END LOOP;
+
+  _result := _result || pText;
+  RETURN _result;
+END;
+' LANGUAGE 'plpgsql' VOLATILE;
+
diff --git a/foundation-database/public/functions/formatinterval.sql b/foundation-database/public/functions/formatinterval.sql
new file mode 100644 (file)
index 0000000..eb76170
--- /dev/null
@@ -0,0 +1,22 @@
+
+-- treat NUMERIC as number of minutes
+CREATE OR REPLACE FUNCTION formatInterval(NUMERIC) RETURNS TEXT IMMUTABLE AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+  SELECT TO_CHAR((''@ '' || trunc($1) || '' min '' ||
+                             ($1 - trunc($1)) * 60 || '' sec'')::INTERVAL,
+                 ( SELECT locale_intervalformat
+                       FROM locale, usr
+                       WHERE ((usr_locale_id=locale_id)
+                         AND  (usr_username=getEffectiveXtUser())) ) ) AS result
+' LANGUAGE 'sql';
+
+CREATE OR REPLACE FUNCTION formatInterval(INTERVAL) RETURNS TEXT IMMUTABLE AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+  SELECT TO_CHAR($1, ( SELECT locale_intervalformat
+                       FROM locale, usr
+                       WHERE ((usr_locale_id=locale_id)
+                         AND  (usr_username=getEffectiveXtUser())) ) ) AS result
+' LANGUAGE 'sql';
+
diff --git a/foundation-database/public/functions/formatinvcnumber.sql b/foundation-database/public/functions/formatinvcnumber.sql
new file mode 100644 (file)
index 0000000..b8665f4
--- /dev/null
@@ -0,0 +1,14 @@
+
+CREATE OR REPLACE FUNCTION formatInvcNumber(INTEGER) RETURNS TEXT IMMUTABLE AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCobmiscid ALIAS FOR $1;
+
+BEGIN
+  RETURN ( SELECT COALESCE(cobmisc_invcnumber::TEXT, '''')
+           FROM cobmisc
+           WHERE (cobmisc_id=pCobmiscid) );
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/formatitemsitebarcode.sql b/foundation-database/public/functions/formatitemsitebarcode.sql
new file mode 100644 (file)
index 0000000..aa4172a
--- /dev/null
@@ -0,0 +1,21 @@
+CREATE OR REPLACE FUNCTION formatItemSiteBarcode(INTEGER) RETURNS TEXT IMMUTABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  _barcode TEXT;
+BEGIN
+
+  SELECT ( E'\138ISXX' ||
+           LTRIM(TO_CHAR(LENGTH(item_number), '00')) || LENGTH(warehous_code)::TEXT ||
+           item_number || warehous_code ) INTO _barcode
+  FROM itemsite, item, whsinfo
+  WHERE ( (itemsite_item_id=item_id)
+   AND (itemsite_warehous_id=warehous_id)
+   AND (itemsite_id=pItemsiteid) );
+
+  RETURN _barcode;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/formatlocationbarcode.sql b/foundation-database/public/functions/formatlocationbarcode.sql
new file mode 100644 (file)
index 0000000..08caa7b
--- /dev/null
@@ -0,0 +1,21 @@
+
+CREATE OR REPLACE FUNCTION formatLocationBarcode(INTEGER) RETURNS TEXT IMMUTABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pLocationid ALIAS FOR $1;
+  _barcode TEXT;
+BEGIN
+
+  SELECT ( E'\138LOXX' ||
+           LENGTH(warehous_code)::TEXT || LTRIM(TO_CHAR(LENGTH(location_name::TEXT), '00')) ||
+           warehous_code || location_name ) INTO _barcode
+  FROM location, whsinfo
+  WHERE ( (location_warehous_id=warehous_id)
+   AND (location_id=pLocationid) );
+
+  RETURN _barcode;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/formatlocationcontentsbarcode.sql b/foundation-database/public/functions/formatlocationcontentsbarcode.sql
new file mode 100644 (file)
index 0000000..95afeb9
--- /dev/null
@@ -0,0 +1,21 @@
+
+CREATE OR REPLACE FUNCTION formatLocationContentsBarcode(INTEGER) RETURNS TEXT IMMUTABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pLocationid ALIAS FOR $1;
+  _barcode TEXT;
+BEGIN
+
+  SELECT ( E'\138LOCN' ||
+           LENGTH(warehous_code)::TEXT || LTRIM(TO_CHAR(LENGTH(location_name), '00')) ||
+           warehous_code || location_name ) INTO _barcode
+  FROM location, whsinfo
+  WHERE ( (location_warehous_id=warehous_id)
+   AND (location_id=pLocationid) );
+
+  RETURN _barcode;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/formatlocationissuebarcode.sql b/foundation-database/public/functions/formatlocationissuebarcode.sql
new file mode 100644 (file)
index 0000000..402f2b9
--- /dev/null
@@ -0,0 +1,21 @@
+
+CREATE OR REPLACE FUNCTION formatLocationIssueBarcode(INTEGER) RETURNS TEXT IMMUTABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pLocationid ALIAS FOR $1;
+  _barcode TEXT;
+BEGIN
+
+  SELECT ( E'\138LOIS' ||
+           LENGTH(warehous_code)::TEXT || LTRIM(TO_CHAR(LENGTH(location_name), '00')) ||
+           warehous_code || location_name ) INTO _barcode
+  FROM location, whsinfo
+  WHERE ( (location_warehous_id=warehous_id)
+   AND (location_id=pLocationid) );
+
+  RETURN _barcode;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/formatlocationname.sql b/foundation-database/public/functions/formatlocationname.sql
new file mode 100644 (file)
index 0000000..66d5ce9
--- /dev/null
@@ -0,0 +1,42 @@
+
+CREATE OR REPLACE FUNCTION formatLocationName(INTEGER) RETURNS TEXT IMMUTABLE AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pLocationid ALIAS FOR $1;
+  _name TEXT;
+  _r RECORD;
+
+BEGIN
+
+  SELECT location_aisle, location_rack,
+         location_bin, location_name INTO _r
+  FROM location
+  WHERE (location_id=pLocationid);
+  IF (FOUND) THEN
+    IF (_r.location_aisle IS NOT NULL) THEN
+      _name := _r.location_aisle;
+    ELSE
+      _name := '''';
+    END IF;
+
+    IF (_r.location_rack IS NOT NULL) THEN
+      _name := (_name || _r.location_rack);
+    END IF;
+
+    IF (_r.location_bin IS NOT NULL) THEN
+      _name := (_name || _r.location_bin);
+    END IF;
+
+    IF (_r.location_name IS NOT NULL) THEN
+      _name := (_name || _r.location_name);
+    END IF;
+
+    RETURN _name;
+  ELSE
+    RETURN ''N/A'';
+  END IF;
+
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/formatlotserialnumber.sql b/foundation-database/public/functions/formatlotserialnumber.sql
new file mode 100644 (file)
index 0000000..4d6cb17
--- /dev/null
@@ -0,0 +1,19 @@
+CREATE OR REPLACE FUNCTION formatLotSerialNumber(INTEGER) RETURNS TEXT AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pLotSerialId ALIAS FOR $1;
+  _lotserial TEXT;
+
+BEGIN
+  --See if lot serial control turned on (Postbooks will not ever have this)
+  IF (fetchmetricbool(''LotSerialControl'')) THEN
+    SELECT ls_number INTO _lotserial
+    FROM ls
+    WHERE (ls_id=pLotSerialId);
+  END IF;
+
+  RETURN COALESCE(_lotserial,'''');
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/formatmoney.sql b/foundation-database/public/functions/formatmoney.sql
new file mode 100644 (file)
index 0000000..2267f6b
--- /dev/null
@@ -0,0 +1,6 @@
+CREATE OR REPLACE FUNCTION formatMoney(NUMERIC) RETURNS TEXT IMMUTABLE AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN formatNumeric($1, ''curr'');
+END;' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/formatnumeric.sql b/foundation-database/public/functions/formatnumeric.sql
new file mode 100644 (file)
index 0000000..2c6a108
--- /dev/null
@@ -0,0 +1,79 @@
+CREATE OR REPLACE FUNCTION formatNumeric(NUMERIC, TEXT) RETURNS TEXT IMMUTABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _value        NUMERIC := $1;
+  _type         TEXT    := LOWER(COALESCE($2, 'curr'));
+  _abs          NUMERIC;
+  _magnitudecnt NUMERIC(1000);
+  _wholefmt     TEXT    := '0';
+  _scale        INTEGER;
+  _neg          TEXT;
+  _decimal      TEXT;
+  _group        TEXT;
+  _string       TEXT;
+  _debug        BOOL := false;
+  _r            RECORD;
+
+BEGIN
+  -- If the value passed in is NULL then we want to pass back an empty string
+  IF(_value IS NULL) THEN
+    RETURN '';
+  END IF;
+
+  SELECT * INTO _r
+  FROM locale
+  WHERE (locale_id=getUsrLocaleId());
+
+  _decimal := COALESCE(SUBSTRING(_r.locale_qtyformat FROM 1 FOR 1), '.');
+  _neg     := COALESCE(SUBSTRING(_r.locale_qtyformat FROM 2 FOR 1), '-');
+  _group   := COALESCE(SUBSTRING(_r.locale_qtyformat FROM 3 FOR 1), ',');
+
+  _scale   := CASE WHEN _type = 'cost'       THEN _r.locale_cost_scale
+                   WHEN _type = 'extprice'   THEN _r.locale_extprice_scale
+                   WHEN _type = 'percent'    THEN _r.locale_percent_scale
+                   WHEN _type = 'purchprice' THEN _r.locale_purchprice_scale
+                   WHEN _type = 'qty'        THEN _r.locale_qty_scale
+                   WHEN _type = 'qtyper'     THEN _r.locale_qtyper_scale
+                   WHEN _type = 'salesprice' THEN _r.locale_salesprice_scale
+                   WHEN _type = 'uomratio'   THEN _r.locale_uomratio_scale
+                   WHEN _type = 'weight'     THEN _r.locale_weight_scale
+                   WHEN SUBSTRING(_type FOR 4) = 'curr' THEN _r.locale_curr_scale
+                   ELSE 2
+              END;
+
+  _value        := round(_value, _scale);
+  _abs          := ABS(_value);
+  _magnitudecnt := TRUNC(_abs / 10);
+
+  IF (_debug) THEN
+    RAISE NOTICE '_value % _abs % _scale % _neg % _decimal % _group % ',
+                 _value, _abs, _scale, _decimal, _group, _scale;
+  END IF;
+
+  IF (_value < 0) THEN
+    _string := _neg;
+  ELSE
+    _string := '';
+  END IF;
+
+  WHILE (_magnitudecnt >= 1) LOOP
+    _magnitudecnt := TRUNC(_magnitudecnt / 10);
+    IF (LENGTH(_wholefmt) % 3 = 0) THEN
+      _wholefmt := '"' || _group || '"' || _wholefmt;
+    END IF;
+    _wholefmt := '9' || _wholefmt;
+  END LOOP;
+
+  IF (_scale > 0) THEN
+    _abs := (_abs * (10 ^ _scale));
+    _abs := TRUNC(_abs);
+    _wholefmt := _wholefmt || '"' || _decimal || '"' || REPEAT('0', _scale);
+  END IF;
+
+  _wholefmt := 'FM' || _wholefmt;
+  _string := _string || to_char(_abs, _wholefmt);
+
+  RETURN _string;
+END;$$
+LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/formatperiodname.sql b/foundation-database/public/functions/formatperiodname.sql
new file mode 100644 (file)
index 0000000..4918694
--- /dev/null
@@ -0,0 +1,52 @@
+
+CREATE OR REPLACE FUNCTION formatPeriodName( INTEGER,  char) RETURNS text AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pPeriodId ALIAS FOR $1;
+  pInterval ALIAS FOR $2;
+  _result TEXT;
+
+BEGIN
+
+--...for Month
+
+   IF (pInterval=''M'') THEN
+        SELECT
+          (CASE
+                      WHEN period_name='''' THEN
+                        formatdate(period_start) || ''-'' || formatdate(period_end)
+                      ELSE period_name
+          END) INTO _result
+        FROM period
+        WHERE (period_id=pPeriodId);
+
+        RETURN _result;
+
+--...for Quarter
+
+        ELSE IF (pInterval=''Q'') THEN
+                SELECT
+                        (''Q'' || period_quarter || ''-'' || EXTRACT(year from yearperiod_end)) INTO _result
+                FROM period, yearperiod
+                WHERE ((period_id=pPeriodId)
+                AND (period_yearperiod_id=yearperiod_id));
+
+                RETURN _result;
+--...for Year
+        ELSE
+                SELECT
+                        EXTRACT(year FROM yearperiod_end) INTO _result
+                FROM period,yearperiod
+                WHERE ((period_id=pPeriodId)
+                AND (period_yearperiod_id=yearperiod_id));
+
+                RETURN _result;
+        END IF;
+   END IF;
+
+   RETURN ''Err'';
+
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/formatplonumber.sql b/foundation-database/public/functions/formatplonumber.sql
new file mode 100644 (file)
index 0000000..0771a6f
--- /dev/null
@@ -0,0 +1,19 @@
+
+CREATE OR REPLACE FUNCTION formatPloNumber(INTEGER) RETURNS TEXT IMMUTABLE AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pPlanordid ALIAS FOR $1;
+  _result TEXT;
+
+BEGIN
+
+  SELECT (TEXT(planord_number) || ''-'' || TEXT(planord_subnumber)) INTO _result
+  FROM planord
+  WHERE (planord_id=pPlanordid);
+
+  RETURN _result;
+
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/formatprcnt.sql b/foundation-database/public/functions/formatprcnt.sql
new file mode 100644 (file)
index 0000000..e2d696f
--- /dev/null
@@ -0,0 +1,7 @@
+
+CREATE OR REPLACE FUNCTION formatPrcnt(NUMERIC) RETURNS TEXT IMMUTABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple.
+-- See www.xtuple.com/CPAL for the full text of the software license.
+SELECT formatNumeric($1 * 100, 'percent')  AS result
+$$ LANGUAGE 'sql';
+
diff --git a/foundation-database/public/functions/formatprice.sql b/foundation-database/public/functions/formatprice.sql
new file mode 100644 (file)
index 0000000..114188b
--- /dev/null
@@ -0,0 +1,5 @@
+CREATE OR REPLACE FUNCTION formatPrice(NUMERIC) RETURNS TEXT IMMUTABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple.
+-- See www.xtuple.com/CPAL for the full text of the software license.
+  SELECT formatNumeric($1, 'salesprice') AS result;
+$$ LANGUAGE 'sql';
diff --git a/foundation-database/public/functions/formatpurchprice.sql b/foundation-database/public/functions/formatpurchprice.sql
new file mode 100644 (file)
index 0000000..a8bf710
--- /dev/null
@@ -0,0 +1,6 @@
+CREATE OR REPLACE FUNCTION formatPurchPrice(NUMERIC) RETURNS TEXT IMMUTABLE AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN formatNumeric($1, ''purchprice'');
+END;' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/formatqty.sql b/foundation-database/public/functions/formatqty.sql
new file mode 100644 (file)
index 0000000..6763578
--- /dev/null
@@ -0,0 +1,6 @@
+CREATE OR REPLACE FUNCTION formatQty(NUMERIC) RETURNS TEXT IMMUTABLE AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN formatNumeric($1, ''qty'');
+END;' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/formatqtyper.sql b/foundation-database/public/functions/formatqtyper.sql
new file mode 100644 (file)
index 0000000..38d1785
--- /dev/null
@@ -0,0 +1,6 @@
+CREATE OR REPLACE FUNCTION formatQtyPer(NUMERIC) RETURNS TEXT IMMUTABLE AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN formatNumeric($1, ''qtyper'');
+END;' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/formatratio.sql b/foundation-database/public/functions/formatratio.sql
new file mode 100644 (file)
index 0000000..1f58055
--- /dev/null
@@ -0,0 +1,6 @@
+CREATE OR REPLACE FUNCTION formatRatio(NUMERIC) RETURNS TEXT IMMUTABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple.
+-- See www.xtuple.com/CPAL for the full text of the software license.
+  SELECT formatNumeric($1, 'uomratio');
+$$ LANGUAGE 'sql';
+
diff --git a/foundation-database/public/functions/formatrevnumber.sql b/foundation-database/public/functions/formatrevnumber.sql
new file mode 100644 (file)
index 0000000..0e375d4
--- /dev/null
@@ -0,0 +1,21 @@
+CREATE OR REPLACE FUNCTION formatRevNumber(TEXT, INTEGER) RETURNS TEXT AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pRevType     ALIAS FOR $1;
+  pRevId       ALIAS FOR $2;
+  _revision    TEXT;
+
+BEGIN
+  --See if revision control turned on (Postbooks will not ever have this)
+  IF (fetchmetricbool(''RevControl'')) THEN
+    SELECT rev_number INTO _revision
+    FROM rev
+    WHERE ((rev_target_type=pRevType)
+    AND (rev_id=pRevId));
+  END IF;
+
+  RETURN COALESCE(_revision,'''');
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/formatsalesprice.sql b/foundation-database/public/functions/formatsalesprice.sql
new file mode 100644 (file)
index 0000000..c414592
--- /dev/null
@@ -0,0 +1,6 @@
+CREATE OR REPLACE FUNCTION formatSalesPrice(NUMERIC) RETURNS TEXT IMMUTABLE AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN formatNumeric($1, ''salesprice'');
+END;' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/formatscrap.sql b/foundation-database/public/functions/formatscrap.sql
new file mode 100644 (file)
index 0000000..d271b9b
--- /dev/null
@@ -0,0 +1,5 @@
+CREATE OR REPLACE FUNCTION formatScrap(NUMERIC) RETURNS TEXT IMMUTABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple.
+-- See www.xtuple.com/CPAL for the full text of the software license.
+  SELECT formatNumeric(($1 * 100), 'percent') AS result
+$$ LANGUAGE 'sql';
diff --git a/foundation-database/public/functions/formatshipmentnumber.sql b/foundation-database/public/functions/formatshipmentnumber.sql
new file mode 100644 (file)
index 0000000..1661bb9
--- /dev/null
@@ -0,0 +1,13 @@
+
+CREATE OR REPLACE FUNCTION formatShipmentNumber(INTEGER) RETURNS TEXT IMMUTABLE AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pshipheadid    ALIAS FOR $1;
+BEGIN
+  RETURN ( SELECT COALESCE(shiphead_number::TEXT, '''')
+           FROM shiphead
+           WHERE (shiphead_id=pshipheadid) );
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/formatsobarcode.sql b/foundation-database/public/functions/formatsobarcode.sql
new file mode 100644 (file)
index 0000000..f9ea059
--- /dev/null
@@ -0,0 +1,19 @@
+
+CREATE OR REPLACE FUNCTION formatSoBarcode(INTEGER) RETURNS TEXT IMMUTABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSoheadid ALIAS FOR $1;
+  _barcode TEXT;
+BEGIN
+
+  SELECT ( E'\138SOXX' ||
+           LENGTH(TEXT(cohead_number)) || TEXT(cohead_number) ) INTO _barcode
+  FROM cohead
+  WHERE (cohead_id=pSoheadid);
+
+  RETURN _barcode;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/formatsoitembarcode.sql b/foundation-database/public/functions/formatsoitembarcode.sql
new file mode 100644 (file)
index 0000000..b6c378c
--- /dev/null
@@ -0,0 +1,21 @@
+
+CREATE OR REPLACE FUNCTION formatSoitemBarcode(INTEGER) RETURNS TEXT IMMUTABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSoitemid ALIAS FOR $1;
+  _barcode TEXT;
+BEGIN
+
+  SELECT ( E'\138SOLI' ||
+           LENGTH(TEXT(cohead_number)) || LENGTH(formatsolinenumber(coitem_id)) ||
+           TEXT(cohead_number) || formatsolinenumber(coitem_id) ) INTO _barcode
+  FROM cohead, coitem
+  WHERE ( (coitem_cohead_id=cohead_id)
+   AND (coitem_id=pSoitemid) );
+
+  RETURN _barcode;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/formatsoitemnumber.sql b/foundation-database/public/functions/formatsoitemnumber.sql
new file mode 100644 (file)
index 0000000..92b1ac2
--- /dev/null
@@ -0,0 +1,12 @@
+CREATE OR REPLACE FUNCTION formatSoitemNumber(INTEGER) RETURNS TEXT AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  targetSoitemid ALIAS FOR $1;
+BEGIN
+  RETURN ( SELECT (cohead_number::TEXT || '-' || formatsolinenumber(coitem_id))
+           FROM cohead, coitem
+           WHERE ((coitem_cohead_id=cohead_id)
+            AND (coitem_id=targetSoitemid)) );
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/formatsolinenumber.sql b/foundation-database/public/functions/formatsolinenumber.sql
new file mode 100644 (file)
index 0000000..a4586c6
--- /dev/null
@@ -0,0 +1,27 @@
+
+CREATE OR REPLACE FUNCTION formatSoLineNumber(INTEGER) RETURNS TEXT AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSoitemid ALIAS FOR $1;
+  _r RECORD;
+
+BEGIN
+
+  SELECT coitem_linenumber, coitem_subnumber
+    INTO _r
+    FROM coitem
+   WHERE(coitem_id=pSoitemid);
+
+  IF(NOT FOUND) THEN
+    RETURN NULL;
+  END IF;
+
+  IF(COALESCE(_r.coitem_subnumber, 0) > 0) THEN
+    RETURN _r.coitem_linenumber || '.' || _r.coitem_subnumber;
+  END IF;
+
+  RETURN _r.coitem_linenumber; 
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/formatsonumber.sql b/foundation-database/public/functions/formatsonumber.sql
new file mode 100644 (file)
index 0000000..1036828
--- /dev/null
@@ -0,0 +1,7 @@
+CREATE OR REPLACE FUNCTION formatSoNumber(INTEGER) RETURNS TEXT AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple.
+-- See www.xtuple.com/CPAL for the full text of the software license.
+SELECT COALESCE((SELECT (text(cohead_number) || '-' || formatSoLineNumber(coitem_id))
+                   FROM coitem JOIN cohead ON (coitem_cohead_id=cohead_id)
+                  WHERE (coitem_id=($1))),'DELETED');
+$$ LANGUAGE 'SQL' STABLE;
diff --git a/foundation-database/public/functions/formattime.sql b/foundation-database/public/functions/formattime.sql
new file mode 100644 (file)
index 0000000..f7c5d4e
--- /dev/null
@@ -0,0 +1,17 @@
+
+CREATE OR REPLACE FUNCTION formatTime(TIMESTAMP WITH TIME ZONE) RETURNS TEXT IMMUTABLE AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+  SELECT TO_CHAR($1, ( SELECT locale_timeformat
+                       FROM locale, usr
+                       WHERE ((usr_locale_id=locale_id)
+                        AND (usr_username=getEffectiveXtUser())) ) ) AS result
+' LANGUAGE 'sql';
+
+
+CREATE OR REPLACE FUNCTION formatTime(NUMERIC) RETURNS TEXT IMMUTABLE AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+  SELECT LTRIM(TO_CHAR(COALESCE($1, 0), ''999999990.0''));
+' LANGUAGE 'sql';
+
diff --git a/foundation-database/public/functions/formatuomratio.sql b/foundation-database/public/functions/formatuomratio.sql
new file mode 100644 (file)
index 0000000..997b944
--- /dev/null
@@ -0,0 +1,6 @@
+CREATE OR REPLACE FUNCTION formatUOMRatio(NUMERIC) RETURNS TEXT IMMUTABLE AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN formatNumeric($1, ''uomratio'');
+END;' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/formatuserbarcode.sql b/foundation-database/public/functions/formatuserbarcode.sql
new file mode 100644 (file)
index 0000000..4a8964f
--- /dev/null
@@ -0,0 +1,33 @@
+
+CREATE OR REPLACE FUNCTION formatUserBarcode(INTEGER) RETURNS TEXT IMMUTABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pUserid ALIAS FOR $1;
+  _barcode TEXT;
+BEGIN
+
+  SELECT formatUserBarcode(usr_username) INTO _barcode
+    FROM usr
+   WHERE(usr_id=pUserid);
+
+  RETURN _barcode;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION formatUserBarcode(TEXT) RETURNS TEXT IMMUTABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pUsername ALIAS FOR $1;
+  _barcode TEXT;
+BEGIN
+
+  _barcode := ( E'\138USER' || LENGTH(pUsername)::TEXT || pUsername );
+
+  RETURN _barcode;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/formatweight.sql b/foundation-database/public/functions/formatweight.sql
new file mode 100644 (file)
index 0000000..a700536
--- /dev/null
@@ -0,0 +1,5 @@
+CREATE OR REPLACE FUNCTION formatWeight(NUMERIC) RETURNS TEXT IMMUTABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple.
+-- See www.xtuple.com/CPAL for the full text of the software license.
+SELECT formatNumeric($1, 'weight') AS result
+$$ LANGUAGE 'sql';
diff --git a/foundation-database/public/functions/formatwobarcode.sql b/foundation-database/public/functions/formatwobarcode.sql
new file mode 100644 (file)
index 0000000..f936709
--- /dev/null
@@ -0,0 +1,20 @@
+
+CREATE OR REPLACE FUNCTION formatWoBarcode(INTEGER) RETURNS TEXT IMMUTABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWoid ALIAS FOR $1;
+  _barcode TEXT;
+BEGIN
+
+  SELECT ( E'\138WOXX' ||
+           LENGTH(wo_number::TEXT) || LENGTH(wo_subnumber::TEXT) ||
+           wo_number::TEXT || wo_subnumber::TEXT ) INTO _barcode
+  FROM wo
+  WHERE (wo_id=pWoid);
+
+  RETURN _barcode;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/formatwonumber.sql b/foundation-database/public/functions/formatwonumber.sql
new file mode 100644 (file)
index 0000000..92c6234
--- /dev/null
@@ -0,0 +1,16 @@
+
+CREATE OR REPLACE FUNCTION formatWoNumber(integer) RETURNS TEXT IMMUTABLE AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWoid ALIAS FOR $1;
+
+BEGIN
+
+  RETURN ( SELECT (wo_number::TEXT || ''-'' || wo_subnumber::TEXT)
+           FROM wo
+           WHERE (wo_id=pWoid) );
+
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/formatwooperseq.sql b/foundation-database/public/functions/formatwooperseq.sql
new file mode 100644 (file)
index 0000000..0c80ae2
--- /dev/null
@@ -0,0 +1,20 @@
+CREATE OR REPLACE FUNCTION formatwooperseq(INTEGER) RETURNS TEXT AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWooperId  ALIAS FOR $1;
+  _result    TEXT;
+
+BEGIN
+
+  IF pWooperId = -1 THEN
+    RETURN '';
+  ELSE
+    SELECT wooper_seqnumber INTO _result
+    FROM xtmfg.wooper
+    WHERE (wooper_id=pWooperId);
+  END IF;
+
+  RETURN _result;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/forwardupdateaccount.sql b/foundation-database/public/functions/forwardupdateaccount.sql
new file mode 100644 (file)
index 0000000..0c90ec1
--- /dev/null
@@ -0,0 +1,48 @@
+
+CREATE OR REPLACE FUNCTION forwardUpdateAccount(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pAccntid ALIAS FOR $1;
+  _r RECORD;
+  _trialbalid INTEGER;
+
+BEGIN
+  SELECT trialbal_id, trialbal_dirty,
+         CASE WHEN (trialbal_dirty) THEN 0
+              ELSE 1
+         END AS dirty_seq INTO _r
+    FROM trialbal, period
+   WHERE ((trialbal_period_id=period_id)
+     AND  (trialbal_accnt_id=pAccntid))
+   ORDER BY dirty_seq, period_start
+   LIMIT 1;
+  IF (FOUND) THEN
+    IF (_r.trialbal_dirty) THEN
+      RETURN forwardUpdateTrialBalance(_r.trialbal_id);
+    ELSE
+      RETURN _r.trialbal_id;
+    END IF;
+  ELSE
+      _trialbalid := nextval('trialbal_trialbal_id_seq');
+      
+      INSERT INTO trialbal
+      ( trialbal_id,
+        trialbal_period_id, trialbal_accnt_id,
+        trialbal_beginning, trialbal_ending,
+        trialbal_debits, trialbal_credits, trialbal_dirty )
+      SELECT
+        _trialbalid,
+        period_id, pAccntid,
+        0, 0,
+        0, 0, FALSE
+      FROM period
+      ORDER BY period_start LIMIT 1;
+
+      RETURN forwardUpdateTrialBalance(_trialbalid);
+  END IF;
+
+  RETURN -1;
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/forwardupdateinvbalance.sql b/foundation-database/public/functions/forwardupdateinvbalance.sql
new file mode 100644 (file)
index 0000000..8b9dff2
--- /dev/null
@@ -0,0 +1,97 @@
+
+CREATE OR REPLACE FUNCTION forwardUpdateInvBalance(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pInvbalid ALIAS FOR $1;
+  _p RECORD;
+  _r RECORD;
+  _qohEnding NUMERIC;
+  _valueEnding NUMERIC;
+  _nnEnding NUMERIC;
+  _nnvalEnding NUMERIC;
+
+BEGIN
+
+  SELECT invbal_itemsite_id, 
+         invbal_qoh_ending,
+         invbal_value_ending,
+         invbal_nn_ending,
+         invbal_nnval_ending,
+         period_end INTO _p
+  FROM invbal
+    JOIN period ON (invbal_period_id=period_id)
+    JOIN itemsite ON (invbal_itemsite_id=itemsite_id)
+  WHERE (invbal_id=pInvbalid);
+
+  _qohEnding = _p.invbal_qoh_ending;
+  _valueEnding = _p.invbal_value_ending;
+  _nnEnding = _p.invbal_nn_ending;
+  _nnvalEnding = _p.invbal_nnval_ending;
+
+--  Find all of the subsequent periods and their inventory balance, if they exist
+  FOR _r IN SELECT period_id, period_end,
+                   invbal_id, 
+                   invbal_qty_in, invbal_qty_out,
+                   invbal_value_in, invbal_value_out,
+                   invbal_nn_in, invbal_nn_out,
+                   invbal_nnval_in, invbal_nnval_out
+            FROM period 
+              LEFT OUTER JOIN invbal
+                 ON ( (invbal_period_id=period_id) AND (invbal_itemsite_id=_p.invbal_itemsite_id) )
+            WHERE (period_start > _p.period_end)
+            ORDER BY period_start LOOP
+
+    IF (_r.invbal_id IS NULL) THEN
+
+      INSERT INTO invbal
+      ( invbal_period_id, invbal_itemsite_id,
+        invbal_qoh_beginning, invbal_qoh_ending,
+        invbal_qty_in, invbal_qty_out,
+        invbal_value_beginning, invbal_value_ending,
+        invbal_value_in, invbal_value_out, 
+        invbal_nn_beginning, invbal_nn_ending,
+        invbal_nn_in, invbal_nn_out,
+        invbal_nnval_beginning, invbal_nnval_ending,
+        invbal_nnval_in, invbal_nnval_out, 
+        invbal_dirty )
+      VALUES
+      ( _r.period_id, _p.invbal_itemsite_id,
+        _qohEnding, _qohEnding,
+        0, 0, 
+        _valueEnding, _valueEnding,
+        0, 0,
+        _nnEnding, _nnEnding,
+        0, 0, 
+        _nnvalEnding, _nnvalEnding,
+        0, 0,
+        FALSE );
+    ELSE
+      UPDATE invbal
+      SET invbal_qoh_beginning = (_qohEnding),
+          invbal_qoh_ending = (_qohEnding + _r.invbal_qty_in - _r.invbal_qty_out),
+          invbal_value_beginning = (_valueEnding),
+          invbal_value_ending = (_valueEnding + _r.invbal_value_in - _r.invbal_value_out),
+          invbal_nn_beginning = (_nnEnding),
+          invbal_nn_ending = (_nnEnding + _r.invbal_nn_in - _r.invbal_nn_out),
+          invbal_nnval_beginning = (_nnvalEnding),
+          invbal_nnval_ending = (_nnvalEnding + _r.invbal_nnval_in - _r.invbal_nnval_out),
+          invbal_dirty = FALSE
+      WHERE (invbal_id=_r.invbal_id);
+
+      _qohEnding = (_qohEnding + _r.invbal_qty_in - _r.invbal_qty_out);
+      _valueEnding = (_valueEnding + _r.invbal_value_in - _r.invbal_value_out);
+      _nnEnding = (_nnEnding + _r.invbal_nn_in - _r.invbal_nn_out);
+      _nnvalEnding = (_nnvalEnding + _r.invbal_nnval_in - _r.invbal_nnval_out);
+    END IF;
+  END LOOP;
+
+  UPDATE invbal
+  SET invbal_dirty = false
+  WHERE (invbal_id=pInvbalid);
+
+  RETURN pInvbalid;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/forwardupdateitemsite.sql b/foundation-database/public/functions/forwardupdateitemsite.sql
new file mode 100644 (file)
index 0000000..2ca4d08
--- /dev/null
@@ -0,0 +1,49 @@
+CREATE OR REPLACE FUNCTION forwardUpdateItemsite(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteId ALIAS FOR $1;
+  _r RECORD;
+  _invbalid INTEGER;
+
+BEGIN
+  SELECT invbal_id INTO _r
+  FROM invbal
+      JOIN period ON (invbal_period_id=period_id)
+  WHERE (invbal_itemsite_id=pItemsiteid)
+  ORDER BY period_start
+  LIMIT 1;
+
+  IF (FOUND) THEN
+    RETURN forwardUpdateInvbalance(_r.invbal_id);
+  ELSE
+      _invbalid := nextval('invbal_invbal_id_seq');
+      
+      INSERT INTO invbal
+      ( invbal_id,
+        invbal_period_id, invbal_itemsite_id,
+        invbal_qoh_beginning, invbal_qoh_ending,
+        invbal_qty_in, invbal_qty_out,
+        invbal_value_beginning, invbal_value_ending,
+        invbal_value_in, invbal_value_out,
+        invbal_nn_beginning, invbal_nn_ending,
+        invbal_nn_in, invbal_nn_out,
+        invbal_nnval_beginning, invbal_nnval_ending,
+        invbal_nnval_in, invbal_nnval_out,
+        invbal_dirty )
+      SELECT
+        _invbalid,
+        period_id, pItemsiteid,
+        0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0,
+        false
+      FROM period
+      ORDER BY period_start LIMIT 1;
+
+      RETURN forwardUpdateInvbalance(_invbalid);
+  END IF;
+
+  RETURN -1;
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/forwardupdatetrialbalance.sql b/foundation-database/public/functions/forwardupdatetrialbalance.sql
new file mode 100644 (file)
index 0000000..6f090c3
--- /dev/null
@@ -0,0 +1,104 @@
+
+CREATE OR REPLACE FUNCTION forwardUpdateTrialBalance(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pTrialbalid ALIAS FOR $1;
+  _p RECORD;
+  _r RECORD;
+  _ending NUMERIC;
+  _prevYear INTEGER;
+  _currYear INTEGER;
+  _prevYearClosed BOOLEAN;
+  _currYearClosed BOOLEAN;
+  _result INTEGER;
+
+BEGIN
+
+  SELECT trialbal_accnt_id, trialbal_ending,
+         yearperiod_id, yearperiod_closed,
+         period_end, accnt_type IN ('E', 'R') AS revexp INTO _p
+  FROM trialbal, period, yearperiod, accnt
+  WHERE ( (trialbal_period_id=period_id)
+   AND (yearperiod_id=period_yearperiod_id)
+   AND (trialbal_accnt_id=accnt_id)
+   AND (trialbal_id=pTrialbalid) );
+
+  _ending = _p.trialbal_ending;
+
+  SELECT yearperiod_id, yearperiod_closed INTO _prevYear, _prevYearClosed
+    FROM yearperiod
+   WHERE (_p.period_end BETWEEN yearperiod_start AND yearperiod_end);
+  IF (NOT FOUND) THEN
+    _prevYear := -1;
+    _prevYearClosed := false;
+  END IF;
+
+--  Find all of the subsequent periods and their trialbal, if they exist
+  FOR _r IN SELECT period_id, period_end,
+                   trialbal_id, trialbal_debits, trialbal_credits,
+                   trialbal_yearend
+            FROM period LEFT OUTER JOIN trialbal
+                 ON ( (trialbal_period_id=period_id) AND (trialbal_accnt_id=_p.trialbal_accnt_id) )
+            WHERE (period_start > _p.period_end)
+            ORDER BY period_start LOOP
+
+    SELECT yearperiod_id, yearperiod_closed INTO _currYear, _currYearClosed
+      FROM yearperiod
+     WHERE (_r.period_end BETWEEN yearperiod_start AND yearperiod_end);
+    IF (NOT FOUND) THEN
+      _currYear := -1;
+      _currYearClosed := false;
+    END IF;
+
+    IF (_p.revexp AND _currYear != _prevYear) THEN
+      _ending := 0;
+      IF (_prevYearClosed) THEN
+        SELECT updateRetainedEarnings(_prevYear) INTO _result;
+        IF (_result < 0) THEN
+          RETURN _result;
+        END IF;
+      END IF;
+    END IF;
+
+    _prevYear := _currYear;
+    _prevYearClosed := _currYearClosed;
+
+    IF (_r.trialbal_id IS NULL) THEN
+      -- SELECT SUM(gltrans_amount) INTO _glAmount
+       -- FROM gltrans
+       -- WHERE ( (gltrans_date BETWEEN _r.period_start and _r.period_end )
+         -- AND   (gltrans_accnt_id=_p.trialbal_accnt_id)
+         -- AND   (gltrans_posted) );
+        -- and change 2nd and 3rd VALUES line of INSERT to read
+        --      _ending, _ending + _glAmount,
+        --      noneg(0 - _glAmount), noneg(_glAmount), FALSE );
+
+      INSERT INTO trialbal
+      ( trialbal_period_id, trialbal_accnt_id,
+        trialbal_beginning, trialbal_ending,
+        trialbal_debits, trialbal_credits, trialbal_dirty )
+      VALUES
+      ( _r.period_id, _p.trialbal_accnt_id,
+        _ending, _ending,
+        0, 0, FALSE );
+    ELSE
+      UPDATE trialbal
+      SET trialbal_beginning = (_ending + trialbal_yearend),
+          trialbal_ending = (_ending + trialbal_yearend - _r.trialbal_debits + _r.trialbal_credits),
+          trialbal_dirty = FALSE
+      WHERE (trialbal_id=_r.trialbal_id);
+
+      _ending = (_ending + _r.trialbal_yearend - _r.trialbal_debits + _r.trialbal_credits);
+    END IF;
+  END LOOP;
+
+  UPDATE trialbal
+  SET trialbal_dirty = FALSE
+  WHERE (trialbal_id=pTrialbalid);
+
+  RETURN pTrialbalid;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/freezeaccountingperiod.sql b/foundation-database/public/functions/freezeaccountingperiod.sql
new file mode 100644 (file)
index 0000000..5e96012
--- /dev/null
@@ -0,0 +1,26 @@
+
+CREATE OR REPLACE FUNCTION freezeAccountingPeriod(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pPeriodid ALIAS FOR $1;
+
+BEGIN
+
+--  Check to make use that the period is not already frozen
+  IF ( ( SELECT period_freeze
+         FROM period
+         WHERE (period_id=pPeriodid) ) ) THEN
+    RETURN -2;
+  END IF;
+
+--  Set the period_freeze flag
+  UPDATE period
+  SET period_freeze=TRUE
+  WHERE (period_id=pPeriodid);
+
+  RETURN pPeriodid;
+
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/freightdetail.sql b/foundation-database/public/functions/freightdetail.sql
new file mode 100644 (file)
index 0000000..c554f8a
--- /dev/null
@@ -0,0 +1,160 @@
+
+CREATE OR REPLACE FUNCTION freightDetail(text,integer,integer,integer,date,text,integer)
+  RETURNS SETOF freightData AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple.
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pOrderType ALIAS FOR $1;
+  pOrderId ALIAS FOR $2;
+  pCustId ALIAS FOR $3;
+  pShiptoId ALIAS FOR $4;
+  pOrderDate ALIAS FOR $5;
+  pShipVia ALIAS FOR $6;
+  pCurrId ALIAS FOR $7;
+
+  _row freightData%ROWTYPE;
+  _order RECORD;
+  _weights RECORD;
+  _includepkgweight BOOLEAN := FALSE;
+  _qry TEXT;
+  _debug BOOLEAN := FALSE;
+
+BEGIN
+  IF (_debug) THEN
+    RAISE NOTICE 'pOrderType = %', pOrderType;
+    RAISE NOTICE 'pOrderId = %', pOrderId;
+    RAISE NOTICE 'pCustId = %', pCustId;
+    RAISE NOTICE 'pShiptoId = %', pShiptoId;
+    RAISE NOTICE 'pOrderDate = %', pOrderDate;
+    RAISE NOTICE 'pShipVia = %', pShipVia;
+    RAISE NOTICE 'pCurrId = %', pCurrId;
+  END IF;
+
+  SELECT fetchMetricBool('IncludePackageWeight') INTO _includepkgweight;
+
+  --Get the order header information need to match
+  --against price schedules
+  IF (pOrderType = 'SO') THEN
+    SELECT
+      cust_id AS cust_id,
+      custtype_id,
+      custtype_code,
+      COALESCE(shipto_id, -1) AS shipto_id,
+      COALESCE(shipto_num, '') AS shipto_num,
+      COALESCE(pOrderDate, cohead_orderdate) AS orderdate,
+      COALESCE(pShipVia, cohead_shipvia) AS shipvia,
+      shipto_shipzone_id AS shipzone_id,
+      COALESCE(pCurrId, cohead_curr_id) AS curr_id,
+      currConcat(COALESCE(pCurrId, cohead_curr_id)) AS currAbbr
+    INTO _order
+      FROM cohead
+      JOIN custinfo ON (cust_id=COALESCE(pCustId, cohead_cust_id))
+      JOIN custtype ON (custtype_id=cust_custtype_id)
+      LEFT OUTER JOIN shiptoinfo ON (shipto_id=COALESCE(pShiptoId, cohead_shipto_id))
+    WHERE (cohead_id=pOrderId);
+
+  ELSIF (pOrderType = 'QU') THEN
+    SELECT
+      quhead_cust_id AS cust_id,
+      custtype_id,
+      custtype_code,
+      COALESCE(shipto_id, -1) AS shipto_id,
+      COALESCE(shipto_num, '') AS shipto_num,
+      quhead_quotedate AS orderdate,
+      quhead_shipvia AS shipvia,
+      shipto_shipzone_id AS shipzone_id,
+      quhead_curr_id AS curr_id,
+      currConcat(quhead_curr_id) AS currAbbr
+    INTO _order
+      FROM quhead
+      JOIN custinfo ON (cust_id=quhead_cust_id)
+      JOIN custtype ON (custtype_id=cust_custtype_id)
+      LEFT OUTER JOIN shiptoinfo ON (shipto_id=quhead_shipto_id)
+    WHERE (quhead_id=pOrderId);
+
+  ELSIF (pOrderType = 'RA') THEN
+    SELECT
+      cust_id AS cust_id,
+      custtype_id,
+      custtype_code,
+      COALESCE(shipto_id, -1) AS shipto_id,
+      COALESCE(shipto_num, '') AS shipto_num,
+      COALESCE(pOrderDate, rahead_authdate) AS orderdate,
+      ''::text AS shipvia,
+      shipto_shipzone_id AS shipzone_id,
+      COALESCE(pCurrId, rahead_curr_id) AS curr_id,
+      currConcat(COALESCE(pCurrId, rahead_curr_id)) AS currAbbr
+    INTO _order
+      FROM rahead
+      JOIN custinfo ON (cust_id=COALESCE(pCustId, rahead_cust_id))
+      JOIN custtype ON (custtype_id=cust_custtype_id)
+      LEFT OUTER JOIN shiptoinfo ON (shipto_id=COALESCE(pShiptoId, rahead_shipto_id))
+    WHERE (rahead_id=pOrderId);
+
+  ELSE
+    RAISE EXCEPTION 'Invalid order type.';
+  END IF;
+
+  IF (_debug) THEN
+    RAISE NOTICE 'cust_id = %', _order.cust_id;
+    RAISE NOTICE 'custtype_id = %', _order.custtype_id;
+    RAISE NOTICE 'shipto_id = %', _order.shipto_id;
+    RAISE NOTICE 'shipto_num = %', _order.shipto_num;
+    RAISE NOTICE 'orderdate = %', _order.orderdate;
+    RAISE NOTICE 'shipvia = %', _order.shipvia;
+    RAISE NOTICE 'shipzone_id = %', _order.shipzone_id;
+    RAISE NOTICE 'curr_id = %', _order.curr_id;
+    RAISE NOTICE 'currAbbr = %', _order.currAbbr;
+  END IF;
+
+  --Get a list of aggregated weights from sites and
+  --freight classes used on order lines
+
+  IF (_includePkgWeight) THEN
+    _qry := 'SELECT SUM(orderitem_qty_ordered * orderitem_qty_invuomratio * (item_prodweight + item_packweight)) AS weight, ';
+  ELSE
+    _qry := 'SELECT SUM(orderitem_qty_ordered * orderitem_qty_invuomratio * item_prodweight) AS weight, ';
+  END IF;
+
+  _qry := _qry || 'itemsite_warehous_id, COALESCE(item_freightclass_id, -1) AS item_freightclass_id
+    FROM orderitem
+    JOIN itemsite ON (itemsite_id=orderitem_itemsite_id)
+    JOIN item ON (item_id=itemsite_item_id) ';
+
+  IF (pOrderType = 'RA') THEN
+    _qry := _qry || 'JOIN raitem ON ((orderitem_id=raitem_id)
+    AND (raitem_disposition IN (''C'',''R'',''P''))) ';
+  END IF;
+
+  _qry := _qry || '
+    WHERE ( (orderitem_orderhead_type=' || quote_literal(pOrderType) || ')
+      AND (orderitem_orderhead_id=' || quote_literal(pOrderId) || ')
+      AND (orderitem_status <> ''X'') )
+    GROUP BY itemsite_warehous_id, item_freightclass_id;';
+
+  FOR _weights IN
+    EXECUTE _qry LOOP
+
+    _row := calculateFreightDetail(
+      _order.cust_id, --pCustId
+      _order.custtype_id, --pCustTypeId
+      _order.custtype_code, --pCustTypeCode
+      _order.shipto_id, --pShiptoId
+      _order.shipzone_id, --pShipZoneId
+      _order.shipto_num, --pShiptoNum
+      _order.orderdate, --pOrderDate
+      _order.shipvia, --pShipVia
+      _order.curr_id, --pCurrId
+      _order.currAbbr, --pCurrAbbr
+      _weights.itemsite_warehous_id, --pItemSiteWhsId
+      _weights.item_freightclass_id, --pItemFreightclassId
+      _weights.weight --pWeight
+      );
+
+    RETURN NEXT _row;
+
+  END LOOP;
+  RETURN;
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/freightdetailquote.sql b/foundation-database/public/functions/freightdetailquote.sql
new file mode 100644 (file)
index 0000000..7bc2346
--- /dev/null
@@ -0,0 +1,316 @@
+
+CREATE OR REPLACE FUNCTION freightDetailQuote(integer,text,integer,text,date,text,text,text[][])
+  RETURNS SETOF freightData AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple.
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCustId ALIAS FOR $1;
+  pCustNumber ALIAS FOR $2;
+  pShiptoId ALIAS FOR $3;
+  pShiptoNum ALIAS FOR $4;
+  pOrderDate ALIAS FOR $5;
+  pShipVia ALIAS FOR $6;
+  pItemArrayType ALIAS FOR $7;
+  pItemQty ALIAS FOR $8;
+    -- Array item_id format = ARRAY[['300','3'],['310','50']]
+    -- Array item_number format = ARRAY[['YTRUCK1','3'],['RTRUCK1','50']]
+    -- Array itemsite_id format = ARRAY[['293','3'],['302','50']]
+
+  _cust RECORD;
+  _shipto RECORD;
+  _curr RECORD;
+  _includepkgweight BOOLEAN := FALSE;
+  _order_date DATE;
+  _ship_via TEXT;
+  _item_num RECORD;
+  _item_id RECORD;
+  _weights RECORD;
+  _row freightData%ROWTYPE;
+  _debug BOOLEAN := FALSE;
+
+BEGIN
+-- Parameters are setup to allow this function to be called multiple ways.
+-- Check parameters and lookup what is NULL.
+
+  -- Check pCustId and pCustNumber.
+  IF (pCustId IS NULL AND (pCustNumber IS NULL OR pCustNumber = '')) THEN
+    RAISE EXCEPTION 'You must specify a Customer ID or Number to get a freight quote.';
+  ELSIF (pCustId IS NULL AND pCustNumber IS NOT NULL) THEN
+    -- Get customer info using pCustNumber.
+    SELECT
+      cust_id,
+      cust_number,
+      custtype_id,
+      custtype_code,
+      cust_curr_id,
+      cust_shipvia
+    INTO _cust
+    FROM custinfo
+    LEFT JOIN custtype ON cust_custtype_id = custtype_id
+    WHERE 1=1
+      AND cust_number = pCustNumber;
+  ELSE
+    -- Get customer info using pCustId.
+    SELECT
+      cust_id,
+      cust_number,
+      custtype_id,
+      custtype_code,
+      cust_curr_id,
+      cust_shipvia
+    INTO _cust
+    FROM custinfo
+    LEFT JOIN custtype ON cust_custtype_id = custtype_id
+    WHERE 1=1
+      AND cust_id = pCustId;
+  END IF;
+
+  IF(NOT FOUND) THEN
+    RAISE EXCEPTION 'Invalid Customer specified when trying to get a freight quote.';
+  END IF;
+
+  -- Check pShiptoId and pShiptoNum.
+  IF (pShiptoId IS NULL AND (pShiptoNum IS NULL OR pShiptoNum = '')) THEN
+    -- Get Customer's default shipto.
+    SELECT
+      shipto_id,
+      shipto_name,
+      shipto_shipzone_id,
+      shipto_shipvia,
+      shipto_num
+    INTO _shipto
+    FROM shiptoinfo
+    WHERE 1=1
+      AND shipto_cust_id = _cust.cust_id
+      AND shipto_default;
+  ELSIF (pShiptoId IS NULL AND pShiptoNum IS NOT NULL) THEN
+    -- Get shipto info using pShiptoNum.
+    SELECT
+      shipto_id,
+      shipto_name,
+      shipto_shipzone_id,
+      shipto_shipvia,
+      shipto_num
+    INTO _shipto
+    FROM shiptoinfo
+    WHERE 1=1
+      AND shipto_cust_id = _cust.cust_id
+      AND shipto_num = pShiptoNum;
+  ELSE
+    -- Get shipto info using pShiptoId.
+    SELECT
+      shipto_id,
+      shipto_name,
+      shipto_shipzone_id,
+      shipto_shipvia,
+      shipto_num
+    INTO _shipto
+    FROM shiptoinfo
+    WHERE 1=1
+      AND shipto_cust_id = _cust.cust_id
+      AND shipto_id = pShiptoId;
+  END IF;
+
+  IF(NOT FOUND) THEN
+    RAISE EXCEPTION 'Invalid Ship-to specified when trying to get a freight quote.';
+  END IF;
+
+  -- Get curr info.
+  SELECT
+    curr_id,
+    curr_abbr
+  INTO _curr
+  FROM curr_symbol
+  WHERE 1=1
+    AND curr_id = _cust.cust_curr_id;
+
+  IF(NOT FOUND) THEN
+    RAISE EXCEPTION 'Could not find currency when trying to get a freight quote.';
+  END IF;
+
+  -- Check pOrderDate.
+  IF (pOrderDate IS NULL) THEN
+    _order_date := CURRENT_DATE;
+  ELSE
+    _order_date := pOrderDate;
+  END IF;
+
+  -- Check pShipVia.
+  IF (pShipVia IS NULL OR pShipVia = '') THEN
+    IF (_shipto.shipto_shipvia IS NULL OR _shipto.shipto_shipvia = '') THEN
+      _ship_via := _cust.cust_shipvia;
+    ELSE
+      _ship_via := _shipto.shipto_shipvia;
+    END IF;
+  ELSE
+    _ship_via := pShipVia;
+  END IF;
+
+  -- Determine if package weight should be included in freight calculation.
+  SELECT fetchMetricBool('IncludePackageWeight') INTO _includepkgweight;
+
+  -- Check pItemQty.
+  IF (pItemQty IS NULL OR array_upper(pItemQty,1) IS NULL) THEN
+    -- Item Array is NULL.
+    RAISE EXCEPTION 'You must specify an Item ID, Item Number or Itemsite ID to get a freight quote.';
+  ELSIF (pItemArrayType = 'item_number' AND (array_upper(pItemQty,1) > 0)) THEN
+    -- Using item_number.
+    FOR _weights IN
+      -- Get a list of aggregated weights from sites and freight classes for items.
+      SELECT
+        CASE WHEN _includepkgweight THEN
+          SUM(qty * (item_prodweight + item_packweight))
+        ELSE
+          SUM(qty * (item_prodweight))
+        END AS weight,
+        itemsite_warehous_id,
+        COALESCE(item_freightclass_id, -1) AS item_freightclass_id
+      FROM
+        -- Create item_number -> qty record from array.
+        (SELECT
+          unnest((SELECT pItemQty[1:array_upper(pItemQty,1)][1])) AS item_number,
+          unnest((SELECT pItemQty[1:array_upper(pItemQty,1)][2:array_ndims(pItemQty)]))::numeric AS qty
+        ) AS itemnum_qty
+        JOIN item USING (item_number)
+        JOIN itemsite ON item_id=itemsite_item_id
+      WHERE 1=1
+        AND itemsite_warehous_id = fetchprefwarehousid()
+      GROUP BY
+        itemsite_warehous_id,
+        item_freightclass_id
+    LOOP
+      -- Calculate the freight detail for these item weights.
+      _row := calculateFreightDetail(
+        _cust.cust_id, --pCustId
+        _cust.custtype_id, --pCustTypeId
+        _cust.custtype_code, --pCustTypeCode
+        _shipto.shipto_id, --pShiptoId
+        _shipto.shipto_shipzone_id, --pShipZoneId
+        _shipto.shipto_num, --pShiptoNum
+        _order_date, --pOrderDate
+        _ship_via, --pShipVia
+        _curr.curr_id, --pCurrId
+        _curr.curr_abbr, --pCurrAbbr
+        _weights.itemsite_warehous_id, --pItemSiteWhsId
+        _weights.item_freightclass_id, --pItemFreightclassId
+        _weights.weight --pWeight
+        );
+
+      RETURN NEXT _row;
+    END LOOP;
+
+  ELSIF (pItemArrayType = 'item_id' AND (array_upper(pItemQty,1) > 0)) THEN
+    -- Using item_id.
+    FOR _weights IN
+      -- Get a list of aggregated weights from sites and freight classes for items.
+      SELECT
+        CASE WHEN _includepkgweight THEN
+          SUM(qty * (item_prodweight + item_packweight))
+        ELSE
+          SUM(qty * (item_prodweight))
+        END AS weight,
+        itemsite_warehous_id,
+        COALESCE(item_freightclass_id, -1) AS item_freightclass_id
+      FROM
+        -- Create item_id -> qty record from array.
+        (SELECT
+          unnest((SELECT pItemQty[1:array_upper(pItemQty,1)][1]))::integer AS item_id,
+          unnest((SELECT pItemQty[1:array_upper(pItemQty,1)][2:array_ndims(pItemQty)]))::numeric AS qty
+        ) AS itemid_qty
+        JOIN item USING (item_id)
+        JOIN itemsite ON item_id=itemsite_item_id
+      WHERE 1=1
+        AND itemsite_warehous_id = fetchprefwarehousid()
+      GROUP BY
+        itemsite_warehous_id,
+        item_freightclass_id
+    LOOP
+      -- Calculate the freight detail for these item weights.
+      _row := calculateFreightDetail(
+        _cust.cust_id, --pCustId
+        _cust.custtype_id, --pCustTypeId
+        _cust.custtype_code, --pCustTypeCode
+        _shipto.shipto_id, --pShiptoId
+        _shipto.shipto_shipzone_id, --pShipZoneId
+        _shipto.shipto_num, --pShiptoNum
+        _order_date, --pOrderDate
+        _ship_via, --pShipVia
+        _curr.curr_id, --pCurrId
+        _curr.curr_abbr, --pCurrAbbr
+        _weights.itemsite_warehous_id, --pItemSiteWhsId
+        _weights.item_freightclass_id, --pItemFreightclassId
+        _weights.weight --pWeight
+        );
+
+      RETURN NEXT _row;
+    END LOOP;
+  ELSIF (pItemArrayType = 'itemsite_id' AND (array_upper(pItemQty,1) > 0)) THEN
+    -- Using itemsite_id.
+    FOR _weights IN
+      -- Get a list of aggregated weights from sites and freight classes for items.
+      SELECT
+        CASE WHEN _includepkgweight THEN
+          SUM(qty * (item_prodweight + item_packweight))
+        ELSE
+          SUM(qty * (item_prodweight))
+        END AS weight,
+        itemsite_warehous_id,
+        COALESCE(item_freightclass_id, -1) AS item_freightclass_id
+      FROM
+        -- Create itemsite_id -> qty record from array.
+        (SELECT
+          unnest((SELECT pItemQty[1:array_upper(pItemQty,1)][1]))::integer AS itemsite_id,
+          unnest((SELECT pItemQty[1:array_upper(pItemQty,1)][2:array_ndims(pItemQty)]))::numeric AS qty
+        ) AS itemsiteid_qty
+        JOIN itemsite USING (itemsite_id)
+        JOIN item ON item_id=itemsite_item_id
+      WHERE 1=1
+      GROUP BY
+        itemsite_warehous_id,
+        item_freightclass_id
+    LOOP
+      -- Calculate the freight detail for these item weights.
+      _row := calculateFreightDetail(
+        _cust.cust_id, --pCustId
+        _cust.custtype_id, --pCustTypeId
+        _cust.custtype_code, --pCustTypeCode
+        _shipto.shipto_id, --pShiptoId
+        _shipto.shipto_shipzone_id, --pShipZoneId
+        _shipto.shipto_num, --pShiptoNum
+        _order_date, --pOrderDate
+        _ship_via, --pShipVia
+        _curr.curr_id, --pCurrId
+        _curr.curr_abbr, --pCurrAbbr
+        _weights.itemsite_warehous_id, --pItemSiteWhsId
+        _weights.item_freightclass_id, --pItemFreightclassId
+        _weights.weight --pWeight
+        );
+
+      RETURN NEXT _row;
+    END LOOP;
+  ELSE -- The item array provided is invalid.
+    RAISE EXCEPTION 'The Item/Itemsite array provided when trying to get a freight quote is invalid.';
+  END IF;
+
+  IF(NOT FOUND) THEN
+    RAISE EXCEPTION 'Error trying to aggregated weights when getting a freight quote.';
+  END IF;
+
+  -- Print debug.
+  IF (_debug) THEN
+    RAISE NOTICE 'pCustId = %', _cust.cust_id;
+    RAISE NOTICE 'pCustTypeId = %', _cust.custtype_id;
+    RAISE NOTICE 'pCustTypeCode = %', _cust.custtype_code;
+    RAISE NOTICE 'pShiptoId = %', _shipto.shipto_id;
+    RAISE NOTICE 'pShipZoneId = %', _shipto.shipto_shipzone_id;
+    RAISE NOTICE 'pShiptoNum = %', _shipto.shipto_num;
+    RAISE NOTICE 'pOrderDate = %', _order_date;
+    RAISE NOTICE 'pShipVia = %', _ship_via;
+    RAISE NOTICE 'pCurrId = %', _curr.curr_id;
+    RAISE NOTICE 'pCurrAbbr = %', _curr.curr_abbr;
+  END IF;
+
+  RETURN;
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/freightforrecv.sql b/foundation-database/public/functions/freightforrecv.sql
new file mode 100644 (file)
index 0000000..684de97
--- /dev/null
@@ -0,0 +1,28 @@
+-- if $3 == true then calculate freight on posted records
+-- if $3 == false then calculate freight on unposted records
+CREATE OR REPLACE FUNCTION freightForRecv(TEXT, INTEGER, BOOLEAN) RETURNS NUMERIC AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pordertype   ALIAS FOR $1;
+  porderitemid ALIAS FOR $2;
+  pposted      ALIAS FOR $3;
+  _freight     NUMERIC;
+
+BEGIN
+  IF (pordertype = ''TO'' AND NOT fetchMetricBool(''MultiWhs'')) THEN
+    RETURN 0;
+  ELSIF (pordertype = ''RA'' AND NOT fetchMetricBool(''EnableReturnAuth'')) THEN
+    RETURN 0;
+  END IF;
+
+  SELECT SUM(COALESCE(recv_freight, 0)) INTO _freight
+  FROM recv
+  WHERE ((recv_orderitem_id=porderitemid)
+    AND  (recv_posted = pposted)
+    AND  (recv_order_type=pordertype));
+
+  RETURN COALESCE(_freight, 0.0);
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getactiverevid.sql b/foundation-database/public/functions/getactiverevid.sql
new file mode 100644 (file)
index 0000000..57b809a
--- /dev/null
@@ -0,0 +1,45 @@
+CREATE OR REPLACE FUNCTION getActiveRevId(TEXT,INTEGER) RETURNS INTEGER STABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pTargetType ALIAS FOR $1;
+  pTargetid ALIAS FOR $2;
+  _revid INTEGER;
+
+BEGIN
+  --See if revcontrol turned on
+  IF (fetchmetricbool('RevControl')) THEN
+
+    IF (pTargetType='BOM') THEN
+      SELECT rev_id INTO _revid
+      FROM rev
+      WHERE ((rev_target_type='BOM')
+      AND (rev_target_id=pTargetid)
+      AND (rev_status='A'));
+      IF (NOT FOUND) THEN
+        _revid:=-1;
+      END IF;
+
+      ELSE IF (pTargetType='BOO') THEN
+      SELECT rev_id INTO _revid
+      FROM rev
+      WHERE ((rev_target_type='BOO')
+      AND (rev_target_id=pTargetid)
+      AND (rev_status='A'));
+      IF (NOT FOUND) THEN
+        _revid:=-1;
+      END IF;
+
+      ELSE
+        RAISE EXCEPTION 'Invalid Revision Type';
+      END IF;
+    END IF;
+
+  ELSE
+    _revid:=-1;
+  END IF;
+
+  RETURN _revid;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getaddrid.sql b/foundation-database/public/functions/getaddrid.sql
new file mode 100644 (file)
index 0000000..c632b5b
--- /dev/null
@@ -0,0 +1,22 @@
+CREATE OR REPLACE FUNCTION getAddrId(text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pAddressNumber ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  IF (pAddressNumber IS NULL OR pAddressNumber = '''') THEN
+    RETURN NULL;
+  END IF;
+
+  SELECT addr_id INTO _returnVal
+  FROM addr
+  WHERE (addr_number=pAddressNumber);
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION ''Address Number % not found.'', pAddressNumber;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getadjustmenttaxtypeid.sql b/foundation-database/public/functions/getadjustmenttaxtypeid.sql
new file mode 100644 (file)
index 0000000..a1799c7
--- /dev/null
@@ -0,0 +1,17 @@
+CREATE OR REPLACE FUNCTION getadjustmenttaxtypeid()
+  RETURNS integer AS
+$$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _taxtypeid INTEGER;
+BEGIN
+  SELECT taxtype_id
+    INTO _taxtypeid
+  FROM taxtype
+  WHERE (taxtype_name='Adjustment');
+
+  RETURN _taxtypeid;
+END;
+$$
+  LANGUAGE 'plpgsql' IMMUTABLE;
diff --git a/foundation-database/public/functions/getaropenid.sql b/foundation-database/public/functions/getaropenid.sql
new file mode 100644 (file)
index 0000000..66787a4
--- /dev/null
@@ -0,0 +1,26 @@
+CREATE OR REPLACE FUNCTION getAropenId(text, character, text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCustNumber ALIAS FOR $1;
+  pDocType ALIAS FOR $2;
+  pDocNumber ALIAS FOR $3;
+  _returnVal INTEGER;
+BEGIN
+  IF ((pCustNumber IS NULL) OR (pDocType IS NULL) OR (pDocNumber IS NULL)) THEN
+       RETURN NULL;
+  END IF;
+
+  SELECT aropen_id INTO _returnVal
+  FROM aropen
+  WHERE ((aropen_cust_id=getCustId(pCustNumber,true))
+    AND  (UPPER(aropen_doctype)=UPPER(pDocType))
+    AND  (UPPER(aropen_docnumber)=UPPER(pDocNumber)));
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION ''AR Open Item % not found.'', pDocNumber;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getbankaccntid.sql b/foundation-database/public/functions/getbankaccntid.sql
new file mode 100644 (file)
index 0000000..bd76d94
--- /dev/null
@@ -0,0 +1,22 @@
+CREATE OR REPLACE FUNCTION getBankAccntId(text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pBankAccntName ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  IF (pBankAccntName IS NULL) THEN
+       RETURN NULL;
+  END IF;
+
+  SELECT bankaccnt_id INTO _returnVal
+  FROM bankaccnt
+  WHERE (UPPER(bankaccnt_name)=UPPER(pBankAccntName));
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION ''Bank Account % not found.'', pBankAccntName;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getbomitemid.sql b/foundation-database/public/functions/getbomitemid.sql
new file mode 100644 (file)
index 0000000..36657d1
--- /dev/null
@@ -0,0 +1,25 @@
+CREATE OR REPLACE FUNCTION getBomitemId(text,text,text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemNumber ALIAS FOR $1;
+  pRevision ALIAS FOR $2;
+  pSeqNumber ALIAS FOR $3;
+  _returnVal INTEGER;
+  
+BEGIN
+  IF ((pItemNumber IS NULL) OR (pSeqNumber IS NULL) OR (pItemNumber = '''') OR (pSeqNumber = '''') ) THEN
+    RETURN NULL;
+  END IF;
+
+  SELECT bomitem_id INTO _returnVal
+  FROM bomitem(getItemId(pItemNumber),COALESCE(getRevId(''BOM'',pItemNumber,pRevision)))
+  WHERE (bomitem_seqnumber=pSeqNumber);
+    
+  IF (_returnVal IS NULL) THEN
+    RAISE EXCEPTION ''Sequence % on Bill of Material % Revision % not found.'', pSeqNumber, pItemNumber, pRevision;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getbooitemseqid.sql b/foundation-database/public/functions/getbooitemseqid.sql
new file mode 100644 (file)
index 0000000..ca18cfe
--- /dev/null
@@ -0,0 +1,29 @@
+CREATE OR REPLACE FUNCTION getBooitemSeqId(text,text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemNumber ALIAS FOR $1;
+  pSeqNumber ALIAS FOR $2;
+  _revid INTEGER;
+  _returnVal INTEGER;
+  
+BEGIN
+  IF ((pItemNumber IS NULL) OR (pSeqNumber IS NULL)) THEN
+    RETURN NULL;
+  END IF;
+
+  IF (NOT fetchMetricBool(''Routings'')) THEN
+    RETURN -1;
+  ELSE
+    SELECT booitem_seq_id INTO _returnVal
+    FROM booitem(getItemId(pItemNumber))
+    WHERE (booitem_seqnumber=CAST(pSeqNumber AS integer));
+  END IF;
+    
+  IF (_returnVal IS NULL) THEN
+    RAISE EXCEPTION ''Boo Sequence % for Item % not found.'', pSeqNumber, pItemNumber;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getbudgheadid.sql b/foundation-database/public/functions/getbudgheadid.sql
new file mode 100755 (executable)
index 0000000..eb5aef5
--- /dev/null
@@ -0,0 +1,23 @@
+CREATE OR REPLACE FUNCTION getBudgheadId(text)
+  RETURNS integer AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pBudghead ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  IF (pBudghead IS NULL) THEN
+       RETURN NULL;
+  END IF;
+
+  SELECT budghead_id INTO _returnVal
+  FROM budghead
+  WHERE (budghead_name=(pBudghead));
+
+  IF (_returnVal IS NULL) THEN
+    RAISE EXCEPTION 'Budget % not found.', pBudghead;
+  END IF;
+
+  RETURN _returnVal;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getcashrcptid.sql b/foundation-database/public/functions/getcashrcptid.sql
new file mode 100644 (file)
index 0000000..105895a
--- /dev/null
@@ -0,0 +1,26 @@
+CREATE OR REPLACE FUNCTION getCashrcptId(text, text, text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCustNumber ALIAS FOR $1;
+  pFundsType ALIAS FOR $2;
+  pDocNumber ALIAS FOR $3;
+  _returnVal INTEGER;
+BEGIN
+  IF ((pCustNumber IS NULL) OR (pFundsType IS NULL) OR (pDocNumber IS NULL)) THEN
+       RETURN NULL;
+  END IF;
+
+  SELECT cashrcpt_id INTO _returnVal
+  FROM cashrcpt
+  WHERE ((cashrcpt_cust_id=getCustId(pCustNumber,true))
+    AND  (UPPER(cashrcpt_fundstype)=UPPER(pFundsType))
+    AND  (UPPER(cashrcpt_docnumber)=UPPER(pDocNumber)));
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION ''Cash Receipt % not found.'', pDocNumber;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getcharid.sql b/foundation-database/public/functions/getcharid.sql
new file mode 100644 (file)
index 0000000..ff0779a
--- /dev/null
@@ -0,0 +1,32 @@
+CREATE OR REPLACE FUNCTION getCharId(text, text) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pChar ALIAS FOR $1;
+  pType ALIAS FOR $2;
+  _returnVal INTEGER;
+BEGIN
+  IF (COALESCE(pChar, '') = '') THEN
+    RETURN NULL;
+  END IF;
+
+  SELECT char_id INTO _returnVal
+  FROM char
+  WHERE ((char_name=pChar)
+  AND ((pType IN ('C','CT') AND char_customers)
+    OR (pType IN ('I','SI','QI','W','PI','TI') AND char_items)
+    OR (pType='CRMACCT' AND char_crmaccounts)
+    OR (pType='ADDR' AND char_addresses)
+    OR (pType='CNTCT' AND char_contacts)
+    OR (pType='LS' AND char_lotserial)
+    OR (pType='EMP' AND char_employees)
+    OR (pType='INCDT' AND char_incidents)
+    )) LIMIT 1;
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION 'Characteristic % not found.', pChar;
+  END IF;
+
+  RETURN _returnVal;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getclasscodeid.sql b/foundation-database/public/functions/getclasscodeid.sql
new file mode 100644 (file)
index 0000000..62ff3de
--- /dev/null
@@ -0,0 +1,22 @@
+CREATE OR REPLACE FUNCTION getClassCodeId(text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pClassCode ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  IF (pClassCode IS NULL) THEN
+    RETURN NULL;
+  END IF;
+
+  SELECT classcode_id INTO _returnVal
+  FROM classcode
+  WHERE (classcode_code=pClassCode);
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION ''Class Code % not found.'', pClassCode;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getcmheadid.sql b/foundation-database/public/functions/getcmheadid.sql
new file mode 100644 (file)
index 0000000..c79d086
--- /dev/null
@@ -0,0 +1,34 @@
+CREATE OR REPLACE FUNCTION getCmheadId(text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCreditMemoNumber ALIAS FOR $1;
+BEGIN
+  RETURN getCmheadId(pCreditMemoNumber, NULL);
+END;
+' LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION getCmheadId(text, boolean) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCreditMemoNumber ALIAS FOR $1;
+  pPosted ALIAS FOR $2;
+  _returnVal INTEGER;
+BEGIN
+  IF (pCreditMemoNumber IS NULL) THEN
+    RETURN NULL;
+  END IF;
+
+  SELECT cmhead_id INTO _returnVal
+  FROM cmhead
+  WHERE (UPPER(cmhead_number)=UPPER(pCreditMemoNumber))
+    AND ((pPosted IS NULL) OR (cmhead_posted=pPosted));
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION ''Credit Memo % not found.'', pCreditMemoNumber;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getcmnttypeid.sql b/foundation-database/public/functions/getcmnttypeid.sql
new file mode 100644 (file)
index 0000000..4144f8a
--- /dev/null
@@ -0,0 +1,22 @@
+CREATE OR REPLACE FUNCTION getCmntTypeId(text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCmntType ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  IF (COALESCE(TRIM(pCmntType), '''') = '''') THEN
+    RETURN NULL;
+  END IF;
+
+  SELECT cmnttype_id INTO _returnVal
+  FROM cmnttype
+  WHERE (cmnttype_name=pCmntType) LIMIT 1;
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION ''Comment Type % not found.'', pCmntType;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getcntctid.sql b/foundation-database/public/functions/getcntctid.sql
new file mode 100644 (file)
index 0000000..5bd113f
--- /dev/null
@@ -0,0 +1,38 @@
+CREATE OR REPLACE FUNCTION getCntctId(text) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pContactNumber ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  SELECT getCntctId(pContactNumber,true) INTO _returnVal;
+
+  RETURN _returnVal;
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION getCntctId(text,boolean) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pContactNumber ALIAS FOR $1;
+  pNotFoundErr ALIAS FOR $2;
+  _returnVal INTEGER;
+BEGIN
+  IF (COALESCE(TRIM(pContactNumber), '') = '') THEN
+    RETURN NULL;
+  END IF;
+
+  SELECT cntct_id INTO _returnVal
+  FROM cntct
+  WHERE (cntct_number=pContactNumber);
+
+  IF (_returnVal IS NULL AND pNotFoundErr) THEN
+    RAISE EXCEPTION 'Contact Number % not found.', pContactNumber;
+  ELSIF (_returnVal IS NULL) THEN
+    RETURN NULL;
+  END IF;
+
+  RETURN _returnVal;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getcoheadid.sql b/foundation-database/public/functions/getcoheadid.sql
new file mode 100644 (file)
index 0000000..f55d42b
--- /dev/null
@@ -0,0 +1,22 @@
+CREATE OR REPLACE FUNCTION getCoheadId(text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSalesOrderNumber ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  IF (pSalesOrderNumber IS NULL) THEN
+    RETURN NULL;
+  END IF;
+
+  SELECT cohead_id INTO _returnVal
+  FROM cohead
+  WHERE (cohead_number=pSalesOrderNumber);
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION ''Sales Order % not found.'', pSalesOrderNumber;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getcoitemid.sql b/foundation-database/public/functions/getcoitemid.sql
new file mode 100644 (file)
index 0000000..8cb8b8c
--- /dev/null
@@ -0,0 +1,37 @@
+CREATE OR REPLACE FUNCTION getCoItemId(text,text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSalesOrderNumber    ALIAS FOR $1;
+  pLineNumber          ALIAS FOR $2;
+  _linenumber          INTEGER;
+  _subnumber           INTEGER;
+  _returnVal           INTEGER;
+BEGIN
+  IF (pSalesOrderNumber IS NULL OR pLineNumber IS NULL) THEN
+    RETURN NULL;
+  END IF;
+
+  --Parse Line Number
+  IF (position(''.'' in pLineNumber) > 0) THEN
+    _linenumber        := CAST(substring(pLineNumber from 1 for position(''.'' in pLineNumber)-1) AS INTEGER);
+    _subnumber := CAST(substring(pLineNumber from position(''.'' in pLineNumber)+1 for length(pLineNumber)) AS INTEGER);
+  ELSE
+    _linenumber        := CAST(pLineNumber AS INTEGER);
+    _subnumber := 0;
+  END IF;
+
+  SELECT coitem_id INTO _returnVal
+  FROM cohead, coitem
+  WHERE ((cohead_number=pSalesOrderNumber)
+  AND (coitem_cohead_id=cohead_id)
+  AND (coitem_linenumber=_linenumber)
+  AND (coitem_subnumber=_subnumber));
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION ''Sales Order % not found.'', pSalesOrderNumber;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getcontrcteffective.sql b/foundation-database/public/functions/getcontrcteffective.sql
new file mode 100644 (file)
index 0000000..d0113f0
--- /dev/null
@@ -0,0 +1,21 @@
+CREATE OR REPLACE FUNCTION getContrctEffective(pContrctNumber text) RETURNS DATE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _returnVal DATE;
+BEGIN
+  IF (pContrctNumber IS NULL) THEN
+    RETURN NULL;
+  END IF;
+
+  SELECT contrct_effective INTO _returnVal
+  FROM contrct
+  WHERE (contrct_number=pContrctNumber);
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION 'Contract Number % not found.', pContrctNumber;
+  END IF;
+
+  RETURN _returnVal;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getcontrctexpires.sql b/foundation-database/public/functions/getcontrctexpires.sql
new file mode 100644 (file)
index 0000000..a040704
--- /dev/null
@@ -0,0 +1,21 @@
+CREATE OR REPLACE FUNCTION getContrctExpires(pContrctNumber text) RETURNS DATE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _returnVal DATE;
+BEGIN
+  IF (pContrctNumber IS NULL) THEN
+    RETURN NULL;
+  END IF;
+
+  SELECT contrct_expires INTO _returnVal
+  FROM contrct
+  WHERE (contrct_number=pContrctNumber);
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION 'Contract Number % not found.', pContrctNumber;
+  END IF;
+
+  RETURN _returnVal;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getcontrctid.sql b/foundation-database/public/functions/getcontrctid.sql
new file mode 100644 (file)
index 0000000..dad1ff9
--- /dev/null
@@ -0,0 +1,21 @@
+CREATE OR REPLACE FUNCTION getContrctId(pContrctNumber text) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _returnVal INTEGER;
+BEGIN
+  IF (pContrctNumber IS NULL) THEN
+    RETURN NULL;
+  END IF;
+
+  SELECT contrct_id INTO _returnVal
+  FROM contrct
+  WHERE (contrct_number=pContrctNumber);
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION 'Contract Number % not found.', pContrctNumber;
+  END IF;
+
+  RETURN _returnVal;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getcostcatid.sql b/foundation-database/public/functions/getcostcatid.sql
new file mode 100644 (file)
index 0000000..a40395f
--- /dev/null
@@ -0,0 +1,22 @@
+CREATE OR REPLACE FUNCTION getCostCatId(text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCostCat ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  IF (pCostCat IS NULL) THEN
+       RETURN NULL;
+  END IF;
+
+  SELECT costcat_id INTO _returnVal
+  FROM costcat
+  WHERE (costcat_code=pCostCat);
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION ''Cost Category Code % not found.'', pCostCat;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getcostelemid.sql b/foundation-database/public/functions/getcostelemid.sql
new file mode 100644 (file)
index 0000000..c26f8a6
--- /dev/null
@@ -0,0 +1,22 @@
+CREATE OR REPLACE FUNCTION getCostElemId(text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCostElemType ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  IF (pCostElemType IS NULL) THEN
+       RETURN NULL;
+  END IF;
+
+  SELECT costelem_id INTO _returnVal
+  FROM costelem
+  WHERE (costelem_type=pCostElemType);
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION ''Cost Element % not found.'', pCostElemType;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getcrmacctid.sql b/foundation-database/public/functions/getcrmacctid.sql
new file mode 100644 (file)
index 0000000..9831501
--- /dev/null
@@ -0,0 +1,23 @@
+CREATE OR REPLACE FUNCTION getCrmAcctId(text) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pAcctNumber ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  
+  IF (pAcctNumber IS NULL) THEN
+    RETURN NULL;
+  END IF;
+
+  SELECT crmacct_id INTO _returnVal
+  FROM crmacct
+  WHERE (UPPER(crmacct_number)=UPPER(pAcctNumber));
+  
+  IF (_returnVal IS NULL) THEN
+      RAISE EXCEPTION 'CRM Account Number % not found.', pAcctNumber;
+  END IF;
+
+  RETURN _returnVal;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getcurrid.sql b/foundation-database/public/functions/getcurrid.sql
new file mode 100644 (file)
index 0000000..ff473da
--- /dev/null
@@ -0,0 +1,22 @@
+CREATE OR REPLACE FUNCTION getCurrId(text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCurrName ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  IF (pCurrName IS NULL) THEN
+       RETURN NULL;
+  END IF;
+
+  SELECT curr_id INTO _returnVal
+  FROM curr_symbol
+  WHERE (curr_abbr=pCurrName);
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION ''Currency % not found.'', pCurrName;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getcustid.sql b/foundation-database/public/functions/getcustid.sql
new file mode 100644 (file)
index 0000000..635c593
--- /dev/null
@@ -0,0 +1,46 @@
+CREATE OR REPLACE FUNCTION getCustId(text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCustNumber ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  
+  SELECT getCustId(pCustNumber,false) INTO _returnVal;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION getCustId(text,boolean) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCustNumber ALIAS FOR $1;
+  pInclProspects ALIAS FOR $2;
+  _returnVal INTEGER;
+BEGIN
+  IF (pCustNumber IS NULL) THEN
+    RETURN NULL;
+  END IF;
+
+  SELECT cust_id INTO _returnVal
+  FROM custinfo
+  WHERE (cust_number=UPPER(pCustNumber));
+  
+  IF (_returnVal IS NULL) THEN
+    IF (pInclProspects) THEN
+      SELECT prospect_id INTO _returnVal
+      FROM prospect
+      WHERE (UPPER(prospect_number)=UPPER(pCustNumber));
+      IF (_returnVal IS NULL) THEN
+        RAISE EXCEPTION ''Neither Customer nor Prospect Number % found.'', pCustNumber;
+      END IF;
+    ELSE
+      RAISE EXCEPTION ''Customer Number % not found.'', pCustNumber;
+    END IF;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getcustnamefrominfo.sql b/foundation-database/public/functions/getcustnamefrominfo.sql
new file mode 100644 (file)
index 0000000..29925b2
--- /dev/null
@@ -0,0 +1,81 @@
+CREATE OR REPLACE FUNCTION getCustNameFromInfo(TEXT, TEXT, TEXT, TEXT, TEXT, BOOLEAN) RETURNS TEXT AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _email       TEXT    := COALESCE(UPPER($1), '''');
+  _company     TEXT    := COALESCE(UPPER($2), '''');
+  _first       TEXT    := COALESCE(UPPER($3), '''');
+  _last                TEXT    := COALESCE(UPPER($4), '''');
+  _fullname    TEXT    := COALESCE(UPPER($5), '''');
+  _generate    BOOLEAN := COALESCE($6, FALSE);
+  _counter     INTEGER;
+  _custcount   INTEGER := 0;
+  _custname    TEXT;
+  _candidate   RECORD;
+  _r           RECORD;
+BEGIN
+  IF (_email != '''') THEN
+    SELECT count(*), cust_name INTO _custcount, _custname
+    FROM custinfo LEFT OUTER JOIN cntct ON (cust_cntct_id=cntct_id)
+    WHERE (UPPER(cntct_email)=_email)
+    GROUP BY cust_name;
+    IF (NOT FOUND) THEN
+      _custcount := 0;
+    ELSIF(_custcount = 1) THEN
+      RETURN _custname;
+    END IF;
+  END IF;
+
+  IF (_company != '''') THEN
+    SELECT count(*), cust_name INTO _custcount, _custname
+    FROM custinfo
+    WHERE (UPPER(cust_name)=_company)
+    GROUP BY cust_name;
+    IF (NOT FOUND) THEN
+      _custcount := 0;
+    ELSIF(_custcount = 1) THEN
+      RETURN _custname;
+    END IF;
+  END IF;
+
+  IF (_fullname = '''' AND (_first != '''' OR _last != '''')) THEN
+    _fullname := TRIM(_first || '' '' || _last);
+  END IF;
+
+  IF (_custcount <= 0 AND _fullname != '''') THEN
+    SELECT count(*), cust_name INTO _custcount, _custname
+    FROM custinfo
+    WHERE (UPPER(cust_name)=_fullname)
+    GROUP BY cust_name;
+    IF (NOT FOUND) THEN
+      _custcount := 0;
+    ELSIF(_custcount = 1) THEN
+      RETURN _custname;
+    END IF;
+  END IF;
+
+  IF (_custcount > 1) THEN
+    RAISE EXCEPTION ''Found % possible Customers for % and % and %'',
+                   _custcount, _email, _company, _fullname;
+  END IF;
+
+  IF (_custcount <= 0 AND _generate) THEN
+    IF (_company != '''') THEN
+      RETURN _company;
+    ELSIF (_email != '''') THEN
+      RETURN _email;
+    ELSIF (_fullname != '''') THEN
+      RETURN _fullname;
+    ELSE
+      RAISE EXCEPTION ''Could not generate a new Customer Name without an email address or the name of a company or person'';
+    END IF;
+  END IF;
+
+  IF (_custname IS NULL OR _custname = '''') THEN
+    RAISE EXCEPTION ''Could not find Customer Name for % and %'',
+                   _company, _fullname;
+  END IF;
+
+  RETURN _custname;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getcusttypeid.sql b/foundation-database/public/functions/getcusttypeid.sql
new file mode 100644 (file)
index 0000000..b47894f
--- /dev/null
@@ -0,0 +1,22 @@
+CREATE OR REPLACE FUNCTION getCustTypeId(text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCustTypeCode ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  IF (pCustTypeCode IS NULL) THEN
+    RETURN NULL;
+  END IF;
+
+  SELECT custtype_id INTO _returnVal
+  FROM custtype
+  WHERE (custtype_code=pCustTypeCode);
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION ''Customer Type % not found.'', pCustTypeCode;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getdeptid.sql b/foundation-database/public/functions/getdeptid.sql
new file mode 100644 (file)
index 0000000..cc646d9
--- /dev/null
@@ -0,0 +1,22 @@
+CREATE OR REPLACE FUNCTION getDeptId(text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pDeptNumber ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  IF (COALESCE(TRIM(pDeptNumber), '''') = '''') THEN
+    RETURN NULL;
+  END IF;
+
+  SELECT dept_id INTO _returnVal
+  FROM dept
+  WHERE (UPPER(dept_number)=UPPER(pDeptNumber));
+
+  IF (_returnVal IS NULL) THEN
+    RAISE EXCEPTION ''Department % not found.'', pDeptNumber;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getediprofileid.sql b/foundation-database/public/functions/getediprofileid.sql
new file mode 100644 (file)
index 0000000..aac3d57
--- /dev/null
@@ -0,0 +1,31 @@
+
+CREATE OR REPLACE FUNCTION getEdiProfileId(text) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pEdiProfileName ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  IF (pEdiProfileName IS NULL) THEN
+       RETURN NULL;
+  END IF;
+
+  SELECT pkghead_id INTO _returnVal
+    FROM pkghead
+   WHERE(pkghead_name='xtbatch');
+  IF(NOT FOUND) THEN
+    RETURN NULL;
+  END IF;
+
+  SELECT ediprofile_id INTO _returnVal
+  FROM xtbatch.ediprofile
+  WHERE (ediprofile_name=pEdiProfileName);
+
+  IF (_returnVal IS NULL) THEN
+    RAISE EXCEPTION 'EDI Profile % not found.', pEdiProfileName;
+  END IF;
+
+  RETURN _returnVal;
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/getediprofilename.sql b/foundation-database/public/functions/getediprofilename.sql
new file mode 100644 (file)
index 0000000..59df648
--- /dev/null
@@ -0,0 +1,27 @@
+
+CREATE OR REPLACE FUNCTION getEdiProfileName(INTEGER) RETURNS TEXT AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pEdiProfileId ALIAS FOR $1;
+  _returnVal TEXT;
+BEGIN
+  IF (pEdiProfileId IS NULL) THEN
+       RETURN NULL;
+  END IF;
+
+  SELECT pkghead_name INTO _returnVal
+    FROM pkghead
+   WHERE(pkghead_name='xtbatch');
+  IF(NOT FOUND) THEN
+    RETURN NULL;
+  END IF;
+
+  SELECT ediprofile_name INTO _returnVal
+  FROM xtbatch.ediprofile
+  WHERE (ediprofile_id=pEdiProfileId);
+
+  RETURN _returnVal;
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/geteffectivextuser.sql b/foundation-database/public/functions/geteffectivextuser.sql
new file mode 100644 (file)
index 0000000..f8b1ce1
--- /dev/null
@@ -0,0 +1,25 @@
+CREATE OR REPLACE FUNCTION getEffectiveXtUser() RETURNS TEXT AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+/*
+  The default return value of this function is simply
+  the user currently connected.
+  
+  Overload this function from another schema 
+  to implement specific user handling from an external 
+  application that uses connection pooling. 
+  Use setEffectiveXtUser(text) to create a temporary table that 
+  inserts user data that can in turn be used as a lookup 
+  reference for an over loaded version of this function like so:
+  
+  SELECT effective_value
+  FROM effective_user
+  WHERE effective_key = 'username'
+*/
+
+  RETURN CURRENT_USER;
+
+END;
+$$ LANGUAGE 'plpgsql' STABLE;
+
diff --git a/foundation-database/public/functions/getempid.sql b/foundation-database/public/functions/getempid.sql
new file mode 100644 (file)
index 0000000..ee13021
--- /dev/null
@@ -0,0 +1,22 @@
+CREATE OR REPLACE FUNCTION getEmpId(text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pEmpCode ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  IF (COALESCE(TRIM(pEmpCode), '''') = '''') THEN
+    RETURN NULL;
+  END IF;
+
+  SELECT emp_id INTO _returnVal
+  FROM emp
+  WHERE (UPPER(emp_code)=UPPER(pEmpCode));
+
+  IF (_returnVal IS NULL) THEN
+    RAISE EXCEPTION ''Employee % not found.'', pEmpCode;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getexpcatid.sql b/foundation-database/public/functions/getexpcatid.sql
new file mode 100644 (file)
index 0000000..87132fc
--- /dev/null
@@ -0,0 +1,22 @@
+CREATE OR REPLACE FUNCTION getExpcatId(text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pExpcatCode ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  IF (COALESCE(TRIM(pExpcatCode), '''') = '''') THEN
+    RETURN NULL;
+  END IF;
+
+  SELECT expcat_id INTO _returnVal
+  FROM expcat
+  WHERE (expcat_code=UPPER(pExpcatCode));
+
+  IF (_returnVal IS NULL) THEN
+    RAISE EXCEPTION ''Expense Category % not found.'', pExpcatCode;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getflcoldata.sql b/foundation-database/public/functions/getflcoldata.sql
new file mode 100644 (file)
index 0000000..fc6cfa8
--- /dev/null
@@ -0,0 +1,354 @@
+CREATE OR REPLACE FUNCTION getflcoldata(int4, int4)
+  RETURNS SETOF flcoldata AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pFlcolid ALIAS FOR $1;
+  pPeriodid ALIAS FOR $2;
+  _row flcoldata%ROWTYPE;
+  _r RECORD;
+  _start DATE;
+  _end DATE;
+  _col INTEGER := 1;
+  _mult INTEGER;
+
+BEGIN
+
+--Get Layout Data
+  SELECT * INTO _r
+  FROM flcol
+  WHERE (flcol_id=pFlcolid);
+
+-- Handle Month...
+  IF (_r.flcol_month) THEN
+    SELECT period_start, period_end INTO _start, _end
+    FROM period
+    WHERE (period_id=pPeriodid);
+    
+    IF (_r.flcol_showdb) THEN
+      -- Debits Column
+      _row.flcoldata_column := _col;
+      _row.flcoldata_start := _start;
+      _row.flcoldata_end := _end;
+      RETURN NEXT _row;
+      _col := _col + 1;
+
+      -- Credits Column
+      _row.flcoldata_column := _col;
+      _row.flcoldata_start := _start;
+      _row.flcoldata_end := _end;
+      RETURN NEXT _row;
+      _col := _col + 1;
+    END IF;
+
+    -- Month Column
+    _row.flcoldata_column := _col;
+    _row.flcoldata_start := _start;
+    _row.flcoldata_end := _end;
+    RETURN NEXT _row;
+    _col := _col + 1;
+
+    -- These don't have drill down
+    IF (_r.flcol_prcnt) THEN 
+      _col := _col + 1;
+    END IF;
+    IF (_r.flcol_budget) THEN
+      _col := _col + 1;
+      IF (_r.flcol_budgetprcnt) THEN
+        _col := _col + 1;
+      END IF;
+      IF (_r.flcol_budgetdiff) THEN
+        _col := _col + 1;
+      END IF;
+      IF (_r.flcol_budgetdiffprcnt) THEN
+        _col := _col + 1;
+      END IF;
+    END IF; 
+  END IF;
+
+-- Handle Quarter...
+  IF (_r.flcol_quarter) THEN
+    SELECT min(qtr.period_start), max(qtr.period_end) INTO _start, _end
+    FROM period p
+     JOIN period qtr ON (p.period_quarter=qtr.period_quarter)
+                     AND (p.period_yearperiod_id=qtr.period_yearperiod_id)
+    WHERE (p.period_id=pPeriodid);
+    
+    IF (_r.flcol_showdb) THEN
+      -- Debits Column
+      _row.flcoldata_column := _col;
+      _row.flcoldata_start := _start;
+      _row.flcoldata_end := _end;
+      RETURN NEXT _row;
+      _col := _col + 1;
+
+      -- Credits Column
+      _row.flcoldata_column := _col;
+      _row.flcoldata_start := _start;
+      _row.flcoldata_end := _end;
+      RETURN NEXT _row;
+      _col := _col + 1;
+    END IF;
+
+    -- Quarter Column
+    _row.flcoldata_column := _col;
+    _row.flcoldata_start := _start;
+    _row.flcoldata_end := _end;
+    RETURN NEXT _row;
+    _col := _col + 1;
+
+    -- These don't have drill down
+    IF (_r.flcol_prcnt) THEN 
+      _col := _col + 1;
+    END IF;
+    IF (_r.flcol_budget) THEN
+      _col := _col + 1;
+      IF (_r.flcol_budgetprcnt) THEN
+        _col := _col + 1;
+      END IF;
+      IF (_r.flcol_budgetdiff) THEN
+        _col := _col + 1;
+      END IF;
+      IF (_r.flcol_budgetdiffprcnt) THEN
+        _col := _col + 1;
+      END IF;
+    END IF; 
+  END IF;
+
+-- Handle Year...
+  IF (_r.flcol_year) THEN
+    SELECT yearperiod_start, period_end INTO _start, _end
+    FROM period p
+     JOIN yearperiod ON (period_yearperiod_id=yearperiod_id)
+    WHERE (p.period_id=pPeriodid);
+    
+    IF (_r.flcol_showdb) THEN
+      -- Debits Column
+      _row.flcoldata_column := _col;
+      _row.flcoldata_start := _start;
+      _row.flcoldata_end := _end;
+      RETURN NEXT _row;
+      _col := _col + 1;
+
+      -- Credits Column
+      _row.flcoldata_column := _col;
+      _row.flcoldata_start := _start;
+      _row.flcoldata_end := _end;
+      RETURN NEXT _row;
+      _col := _col + 1;
+    END IF;
+
+    -- Year Column
+    _row.flcoldata_column := _col;
+    _row.flcoldata_start := _start;
+    _row.flcoldata_end := _end;
+    RETURN NEXT _row;
+    _col := _col + 1;
+
+    -- These don't have drill down
+    IF (_r.flcol_prcnt) THEN 
+      _col := _col + 1;
+    END IF;
+    IF (_r.flcol_budget) THEN
+      _col := _col + 1;
+      IF (_r.flcol_budgetprcnt) THEN
+        _col := _col + 1;
+      END IF;
+      IF (_r.flcol_budgetdiff) THEN
+        _col := _col + 1;
+      END IF;
+      IF (_r.flcol_budgetdiffprcnt) THEN
+        _col := _col + 1;
+      END IF;
+    END IF;
+  END IF;
+
+  -- Handle Prior Month...
+  IF (_r.flcol_priormonth) THEN
+    SELECT prv.period_start, prv.period_end INTO _start, _end
+    FROM period p
+      JOIN period prv ON (prv.period_start < p.period_start)
+    WHERE (p.period_id=pPeriodid)
+    ORDER BY prv.period_start DESC
+    LIMIT 1;
+
+    -- Prior Month Column
+    _row.flcoldata_column := _col;
+    _row.flcoldata_start := _start;
+    _row.flcoldata_end := _end;
+    RETURN NEXT _row;
+    _col := _col + 1;
+
+    -- These don't have drill down
+    IF (_r.flcol_priorprcnt) THEN 
+      _col := _col + 1;
+    END IF;
+    IF (_r.flcol_priordiff) THEN 
+      _col := _col + 1;
+    END IF;
+    IF (_r.flcol_priordiffprcnt) THEN 
+      _col := _col + 1;
+    END IF;
+  END IF;
+
+-- Handle Prior Quarter...
+  IF (_r.flcol_priorquarter) THEN
+    IF (_r.flcol_priortype = 'P') THEN
+      -- Prior Quarter
+      SELECT min(period_start), max(period_end)
+      INTO _start, _end
+      FROM (
+        SELECT prv.period_start, prv.period_end, prv.period_quarter, prv.period_yearperiod_id
+        FROM period p
+          JOIN period prv ON (prv.period_start < p.period_start)
+                          AND (prv.period_quarter != p.period_quarter)
+        WHERE (p.period_id=pPeriodid)) data
+      GROUP BY period_quarter, period_yearperiod_id
+      ORDER BY min(period_start) DESC
+      LIMIT 1;
+    ELSE
+      -- Prior Year Quarter
+      SELECT min(period_start), max(period_end)
+      INTO _start, _end
+      FROM (
+        SELECT prv.period_start, prv.period_end, prv.period_quarter, prv.period_yearperiod_id
+        FROM period p
+          JOIN period prv ON (prv.period_start < p.period_start)
+                          AND (prv.period_yearperiod_id != p.period_yearperiod_id)
+                          AND (prv.period_quarter = p.period_quarter)
+        WHERE (p.period_id=pPeriodid)) data
+      GROUP BY period_quarter, period_yearperiod_id
+      ORDER BY min(period_start) DESC
+      LIMIT 1;
+    END IF;
+
+    -- Prior Quarter Column
+    _row.flcoldata_column := _col;
+    _row.flcoldata_start := _start;
+    _row.flcoldata_end := _end;
+    RETURN NEXT _row;
+    _col := _col + 1;
+
+    -- These don't have drill down
+    IF (_r.flcol_priorprcnt) THEN 
+      _col := _col + 1;
+    END IF;
+    IF (_r.flcol_priordiff) THEN 
+      _col := _col + 1;
+    END IF;
+    IF (_r.flcol_priordiffprcnt) THEN 
+      _col := _col + 1;
+    END IF;
+  END IF;
+
+  -- Handle Prior Year...
+  IF (_r.flcol_prioryear IN ('D','F')) THEN
+    IF (_r.flcol_prioryear = 'D') THEN
+      -- Prior Year to Date
+      SELECT yearperiod_start, prv.period_end INTO _start, _end
+      FROM period p
+        JOIN period prv ON (prv.period_number = p.period_number)
+                        AND (prv.period_yearperiod_id != p.period_yearperiod_id)
+                        AND (prv.period_start < p.period_start)
+        JOIN yearperiod ON (prv.period_yearperiod_id=yearperiod_id)   
+      WHERE (p.period_id=pPeriodid)
+      ORDER BY prv.period_start DESC
+      LIMIT 1;
+    ELSE
+      -- Prior Full Year
+      SELECT prv.yearperiod_start, prv.yearperiod_end INTO _start, _end
+      FROM period p
+        JOIN yearperiod cur ON (cur.yearperiod_id=p.period_yearperiod_id)
+        JOIN yearperiod prv ON (prv.yearperiod_start < cur.yearperiod_start)
+      WHERE (p.period_id=pPeriodid)
+      ORDER BY prv.yearperiod_start DESC
+      LIMIT 1;
+    END IF;
+
+    -- Prior Year Column
+    _row.flcoldata_column := _col;
+    _row.flcoldata_start := _start;
+    _row.flcoldata_end := _end;
+    RETURN NEXT _row;
+    _col := _col + 1;
+
+  END IF;
+
+  RETURN;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION getflcoldata(char(1), int[], boolean)
+  RETURNS SETOF flcoldata AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pInterval ALIAS FOR $1;
+  pPeriodids ALIAS FOR $2;
+  pBudgets ALIAS FOR $3;
+  _row flcoldata%ROWTYPE;
+  _r RECORD;
+  _start DATE;
+  _end DATE;
+  _col INTEGER := 1;
+  _count INTEGER;
+  _i INTEGER := 1;
+  _incr INTEGER := 1;
+
+BEGIN
+
+  IF (pBudgets) THEN
+    _col := 2;
+    _incr := 2;
+  END IF;
+  
+  _count := ARRAY_UPPER(pPeriodIds,1);
+  
+  IF (pInterval = 'M') THEN
+    FOR _i IN 1.._count
+    LOOP
+      SELECT period_start, period_end INTO _start, _end
+      FROM period
+      WHERE (period_id=pPeriodids[_i]);
+
+      _row.flcoldata_column := _col;
+      _row.flcoldata_start := _start;
+      _row.flcoldata_end := _end;
+      RETURN NEXT _row;
+      _col := _col + _incr;
+    END LOOP;
+  ELSIF (pInterval = 'Q') THEN
+    FOR _i IN 1.._count
+    LOOP
+      SELECT min(qtr.period_start), max(qtr.period_end) INTO _start, _end
+      FROM period cur
+        JOIN period qtr ON (cur.period_yearperiod_id=qtr.period_yearperiod_id)
+                        AND (cur.period_quarter=qtr.period_quarter)
+      WHERE (cur.period_id=pPeriodids[_i]);
+
+      _row.flcoldata_column := _col;
+      _row.flcoldata_start := _start;
+      _row.flcoldata_end := _end;
+      RETURN NEXT _row;
+      _col := _col + _incr;
+    END LOOP;
+  ELSE
+    FOR _i IN 1.._count
+    LOOP
+      SELECT yearperiod_start, yearperiod_end INTO _start, _end
+      FROM period
+        JOIN yearperiod ON (period_yearperiod_id=yearperiod_id)
+      WHERE (period_id=pPeriodids[_i]);
+
+      _row.flcoldata_column := _col;
+      _row.flcoldata_start := _start;
+      _row.flcoldata_end := _end;
+      RETURN NEXT _row;
+      _col := _col + _incr;
+    END LOOP;
+  END IF;
+  RETURN;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getflstmthead.sql b/foundation-database/public/functions/getflstmthead.sql
new file mode 100644 (file)
index 0000000..9463920
--- /dev/null
@@ -0,0 +1,213 @@
+CREATE OR REPLACE FUNCTION getflstmthead(int4, int4)
+  RETURNS SETOF flstmthead AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pFlcolid ALIAS FOR $1;
+  pPeriodid ALIAS FOR $2;
+  _row flstmthead%ROWTYPE;
+  _p RECORD;
+  _month TEXT;
+  _qtr TEXT;
+  _year TEXT;
+  _prmonth TEXT;
+  _prqtr TEXT;
+  _pryear TEXT;
+  _err TEXT;
+
+BEGIN
+
+  SELECT 'No Data' INTO _err;
+
+--Get Layout Data
+  SELECT flcol_priortype, flcol_prioryear INTO _p
+  FROM flcol
+  WHERE (flcol_id=pFlcolid);
+
+--get data...
+--...for current Month
+        SELECT
+          (CASE
+                      WHEN period_name='' THEN
+                        formatdate(period_start) || '-' || formatdate(period_end)
+                      ELSE period_name
+          END) INTO _month
+        FROM period
+        WHERE (period_id=pPeriodId);
+
+        IF _month IS NULL THEN
+          _month := _err;
+        END IF;
+
+--...for Quarter
+        SELECT
+          ('Q' || period_quarter || '-' || EXTRACT(year from yearperiod_end)) INTO _qtr
+        FROM period, yearperiod
+        WHERE ((period_id=pPeriodId)
+        AND (period_yearperiod_id=yearperiod_id));
+
+        IF _qtr IS NULL THEN
+          _qtr := _err;
+        END IF;
+
+--...for Year
+        SELECT
+          COALESCE((CASE WHEN period_name='' THEN
+                (formatdate(period_start) || '-' || formatdate(period_end) || ' YTD')
+          ELSE (period_name || ' YTD')
+          END),'No Data') INTO _year
+        FROM period
+        WHERE (period_id=pPeriodId);
+
+        IF _year IS NULL THEN
+          _year := _err;
+        END IF;
+
+--...for prior month
+
+        IF (_p.flcol_priortype = 'P') THEN
+
+          SELECT
+            (CASE WHEN pp.period_name='' THEN
+              formatdate(pp.period_start) || '-' || formatdate(pp.period_end)
+            ELSE pp.period_name END) INTO _prmonth
+          FROM period cp, period pp
+          WHERE ((cp.period_id=pPeriodId)
+          AND (cp.period_start > pp.period_start))
+          ORDER BY pp.period_start DESC LIMIT 1;
+
+        ELSE
+
+          SELECT
+            (CASE WHEN pp.period_name='' THEN
+              formatdate(pp.period_start) || '-' || formatdate(pp.period_end)
+            ELSE pp.period_name END) INTO _prmonth
+          FROM period cp, period pp
+          WHERE ((cp.period_id=pPeriodId)
+           AND (cp.period_id != pp.period_id)
+           AND (cp.period_start > pp.period_start)
+           AND (cp.period_number = pp.period_number))
+          ORDER BY pp.period_start DESC LIMIT 1;
+
+        END IF;
+
+          IF _prmonth IS NULL THEN
+            _prmonth := _err;
+          END IF;
+
+
+--...for prior quarter
+
+        IF (_p.flcol_priortype='P') THEN
+
+          SELECT ('Q' || pp.period_quarter || '-' || EXTRACT(year from yearperiod_end)) INTO _prqtr
+          FROM period cp, period pp, yearperiod
+          WHERE ((cp.period_id=pPeriodId)
+          AND (cp.period_start > pp.period_start)
+          AND (pp.period_quarter=
+            CASE WHEN cp.period_quarter > 1 THEN
+              cp.period_quarter - 1
+          ELSE 4 END)
+          AND (pp.period_start >= cp.period_start - interval '1 year')
+          AND (pp.period_yearperiod_id=yearperiod_id))
+          ORDER BY pp.period_start DESC LIMIT 1;
+
+        ELSE
+
+          SELECT
+            ('Q' || pp.period_quarter || '-' || EXTRACT(year from pp.period_start)) INTO _prqtr
+          FROM period cp, period pp, yearperiod cy, yearperiod py
+          WHERE ((cp.period_id=pPeriodId)
+          AND (cp.period_yearperiod_id=cy.yearperiod_id)
+          AND (pp.period_yearperiod_id=py.yearperiod_id)
+          AND (cp.period_quarter=pp.period_quarter)
+          AND (cy.yearperiod_start > py.yearperiod_start))
+          ORDER BY py.yearperiod_start DESC, pp.period_start DESC LIMIT 1;
+
+        END IF;
+
+        IF _prqtr IS NULL THEN
+          _prqtr := _err;
+        END IF;
+
+--...for prior year
+
+        IF (_p.flcol_prioryear='F') THEN
+
+          SELECT (EXTRACT(year from py.yearperiod_end)||'') INTO _pryear
+          FROM period cp, yearperiod cy, yearperiod py
+          WHERE ((cp.period_id=pPeriodId)
+           AND (cp.period_yearperiod_id = cy.yearperiod_id)
+           AND (cy.yearperiod_start > py.yearperiod_start))
+          ORDER BY py.yearperiod_start DESC LIMIT 1;
+
+        ELSE
+
+          SELECT
+          (CASE
+                      WHEN pp.period_name='' THEN
+                        formatdate(pp.period_start) || '-' || formatdate(pp.period_end) || ' YTD'
+                      ELSE pp.period_name || ' YTD'
+          END) INTO _pryear
+          FROM period cp, period pp
+          WHERE ((cp.period_id=pPeriodId)
+            AND (cp.period_number = pp.period_number)
+            AND (cp.period_start > pp.period_start))
+          ORDER BY pp.period_start DESC LIMIT 1;
+
+        END IF;
+
+        IF _pryear IS NULL THEN
+          _pryear := _err;
+        END IF;
+
+-- RETURN RESULTS
+
+        SELECT
+                flhead_id AS flstmthead_flhead_id,
+                flcol_id AS flstmthead_flcol_id,
+                pPeriodid AS flstmthead_period,
+                getEffectiveXtUser() AS flstmthead_username,
+                CASE
+                        WHEN flhead_type = 'I' THEN 'Income Statement'
+                        WHEN flhead_type = 'B' THEN 'Balance Sheet'
+                        WHEN flhead_type = 'C' THEN 'Cash Flow Statement'
+                        ELSE 'N/A'
+                END AS flstmthead_flhead_typedescrip1,
+                CASE
+                        WHEN flhead_type = 'I' THEN 'Income'
+                        WHEN flhead_type = 'B' THEN 'Balance'
+                        WHEN flhead_type = 'C' THEN 'Cash'
+                        ELSE 'N/A'
+                END AS flstmthead_flhead_typedescrip2,
+                flhead_name AS flstmthead_flhead_name,
+                flcol_name AS flstmthead_flcol_name,
+                _month AS flstmthead_month,
+                _qtr AS flstmthead_qtr,
+                _year AS flstmthead_year,
+                _prmonth AS flstmthead_prmonth,
+                _prqtr AS flstmthead_prqtr,
+                _pryear AS flstmthead_pryear INTO _p
+        FROM flhead,flcol
+        WHERE ((flcol_id=pFlcolid)
+        AND (flhead_id=flcol_flhead_id));
+
+                _row.flstmthead_flhead_id := _p.flstmthead_flhead_id;
+                _row.flstmthead_flcol_id := _p.flstmthead_flcol_id;
+                _row.flstmthead_period_id := _p.flstmthead_period;
+                _row.flstmthead_username := _p.flstmthead_username;
+                _row.flstmthead_typedescrip1 := _p.flstmthead_flhead_typedescrip1;
+                _row.flstmthead_typedescrip2 := _p.flstmthead_flhead_typedescrip2;
+                _row.flstmthead_flhead_name := _p.flstmthead_flhead_name;
+                _row.flstmthead_flcol_name := _p.flstmthead_flcol_name;
+                _row.flstmthead_month := _p.flstmthead_month;
+                _row.flstmthead_qtr := _p.flstmthead_qtr;
+                _row.flstmthead_year := _p.flstmthead_year;
+                _row.flstmthead_prmonth := _p.flstmthead_prmonth;
+                _row.flstmthead_prqtr := _p.flstmthead_prqtr;
+                _row.flstmthead_pryear := _p.flstmthead_pryear;
+
+        RETURN NEXT _row;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getfltrendhead.sql b/foundation-database/public/functions/getfltrendhead.sql
new file mode 100644 (file)
index 0000000..a945077
--- /dev/null
@@ -0,0 +1,117 @@
+CREATE OR REPLACE FUNCTION getfltrendhead(INTEGER, _int4, bpChar)
+  RETURNS SETOF fltrendhead AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pFlheadid ALIAS FOR $1;
+  pPeriodids ALIAS FOR $2;
+  pInterval ALIAS FOR $3;
+  _row fltrendhead%ROWTYPE;
+  _p RECORD;
+  _count INTEGER;
+  _i INTEGER;
+  _t TEXT;
+  _fld TEXT[];
+  _type CHAR;
+
+BEGIN
+
+-- Validate Interval
+   IF pInterval <> ''M'' AND pInterval <> ''Q'' AND pInterval <> ''Y'' THEN
+     RAISE EXCEPTION ''Invalid Interval --> %'', pInterval;
+   END IF;
+
+   IF ARRAY_UPPER(pPeriodIds,1) <= 12 THEN
+        _count := ARRAY_UPPER(pPeriodIds,1);
+   ELSE
+        _count := 12;
+   END IF;
+
+   SELECT flhead_type INTO _type FROM flhead WHERE flhead_id = pFlheadId;
+
+--get data...
+--...for Month
+        IF (pInterval = ''M'') THEN
+                FOR _i IN 1.._count
+                LOOP
+                        SELECT
+                        (CASE
+                                WHEN period_name='''' THEN
+                                        formatdate(period_start) || ''-'' || formatdate(period_end)
+                                ELSE period_name
+                        END) INTO _t
+                        FROM period
+                        WHERE (period_id=pPeriodIds[_i]);
+
+                        _fld[_i] := _t;
+
+                END LOOP;
+
+--...for Quarter
+                ELSE IF (pInterval = ''Q'') THEN
+                        FOR _i IN 1.._count
+                        LOOP
+                                SELECT
+                                        (''Q'' || period_quarter || ''-'' || EXTRACT(year from yearperiod_end)) INTO _t
+                                FROM period, yearperiod
+                                WHERE ((period_id=pPeriodIds[_i])
+                                AND (period_yearperiod_id=yearperiod_id));
+
+                                        _fld[_i] := _t;
+
+                        END LOOP;
+--...for Year
+                ELSE
+                        FOR _i IN 1.._count
+                        LOOP
+                                SELECT (EXTRACT(year from yearperiod_end)||'''') INTO _t
+                                FROM period, yearperiod
+                                WHERE ((period_id=pPeriodIds[_i])
+                                AND (period_yearperiod_id=yearperiod_id));
+
+                                _fld[_i] := _t;
+
+                        END LOOP;
+                END IF;
+        END IF;
+
+
+-- RETURN RESULTS
+
+        SELECT
+                flhead_id AS fltrendhead_flhead_id,
+                getEffectiveXtUser() AS fltrendhead_username,
+                CASE
+                        WHEN flhead_type = ''I'' THEN ''Income Statement''
+                        WHEN flhead_type = ''B'' THEN ''Balance Sheet''
+                        WHEN flhead_type = ''C'' THEN ''Cash Flow Statement''
+                        ELSE ''Ad Hoc''
+                END AS fltrendhead_flhead_typedescrip,
+                flhead_name AS fltrendhead_flhead_name INTO _p
+        FROM flhead
+        WHERE (flhead_id=pFlheadId);
+
+                _row.fltrendhead_flhead_id := _p.fltrendhead_flhead_id;
+                _row.fltrendhead_username := _p.fltrendhead_username;
+               _row.fltrendhead_typedescrip := _p.fltrendhead_flhead_typedescrip;
+                _row.fltrendhead_flhead_name := _p.fltrendhead_flhead_name;
+                _row.fltrendhead_fld1 := _fld[1];
+                _row.fltrendhead_fld2 := _fld[2];
+                _row.fltrendhead_fld3 := _fld[3];
+                _row.fltrendhead_fld4 := _fld[4];
+                _row.fltrendhead_fld5 := _fld[5];
+                _row.fltrendhead_fld6 := _fld[6];
+                _row.fltrendhead_fld7 := _fld[7];
+                _row.fltrendhead_fld8 := _fld[8];
+                _row.fltrendhead_fld9 := _fld[9];
+                _row.fltrendhead_fld10 := _fld[10];
+                _row.fltrendhead_fld11 := _fld[11];
+                _row.fltrendhead_fld12 := _fld[12];
+                IF (_type IN (''I'',''C'')) THEN
+                        _row.fltrendhead_grndttl := ''Total'';
+                END IF;
+
+        RETURN NEXT _row;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getfreightclassid.sql b/foundation-database/public/functions/getfreightclassid.sql
new file mode 100644 (file)
index 0000000..aee14ae
--- /dev/null
@@ -0,0 +1,22 @@
+CREATE OR REPLACE FUNCTION getFreightClassId(text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pFreightClassCode ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  IF (pFreightClassCode IS NULL) THEN
+    RETURN NULL;
+  END IF;
+
+  SELECT freightclass_id INTO _returnVal
+  FROM freightclass
+  WHERE (freightclass_code=pFreightClassCode);
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION ''Freight Class % not found.'', pFreightClassCode;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getfreighttaxtypeid.sql b/foundation-database/public/functions/getfreighttaxtypeid.sql
new file mode 100644 (file)
index 0000000..4839e6c
--- /dev/null
@@ -0,0 +1,14 @@
+CREATE OR REPLACE FUNCTION getFreightTaxTypeId() RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _taxtypeid INTEGER;
+BEGIN
+  SELECT taxtype_id
+    INTO _taxtypeid
+    FROM taxtype
+   WHERE (taxtype_name=''Freight'');
+
+  RETURN _taxtypeid;
+END;
+' LANGUAGE 'plpgsql' IMMUTABLE;
diff --git a/foundation-database/public/functions/getgainlossaccntid.sql b/foundation-database/public/functions/getgainlossaccntid.sql
new file mode 100644 (file)
index 0000000..db3068c
--- /dev/null
@@ -0,0 +1,29 @@
+
+CREATE OR REPLACE FUNCTION getGainLossAccntId(integer) RETURNS INTEGER STABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pAccntId ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  IF ( (pAccntId = 0) OR (pAccntId IS NULL) ) THEN
+       RETURN 0;
+  END IF;
+
+  IF (fetchMetricValue('GLCompanySize') = 0) THEN
+    _returnVal := fetchMetricValue('CurrencyGainLossAccount')::integer;
+  ELSE
+    SELECT company_gainloss_accnt_id INTO _returnVal
+    FROM company
+      JOIN accnt ON (company_number=accnt_company)
+    WHERE (accnt_id=pAccntId);
+  END IF;
+
+  IF (_returnVal IS NULL) THEN
+    RAISE EXCEPTION 'Currency Gain/Loss Account not found for %', formatGlAccountLong(pAccntId);
+  END IF;
+
+  RETURN _returnVal;
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/getglaccntid.sql b/foundation-database/public/functions/getglaccntid.sql
new file mode 100644 (file)
index 0000000..54e37af
--- /dev/null
@@ -0,0 +1,70 @@
+
+CREATE OR REPLACE FUNCTION getGlAccntId(text) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pGlAccnt ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  IF (pGlAccnt IS NULL) THEN
+       RETURN NULL;
+  END IF;
+
+  SELECT accnt_id INTO _returnVal
+  FROM accnt
+  WHERE (formatglaccount(accnt_id)=pGlAccnt);
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION 'Account Number % not found.', pGlAccnt;
+  END IF;
+
+  RETURN _returnVal;
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION getglaccntid(TEXT,TEXT,TEXT,TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCompany ALIAS FOR $1;
+  pProfit ALIAS FOR $2;
+  pGlAccnt ALIAS FOR $3;
+  pSub ALIAS FOR $4;
+  _account TEXT;
+  _returnVal INTEGER;
+BEGIN
+  IF (pGlAccnt IS NULL) THEN
+       RETURN NULL;
+  END IF;
+
+  IF (pCompany is not null) THEN
+    _account := pCompany || '-';
+  END IF;
+
+  IF (pProfit is not null) THEN
+    _account := _account || pProfit || '-';
+  END IF;
+  IF (pGlAccnt is not null) THEN
+    if (_account is null) then
+       _account := pGlAccnt;
+    else
+       _account := _account || pGlAccnt;
+    end if;
+  END IF;
+
+  IF (pSub is not null) THEN
+    _account := _account || '-' || pSub;
+  END IF;
+
+  SELECT accnt_id INTO _returnVal
+  FROM accnt
+  WHERE (formatglaccount(accnt_id)=_account);
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION 'Account Number % not found.', _account;
+  END IF;
+
+  RETURN _returnVal;
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/getimageid.sql b/foundation-database/public/functions/getimageid.sql
new file mode 100644 (file)
index 0000000..8124a37
--- /dev/null
@@ -0,0 +1,22 @@
+CREATE OR REPLACE FUNCTION getImageId(text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pImageName ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  IF (COALESCE(TRIM(pImageName), '''') = '''') THEN
+    RETURN NULL;
+  END IF;
+
+  SELECT image_id INTO _returnVal
+  FROM image
+  WHERE (image_name=pImageName);
+
+  IF (_returnVal IS NULL) THEN
+    RAISE EXCEPTION ''Image % not found.'', pImageName;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getincdtcatid.sql b/foundation-database/public/functions/getincdtcatid.sql
new file mode 100644 (file)
index 0000000..81e3c8c
--- /dev/null
@@ -0,0 +1,24 @@
+
+CREATE OR REPLACE FUNCTION getIncdtCatId(text) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pIncdtCatName ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  IF (pIncdtCatName IS NULL) THEN
+       RETURN NULL;
+  END IF;
+
+  SELECT incdtcat_id INTO _returnVal
+  FROM incdtcat
+  WHERE (incdtcat_name=pIncdtCatName);
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION 'Incident Category Name % not found.', pIncdtCatName;
+  END IF;
+
+  RETURN _returnVal;
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/getincdtcrmacctid.sql b/foundation-database/public/functions/getincdtcrmacctid.sql
new file mode 100644 (file)
index 0000000..9a8fb1c
--- /dev/null
@@ -0,0 +1,22 @@
+CREATE OR REPLACE FUNCTION getIncdtCrmAcctId(integer) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pIncidentNumber ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  IF (pIncidentNumber IS NULL) THEN
+       RETURN NULL;
+  END IF;
+
+  SELECT incdt_crmacct_id INTO _returnVal
+  FROM incdt
+  WHERE (incdt_number=pIncidentNumber);
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION ''Incident Number % not found.'', pIncidentNumber;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getincdtpriorityid.sql b/foundation-database/public/functions/getincdtpriorityid.sql
new file mode 100644 (file)
index 0000000..688b003
--- /dev/null
@@ -0,0 +1,22 @@
+CREATE OR REPLACE FUNCTION getIncdtPriorityId(text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pIncdtPriorityName ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  IF (pIncdtPriorityName IS NULL) THEN
+       RETURN NULL;
+  END IF;
+
+  SELECT incdtpriority_id INTO _returnVal
+  FROM incdtpriority
+  WHERE (incdtpriority_name=pIncdtPriorityName);
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION ''Incident Priority Name % not found.'', pIncdtPriorityName;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getincdtresolutionid.sql b/foundation-database/public/functions/getincdtresolutionid.sql
new file mode 100644 (file)
index 0000000..82d45b0
--- /dev/null
@@ -0,0 +1,22 @@
+CREATE OR REPLACE FUNCTION getIncdtResolutionId(text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pIncdtResolutionName ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  IF (pIncdtResolutionName IS NULL) THEN
+       RETURN NULL;
+  END IF;
+
+  SELECT incdtresolution_id INTO _returnVal
+  FROM incdtresolution
+  WHERE (incdtresolution_name=pIncdtResolutionName);
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION ''Incident Resolution Name % not found.'', pIncdtResolutionName;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getincdtseverityid.sql b/foundation-database/public/functions/getincdtseverityid.sql
new file mode 100644 (file)
index 0000000..9c6d985
--- /dev/null
@@ -0,0 +1,22 @@
+CREATE OR REPLACE FUNCTION getIncdtSeverityId(text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pIncdtSeverityName ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  IF (pIncdtSeverityName IS NULL) THEN
+       RETURN NULL;
+  END IF;
+
+  SELECT incdtseverity_id INTO _returnVal
+  FROM incdtseverity
+  WHERE (incdtseverity_name=pIncdtSeverityName);
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION ''Incident Severity Name % not found.'', pIncdtSeverityName;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getincidentid.sql b/foundation-database/public/functions/getincidentid.sql
new file mode 100644 (file)
index 0000000..3e1dd0e
--- /dev/null
@@ -0,0 +1,22 @@
+CREATE OR REPLACE FUNCTION getIncidentId(integer) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pIncidentNumber ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  IF (pIncidentNumber IS NULL) THEN
+       RETURN NULL;
+  END IF;
+
+  SELECT incdt_id INTO _returnVal
+  FROM incdt
+  WHERE (incdt_number=pIncidentNumber);
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION ''Incident Number % not found.'', pIncidentNumber;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getinvcheadid.sql b/foundation-database/public/functions/getinvcheadid.sql
new file mode 100644 (file)
index 0000000..c4adb90
--- /dev/null
@@ -0,0 +1,22 @@
+CREATE OR REPLACE FUNCTION getInvcheadId(text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pInvcNumber ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  IF (pInvcNumber IS NULL) THEN
+       RETURN NULL;
+  END IF;
+
+  SELECT invchead_id INTO _returnVal
+  FROM invchead
+  WHERE (UPPER(invchead_invcnumber)=UPPER(pInvcNumber));
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION ''Invoice % not found.'', pInvcNumber;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getinvcitemlotserial.sql b/foundation-database/public/functions/getinvcitemlotserial.sql
new file mode 100644 (file)
index 0000000..0d6fc4f
--- /dev/null
@@ -0,0 +1,71 @@
+
+CREATE OR REPLACE FUNCTION getInvcitemLotSerial(INTEGER) RETURNS TEXT AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pInvcitemid ALIAS FOR $1;
+  _lotserial text;
+  _r RECORD;
+  _first BOOLEAN;
+  _newMethod BOOLEAN;
+BEGIN
+  --Test to see if Lot/Serial Enabled
+  SELECT metric_value INTO _lotserial
+  FROM metric
+  WHERE ((metric_name='LotSerialControl')
+  AND (metric_value ='t'));
+
+  IF (FOUND) THEN
+    _lotserial := '';
+    _first := true;
+
+--  Two ways of doing this: old method and new method
+--  First, find out if new method employed.
+--  (new method is more accurate, but unfortunately no
+--  way to migrate or correct old data.  Have to keep
+--  old method in case someone reprints an old invoice.)
+
+    SELECT (COUNT(*) > 0) INTO _newMethod 
+    FROM shipitem 
+    WHERE ((shipitem_invcitem_id=pInvcitemid)
+    AND (shipitem_invhist_id IS NOT NULL));
+
+    IF (_newMethod) THEN
+
+      FOR _r IN SELECT DISTINCT ls_number
+                FROM invdetail, invhist, shipitem, ls
+               WHERE ((shipitem_invcitem_id=pInvcitemid)
+                 AND  (shipitem_invhist_id=invhist_id)
+                 AND  (invhist_id=invdetail_invhist_id)
+                 AND  (invdetail_ls_id=ls_id)) LOOP
+        IF (_first = false) THEN
+          _lotserial := _lotserial || ', ';
+        END IF;
+        _lotserial := _lotserial || _r.ls_number;
+        _first := false;
+      END LOOP;
+
+      RETURN _lotserial;
+    ELSE
+      -- Handle it old way
+      FOR _r IN SELECT DISTINCT ls_number
+                FROM ls, invdetail JOIN invhist ON (invdetail_invhist_id=invhist_id)
+               WHERE ((invhist_transtype='SH')
+                 AND  (invdetail_invcitem_id=pInvcitemid)
+                 AND  (invdetail_ls_id=ls_id)) LOOP
+        IF (_first = false) THEN
+          _lotserial := _lotserial || ', ';
+        END IF;
+        _lotserial := _lotserial || _r.ls_number;
+        _first := false;
+      END LOOP;
+
+      RETURN _lotserial;
+    END IF;
+  ELSE
+    RETURN '';
+  END IF;
+  
+END
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getipsheadid.sql b/foundation-database/public/functions/getipsheadid.sql
new file mode 100644 (file)
index 0000000..a84d19b
--- /dev/null
@@ -0,0 +1,21 @@
+CREATE OR REPLACE FUNCTION getIpsheadId(pIpsName TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _returnVal INTEGER;
+BEGIN
+  IF (pIpsName IS NULL) THEN
+       RETURN NULL;
+  END IF;
+
+  SELECT ipshead_id INTO _returnVal
+  FROM ipshead
+  WHERE (ipshead_name=pIpsName);
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION 'Pricing Schedule % not found.', pIpsName;
+  END IF;
+
+  RETURN _returnVal;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getipsitemid.sql b/foundation-database/public/functions/getipsitemid.sql
new file mode 100644 (file)
index 0000000..e3acb6f
--- /dev/null
@@ -0,0 +1,31 @@
+CREATE OR REPLACE FUNCTION getIpsitemId(pIpsName TEXT,
+                                        pItemNumber TEXT,
+                                        pQtyBreak NUMERIC,
+                                        pQtyUom TEXT,
+                                        pPriceUom TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _returnVal INTEGER;
+  
+BEGIN
+  IF (pIpsName IS NULL AND pItemNumber IS NULL AND pQtyBreak IS NULL AND pQtyUom IS NULL AND pPriceUom IS NULL) THEN
+       RETURN NULL;
+  END IF;
+
+  SELECT ipsitem_id INTO _returnVal
+  FROM ipsiteminfo
+  WHERE ((ipsitem_ipshead_id=getIpsheadId(pIpsName))
+  AND (ipsitem_item_id=getItemId(pItemNumber))
+  AND (ipsitem_qtybreak=pQtyBreak)
+  AND (ipsitem_qty_uom_id=getUomId(pQtyUom))
+  AND (ipsitem_price_uom_id=getUomId(pPriceUom)));
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION 'Pricing Schedule Item for Schedule %, Item %,Qt Break %,Qty UOM %, Price UOM % not found.', 
+       pIpsName, pItemNumber, pQtyBreak, pQtyUom, pPriceUom;
+  END IF;
+
+  RETURN _returnVal;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getipsprodcatid.sql b/foundation-database/public/functions/getipsprodcatid.sql
new file mode 100644 (file)
index 0000000..9eaf2f5
--- /dev/null
@@ -0,0 +1,27 @@
+CREATE OR REPLACE FUNCTION getIpsProdcatId(pIpsName TEXT,
+                                           pProdCat TEXT,
+                                           pQtyBreak NUMERIC) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _returnVal INTEGER;
+  
+BEGIN
+  IF (pIpsName IS NULL AND pProdCat IS NULL AND pQtyBreak IS NULL) THEN
+       RETURN NULL;
+  END IF;
+
+  SELECT ipsitem_id INTO _returnVal
+  FROM ipsiteminfo
+  WHERE ((ipsitem_ipshead_id=getIpsheadId(pIpsName))
+  AND (ipsitem_prodcat_id=getProdcatId(pProdCat))
+  AND (ipsitem_qtybreak=pQtyBreak));
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION 'Pricing Schedule Product Category for Schedule %, Product Category %,Qt Break % not found.', 
+       pIpsName, pProdCat, pQtyBreak;
+  END IF;
+
+  RETURN _returnVal;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getitemid.sql b/foundation-database/public/functions/getitemid.sql
new file mode 100644 (file)
index 0000000..4b53903
--- /dev/null
@@ -0,0 +1,22 @@
+CREATE OR REPLACE FUNCTION getItemId(text) RETURNS INTEGER IMMUTABLE AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemNumber ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  IF (pItemNumber IS NULL) THEN
+       RETURN NULL;
+  END IF;
+
+  SELECT item_id INTO _returnVal
+  FROM item
+  WHERE (item_number=UPPER(pItemNumber));
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION ''Item % not found.'', pItemNumber;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getitemidfromupc.sql b/foundation-database/public/functions/getitemidfromupc.sql
new file mode 100644 (file)
index 0000000..8cd538a
--- /dev/null
@@ -0,0 +1,27 @@
+
+CREATE OR REPLACE FUNCTION getitemidfromupc(text)
+  RETURNS integer AS
+$BODY$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemUPC ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  IF (pItemUPC IS NULL) THEN
+       RETURN NULL;
+  END IF;
+
+  SELECT item_id INTO _returnVal
+  FROM item
+  WHERE (item_upccode=UPPER(pItemUPC));
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION 'Item % not found.', pItemUPC;
+  END IF;
+
+  RETURN _returnVal;
+END;
+$BODY$
+  LANGUAGE 'plpgsql' VOLATILE;
+
diff --git a/foundation-database/public/functions/getitemsiteid.sql b/foundation-database/public/functions/getitemsiteid.sql
new file mode 100644 (file)
index 0000000..abd19f7
--- /dev/null
@@ -0,0 +1,61 @@
+CREATE OR REPLACE FUNCTION getItemsiteId(text, text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWarehouseCode ALIAS FOR $1;
+  pItemNumber ALIAS FOR $2;
+  _returnVal INTEGER;
+BEGIN
+  SELECT getItemsiteId(pWarehouseCode,pItemNumber,''ALL'') INTO _returnVal;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION getItemsiteId(text, text, text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWarehouseCode ALIAS FOR $1;
+  pItemNumber ALIAS FOR $2;
+  pType ALIAS FOR $3;
+  _type TEXT;
+  _p RECORD;
+BEGIN
+  IF ((pWarehouseCode IS NULL) OR (pItemNumber IS NULL)) THEN
+       RETURN NULL;
+  END IF;
+  IF UPPER(pType) NOT IN (''ALL'',''ACTIVE'',''SOLD'') THEN
+    RAISE EXCEPTION ''Invalid Type %. Valid Itemsite types are ALL and SOLD'', pType;
+  END IF;
+
+  SELECT * INTO _p
+  FROM itemsite, item
+  WHERE ((itemsite_item_id=item_id)
+  AND (itemsite_warehous_id=getWarehousId(pWarehouseCode,''ALL''))
+  AND (item_number=UPPER(pItemNumber)));
+
+  IF NOT (FOUND) THEN
+    RAISE EXCEPTION ''Item % not found in Warehouse %'', pItemNumber, pWarehouseCode;
+  ELSIF ((UPPER(pType)=''ACTIVE'') OR (UPPER(pType)=''SOLD'')) THEN
+    IF NOT (_p.item_active) THEN
+      RAISE EXCEPTION ''Item % is inactive.'', pItemNumber;
+    ELSE
+      IF NOT (_p.itemsite_active) THEN
+        RAISE EXCEPTION ''Item % is inactive in Warehouse %'', pItemNumber, pWarehouseCode;
+      ELSE
+        IF ((UPPER(pType)=''SOLD'') AND NOT _p.item_sold) THEN
+          RAISE EXCEPTION ''Item % is not sold'', pItemNumber;
+        ELSE
+          IF ((UPPER(pType)=''SOLD'') AND NOT _p.itemsite_sold) THEN
+            RAISE EXCEPTION ''Item % is not sold from Warehouse %'', pItemNumber, pWarehouseCode;
+          END IF;
+        END IF;
+      END IF;
+    END IF;
+  END IF;
+
+  RETURN _p.itemsite_id;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getitemsrcid.sql b/foundation-database/public/functions/getitemsrcid.sql
new file mode 100644 (file)
index 0000000..8d5a5c0
--- /dev/null
@@ -0,0 +1,24 @@
+CREATE OR REPLACE FUNCTION getItemSrcId(text,text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemNumber ALIAS FOR $1;
+  pVendNumber ALIAS FOR $2;
+  _returnVal INTEGER;
+BEGIN
+  IF ((pItemNumber IS NULL) OR (pVendNumber IS NULL)) THEN
+    RETURN NULL;
+  END IF;
+
+  SELECT itemsrc_id INTO _returnVal
+  FROM itemsrc
+  WHERE ((itemsrc_item_id=getItemId(pItemNumber))
+  AND (itemsrc_vend_id=getVendId(pVendNumber)));
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION ''Item Source Item % Vendor % not found.'', pItemNumber,pVendNumber;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getitemtaxtype.sql b/foundation-database/public/functions/getitemtaxtype.sql
new file mode 100644 (file)
index 0000000..c31917c
--- /dev/null
@@ -0,0 +1,27 @@
+CREATE OR REPLACE FUNCTION getItemTaxType(INTEGER, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemid ALIAS FOR $1;
+  pTaxzoneid ALIAS FOR $2;
+  _taxtypeid INTEGER;
+BEGIN
+  SELECT itemtax_taxtype_id
+    INTO _taxtypeid
+    FROM itemtax
+   WHERE ((itemtax_item_id=pItemid)
+     AND  (itemtax_taxzone_id=pTaxzoneid));
+  IF (NOT FOUND) THEN
+    SELECT itemtax_taxtype_id
+      INTO _taxtypeid
+      FROM itemtax
+     WHERE ((itemtax_item_id=pItemid)
+       AND  (itemtax_taxzone_id IS NULL));
+    IF (NOT FOUND) THEN
+      RETURN NULL;
+    END IF;
+  END IF;
+
+  RETURN _taxtypeid;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getlasttrialbalid.sql b/foundation-database/public/functions/getlasttrialbalid.sql
new file mode 100644 (file)
index 0000000..a806569
--- /dev/null
@@ -0,0 +1,43 @@
+
+CREATE OR REPLACE FUNCTION getlasttrialbalid(INTEGER, INTEGER)
+  RETURNS INTEGER STABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pAccntId ALIAS FOR $1;
+  pPeriodId ALIAS FOR $2;
+  _p RECORD;
+  _accntType TEXT;
+  _result NUMERIC;
+
+BEGIN
+
+  SELECT period_end,period_yearperiod_id INTO _p
+  FROM period
+  WHERE period_id=pPeriodId;
+
+  SELECT accnt_type INTO _accntType
+  FROM accnt
+  WHERE accnt_id=pAccntId;
+
+  IF (_accntType IN ('R','E')) THEN
+        SELECT trialbal_id INTO _result
+        FROM trialbal
+        WHERE ((trialbal_accnt_id=pAccntId)
+        AND (trialbal_period_id=pPeriodId));
+  ELSE
+        SELECT trialbal_id INTO _result
+        FROM (SELECT trialbal_id
+                FROM trialbal,period
+                WHERE ((trialbal_accnt_id=pAccntId)
+                AND (trialbal_period_id=period_id)
+                AND (period_end <= _p.period_end)
+                AND (period_yearperiod_id=_p.period_yearperiod_id))
+                ORDER BY period_end DESC) AS data;
+  END IF;
+
+  RETURN _result;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/getlocationid.sql b/foundation-database/public/functions/getlocationid.sql
new file mode 100644 (file)
index 0000000..2735367
--- /dev/null
@@ -0,0 +1,25 @@
+CREATE OR REPLACE FUNCTION getLocationId(text,text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWarehouse ALIAS FOR $1;
+  pLocation ALIAS FOR $2;
+  _returnVal INTEGER;
+BEGIN
+  IF (pLocation IS NULL) THEN
+       RETURN NULL;
+  END IF;
+
+  SELECT location_id INTO _returnVal
+  FROM location
+  WHERE ((location_warehous_id=getWarehousId(pWarehouse,''ACTIVE''))
+  AND (formatLocationname(location_id)=pLocation))
+  LIMIT 1;
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION ''Location % not found in Warehouse %.'', pLocation, pWarehouse;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getlotserialid.sql b/foundation-database/public/functions/getlotserialid.sql
new file mode 100644 (file)
index 0000000..f429130
--- /dev/null
@@ -0,0 +1,24 @@
+CREATE OR REPLACE FUNCTION getLotSerialId(text, text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemNumber ALIAS FOR $1;
+  pLotSerialNumber ALIAS FOR $2;
+  _returnVal INTEGER;
+BEGIN
+  IF ((pItemNumber IS NULL) OR (pLotSerialNumber IS NULL) OR (pLotSerialNumber='''')) THEN
+       RETURN NULL;
+  END IF;
+
+  SELECT ls_id INTO _returnVal
+  FROM ls
+  WHERE ((ls_item_id=getItemId(pItemNumber))
+  AND (UPPER(ls_number)=UPPER(pLotSerialNumber)));
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION ''LotSerial % not found.'', pLotSerialNumber;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getopheadid.sql b/foundation-database/public/functions/getopheadid.sql
new file mode 100644 (file)
index 0000000..55cffab
--- /dev/null
@@ -0,0 +1,23 @@
+CREATE OR REPLACE FUNCTION getOpHeadId(text) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pOpHeadName ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  
+  IF (pOpHeadName IS NULL) THEN
+    RETURN NULL;
+  END IF;
+
+  SELECT ophead_id INTO _returnVal
+  FROM ophead
+  WHERE (UPPER(ophead_name)=UPPER(pOpHeadName));
+  
+  IF (_returnVal IS NULL) THEN
+      RAISE EXCEPTION 'Opportunity % not found.', pOpHeadName;
+  END IF;
+
+  RETURN _returnVal;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getpacklistcharname.sql b/foundation-database/public/functions/getpacklistcharname.sql
new file mode 100644 (file)
index 0000000..04dff18
--- /dev/null
@@ -0,0 +1,43 @@
+CREATE OR REPLACE FUNCTION getPacklistCharName(INTEGER, INTEGER) RETURNS TEXT AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pShipheadId ALIAS FOR $1;
+  pOrderItemId ALIAS FOR $2;
+  _charname text;
+  _r RECORD;
+  _first BOOLEAN;
+  
+BEGIN
+
+-- If transfer order, get out
+  SELECT shiphead_order_type INTO _r
+  FROM shiphead
+  WHERE ((shiphead_id=pShipheadId)
+  AND (shiphead_order_type=''TO''));
+  
+  IF (FOUND) THEN
+    RETURN '''';
+  END IF;
+
+  _charname := '''';
+  _first := true;
+
+  FOR _r IN SELECT char_name
+            FROM char, charass
+            WHERE ((char_id=charass_char_id)
+            AND (charass_target_type=''SI'')
+            AND (charass_target_id=pOrderItemId)) 
+  LOOP
+        IF (_first = false) THEN
+          _charname := _charname || ''
+'';
+        END IF;
+        _charname := _charname || _r.char_name;
+        _first := false;
+  END LOOP;
+
+  RETURN _charname;
+  
+END
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getpacklistcharvalue.sql b/foundation-database/public/functions/getpacklistcharvalue.sql
new file mode 100644 (file)
index 0000000..ec6b1fc
--- /dev/null
@@ -0,0 +1,43 @@
+CREATE OR REPLACE FUNCTION getPacklistCharValue(INTEGER, INTEGER) RETURNS TEXT AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pShipheadId ALIAS FOR $1;
+  pOrderItemId ALIAS FOR $2;
+  _charval text;
+  _r RECORD;
+  _first BOOLEAN;
+  
+BEGIN
+
+-- If transfer order, get out
+  SELECT shiphead_order_type INTO _r
+  FROM shiphead
+  WHERE ((shiphead_id=pShipheadId)
+  AND (shiphead_order_type=''TO''));
+  
+  IF (FOUND) THEN
+    RETURN '''';
+  END IF;
+
+  _charval := '''';
+  _first := true;
+
+  FOR _r IN SELECT charass_value
+            FROM char, charass
+            WHERE ((char_id=charass_char_id)
+            AND (charass_target_type=''SI'')
+            AND (charass_target_id=pOrderItemId)) 
+  LOOP
+        IF (_first = false) THEN
+          _charval := _charval || ''
+'';
+        END IF;
+        _charval := _charval || _r.charass_value;
+        _first := false;
+  END LOOP;
+
+  RETURN _charval;
+  
+END
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getpacklistitemlotserial.sql b/foundation-database/public/functions/getpacklistitemlotserial.sql
new file mode 100644 (file)
index 0000000..c6cbb2f
--- /dev/null
@@ -0,0 +1,44 @@
+
+CREATE OR REPLACE FUNCTION getPacklistItemLotSerial(INTEGER, INTEGER) RETURNS TEXT AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pShipheadId ALIAS FOR $1;
+  pOrderItemId ALIAS FOR $2;
+  _lotserial text;
+  _r RECORD;
+  _first BOOLEAN;
+  
+BEGIN
+  --Test to see if Lot/Serial Enabled
+  SELECT metric_value INTO _lotserial
+  FROM metric
+  WHERE ((metric_name=''LotSerialControl'')
+  AND (metric_value =''t''));
+
+  IF (FOUND) THEN
+    _lotserial := '''';
+    _first := true;
+
+    FOR _r IN SELECT DISTINCT ls_number
+              FROM invdetail, invhist, shipitem, ls
+             WHERE ((shipitem_shiphead_id=pShipheadId)
+               AND  (shipitem_orderitem_id=pOrderItemId)
+               AND  (shipitem_invhist_id=invhist_id)
+               AND  (invhist_id=invdetail_invhist_id)
+               AND  (invdetail_ls_id=ls_id)) LOOP
+      IF (_first = false) THEN
+        _lotserial := _lotserial || '', '';
+      END IF;
+      _lotserial := _lotserial || _r.ls_number;
+      _first := false;
+    END LOOP;
+
+    RETURN _lotserial;
+  ELSE
+    RETURN '''';
+  END IF;
+  
+END
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getperiodid.sql b/foundation-database/public/functions/getperiodid.sql
new file mode 100644 (file)
index 0000000..3e16683
--- /dev/null
@@ -0,0 +1,67 @@
+
+CREATE OR REPLACE FUNCTION getperiodid(INTEGER,  char) RETURNS SETOF INTEGER IMMUTABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pPeriodId ALIAS FOR $1;
+  pInterval ALIAS FOR $2;
+  _x RECORD;
+BEGIN
+
+-- Validate Interval
+   IF pInterval <> 'M' AND pInterval <> 'Q' AND pInterval <> 'Y' THEN
+     RAISE EXCEPTION 'Invalid Interval --> %', pInterval;
+   END IF;
+
+   IF pInterval='M' THEN
+       RETURN NEXT pPeriodId;
+     ELSE IF pInterval='Q' THEN
+        FOR _x IN SELECT qp.period_id AS period_id
+                FROM period cp, period qp
+                WHERE ((cp.period_id=pPeriodId)
+                AND (cp.period_yearperiod_id=qp.period_yearperiod_id)
+                AND (cp.period_quarter=qp.period_quarter)
+                AND (cp.period_start>=qp.period_start))
+        ORDER BY qp.period_start
+        LOOP
+                RETURN NEXT _x.period_id;
+        END LOOP;
+     ELSE
+        FOR _x IN SELECT yp.period_id AS period_id
+                FROM period cp, period yp
+                WHERE ((cp.period_id=pPeriodId)
+                AND (cp.period_yearperiod_id=yp.period_yearperiod_id)
+                AND (cp.period_start>=yp.period_start))
+        ORDER BY yp.period_start
+        LOOP
+                RETURN NEXT _x.period_id;
+        END LOOP;
+     END IF;
+   END IF;
+  RETURN;
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION getPeriodId(date)
+  RETURNS integer AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pPeriodDate ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  IF (pPeriodDate IS NULL) THEN
+       RETURN NULL;
+  END IF;
+
+  SELECT period_id INTO _returnVal
+  FROM period
+  WHERE ((pPeriodDate) between period_start AND period_end);
+
+  IF (_returnVal IS NULL) THEN
+    RAISE EXCEPTION 'Period for % not found.', pPeriodDate;
+  END IF;
+
+  RETURN _returnVal;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getpkgheadid.sql b/foundation-database/public/functions/getpkgheadid.sql
new file mode 100644 (file)
index 0000000..33a1f6b
--- /dev/null
@@ -0,0 +1,22 @@
+CREATE OR REPLACE FUNCTION getPkgheadId(text) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  ppkgname ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  IF (ppkgname IS NULL) THEN
+    RETURN NULL;
+  END IF;
+
+  SELECT pkghead_id INTO _returnVal
+  FROM pkghead
+  WHERE (UPPER(pkghead_name)=UPPER(ppkgname));
+
+  IF (_returnVal IS NULL) THEN
+    RAISE EXCEPTION 'Package % not found.', ppkgname;
+  END IF;
+
+  RETURN _returnVal;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getplancodeid.sql b/foundation-database/public/functions/getplancodeid.sql
new file mode 100644 (file)
index 0000000..8a4954d
--- /dev/null
@@ -0,0 +1,22 @@
+CREATE OR REPLACE FUNCTION getPlanCodeId(text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pPlanCode ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  IF (pPlanCode IS NULL) THEN
+       RETURN NULL;
+  END IF;
+
+  SELECT plancode_id INTO _returnVal
+  FROM plancode
+  WHERE (plancode_code=pPlanCode);
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION ''Plan Code % not found.'', pPlanCode;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getpoheadid.sql b/foundation-database/public/functions/getpoheadid.sql
new file mode 100644 (file)
index 0000000..440ce7e
--- /dev/null
@@ -0,0 +1,22 @@
+CREATE OR REPLACE FUNCTION getPoheadId(text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pPurchaseOrderNumber ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  IF (pPurchaseOrderNumber IS NULL) THEN
+    RETURN NULL;
+  END IF;
+
+  SELECT pohead_id INTO _returnVal
+  FROM pohead
+  WHERE (pohead_number=pPurchaseOrderNumber);
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION ''Purchase Order % not found.'', pPurchaseOrderNumber;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getpoitemid.sql b/foundation-database/public/functions/getpoitemid.sql
new file mode 100644 (file)
index 0000000..98c031f
--- /dev/null
@@ -0,0 +1,25 @@
+CREATE OR REPLACE FUNCTION getPoitemId(text,integer) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pPurchaseOrderNumber ALIAS FOR $1;
+  pLineNumber          ALIAS FOR $2;
+  _returnVal           INTEGER;
+BEGIN
+  IF (pPurchaseOrderNumber IS NULL OR pLineNumber IS NULL) THEN
+    RETURN NULL;
+  END IF;
+
+  SELECT poitem_id INTO _returnVal
+  FROM pohead, poitem
+  WHERE ((pohead_number=pPurchaseOrderNumber)
+  AND (poitem_pohead_id=pohead_id)
+  AND (poitem_linenumber=pLineNumber));
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION ''Purchase Order % not found.'', pSalesOrderNumber;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getprjaccntid.sql b/foundation-database/public/functions/getprjaccntid.sql
new file mode 100644 (file)
index 0000000..67fd630
--- /dev/null
@@ -0,0 +1,12 @@
+CREATE OR REPLACE FUNCTION getPrjAccntId(INTEGER, INTEGER) RETURNS INTEGER IMMUTABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pPrjid ALIAS FOR $1;
+  pAccntid ALIAS FOR $2;
+  
+BEGIN
+  -- Project Accounting is required to fully implement this functionality
+  RETURN pAccntId;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getprjid.sql b/foundation-database/public/functions/getprjid.sql
new file mode 100644 (file)
index 0000000..a42ef98
--- /dev/null
@@ -0,0 +1,22 @@
+CREATE OR REPLACE FUNCTION getPrjId(text) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pPrjNumber ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  IF (pPrjNumber IS NULL) THEN
+       RETURN NULL;
+  END IF;
+
+  SELECT prj_id INTO _returnVal
+  FROM prj
+  WHERE (prj_number=pPrjNumber);
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION 'Project Number % not found.', pPrjNumber;
+  END IF;
+
+  RETURN _returnVal;
+END;
+$$ LANGUAGE 'plpgsql' STABLE;
diff --git a/foundation-database/public/functions/getprjtaskid.sql b/foundation-database/public/functions/getprjtaskid.sql
new file mode 100644 (file)
index 0000000..bde40f4
--- /dev/null
@@ -0,0 +1,25 @@
+CREATE OR REPLACE FUNCTION getPrjTaskId(text, text) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pPrjNumber ALIAS FOR $1;
+  pTaskNumber ALIAS FOR $2;
+  _returnVal INTEGER;
+BEGIN
+  IF (pPrjNumber IS NULL OR pTaskNumber IS NULL) THEN
+       RETURN NULL;
+  END IF;
+
+  SELECT prjtask_id INTO _returnVal
+  FROM prjtask
+    JOIN prj ON (prj_id=prjtask_prj_id)
+  WHERE ((prj_number=pPrjNumber)
+  AND (prjtask_number=pTaskNumber));
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION 'Project Task Number %-% not found.', pPrjNumber, pTaskNumber;
+  END IF;
+
+  RETURN _returnVal;
+END;
+$$ LANGUAGE 'plpgsql' STABLE;
diff --git a/foundation-database/public/functions/getprodcatid.sql b/foundation-database/public/functions/getprodcatid.sql
new file mode 100644 (file)
index 0000000..51f74e7
--- /dev/null
@@ -0,0 +1,22 @@
+CREATE OR REPLACE FUNCTION getProdCatId(text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pProdCat ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  IF (pProdCat IS NULL) THEN
+    RETURN NULL;
+  END IF;
+
+  SELECT prodcat_id INTO _returnVal
+  FROM prodcat
+  WHERE (prodcat_code=pProdCat);
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION ''Product Category % not found.'', pProdCat;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getprospectid.sql b/foundation-database/public/functions/getprospectid.sql
new file mode 100644 (file)
index 0000000..ebe2585
--- /dev/null
@@ -0,0 +1,21 @@
+CREATE OR REPLACE FUNCTION getProspectId(TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pProspectNumber ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  IF (pProspectNumber IS NULL) THEN
+    RETURN NULL;
+  END IF;
+
+  SELECT prospect_id INTO _returnVal
+    FROM prospect
+   WHERE(UPPER(prospect_number)=UPPER(pProspectNumber));
+  IF (_returnVal IS NULL) THEN
+    RAISE EXCEPTION 'Prospect Number % found.', pProspectNumber;
+  END IF;
+
+  RETURN _returnVal;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getquoteid.sql b/foundation-database/public/functions/getquoteid.sql
new file mode 100644 (file)
index 0000000..7d98ee3
--- /dev/null
@@ -0,0 +1,22 @@
+CREATE OR REPLACE FUNCTION getQuoteId(text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pQuoteNumber ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  IF (pQuoteNumber IS NULL) THEN
+    RETURN NULL;
+  END IF;
+
+  SELECT quhead_id INTO _returnVal
+  FROM quhead
+  WHERE (quhead_number=pQuoteNumber);
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION ''Quote Number % not found.'', pQuoteNumber;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getquotelineitemid.sql b/foundation-database/public/functions/getquotelineitemid.sql
new file mode 100644 (file)
index 0000000..6b3f695
--- /dev/null
@@ -0,0 +1,25 @@
+CREATE OR REPLACE FUNCTION getQuoteLineItemId(text,integer) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pQuoteNumber ALIAS FOR $1;
+  pLineNumber ALIAS FOR $2;
+  _returnVal INTEGER;
+BEGIN
+  IF ((pQuoteNumber IS NULL) OR (pLineNumber IS NULL)) THEN
+    RETURN NULL;
+  END IF;
+
+  SELECT quitem_id INTO _returnVal
+  FROM quhead, quitem
+  WHERE ((quhead_number=pQuoteNumber)
+  AND (quhead_id=quitem_quhead_id)
+  AND (quitem_linenumber=pLineNumber));
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION ''Quote Line Item %-%not found.'', pQuoteNumber,pLineNumber;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getrevid.sql b/foundation-database/public/functions/getrevid.sql
new file mode 100644 (file)
index 0000000..8d09005
--- /dev/null
@@ -0,0 +1,44 @@
+CREATE OR REPLACE FUNCTION getRevId(text,text,text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pType ALIAS FOR $1;
+  pItemNumber ALIAS FOR $2;
+  pRevision ALIAS FOR $3;
+  _returnVal INTEGER;
+BEGIN
+  IF (pItemNumber IS NULL) THEN
+    RETURN NULL;
+  END IF;
+
+  IF (NOT fetchMetricBool(''RevControl'')) THEN
+    RETURN -1;
+  ELSIF ( (pRevision IS NULL) OR (LENGTH(pRevision)=0) ) THEN
+    SELECT getActiveRevId(pType, getItemId(pItemNumber)) INTO _returnVal;
+  ELSE
+    IF (pType=''BOM'') THEN
+      SELECT rev_id INTO _returnVal
+      FROM item, bomhead, rev
+      WHERE ((item_id=bomhead_item_id)
+      AND (bomhead_rev_id=rev_id)
+      AND (UPPER(item_number)=UPPER(pItemNumber))
+      AND (rev_number=pRevision));
+    ELSIF (pType=''BOO'') THEN
+      SELECT rev_id INTO _returnVal
+      FROM item, xtmfg.boohead, rev
+      WHERE ((item_id=boohead_item_id)
+      AND (boohead_rev_id=rev_id)
+      AND (UPPER(item_number)=UPPER(pItemNumber))
+      AND (rev_number=pRevision));   
+    ELSE
+      RAISE EXCEPTION ''Invalid Revision Type.'';
+    END IF;
+  END IF;
+    
+  IF (_returnVal IS NULL) THEN
+    RAISE EXCEPTION ''% revision % for % not found.'', pType, pRevision, pItemNumber;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getrsnid.sql b/foundation-database/public/functions/getrsnid.sql
new file mode 100644 (file)
index 0000000..7f8d65a
--- /dev/null
@@ -0,0 +1,22 @@
+CREATE OR REPLACE FUNCTION getRsnId(text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pRsnCode ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  IF (pRsnCode IS NULL) THEN
+    RETURN NULL;
+  END IF;
+
+  SELECT rsncode_id INTO _returnVal
+  FROM rsncode
+  WHERE (rsncode_code=pRsnCode);
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION ''Reason Code % not found.'', pRsnCode;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getsalescatid.sql b/foundation-database/public/functions/getsalescatid.sql
new file mode 100644 (file)
index 0000000..d9c6ad4
--- /dev/null
@@ -0,0 +1,22 @@
+CREATE OR REPLACE FUNCTION getSalesCatId(text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSalesCatName ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  IF (pSalesCatName IS NULL) THEN
+    RETURN NULL;
+  END IF;
+
+  SELECT salescat_id INTO _returnVal
+  FROM salescat
+  WHERE (salescat_name=pSalesCatName);
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION ''Sales Category % not found.'', pSalesCatName;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getsaleslineitemid.sql b/foundation-database/public/functions/getsaleslineitemid.sql
new file mode 100644 (file)
index 0000000..86f637d
--- /dev/null
@@ -0,0 +1,67 @@
+CREATE OR REPLACE FUNCTION getSalesLineItemId(TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSalesOrderItem ALIAS FOR $1;
+  _delpos INTEGER = 0;
+  _order TEXT;
+  _part TEXT;
+  _ln INTEGER;
+  _sn INTEGER;
+BEGIN
+  IF (pSalesOrderItem IS NULL) THEN
+    RETURN NULL;
+  END IF;
+  _delpos := strpos(pSalesOrderItem, '-');
+  IF (_delpos > 0) THEN
+    _order := substr(pSalesOrderItem, 1, (_delpos - 1));
+    _part := substr(pSalesOrderItem, (_delpos + 1));
+    _delpos := strpos(_part, '.');
+    IF (_delpos > 0) THEN
+      _ln := CAST(substr(_part, 1, (_delpos - 1)) AS INTEGER);
+      _sn := CAST(substr(_part, (_delpos + 1)) AS INTEGER);
+    ELSE
+      _ln := CAST(_part AS INTEGER);
+      _sn := 0;
+    END IF;
+    RETURN getSalesLineItemId( _order, _ln, _sn );
+  END IF;
+  RETURN 0;
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION getSalesLineItemId(TEXT, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN getSalesLineItemId($1, $2, 0);
+END
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION getSalesLineItemId(TEXT, INTEGER, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSalesOrderNumber ALIAS FOR $1;
+  pLineNumber ALIAS FOR $2;
+  pSubNumber ALIAS FOR $3;
+  _returnVal INTEGER;
+BEGIN
+  IF ((pSalesOrderNumber IS NULL) OR (pLineNumber IS NULL)) THEN
+    RETURN NULL;
+  END IF;
+
+  SELECT coitem_id INTO _returnVal
+  FROM cohead, coitem
+  WHERE ((cohead_number=pSalesOrderNumber)
+  AND (cohead_id=coitem_cohead_id)
+  AND (coitem_linenumber=pLineNumber)
+  AND (coitem_subnumber=pSubNumber));
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION 'Sales Line Item %-%not found.', pSalesOrderNumber,pLineNumber;
+  END IF;
+
+  RETURN _returnVal;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getsalesorderid.sql b/foundation-database/public/functions/getsalesorderid.sql
new file mode 100644 (file)
index 0000000..80a67e5
--- /dev/null
@@ -0,0 +1,22 @@
+CREATE OR REPLACE FUNCTION getSalesOrderId(text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSalesOrderNumber ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  IF (pSalesOrderNumber IS NULL) THEN
+    RETURN NULL;
+  END IF;
+
+  SELECT cohead_id INTO _returnVal
+  FROM cohead
+  WHERE (cohead_number=pSalesOrderNumber);
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION ''Sales Order Number % not found.'', pSalesOrderNumber;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getsalesrepid.sql b/foundation-database/public/functions/getsalesrepid.sql
new file mode 100644 (file)
index 0000000..113d98a
--- /dev/null
@@ -0,0 +1,22 @@
+CREATE OR REPLACE FUNCTION getSalesRepId(text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSalesRepNumber ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  IF (pSalesRepNumber IS NULL) THEN
+    RETURN NULL;
+  END IF;
+
+  SELECT salesrep_id INTO _returnVal
+  FROM salesrep
+  WHERE (salesrep_number=pSalesRepNumber);
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION ''Sales Rep Number % not found.'', pSalesRepNumber;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getsaletypeid.sql b/foundation-database/public/functions/getsaletypeid.sql
new file mode 100644 (file)
index 0000000..a4ea2bd
--- /dev/null
@@ -0,0 +1,21 @@
+CREATE OR REPLACE FUNCTION getSaleTypeId(pSaleType TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _returnVal INTEGER;
+BEGIN
+  IF (pSaleType IS NULL) THEN
+    RETURN NULL;
+  END IF;
+
+  SELECT saletype_id INTO _returnVal
+  FROM saletype
+  WHERE (saletype_code=UPPER(pSaleType));
+
+  IF (_returnVal IS NULL) THEN
+    RAISE EXCEPTION 'Sale Type % not found.', pSaleType;
+  END IF;
+
+  RETURN _returnVal;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getshiftid.sql b/foundation-database/public/functions/getshiftid.sql
new file mode 100644 (file)
index 0000000..f4280fb
--- /dev/null
@@ -0,0 +1,22 @@
+CREATE OR REPLACE FUNCTION getShiftId(text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pShiftNumber ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  IF (COALESCE(TRIM(pShiftNumber), '''') = '''') THEN
+      RETURN NULL;
+  END IF;
+
+  SELECT shift_id INTO _returnVal
+  FROM shift
+  WHERE (UPPER(shift_number)=UPPER(pShiftNumber));
+
+  IF (_returnVal IS NULL) THEN
+    RAISE EXCEPTION ''Shift % not found.'', pShiftNumber;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getshipchrgid.sql b/foundation-database/public/functions/getshipchrgid.sql
new file mode 100644 (file)
index 0000000..b0dcae9
--- /dev/null
@@ -0,0 +1,22 @@
+CREATE OR REPLACE FUNCTION getShipChrgId(text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pShipChrgName ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  IF (pShipChrgName IS NULL) THEN
+       RETURN NULL;
+  END IF;
+
+  SELECT shipchrg_id INTO _returnVal
+  FROM shipchrg
+  WHERE (shipchrg_name=pShipChrgName);
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION ''Ship Charge % not found.'', pShipChrgName;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getshipformid.sql b/foundation-database/public/functions/getshipformid.sql
new file mode 100644 (file)
index 0000000..5c097d6
--- /dev/null
@@ -0,0 +1,22 @@
+CREATE OR REPLACE FUNCTION getShipFormId(text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pShipFormName ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  IF (pShipFormName IS NULL) THEN
+       RETURN NULL;
+  END IF;
+
+  SELECT shipform_id INTO _returnVal
+  FROM shipform
+  WHERE (shipform_name=pShipFormName) LIMIT 1;
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION ''Ship Form % not found.'', pShipFormName;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getshipheadid.sql b/foundation-database/public/functions/getshipheadid.sql
new file mode 100644 (file)
index 0000000..3faf603
--- /dev/null
@@ -0,0 +1,22 @@
+CREATE OR REPLACE FUNCTION getShipheadId(text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pShipmentNumber ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  IF (pShipmentNumber IS NULL OR pShipmentNumber = '''') THEN
+    RETURN NULL;
+  END IF;
+
+  SELECT shiphead_id INTO _returnVal
+  FROM shiphead
+  WHERE (shiphead_number=pShipmentNumber);
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION ''Shipment % not found.'', pShipmentNumber;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getshiptoid.sql b/foundation-database/public/functions/getshiptoid.sql
new file mode 100644 (file)
index 0000000..9890b6b
--- /dev/null
@@ -0,0 +1,24 @@
+CREATE OR REPLACE FUNCTION getShiptoId(text, text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCustNumber ALIAS FOR $1;
+  pShiptoNumber ALIAS FOR $2;
+  _returnVal INTEGER;
+BEGIN
+  IF ((pCustNumber IS NULL) OR (pShiptoNumber IS NULL)) THEN
+       RETURN NULL;
+  END IF;
+
+  SELECT shipto_id INTO _returnVal
+  FROM shiptoinfo
+  WHERE ((shipto_cust_id=getCustId(pCustNumber,true))
+  AND (UPPER(shipto_num)=UPPER(pShiptoNumber)));
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION ''Shipto % not found.'', pShiptoNumber;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getshiptonumberfrominfo.sql b/foundation-database/public/functions/getshiptonumberfrominfo.sql
new file mode 100644 (file)
index 0000000..fb2db7e
--- /dev/null
@@ -0,0 +1,156 @@
+
+CREATE OR REPLACE FUNCTION getShipToNumberFromInfo(TEXT, TEXT, TEXT, TEXT, TEXT, TEXT, TEXT, TEXT, TEXT, TEXT, TEXT, TEXT, TEXT, BOOLEAN, BOOLEAN) RETURNS TEXT AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _custname TEXT := COALESCE(TRIM(UPPER( $1)), '');
+  _email TEXT   := COALESCE(TRIM(UPPER( $2)), '');
+  _company TEXT  := COALESCE(TRIM(UPPER( $3)), '');
+  _first TEXT   := COALESCE(TRIM(UPPER( $4)), '');
+  _last TEXT    := COALESCE(TRIM(UPPER( $5)), '');
+  _fullname TEXT := COALESCE(TRIM(UPPER( $6)), '');
+  _addr1 TEXT   := COALESCE(TRIM(UPPER( $7)), '');
+  _addr2 TEXT   := COALESCE(TRIM(UPPER( $8)), '');
+  _addr3 TEXT   := COALESCE(TRIM(UPPER( $9)), '');
+  _city TEXT    := COALESCE(TRIM(UPPER($10)), '');
+  _state TEXT   := COALESCE(TRIM(UPPER($11)), '');
+  _postalcode TEXT := COALESCE(TRIM(UPPER($12)), '');
+  _country TEXT  := COALESCE(TRIM(UPPER($13)), '');
+  _generate BOOLEAN := COALESCE($14, FALSE);
+  _create BOOLEAN := COALESCE($15, FALSE);
+
+  _citytrunc TEXT;
+  _counter INTEGER;
+  _custid INTEGER;
+  _custnumber TEXT;
+  _candidate TEXT;
+  _r RECORD;
+  _statetrunc TEXT;
+BEGIN
+  IF (_custname = '') THEN
+    _custname := getCustNameFromInfo(_email, _company, _first, _last,
+                     _fullname, FALSE);
+  END IF;
+
+
+  SELECT COUNT(*) INTO _counter
+  FROM custinfo, shiptoinfo, addr
+  WHERE ((UPPER(cust_name)=UPPER(_custname))
+    AND UPPER(shipto_name)=UPPER(_fullname)
+    AND (cust_id=shipto_cust_id)
+    AND (shipto_addr_id=addr_id));
+
+  IF (_counter = 1) THEN
+    SELECT shipto_num INTO _candidate
+    FROM custinfo, shiptoinfo, addr
+    WHERE ((UPPER(cust_name)=UPPER(_custname))
+      AND UPPER(shipto_name)=UPPER(_fullname)
+      AND (cust_id=shipto_cust_id)
+      AND (shipto_addr_id=addr_id));
+
+    RETURN _candidate;
+
+  ELSE
+
+    SELECT COUNT(*) INTO _counter
+    FROM custinfo, shiptoinfo, addr
+    WHERE ((UPPER(cust_name)=UPPER(_custname))
+      AND (cust_id=shipto_cust_id)
+      AND (shipto_addr_id=addr_id));
+
+    IF (_counter = 1) THEN
+      SELECT shipto_num INTO _candidate
+      FROM custinfo, shiptoinfo, addr
+      WHERE ((UPPER(cust_name)=UPPER(_custname))
+        AND (cust_id=shipto_cust_id)
+        AND (shipto_addr_id=addr_id));
+
+      RETURN _candidate;
+
+    ELSIF (_counter > 1) THEN
+      SELECT shipto_num,
+         CASE WHEN (UPPER(addr_country) = _country) THEN 1 ELSE 0 END +
+         CASE WHEN (UPPER(addr_postalcode) = _postalcode) THEN 1 ELSE 0 END +
+         CASE WHEN (UPPER(addr_state) = _state) THEN 1 ELSE 0 END +
+         CASE WHEN (UPPER(addr_city) = _city) THEN 1 ELSE 0 END +
+         CASE WHEN (UPPER(addr_line3) = _addr3) THEN 1 ELSE 0 END +
+         CASE WHEN (UPPER(addr_line2) = _addr2) THEN 1 ELSE 0 END +
+         CASE WHEN (UPPER(addr_line1) = _addr1) THEN 1 ELSE 0 END
+         AS maxquotient INTO _candidate, _counter
+      FROM custinfo, shiptoinfo, addr
+      WHERE ((UPPER(cust_name)=_custname)
+        AND (cust_id=shipto_cust_id)
+        AND (shipto_addr_id=addr_id))
+      ORDER BY maxquotient desc
+      LIMIT 1;
+
+      RETURN _candidate;
+    END IF;
+  END IF;
+
+  IF (_generate) THEN
+    SELECT cust_number, cust_id INTO _custnumber, _custid
+    FROM custinfo
+    WHERE (UPPER(cust_name)=_custname);
+
+    -- keep the number short
+    _citytrunc := SUBSTRING(_city FOR 5);
+    _statetrunc := SUBSTRING(_state FOR 5);
+
+    IF (LENGTH(_citytrunc) > 0 AND NOT EXISTS(SELECT UPPER(shipto_num)
+              FROM shiptoinfo
+              WHERE ((shipto_cust_id=_custid)
+                AND (UPPER(shipto_num)=_citytrunc)) )) THEN
+      _candidate := _citytrunc;
+    ELSIF (LENGTH(_last || _citytrunc) > 0 AND NOT EXISTS(SELECT UPPER(shipto_num)
+              FROM shiptoinfo
+              WHERE ((shipto_cust_id=_custid)
+                AND (UPPER(shipto_num)=_last || _citytrunc)) )) THEN
+      _candidate := _last || _citytrunc;
+    ELSIF (LENGTH(_statetrunc) > 0 AND NOT EXISTS(SELECT UPPER(shipto_num)
+           FROM shiptoinfo
+           WHERE ((shipto_cust_id=_custid)
+             AND (UPPER(shipto_num)=_statetrunc)) )) THEN
+      _candidate := _statetrunc;
+    ELSIF (LENGTH(_last || _statetrunc) > 0 AND NOT EXISTS(SELECT UPPER(shipto_num)
+           FROM shiptoinfo
+           WHERE ((shipto_cust_id=_custid)
+             AND (UPPER(shipto_num)=_last || _statetrunc)) )) THEN
+      _candidate := _last || _statetrunc;
+
+    ELSIF (LENGTH(_citytrunc || _statetrunc) > 0 AND NOT EXISTS(SELECT UPPER(shipto_num)
+              FROM shiptoinfo
+              WHERE ((shipto_cust_id=_custid)
+                AND (UPPER(shipto_num)=_citytrunc || _statetrunc)) )) THEN
+      _candidate := _citytrunc || _statetrunc;
+
+    ELSE
+      SELECT CAST(COALESCE(MAX(CAST(shipto_num AS INTEGER)), 0) + 1 AS TEXT)
+      INTO _candidate
+      FROM shiptoinfo
+      WHERE ((shipto_cust_id=_custid)
+       AND (shipto_num~'^[0-9]*$'));
+    END IF;
+
+    IF (_create) THEN
+      INSERT INTO api.custshipto (
+    customer_number, shipto_number, name,
+    address1, address2, address3,
+    city, state, postal_code, country, address_change,
+    first, last, email,
+    edi_profile
+      ) VALUES (
+    _custnumber, _candidate, _candidate,
+    _addr1, _addr2, _addr3,
+    _city, _state, _postalcode, _country, 'CHANGEONE',
+    _first, _last, LOWER(_email),
+    'No EDI'
+      );
+    END IF;
+
+    RETURN _candidate;
+  END IF;
+
+  RETURN '';
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getshipviaid.sql b/foundation-database/public/functions/getshipviaid.sql
new file mode 100644 (file)
index 0000000..8361a5a
--- /dev/null
@@ -0,0 +1,22 @@
+CREATE OR REPLACE FUNCTION getShipViaId(text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pShipViaCode ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  IF (pShipViaCode IS NULL) THEN
+       RETURN NULL;
+  END IF;
+
+  SELECT shipvia_id INTO _returnVal
+  FROM shipvia
+  WHERE (shipvia_code=pShipViaCode);
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION ''ShipVia Code % not found.'', pShipViaCode;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getshipzoneid.sql b/foundation-database/public/functions/getshipzoneid.sql
new file mode 100644 (file)
index 0000000..6c3551c
--- /dev/null
@@ -0,0 +1,21 @@
+CREATE OR REPLACE FUNCTION getShipZoneId(pShipZoneName TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _returnVal INTEGER;
+BEGIN
+  IF (pShipZoneName IS NULL) THEN
+    RETURN NULL;
+  END IF;
+
+  SELECT shipzone_id INTO _returnVal
+  FROM shipzone
+  WHERE (shipzone_name=pShipZoneName);
+
+  IF (_returnVal IS NULL) THEN
+    RAISE EXCEPTION 'Ship Zone % not found.', pShipZoneName;
+  END IF;
+
+  RETURN _returnVal;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getsitetypeid.sql b/foundation-database/public/functions/getsitetypeid.sql
new file mode 100644 (file)
index 0000000..90c1d98
--- /dev/null
@@ -0,0 +1,22 @@
+CREATE OR REPLACE FUNCTION getSiteTypeId(text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSiteType ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  IF (pSiteType IS NULL) THEN
+       RETURN NULL;
+  END IF;
+
+  SELECT sitetype_id INTO _returnVal
+  FROM sitetype
+  WHERE (sitetype_name=pSiteType);
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION ''Site Type % not found.'', pSiteType;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getsoscheddate.sql b/foundation-database/public/functions/getsoscheddate.sql
new file mode 100644 (file)
index 0000000..ef09233
--- /dev/null
@@ -0,0 +1,18 @@
+CREATE OR REPLACE FUNCTION getSoSchedDate(INTEGER) RETURNS DATE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCoheadid ALIAS FOR $1;
+  _minscheddate DATE;
+
+BEGIN
+
+  SELECT MIN(coitem_scheddate) INTO _minscheddate
+  FROM coitem
+  WHERE ( (coitem_cohead_id=pCoheadid)
+    AND   (coitem_status NOT IN ('C', 'X')) );
+
+  RETURN _minscheddate;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getsostatus.sql b/foundation-database/public/functions/getsostatus.sql
new file mode 100644 (file)
index 0000000..663c327
--- /dev/null
@@ -0,0 +1,17 @@
+CREATE OR REPLACE FUNCTION getSoStatus(INTEGER) RETURNS CHAR(1) AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCoheadid ALIAS FOR $1;
+  _result char(1);
+
+BEGIN
+
+  SELECT cohead_status INTO _result
+  FROM cohead
+  WHERE (cohead_id=pCoheadid);
+
+  RETURN _result;
+
+END;
+$$ LANGUAGE 'plpgsql' STABLE;
diff --git a/foundation-database/public/functions/getsubtax.sql b/foundation-database/public/functions/getsubtax.sql
new file mode 100644 (file)
index 0000000..5bf8f4d
--- /dev/null
@@ -0,0 +1,46 @@
+
+CREATE OR REPLACE FUNCTION getsubtax(integer, integer)
+  RETURNS SETOF subtax AS
+$$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pTaxCodeId ALIAS FOR $1;
+  pLevel ALIAS FOR $2;
+  _row subtax%ROWTYPE;
+  _x RECORD;
+  _y RECORD;
+
+BEGIN
+
+  FOR _x IN --Select all tax codes whose calculation basis is pTaxCodeId
+    SELECT tax_id, tax_code, tax_descrip
+    FROM tax
+    WHERE tax_basis_tax_id = pTaxCodeId
+    
+    
+    LOOP
+    _row.subtax_taxcode_id := _x.tax_id;
+    _row.subtax_taxcode_code := _x.tax_code;
+    _row.subtax_taxcode_descrip := _x.tax_descrip;
+    _row.subtax_taxcode_level := pLevel + 1;
+
+   RETURN NEXT _row;  
+  
+    FOR _y IN SELECT * from getSubTax(_x.tax_id, pLevel + 1) --This is the recursive part.
+    LOOP
+
+      _row.subtax_taxcode_id := _y.subtax_taxcode_id;
+      _row.subtax_taxcode_code := _y.subtax_taxcode_code ;
+      _row.subtax_taxcode_descrip := _y.subtax_taxcode_descrip;
+      _row.subtax_taxcode_level := pLevel + 2;
+
+      RETURN NEXT _row;
+
+    END LOOP;
+  END LOOP;
+
+END;
+$$
+  LANGUAGE 'plpgsql' VOLATILE;
diff --git a/foundation-database/public/functions/gettaxauthid.sql b/foundation-database/public/functions/gettaxauthid.sql
new file mode 100644 (file)
index 0000000..3fadbcd
--- /dev/null
@@ -0,0 +1,22 @@
+CREATE OR REPLACE FUNCTION getTaxAuthId(text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pTaxAuthCode ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  IF (pTaxAuthCode IS NULL) THEN
+       RETURN NULL;
+  END IF;
+
+  SELECT taxauth_id INTO _returnVal
+  FROM taxauth
+  WHERE (taxauth_code=pTaxAuthCode);
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION ''Tax Authority % not found.'', pTaxAuthCode;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/gettaxid.sql b/foundation-database/public/functions/gettaxid.sql
new file mode 100644 (file)
index 0000000..68edce6
--- /dev/null
@@ -0,0 +1,22 @@
+CREATE OR REPLACE FUNCTION getTaxId(text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pTaxCode ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  IF (pTaxCode IS NULL) THEN
+       RETURN NULL;
+  END IF;
+
+  SELECT tax_id INTO _returnVal
+  FROM tax
+  WHERE (tax_code=pTaxCode);
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION ''Tax Code % not found.'', pTaxCode;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/gettaxtypeid.sql b/foundation-database/public/functions/gettaxtypeid.sql
new file mode 100644 (file)
index 0000000..ba287f3
--- /dev/null
@@ -0,0 +1,22 @@
+CREATE OR REPLACE FUNCTION getTaxTypeId(text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pTaxType ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  IF (pTaxType IS NULL) THEN
+       RETURN NULL;
+  END IF;
+
+  SELECT taxtype_id INTO _returnVal
+  FROM taxtype
+  WHERE (taxtype_name=pTaxType);
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION ''Tax Type % not found.'', pTaxType;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/gettaxzoneid.sql b/foundation-database/public/functions/gettaxzoneid.sql
new file mode 100644 (file)
index 0000000..ddc0d2f
--- /dev/null
@@ -0,0 +1,22 @@
+CREATE OR REPLACE FUNCTION getTaxZoneId(text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pTaxZone ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  IF (pTaxZone IS NULL) THEN
+       RETURN NULL;
+  END IF;
+
+  SELECT taxzone_id INTO _returnVal
+  FROM taxzone
+  WHERE (taxzone_code=pTaxZone);
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION ''Tax Zone % not found.'', pTaxZone;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/gettermsid.sql b/foundation-database/public/functions/gettermsid.sql
new file mode 100644 (file)
index 0000000..24e1904
--- /dev/null
@@ -0,0 +1,22 @@
+CREATE OR REPLACE FUNCTION getTermsId(text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pTermsCode ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  IF (pTermsCode IS NULL) THEN
+       RETURN NULL;
+  END IF;
+
+  SELECT terms_id INTO _returnVal
+  FROM terms
+  WHERE (terms_code=pTermsCode);
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION ''Terms Code % not found.'', pTermsCode;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getunassignedaccntid.sql b/foundation-database/public/functions/getunassignedaccntid.sql
new file mode 100644 (file)
index 0000000..bb790f3
--- /dev/null
@@ -0,0 +1,26 @@
+
+CREATE OR REPLACE FUNCTION getUnassignedAccntId() RETURNS INTEGER STABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _test INTEGER;
+  _returnVal INTEGER;
+BEGIN
+  SELECT fetchMetricValue('UnassignedAccount') INTO _test;
+
+  IF (_test IS NULL) THEN
+    RAISE EXCEPTION 'Metric not found for UnassignedAccount';
+  END IF;
+
+  SELECT accnt_id INTO _returnVal
+  FROM accnt
+  WHERE (accnt_id=_test);
+
+  IF (NOT FOUND) THEN
+    RAISE EXCEPTION 'Metric UnassignedAccount is an invalid G/L Account';
+  END IF;
+
+  RETURN _returnVal;
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/getuomid.sql b/foundation-database/public/functions/getuomid.sql
new file mode 100644 (file)
index 0000000..73b713e
--- /dev/null
@@ -0,0 +1,22 @@
+CREATE OR REPLACE FUNCTION getUomId(text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pUom ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  IF (pUom IS NULL) THEN
+    RETURN NULL;
+  END IF;
+
+  SELECT uom_id INTO _returnVal
+  FROM uom
+  WHERE (uom_name=pUom);
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION ''Unit of Measure % not found.'', pUom;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getuomtypeid.sql b/foundation-database/public/functions/getuomtypeid.sql
new file mode 100644 (file)
index 0000000..cace7b5
--- /dev/null
@@ -0,0 +1,52 @@
+CREATE OR REPLACE FUNCTION getUomTypeId(text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pUomType ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  IF (pUomType IS NULL) THEN
+       RETURN NULL;
+  END IF;
+
+  SELECT uomtype_id INTO _returnVal
+  FROM uomtype
+  WHERE (UPPER(uomtype_name)=UPPER(pUomType));
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION ''Unit of Measuer Type % not found.'', pUomType;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION getUomTypeId(text[]) RETURNS INTEGER[] AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pUomTypes ALIAS FOR $1;
+  _returnVal INTEGER[];
+  _val INTEGER;
+  _i INTEGER;
+BEGIN
+  IF (pUomTypes IS NULL) OR (ARRAY_UPPER(pUomTypes,1) = 0) THEN
+       RETURN NULL;
+  END IF;
+
+  FOR _i IN 1..ARRAY_UPPER(pUomTypes,1)
+  LOOP
+    SELECT uomtype_id INTO _val
+    FROM uomtype
+    WHERE (UPPER(uomtype_name)=UPPER(pUomTypes[_i]));
+
+    IF (_val IS NULL) THEN
+       RAISE EXCEPTION ''Unit of Measure Type % not found.'', pUomTypes[_i];
+    ELSE
+      _returnVal[_i] := _val;
+    END IF;
+  END LOOP;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getusrid.sql b/foundation-database/public/functions/getusrid.sql
new file mode 100644 (file)
index 0000000..c16b314
--- /dev/null
@@ -0,0 +1,18 @@
+CREATE OR REPLACE FUNCTION getUsrId(text) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pUsr ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  SELECT usr_id INTO _returnVal
+  FROM usr
+  WHERE (usr_username=COALESCE(pUsr, getEffectiveXtUser()));
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION 'User % not found.', pUsr;
+  END IF;
+
+  RETURN _returnVal;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getusrlocaleid.sql b/foundation-database/public/functions/getusrlocaleid.sql
new file mode 100644 (file)
index 0000000..bbb6ea8
--- /dev/null
@@ -0,0 +1,32 @@
+CREATE OR REPLACE FUNCTION getUsrLocaleId() RETURNS INTEGER IMMUTABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _returnVal INTEGER;
+BEGIN
+  SELECT CAST(usrpref_value AS INTEGER)
+  FROM usrpref
+  WHERE (usrpref_username=getEffectiveXTUser())
+    AND (usrpref_name='locale_id') INTO _returnVal;
+
+  IF (_returnVal IS NULL) THEN
+    SELECT locale_id
+    FROM locale
+    WHERE (LOWER(locale_code) = 'default')
+    LIMIT 1 INTO _returnVal;
+  END IF;
+
+  IF (_returnVal IS NULL) THEN
+    SELECT locale_id
+    FROM locale
+    ORDER BY locale_id
+    LIMIT 1 INTO _returnVal;
+  END IF;
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION 'User Locale not found.';
+  END IF;
+
+  RETURN _returnVal;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getvendaddrid.sql b/foundation-database/public/functions/getvendaddrid.sql
new file mode 100644 (file)
index 0000000..741b55b
--- /dev/null
@@ -0,0 +1,26 @@
+CREATE OR REPLACE FUNCTION getVendAddrId(text, text) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pVendNumber   ALIAS FOR $1;
+  pVendAddrCode ALIAS FOR $2;
+  _returnVal INTEGER;
+BEGIN
+  IF ( (pVendNumber IS NULL) OR (pVendAddrCode IS NULL) ) THEN
+    RETURN NULL;
+  END IF;
+
+  SELECT vendaddr_id INTO _returnVal
+    FROM vendaddrinfo
+    JOIN vendinfo ON (vend_id=vendaddr_vend_id)
+  WHERE ( (vendaddr_code=pVendAddrCode)
+    AND   (vend_number=pVendNumber) );
+
+  IF (_returnVal IS NULL) THEN
+    RAISE EXCEPTION 'Vendor Number % Address % not found.',
+    pVendNumber, pVendAddrCode;
+  END IF;
+
+  RETURN _returnVal;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getvendid.sql b/foundation-database/public/functions/getvendid.sql
new file mode 100644 (file)
index 0000000..88bbd65
--- /dev/null
@@ -0,0 +1,22 @@
+CREATE OR REPLACE FUNCTION getVendId(text) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pVendNumber ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  IF (pVendNumber IS NULL) THEN
+    RETURN NULL;
+  END IF;
+
+  SELECT vend_id INTO _returnVal
+    FROM vendinfo
+   WHERE (vend_number=pVendNumber);
+
+  IF (_returnVal IS NULL) THEN
+    RAISE EXCEPTION 'Vendor Number % not found.', pVendNumber;
+  END IF;
+
+  RETURN _returnVal;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getvendtypeid.sql b/foundation-database/public/functions/getvendtypeid.sql
new file mode 100644 (file)
index 0000000..ef03ff9
--- /dev/null
@@ -0,0 +1,22 @@
+CREATE OR REPLACE FUNCTION getVendTypeId(text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pVendTypeCode ALIAS FOR $1;
+  _returnVal INTEGER;
+BEGIN
+  IF (pVendTypeCode IS NULL) THEN
+    RETURN NULL;
+  END IF;
+
+  SELECT vendtype_id INTO _returnVal
+  FROM vendtype
+  WHERE (UPPER(vendtype_code)=UPPER(pVendTypeCode));
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION ''Vendor Type % not found.'', pVendTypeCode;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getwarehousid.sql b/foundation-database/public/functions/getwarehousid.sql
new file mode 100644 (file)
index 0000000..a4555de
--- /dev/null
@@ -0,0 +1,35 @@
+CREATE OR REPLACE FUNCTION getWarehousId(text,text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWarehousCode ALIAS FOR $1;
+  pType ALIAS FOR $2;
+  _active BOOL;
+  _shipping BOOL;
+  _returnVal INTEGER;
+BEGIN
+  IF (pWarehousCode IS NULL) THEN
+       RETURN NULL;
+  END IF;
+  IF (UPPER(pType) NOT IN (''ALL'',''ACTIVE'',''SHIPPING'')) THEN
+       RAISE EXCEPTION ''Warehouse lookip type % not valid. Valid types are ALL, ACTIVE and SHIPPING'', pType;
+  END IF;
+
+  SELECT warehous_id, warehous_active, warehous_shipping INTO _returnVal, _active, _shipping
+  FROM site()
+  WHERE (warehous_code=UPPER(pWarehousCode));
+
+  IF (_returnVal IS NULL) THEN
+    RAISE EXCEPTION ''Warehouse Code % not found.'', pWarehousCode;
+    ELSE IF ((pType=''SHIPPING'') AND (_shipping=false)) THEN
+      RAISE EXCEPTION ''Warehouse Code % is not a vaild shipping warehouse.'', pWarehousCode;
+      ELSE IF ((pType IN (''SHIPPING'',''ACTIVE'')) AND (_active=false)) THEN
+        RAISE EXCEPTION ''Warehouse Code % is inactive.'', pWarehousCode;
+      END IF;
+    END IF;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/getwhsezoneid.sql b/foundation-database/public/functions/getwhsezoneid.sql
new file mode 100644 (file)
index 0000000..7057d8e
--- /dev/null
@@ -0,0 +1,24 @@
+CREATE OR REPLACE FUNCTION getWhseZoneId(text, text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWhseCode ALIAS FOR $1;
+  pWhseZoneName ALIAS FOR $2;
+  _returnVal INTEGER;
+BEGIN
+  IF ((pWhseCode IS NULL) OR (pWhseZoneName IS NULL)) THEN
+       RETURN NULL;
+  END IF;
+
+  SELECT whsezone_id INTO _returnVal
+  FROM whsezone
+  WHERE ( (whsezone_warehous_id=getWarehousId(pWhseCode, ''ACTIVE''))
+      AND (UPPER(whsezone_name)=UPPER(pWhseZoneName)) );
+
+  IF (_returnVal IS NULL) THEN
+       RAISE EXCEPTION ''Whsezone % not found.'', pWhseZoneName;
+  END IF;
+
+  RETURN _returnVal;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/grantallmodulecmnttypesource.sql b/foundation-database/public/functions/grantallmodulecmnttypesource.sql
new file mode 100644 (file)
index 0000000..8a9bd8f
--- /dev/null
@@ -0,0 +1,28 @@
+CREATE OR REPLACE FUNCTION grantAllModuleCmnttypeSource(INTEGER, TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCmnttypeid ALIAS FOR $1;
+  pModuleName ALIAS FOR $2;
+  _source RECORD;
+  _sourceCounter INTEGER;
+
+BEGIN
+
+  _sourceCounter := 0;
+
+  FOR _source IN SELECT source_id
+                 FROM source 
+                 WHERE (source_module=pModuleName) LOOP
+
+    IF (SELECT grantCmnttypeSource(pCmnttypeid, _source.source_id)) THEN
+      _sourceCounter := _sourceCounter + 1;
+    END IF;
+
+  END LOOP;
+
+  RETURN _sourceCounter;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/grantallmodulepriv.sql b/foundation-database/public/functions/grantallmodulepriv.sql
new file mode 100644 (file)
index 0000000..3925c94
--- /dev/null
@@ -0,0 +1,28 @@
+CREATE OR REPLACE FUNCTION grantAllModulePriv(TEXT, TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pUsername ALIAS FOR $1;
+  pModuleName ALIAS FOR $2;
+  _priv RECORD;
+  _privCounter INTEGER;
+
+BEGIN
+
+  _privCounter := 0;
+
+  FOR _priv IN SELECT priv_id
+               FROM priv 
+               WHERE (priv_module=pModuleName) LOOP
+
+    IF (SELECT grantPriv(pUsername, _priv.priv_id)) THEN
+      _privCounter := _privCounter + 1;
+    END IF;
+
+  END LOOP;
+
+  RETURN _privCounter;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/grantallmoduleprivgroup.sql b/foundation-database/public/functions/grantallmoduleprivgroup.sql
new file mode 100644 (file)
index 0000000..7ffdeb7
--- /dev/null
@@ -0,0 +1,28 @@
+CREATE OR REPLACE FUNCTION grantAllModulePrivGroup(INTEGER, TEXT) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pGrpid ALIAS FOR $1;
+  pModuleName ALIAS FOR $2;
+  _priv RECORD;
+  _privCounter INTEGER;
+
+BEGIN
+
+  _privCounter := 0;
+
+  FOR _priv IN SELECT priv_id
+               FROM priv 
+               WHERE (priv_module=pModuleName) LOOP
+
+    IF (SELECT grantPrivGroup(pGrpid, _priv.priv_id)) THEN
+      _privCounter := _privCounter + 1;
+    END IF;
+
+  END LOOP;
+
+  RETURN _privCounter;
+
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/grantcmnttypesource.sql b/foundation-database/public/functions/grantcmnttypesource.sql
new file mode 100644 (file)
index 0000000..9e95449
--- /dev/null
@@ -0,0 +1,29 @@
+CREATE OR REPLACE FUNCTION grantCmnttypeSource(INTEGER, INTEGER) RETURNS BOOL AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCmnttypeid ALIAS FOR $1;
+  pSourceid ALIAS FOR $2;
+  _test INTEGER;
+
+BEGIN
+
+  SELECT cmnttypesource_id INTO _test
+  FROM cmnttypesource
+  WHERE ( (cmnttypesource_cmnttype_id=pCmnttypeid)
+    AND (cmnttypesource_source_id=pSourceid) );
+
+  IF (FOUND) THEN
+    RETURN FALSE;
+  END IF;
+
+  INSERT INTO cmnttypesource
+  ( cmnttypesource_cmnttype_id, cmnttypesource_source_id )
+  VALUES
+  ( pCmnttypeid, pSourceid );
+
+  RETURN TRUE;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/grantgroup.sql b/foundation-database/public/functions/grantgroup.sql
new file mode 100644 (file)
index 0000000..aa4b382
--- /dev/null
@@ -0,0 +1,29 @@
+CREATE OR REPLACE FUNCTION grantGroup(TEXT, INTEGER) RETURNS BOOL AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pUsername ALIAS FOR $1;
+  pGrpid ALIAS FOR $2;
+  _test INTEGER;
+
+BEGIN
+
+  SELECT usrgrp_id INTO _test
+  FROM usrgrp
+  WHERE ( (usrgrp_username=pUsername)
+   AND (usrgrp_grp_id=pGrpid) );
+
+  IF (FOUND) THEN
+    RETURN FALSE;
+  END IF;
+
+  INSERT INTO usrgrp
+  ( usrgrp_username, usrgrp_grp_id )
+  VALUES
+  ( pUsername, pGrpid );
+
+  RETURN TRUE;
+
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/grantpriv.sql b/foundation-database/public/functions/grantpriv.sql
new file mode 100644 (file)
index 0000000..1bfd993
--- /dev/null
@@ -0,0 +1,64 @@
+CREATE OR REPLACE FUNCTION grantPriv(TEXT, INTEGER) RETURNS BOOL AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pUsername ALIAS FOR $1;
+  pPrivid ALIAS FOR $2;
+  _test INTEGER;
+
+BEGIN
+
+  SELECT usrpriv_id INTO _test
+  FROM usrpriv
+  WHERE ( (usrpriv_username=pUsername)
+   AND (usrpriv_priv_id=pPrivid) );
+
+  IF (FOUND) THEN
+    RETURN FALSE;
+  END IF;
+
+  INSERT INTO usrpriv
+  ( usrpriv_username, usrpriv_priv_id )
+  VALUES
+  ( pUsername, pPrivid );
+
+  NOTIFY "usrprivUpdated";
+
+  RETURN TRUE;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION grantPriv(TEXT, TEXT) RETURNS BOOL AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pUsername ALIAS FOR $1;
+  pPrivname ALIAS FOR $2;
+  _test INTEGER;
+
+BEGIN
+
+  SELECT usrpriv_id INTO _test
+    FROM usrpriv
+    JOIN priv ON (usrpriv_priv_id=priv_id)
+  WHERE ((usrpriv_username=pUsername)
+     AND (priv_name=pPrivname) );
+
+  IF (FOUND) THEN
+    RETURN FALSE;
+  END IF;
+
+  INSERT INTO usrpriv
+  ( usrpriv_username, usrpriv_priv_id )
+  SELECT pUsername, priv_id
+    FROM priv
+   WHERE (priv_name=pPrivname);
+
+  NOTIFY "usrprivUpdated";
+
+  RETURN TRUE;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/grantprivgroup.sql b/foundation-database/public/functions/grantprivgroup.sql
new file mode 100644 (file)
index 0000000..c5f3641
--- /dev/null
@@ -0,0 +1,28 @@
+CREATE OR REPLACE FUNCTION grantPrivGroup(INTEGER, INTEGER) RETURNS BOOL AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pGrpid ALIAS FOR $1;
+  pPrivid ALIAS FOR $2;
+  _test INTEGER;
+
+BEGIN
+
+  SELECT grppriv_id INTO _test
+  FROM grppriv
+  WHERE ( (grppriv_grp_id=pGrpid)
+   AND (grppriv_priv_id=pPrivid) );
+
+  IF (FOUND) THEN
+    RETURN FALSE;
+  END IF;
+
+  INSERT INTO grppriv
+  ( grppriv_grp_id, grppriv_priv_id )
+  VALUES
+  ( pGrpid, pPrivid );
+
+  RETURN TRUE;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/hasalarms.sql b/foundation-database/public/functions/hasalarms.sql
new file mode 100644 (file)
index 0000000..ce22f87
--- /dev/null
@@ -0,0 +1,114 @@
+
+CREATE OR REPLACE FUNCTION hasAlarms() RETURNS BOOLEAN AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _alarm          RECORD;
+  _batchId        INTEGER;
+  _evntlogordtype TEXT;
+  _evnttypeid     INTEGER;
+  _evnttypename   TEXT;
+  _fromEmail      TEXT;
+  _itemid         INTEGER;
+  _longsource     TEXT;
+  _msgId          INTEGER;
+  _recipient      TEXT;
+  _recipientPart  INTEGER;
+  _returnVal      BOOLEAN := FALSE;
+  _summary        TEXT;
+  _whsId          INTEGER := -1;
+
+BEGIN
+  FOR _alarm IN SELECT *
+                FROM alarm
+                WHERE ((alarm_creator=getEffectiveXtUser())
+                   AND (CURRENT_TIMESTAMP > alarm_trigger)) LOOP
+    _returnVal := TRUE;
+
+    IF (_alarm.alarm_source = 'TODO') THEN
+      SELECT (todoitem_name || '-' || todoitem_description),
+             'T', 'TodoAlarm', 'To-Do Item'
+      INTO _summary, _evntlogordtype, _evnttypename, _longsource
+      FROM todoitem
+      WHERE (todoitem_id = _alarm.alarm_source_id);
+
+    ELSIF (_alarm.alarm_source = 'INCDT') THEN
+      SELECT (incdt_number || '-' || incdt_summary),
+             'I', 'IncidentAlarm', 'Incident'
+      INTO _summary, _evntlogordtype, _evnttypename, _longsource
+      FROM incdt
+      WHERE (incdt_id = _alarm.alarm_source_id);
+
+    ELSIF (_alarm.alarm_source = 'J') THEN
+      SELECT (prj_number || ' ' || prj_name || '-' || prjtask_name),
+              'J', 'TaskAlarm', 'Project Task'
+      INTO _summary, _evntlogordtype, _evnttypename, _longsource
+      FROM prjtask JOIN prj ON (prj_id=prjtask_prj_id)
+      WHERE (prjtask_id = _alarm.alarm_source_id);
+
+    ELSE
+      CONTINUE; -- there's nothing to do for this iteration of the loop
+    END IF;
+
+    -- if event alarm
+    IF (_alarm.alarm_event) THEN
+      SELECT evnttype_id INTO _evnttypeid
+      FROM evnttype
+      WHERE (evnttype_name=_evnttypename);
+
+      _recipientPart := 1;
+      LOOP
+        _recipient := SPLIT_PART(_alarm.alarm_event_recipient, ',', _recipientPart);
+        EXIT WHEN (LENGTH(_recipient) = 0);
+
+        SELECT usrpref_value INTO _whsId
+        FROM usrpref
+        WHERE ( (usrpref_username = _recipient)
+          AND   (usrpref_name = 'PreferredWarehouse') );
+
+        INSERT INTO evntlog (evntlog_evnttime, evntlog_username,
+                             evntlog_evnttype_id, evntlog_ordtype,
+                             evntlog_ord_id, evntlog_warehous_id, evntlog_number
+                   ) VALUES (CURRENT_TIMESTAMP, _recipient,
+                             _evnttypeid, _evntlogordtype,
+                             _alarm.alarm_source_id, _whsId, _summary);
+
+        _recipientPart := _recipientPart + 1;
+      END LOOP;
+    END IF;
+
+    IF (_alarm.alarm_email) THEN
+      SELECT usr_email INTO _fromEmail
+      FROM usr
+      WHERE (usr_username = _alarm.alarm_creator);
+
+      _recipientPart := 1;
+      LOOP
+        _recipient := SPLIT_PART(_alarm.alarm_email_recipient, ',', _recipientPart);
+        EXIT WHEN (LENGTH(_recipient) <= 0);
+        _batchId := xtbatch.submitEmailToBatch(_fromEmail, _recipient, '',
+                                               _summary,
+                                               'Alarm reminder for '
+                                               || _longsource || '.',
+                                               NULL, CURRENT_TIMESTAMP,
+                                               FALSE, NULL, NULL);
+        _recipientPart := _recipientPart + 1;
+      END LOOP;
+    END IF;
+
+    IF (_alarm.alarm_sysmsg) THEN
+      _recipientPart := 1;
+      LOOP
+        _recipient := SPLIT_PART(_alarm.alarm_sysmsg_recipient, ',', _recipientPart);
+        EXIT WHEN (LENGTH(_recipient) <= 0);
+        _msgId := postMessage(_recipient, (_longsource || ' - ' || _summary));
+        _recipientPart := _recipientPart + 1;
+      END LOOP;
+    END IF;
+
+    DELETE FROM alarm WHERE alarm_id=_alarm.alarm_id;
+  END LOOP;
+  RETURN _returnVal;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/hasevents.sql b/foundation-database/public/functions/hasevents.sql
new file mode 100644 (file)
index 0000000..464e2ab
--- /dev/null
@@ -0,0 +1,16 @@
+
+CREATE OR REPLACE FUNCTION hasEvents() RETURNS BOOLEAN AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+
+  PERFORM evntlog_id
+  FROM evntlog
+  WHERE ( (evntlog_dispatched IS NULL)
+   AND (evntlog_username=getEffectiveXtUser()) )
+  LIMIT 1;
+  RETURN FOUND;
+
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/hasmessages.sql b/foundation-database/public/functions/hasmessages.sql
new file mode 100644 (file)
index 0000000..0c0b036
--- /dev/null
@@ -0,0 +1,18 @@
+
+CREATE OR REPLACE FUNCTION hasMessages() RETURNS BOOLEAN AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+
+  PERFORM msguser_id
+  FROM msg, msguser
+  WHERE ( (msguser_username=getEffectiveXtUser())
+   AND (msguser_msg_id=msg_id)
+   AND (CURRENT_TIMESTAMP BETWEEN msg_scheduled AND msg_expires)
+   AND (msguser_viewed IS NULL) )
+  LIMIT 1;
+  RETURN FOUND;
+
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/haspriv.sql b/foundation-database/public/functions/haspriv.sql
new file mode 100644 (file)
index 0000000..7627147
--- /dev/null
@@ -0,0 +1,31 @@
+CREATE OR REPLACE FUNCTION hasPriv(TEXT) RETURNS BOOLEAN AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pPrivName    ALIAS FOR $1;
+  _result       INTEGER;
+  _returnVal   BOOLEAN;
+
+BEGIN
+  RAISE NOTICE 'hasPriv(TEXT) is deprecated. Use checkPrivilege(TEXT) instead.';
+  SELECT priv_id INTO _result
+    FROM priv, grppriv, usrgrp
+   WHERE((usrgrp_grp_id=grppriv_grp_id)
+     AND (grppriv_priv_id=priv_id)
+     AND (priv_name=pPrivName)
+     AND (usrgrp_username=getEffectiveXtUser()));
+  IF (FOUND) THEN
+    RETURN true;
+  END IF;
+
+  SELECT COALESCE(usrpriv_id, 0) != 0 INTO _returnVal
+  FROM priv LEFT OUTER JOIN
+       usrpriv ON (priv_id=usrpriv_priv_id AND usrpriv_username = getEffectiveXtUser())
+  WHERE (priv_name=pPrivName);
+  IF (_returnVal IS NULL) THEN
+    _returnVal := FALSE;
+  END IF;
+
+  RETURN _returnVal;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/hasprivonobject.sql b/foundation-database/public/functions/hasprivonobject.sql
new file mode 100644 (file)
index 0000000..b04d755
--- /dev/null
@@ -0,0 +1,122 @@
+CREATE OR REPLACE FUNCTION hasPrivOnObject(pPrivType   TEXT,
+                                           pObjectType TEXT,
+                                           pObjectId   INTEGER = NULL,
+                                           pUser       TEXT    = NULL) RETURNS BOOLEAN AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _haspriv   BOOLEAN := FALSE;
+  _privfound BOOLEAN := FALSE;
+  _pkey      TEXT[];
+  _privdesc  RECORD;
+  _qstr      TEXT;
+
+BEGIN
+  IF UPPER(pPrivType) NOT IN ('CREATE', 'EDIT', 'VIEW', 'DELETE') THEN
+    RAISE EXCEPTION 'Cannot check if user has % on % [xtuple: hasPrivOnObject, -1, %, %]',
+                     pPrivType, pObjectType, pPrivType, pObjectType;
+  END IF;
+
+  /* TODO: create privdesc table? can't do it yet because this is a fix for a minor release
+     NOTE: only include tables that have a single integer column as pkey
+     NOTE: some of these are part of proprietary extensions. how do we make them part of the extension?
+  */
+  FOR _privdesc IN
+  WITH privdesc AS (
+    SELECT 'ADDR' AS otype,    'public' AS masterschema,
+                                         'addr' AS mastertable,
+                                                     'MaintainAddressMasters' AS editall,
+                                                                                     'ViewAddressMasters' AS viewall,
+                                                                                                               NULL AS ownerfield,        NULL AS editmine,                NULL AS viewmine
+     UNION ALL SELECT 'BBH',   'xtmfg',  'bbom',     'MaintainBBOMs',                'ViewBBOMs',              NULL,                      NULL,                            NULL
+     UNION ALL SELECT 'BBI',   'xtmfg',  'bbom',     'MaintainBBOMs',                'ViewBBOMs',              NULL,                      NULL,                            NULL
+     UNION ALL SELECT 'BMH',   'public', 'bom',      'MaintainBOMs',                 'ViewBOMs',               NULL,                      NULL,                            NULL
+     UNION ALL SELECT 'BMI',   'public', 'bom',      'MaintainBOMs',                 'ViewBOMs',               NULL,                      NULL,                            NULL
+     UNION ALL SELECT 'BOH',   'xtmfg',  'boo',      'MaintainBOOs',                 'ViewBOOs',               NULL,                      NULL,                            NULL
+     UNION ALL SELECT 'BOI',   'xtmfg',  'boo',      'MaintainBOOs',                 'ViewBOOs',               NULL,                      NULL,                            NULL
+     UNION ALL SELECT 'C',     'public', 'custinfo', 'MaintainCustomerMasters',      'ViewCustomerMasters',    NULL,                      NULL,                            NULL
+     UNION ALL SELECT 'CRMA',  'public', 'crmacct',  'MaintainAllCRMAccounts',       'ViewAllCRMAccounts',     'crmacct_owner_username',  'MaintainPersonalCRMAccounts',   'ViewPersonalCRMAccounts'
+     UNION ALL SELECT 'EMP',   'public', 'emp',      'MaintainEmployees',            'ViewEmployees',          NULL,                      NULL,                            NULL
+     UNION ALL SELECT 'I',     'public', 'item',     'MaintainItemMasters',          'ViewItemMasters',        NULL,                      NULL,                            NULL
+     UNION ALL SELECT 'INCDT', 'public', 'incdt',    'MaintainAllIncidents',         'ViewAllIncidents',       'incdt_owner_username',    'MaintainPersonalIncidents',     'ViewPersonalIncidents'
+     UNION ALL SELECT 'IR',    'public', 'itemsrc',  'MaintainItemSources',          'ViewItemSources',        NULL,                      NULL,                            NULL
+     UNION ALL SELECT 'IS',    'public', 'itemsite', 'MaintainItemSites',            'ViewItemSites',          NULL,                      NULL,                            NULL
+     UNION ALL SELECT 'J',     'public', 'prj',      'MaintainAllProjects',          'ViewAllProjects',        'prj_owner_username',      'MaintainPersonalProjects',      'ViewPersonalProjects'
+     UNION ALL SELECT 'J',     'public', 'prj',      'MaintainAllProjects',          'ViewAllProjects',        'prj_username',            'MaintainPersonalProjects',      'ViewPersonalProjects'
+     UNION ALL SELECT 'L',     'public', 'location', 'MaintainLocations',            'ViewLocations',          NULL,                      NULL,                            NULL
+     UNION ALL SELECT 'OPP',   'public', 'ophead',   'MaintainAllOpportunities',     'ViewAllOpportunities',   'ophead_owner_username',   'MaintainPersonalOpportunities', 'ViewPersonalOpportunities'
+     UNION ALL SELECT 'P',     'public', 'pohead',   'MaintainPurchaseOrders',       'ViewPurchaseOrders',     NULL,                      NULL,                            NULL
+     UNION ALL SELECT 'PI',    'public', 'pohead',   'MaintainPurchaseOrders',       'ViewPurchaseOrders',     NULL,                      NULL,                            NULL
+     UNION ALL SELECT 'Q',     'public', 'quhead',   'MaintainQuotes',               'ViewQuotes',             NULL,                      NULL,                            NULL
+     UNION ALL SELECT 'QI',    'public', 'quhead',   'MaintainQuotes',               'ViewQuotes',             NULL,                      NULL,                            NULL
+     UNION ALL SELECT 'RA',    'public', 'rahead',   'MaintainReturns',              'ViewReturns',            NULL,                      NULL,                            NULL
+     UNION ALL SELECT 'RI',    'public', 'rahead',   'MaintainReturns',              'ViewReturns',            NULL,                      NULL,                            NULL
+     UNION ALL SELECT 'S',     'public', 'cohead',   'MaintainSalesOrders',          'ViewSalesOrders',        NULL,                      NULL,                            NULL
+     UNION ALL SELECT 'SI',    'public', 'cohead',   'MaintainSalesOrders',          'ViewSalesOrders',        NULL,                      NULL,                            NULL
+     UNION ALL SELECT 'T',     'public', 'cntct',    'MaintainAllContacts',          'ViewAllContacts',        'cntct_owner_username',    'MaintainPersonalContacts',      'ViewPersonalContacts'
+     UNION ALL SELECT 'TE',    'te',     'tehead',   'MaintainTimeExpense',          'ViewTimeExpenseHistory', 'tehead_username',         'MaintainTimeExpenseSelf',       NULL
+     UNION ALL SELECT 'TE',    'te',     'tehead',   'MaintainTimeExpenseOthers',    'ViewTimeExpenseHistory', 'tehead_username',         'MaintainTimeExpenseSelf',       NULL
+     UNION ALL SELECT 'TI',    'public', 'tohead',   'MaintainTransferOrders',       'ViewTransferOrders',     NULL,                      NULL,                            NULL
+     UNION ALL SELECT 'TO',    'public', 'tohead',   'MaintainTransferOrders',       'ViewTransferOrders',     NULL,                      NULL,                            NULL
+     UNION ALL SELECT 'TODO',  'public', 'todoitem', 'MaintainAllToDoItems',         'ViewAllToDoItems',       'todoitem_owner_username', 'MaintainPersonalToDoItems',     'ViewPersonalToDoItems'
+     UNION ALL SELECT 'V',     'public', 'vendinfo', 'MaintainVendors',              'ViewVendors',            NULL,                      NULL,                            NULL
+     UNION ALL SELECT 'W',     'public', 'wo',       'MaintainWorkOrders',           'ViewWorkOrders',         NULL,                      NULL,                            NULL
+     UNION ALL SELECT 'WH',    'public', 'whsinfo',  'MaintainWarehouses',           'ViewWarehouses',         NULL,                      NULL,                            NULL)
+  -- UNION ALL SELECT 'LS',    'public', 'ls',       NULL,                           NULL,                     NULL,                      NULL,                            NULL
+  -- UNION ALL SELECT 'P',     'public', 'pohead',   'MaintainPostedPurchaseOrders', 'ViewPurchaseOrders',     NULL,                      NULL,                            NULL -- additional criteria?
+  -- UNION ALL SELECT 'PI',    'public', 'pohead',   'MaintainPostedPurchaseOrders', 'ViewPurchaseOrders',     NULL,                      NULL,                            NULL -- additional criteria?
+     SELECT *
+       FROM privdesc
+      WHERE otype = pObjectType
+  LOOP
+    _privfound := TRUE;
+    RAISE DEBUG '% % % % % % %',
+                _privdesc.otype, _privdesc.masterschema, _privdesc.mastertable,
+                _privdesc.editall, _privdesc.viewall, _privdesc.editmine, _privdesc.viewmine;
+
+    IF checkPrivilege(CASE UPPER(pPrivType) WHEN 'CREATE' THEN _privdesc.editall
+                                            WHEN 'EDIT'   THEN _privdesc.editall
+                                            WHEN 'DELETE' THEN _privdesc.editall
+                                            WHEN 'VIEW'   THEN _privdesc.viewall
+                      END) THEN
+      _haspriv = TRUE;
+
+    ELSIF checkPrivilege(CASE UPPER(pPrivType) WHEN 'CREATE' THEN _privdesc.editmine
+                                               WHEN 'EDIT'   THEN _privdesc.editmine
+                                               WHEN 'DELETE' THEN _privdesc.editmine
+                                               WHEN 'VIEW'   THEN _privdesc.viewmine
+                         END) THEN
+      IF pObjectId IS NULL THEN
+      _haspriv = TRUE;
+
+      ELSE
+        _pkey := primaryKeyFields(_privdesc.masterschema, _privdesc.mastertable);
+
+        -- SELECT ... FROM schema.table WHERE pkeyfield = pObjectId AND ownerfield = pUser
+        _qstr := 'SELECT EXISTS(SELECT 1
+                                  FROM ' || quote_ident(_privdesc.masterschema)
+                                   || '.' || quote_ident(_privdesc.mastertable)
+                                   || ' WHERE ' || quote_ident(_pkey[1]) || ' = ' || pObjectId
+                                   || '   AND ' || quote_ident(_privdesc.ownerfield)
+                                   || '= ' || quote_literal(COALESCE(pUser, getEffectiveXtUser())) || ');';
+        RAISE DEBUG '%', _qstr;
+
+        EXECUTE _qstr INTO _haspriv;
+      END IF;
+    END IF;
+
+    EXIT WHEN _haspriv;
+  END LOOP;
+
+  RETURN _haspriv OR NOT _privfound;
+
+END;
+$$ LANGUAGE 'plpgsql' STABLE;
+
+COMMENT ON FUNCTION hasPrivOnObject(pPrivType TEXT, pObjectType TEXT, pObjectId INTEGER, pUser TEXT) IS
+'Return if a user has permission to view or edit a specific database object.
+pPrivType is either CREATE, EDIT, DELETE, or VIEW, and controls which privilege is checked.
+pObjectType is one of the string constants used by the Documents widget, such as ADDR for Addresses.
+pObjectId is the internal id of the record in the table associated with pObjectType (defaults to NULL).
+pUser is the username to be checked for those pObjectTypes that restrict access to individual users (NULL == current user and is the default).';
+
diff --git a/foundation-database/public/functions/implodewo.sql b/foundation-database/public/functions/implodewo.sql
new file mode 100644 (file)
index 0000000..b5458f5
--- /dev/null
@@ -0,0 +1,72 @@
+CREATE OR REPLACE FUNCTION implodeWo(INTEGER, BOOLEAN) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWoid ALIAS FOR $1;
+  implodeChildren ALIAS FOR $2;
+  resultCode INTEGER;
+  _wotcCnt   INTEGER;
+  _routings  BOOLEAN;
+
+BEGIN
+  SELECT metric_value=''t'' INTO _routings
+         FROM metric
+         WHERE (metric_name=''Routings'');
+
+  IF ((SELECT wo_id
+       FROM wo
+       WHERE ((wo_status=''E'')
+        AND (wo_id=pWoid))) IS NULL) THEN
+    RETURN 0;
+  END IF;
+
+  IF (_routings) THEN
+    SELECT count(*) INTO _wotcCnt
+    FROM xtmfg.wotc
+    WHERE (wotc_wo_id=pWoid);
+    IF (_wotcCnt > 0) THEN
+      RETURN -1;
+    END IF;
+  END IF;
+
+--  Delete any created P/R''s for this W/O
+  PERFORM deletePr(''W'', womatl_id)
+  FROM womatl
+  WHERE (womatl_wo_id=pWoid);
+
+  DELETE FROM womatl
+  WHERE (womatl_id IN ( SELECT womatl_id
+                        FROM womatl, wo
+                        WHERE ((womatl_wo_id=wo_id)
+                         AND (wo_status=''E'')
+                         AND (wo_id=pWoid)) ));
+
+  IF _routings THEN
+
+    DELETE FROM xtmfg.wooper
+    WHERE (wooper_id IN ( SELECT wooper_id
+                          FROM xtmfg.wooper, wo
+                          WHERE ((wooper_wo_id=wo_id)
+                           AND (wo_status=''E'')
+                           AND (wo_id=pWoid)) ));
+  END IF;
+
+  UPDATE wo
+  SET wo_status=''O''
+  WHERE (wo_id=pWoid);
+
+  IF (implodeChildren) THEN
+    resultCode := (SELECT MAX(implodeWo(wo_id, TRUE))
+                   FROM wo
+                   WHERE ((wo_ordtype=''W'')
+                    AND (wo_ordid=pWoid)));
+
+    resultCode := (SELECT MAX(deleteWo(wo_id, TRUE))
+                   FROM wo
+                   WHERE ((wo_ordtype=''W'')
+                    AND (wo_ordid=pWoid)));
+  END IF;
+
+  RETURN 0;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/incdt.sql b/foundation-database/public/functions/incdt.sql
new file mode 100644 (file)
index 0000000..d333ed7
--- /dev/null
@@ -0,0 +1,39 @@
+CREATE OR REPLACE FUNCTION incdt() RETURNS SETOF incdt AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _row incdt%ROWTYPE;
+  _priv TEXT;
+  _grant BOOLEAN;
+
+BEGIN
+  -- This query will give us the most permissive privilege the user has been granted
+  SELECT privilege, granted INTO _priv, _grant
+  FROM privgranted 
+  WHERE privilege IN ('MaintainAllIncidents','ViewAllIncidents','MaintainPersonalIncidents','ViewPersonalIncidents')
+  ORDER BY granted DESC, sequence
+  LIMIT 1;
+
+  -- If have an 'All' privilege return all results
+  IF (_priv ~ 'All' AND _grant) THEN
+    FOR _row IN 
+      SELECT * FROM incdt
+    LOOP
+      RETURN NEXT _row;
+    END LOOP;
+  -- Otherwise if have any other grant, must be personal privilege.
+  ELSIF (_grant) THEN
+    FOR _row IN 
+      SELECT * FROM incdt 
+      WHERE  getEffectiveXtUser() IN (incdt_owner_username, incdt_assigned_username)
+    LOOP
+      RETURN NEXT _row;
+    END LOOP;
+  END IF;
+
+  RETURN;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+COMMENT ON FUNCTION incdt() IS 'A table function that returns Incident results according to privilege settings.';
diff --git a/foundation-database/public/functions/indentedbom.sql b/foundation-database/public/functions/indentedbom.sql
new file mode 100644 (file)
index 0000000..01e7fbb
--- /dev/null
@@ -0,0 +1,313 @@
+CREATE OR REPLACE FUNCTION indentedBOM(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemid ALIAS FOR $1;
+  _revid INTEGER;
+
+BEGIN
+
+  SELECT getActiveRevId('BOM',pItemid) INTO _revid;
+
+  RETURN indentedBOM(pItemid, _revid);
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION indentedBOM(INTEGER, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemid ALIAS FOR $1;
+  pRevisionid ALIAS FOR $2;
+  _bomworkid INTEGER;
+  _indexid INTEGER;
+  _r RECORD;
+  _batchsize NUMERIC;
+
+BEGIN
+
+  -- Get the batch quantity
+  SELECT COALESCE( (
+    SELECT bomhead_batchsize
+    FROM bomhead
+    WHERE ((bomhead_item_id=pItemId)
+    AND (bomhead_rev_id=pRevisionid)) LIMIT 1),1) INTO _batchsize;
+--  Check on the temporary workspace
+--  PERFORM maintainBOMWorkspace();
+
+--  Grab a new index for this bomwork set
+  SELECT NEXTVAL('misc_index_seq') INTO _indexid;
+
+--  Step through all of the components of the passed pItemid
+  FOR _r IN SELECT bomitem.*,
+                   item_id,
+                   itemuomtouom(item_id, item_inv_uom_id, NULL, (bomitem_qtyfxd/_batchsize + bomitem_qtyper) * (1 + bomitem_scrap), 'qtyper') AS qtyreq,
+                   (itemuomtouomratio(bomitem_item_id, bomitem_uom_id, NULL)
+                               * bomitem_qtyfxd) AS qtyfxd,
+                   (itemuomtouomratio(bomitem_item_id, bomitem_uom_id, NULL)
+                               * bomitem_qtyper) AS qtyper,
+                   stdcost(item_id, bomitem_id) AS standardcost,
+                   actcost(item_id, bomitem_id) AS actualcost
+  FROM bomitem(pItemId, pRevisionid), item
+  WHERE ( (bomitem_item_id=item_id) ) LOOP
+
+--  Insert the component and bomitem parameters
+    SELECT NEXTVAL('bomwork_bomwork_id_seq') INTO _bomworkid;
+    INSERT INTO bomwork
+    ( bomwork_id, bomwork_set_id, bomwork_parent_id, bomwork_level,
+      bomwork_parent_seqnumber, bomwork_seqnumber,
+      bomwork_item_id, bomwork_createwo, bomwork_qtyreq,
+      bomwork_qtyfxd, bomwork_qtyper, bomwork_scrap, bomwork_issuemethod,
+      bomwork_effective, bomwork_expires,
+      bomwork_stdunitcost, bomwork_actunitcost,
+      bomwork_char_id, bomwork_value, bomwork_notes, bomwork_ref,
+      bomwork_bomitem_id, bomwork_ecn )
+    VALUES
+    ( _bomworkid, _indexid, -1, 1,
+      0, _r.bomitem_seqnumber,
+      _r.item_id, _r.bomitem_createwo, _r.qtyreq,
+      _r.qtyfxd, _r.qtyper, _r.bomitem_scrap, _r.bomitem_issuemethod,
+      _r.bomitem_effective, _r.bomitem_expires,
+      _r.standardcost, _r.actualcost,
+      _r.bomitem_char_id, _r.bomitem_value, _r.bomitem_notes, _r.bomitem_ref,
+      _r.bomitem_id, _r.bomitem_ecn );
+
+--  Explode the components of the current component
+    PERFORM explodeBOM(_r.item_id, _bomworkid, 1);
+
+  END LOOP;
+
+--  Return a key to the result
+  RETURN _indexid;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION indentedBOM(INTEGER, INTEGER, INTEGER, INTEGER) RETURNS SETOF bomdata AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemid ALIAS FOR $1;
+  pRevisionid ALIAS FOR $2;
+  pExpiredDays ALIAS FOR $3;
+  pFutureDays ALIAS FOR $4;
+  _row bomdata%ROWTYPE;
+  _bomworksetid INTEGER;
+  _x RECORD;
+  _check CHAR(1);
+  _inactive BOOLEAN := FALSE;
+  _batchsize NUMERIC;
+BEGIN
+
+  IF (pRevisionid != -1) THEN
+    --Is this a deactivated revision?
+    SELECT rev_status INTO _check
+    FROM rev
+    WHERE ((rev_id=pRevisionid)
+    AND (rev_status='I'));
+    IF (FOUND) THEN
+      _inactive := TRUE;
+    END IF;
+  END IF;
+  -- Get the batch quantity
+  SELECT COALESCE( (
+    SELECT bomhead_batchsize
+    FROM bomhead
+    WHERE ((bomhead_item_id=pItemId)
+    AND (bomhead_rev_id=pRevisionid)) LIMIT 1),1) INTO _batchsize;
+  IF NOT (_inactive) THEN
+
+    --We can explode this out based on current data
+    SELECT indentedBOM(pItemid, pRevisionid) INTO _bomworksetid;  
+
+    FOR _x IN
+        SELECT bomwork_id, bomwork_parent_id, bomwork_level,
+               bomworkSequence(bomwork_id) AS seq_ord,
+               bomwork_seqnumber, item_id, item_number, uom_name,
+               item_descrip1, item_descrip2,
+               (item_descrip1 || ' ' || item_descrip2) AS itemdescription,
+               bomwork_qtyreq, bomwork_qtyfxd, bomwork_qtyper, bomwork_scrap, bomwork_createwo,
+       CASE WHEN (bomwork_issuemethod='S') THEN 'Push'
+            WHEN (bomwork_issuemethod='L') THEN 'Pull'
+            WHEN (bomwork_issuemethod='M') THEN 'Mixed'
+            ELSE 'Special'
+       END AS issuemethod,
+       bomwork_effective, bomwork_expires,
+       (bomwork_expires <= CURRENT_DATE) AS expired,
+       (bomwork_effective > CURRENT_DATE) AS future,
+       bomwork_actunitcost AS actunitcost,
+       bomwork_stdunitcost AS stdunitcost,
+       CASE WHEN item_type NOT IN ('R','T') THEN
+         itemuomtouom(item_id, item_inv_uom_id, NULL, (bomwork_qtyfxd/_batchsize + bomwork_qtyper) * (1 + bomwork_scrap), 'qtyper') * bomwork_actunitcost
+       ELSE 0.0 END AS actextendedcost,
+       CASE WHEN item_type NOT IN ('R','T') THEN
+         itemuomtouom(item_id, item_inv_uom_id, NULL, (bomwork_qtyfxd/_batchsize + bomwork_qtyper) * (1 + bomwork_scrap), 'qtyper') * bomwork_stdunitcost
+       ELSE 0.0 END AS stdextendedcost,
+       bomwork_char_id,
+       bomwork_value, bomwork_notes, bomwork_ref,
+       bomwork_bomitem_id, bomwork_ecn
+       FROM bomwork, item, uom 
+       WHERE ( (bomwork_item_id=item_id)
+       AND (item_inv_uom_id=uom_id)
+       AND (bomwork_set_id=_bomworksetid) )
+       AND (bomwork_expires > (CURRENT_DATE - pExpiredDays))
+       AND (bomwork_effective <= (CURRENT_DATE + pFutureDays))
+       UNION
+       SELECT -1, -1, 1,
+              '0',
+              NULL,-1, costelem_type AS bomdata_item_number, '',
+              '', '',
+              '',
+              NULL, NULL, NULL, NULL, NULL,
+              '',
+              NULL, NULL,
+              false,
+              false,
+              currToBase(itemcost_curr_id, itemcost_actcost, CURRENT_DATE) AS actunitcost,
+              itemcost_stdcost AS stdunitcost,
+              currToBase(itemcost_curr_id, itemcost_actcost, CURRENT_DATE) AS actextendedcost,
+              itemcost_stdcost AS stdextendedcost,
+              NULL,
+              NULL,NULL,NULL,
+              NULL,NULL
+       FROM itemcost, costelem 
+       WHERE ( (itemcost_costelem_id=costelem_id)
+       AND (NOT itemcost_lowlevel)
+       AND (itemcost_item_id=pItemid) )
+       ORDER BY seq_ord
+    LOOP
+        _row.bomdata_bomwork_id := _x.bomwork_id;
+        _row.bomdata_bomwork_parent_id := _x.bomwork_parent_id;
+        _row.bomdata_bomwork_level := _x.bomwork_level;
+        _row.bomdata_bomwork_seqnumber := _x.bomwork_seqnumber;
+        _row.bomdata_bomitem_id := _x.bomwork_bomitem_id;
+        _row.bomdata_item_id := _x.item_id;
+        _row.bomdata_item_number := _x.item_number;
+        _row.bomdata_uom_name := _x.uom_name;
+        _row.bomdata_item_descrip1 := _x.item_descrip1;
+        _row.bomdata_item_descrip2 := _x.item_descrip2;
+        _row.bomdata_itemdescription := _x.itemdescription;
+        _row.bomdata_batchsize := _batchsize;
+        _row.bomdata_qtyreq := _x.bomwork_qtyreq;
+        _row.bomdata_qtyfxd := _x.bomwork_qtyfxd;
+        _row.bomdata_qtyper := _x.bomwork_qtyper;
+        _row.bomdata_scrap := _x.bomwork_scrap;
+        _row.bomdata_createchild := _x.bomwork_createwo;
+        _row.bomdata_issuemethod := _x.issuemethod;
+        _row.bomdata_effective := _x.bomwork_effective;
+        _row.bomdata_expires := _x.bomwork_expires;
+        _row.bomdata_expired := _x.expired;
+        _row.bomdata_future := _x.future;
+        _row.bomdata_actunitcost := _x.actunitcost;
+        _row.bomdata_stdunitcost := _x.stdunitcost;
+        _row.bomdata_actextendedcost := _x.actextendedcost;
+        _row.bomdata_stdextendedcost := _x.stdextendedcost;
+        _row.bomdata_ecn := _x.bomwork_ecn;
+        _row.bomdata_char_id := _x.bomwork_char_id;
+        _row.bomdata_value := _x.bomwork_value;
+        _row.bomdata_notes := _x.bomwork_notes;
+        _row.bomdata_ref := _x.bomwork_ref;
+        RETURN NEXT _row;
+    END LOOP;
+    
+    PERFORM deleteBOMWorkset(_bomworksetid);
+
+  ELSE
+
+-- Use historical snapshot for inactive revisions
+    FOR _x IN
+        SELECT bomhist_id, bomhist_parent_id, bomhist_level,
+               bomhistSequence(bomhist_seq_id) AS seq_ord,
+               bomhist_seqnumber, item_id, item_number, uom_name,
+               item_descrip1, item_descrip2,
+               (item_descrip1 || ' ' || item_descrip2) AS itemdescription,
+               bomhist_qtyreq, bomhist_qtyfxd, bomhist_qtyper, bomhist_scrap,
+               bomhist_createwo,
+       CASE WHEN (bomhist_issuemethod='S') THEN 'Push'
+            WHEN (bomhist_issuemethod='L') THEN 'Pull'
+            WHEN (bomhist_issuemethod='M') THEN 'Mixed'
+            ELSE 'Special'
+       END AS issuemethod,
+       bomhist_effective, bomhist_expires,
+       (bomhist_expires <= CURRENT_DATE) AS expired,
+       (bomhist_effective > CURRENT_DATE) AS future,
+       bomhist_actunitcost AS actunitcost,
+       bomhist_stdunitcost AS stdunitcost,
+       CASE WHEN item_type NOT IN ('R','T') THEN
+         (bomist_qtyfxd/_batchsize + bomhist_qtyper) * (1 + bomhist_scrap) * bomhist_actunitcost
+       ELSE 0 END AS actextendedcost,
+       CASE WHEN item_type NOT IN ('R','T') THEN
+         (bomist_qtyfxd/_batchsize + bomhist_qtyper) * (1 + bomhist_scrap) * bomhist_stdunitcost
+       ELSE 0 END AS stdextendedcost,
+       bomhist_char_id, bomhist_value, bomhist_notes, bomhist_ref 
+       FROM bomhist, item, uom 
+       WHERE ( (bomhist_item_id=item_id)
+       AND (item_inv_uom_id=uom_id)
+       AND (bomhist_rev_id=pRevisionid) )
+       AND (bomhist_expires > (CURRENT_DATE - pExpiredDays))
+       AND (bomhist_effective <= (CURRENT_DATE + pFutureDays))
+       UNION
+       SELECT -1, -1, 1,
+              '0',
+              NULL,-1, costelem_type AS bomdata_item_number, '',
+              '', '',
+              '',
+              NULL, NULL, NULL, NULL,
+              false,
+              '', NULL, NULL,
+              false,
+              false,
+              bomhist_actunitcost AS actunitcost,
+              bomhist_stdunitcost AS stdunitcost,
+              bomhist_actunitcost AS actextendedcost,
+              bomhist_stdunitcost AS stdextendedcost,
+              NULL,NULL,NULL,NULL 
+       FROM bomhist, costelem 
+       WHERE ((bomhist_rev_id=pRevisionid)
+       AND (costelem_id=bomhist_item_id))
+       ORDER BY seq_ord
+    LOOP
+        _row.bomdata_bomwork_id := _x.bomhist_id;
+        _row.bomdata_bomwork_parent_id := _x.bomhist_parent_id;
+        _row.bomdata_bomwork_level := _x.bomhist_level;
+        _row.bomdata_bomwork_seqnumber := _x.bomhist_seqnumber;
+        _row.bomdata_bomitem_id := -1;
+        _row.bomdata_item_id := _x.item_id;
+        _row.bomdata_item_number := _x.item_number;
+        _row.bomdata_uom_name := _x.uom_name;
+        _row.bomdata_item_descrip1 := _x.item_descrip1;
+        _row.bomdata_item_descrip2 := _x.item_descrip2;
+        _row.bomdata_itemdescription := _x.itemdescription;
+        _row.bomdata_batchsize := _batchsize;
+        _row.bomdata_qtyreq := _x.bomhist_qtyreq;
+        _row.bomdata_qtyfxd := _x.bomist_qtyfxd;
+        _row.bomdata_qtyper := _x.bomhist_qtyper;
+        _row.bomdata_scrap := _x.bomhist_scrap;
+        _row.bomdata_createchild := _x.bomhist_createwo;
+        _row.bomdata_issuemethod := _x.issuemethod;
+        _row.bomdata_effective := _x.bomhist_effective;
+        _row.bomdata_expires := _x.bomhist_expires;
+        _row.bomdata_expired := _x.expired;
+        _row.bomdata_future := _x.future;
+        _row.bomdata_actunitcost := _x.actunitcost;
+        _row.bomdata_stdunitcost := _x.stdunitcost;
+        _row.bomdata_actextendedcost := _x.actextendedcost;
+        _row.bomdata_stdextendedcost := _x.stdextendedcost;
+        _row.bomdata_ecn := '';
+        _row.bomdata_char_id := _x.bomhist_char_id;
+        _row.bomdata_value := _x.bomhist_value;
+        _row.bomdata_notes := _x.bomhist_notes;
+        _row.bomdata_ref := _x.bomhist_ref;
+        RETURN NEXT _row;
+    END LOOP;
+  END IF;   
+
+  RETURN;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/indentedwhereused.sql b/foundation-database/public/functions/indentedwhereused.sql
new file mode 100644 (file)
index 0000000..62da13e
--- /dev/null
@@ -0,0 +1,90 @@
+CREATE OR REPLACE FUNCTION indentedWhereUsed(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemid ALIAS FOR $1;
+  _indexid INTEGER;
+  _level INTEGER;
+
+BEGIN
+
+--  Check on the temporary workspace
+--  PERFORM maintainBOMWorkspace();
+
+  _indexid := (SELECT NEXTVAL('misc_index_seq'));
+  _level := 1;
+
+  INSERT INTO bomwork
+  ( bomwork_set_id, bomwork_parent_id,
+    bomwork_level, bomwork_seqnumber,
+    bomwork_item_id, bomwork_item_type, bomwork_createwo,
+    bomwork_qtyfxd, bomwork_qtyper,
+    bomwork_scrap, bomwork_issuemethod,
+    bomwork_effective, bomwork_expires, bomwork_status,
+    bomwork_stdunitcost, bomwork_actunitcost )
+  SELECT _indexid, -1,
+         1, bomitem_seqnumber,
+         item_id, item_type, bomitem_createwo,
+         (itemuomtouomratio(bomitem_item_id, bomitem_uom_id, NULL) * bomitem_qtyfxd),
+         (itemuomtouomratio(bomitem_item_id, bomitem_uom_id, NULL) * bomitem_qtyper),
+         bomitem_scrap, bomitem_issuemethod,
+         bomitem_effective, bomitem_expires, 'U',
+         stdcost(item_id), actcost(item_id)
+  FROM bomitem, item
+  WHERE ((bomitem_item_id=pItemid)
+    AND (bomitem_parent_item_id=item_id)
+    AND (CURRENT_DATE BETWEEN bomitem_effective AND (bomitem_expires - 1))
+    AND (bomitem_rev_id=getActiveRevId('BOM',bomitem_parent_item_id)));
+
+  WHILE ( ( SELECT count(*)
+            FROM bomwork
+            WHERE ((bomwork_item_type IN ('M', 'F'))
+              AND (bomwork_status='U')
+              AND (bomwork_set_id=_indexid)) ) > 0) LOOP
+
+    _level := _level + 1;
+
+    INSERT INTO bomwork
+    ( bomwork_set_id, bomwork_parent_id,
+      bomwork_level, bomwork_seqnumber,
+      bomwork_item_id, bomwork_item_type, bomwork_createwo,
+      bomwork_qtyfxd, bomwork_qtyper,
+      bomwork_scrap, bomwork_issuemethod,
+      bomwork_effective, bomwork_expires, bomwork_status,
+      bomwork_stdunitcost, bomwork_actunitcost )
+    SELECT _indexid, bomwork_id,
+           _level, bomitem_seqnumber,
+           item_id, item_type, bomitem_createwo,
+           (bomwork_qtyper * (itemuomtouomratio(bomitem_item_id, bomitem_uom_id, NULL) * bomitem_qtyfxd)),
+           (bomwork_qtyper * (itemuomtouomratio(bomitem_item_id, bomitem_uom_id, NULL) * bomitem_qtyper)),
+           bomitem_scrap, bomitem_issuemethod,
+           CASE WHEN bomitem_effective < bomwork_effective THEN
+             bomwork_effective
+           ELSE bomitem_effective END, 
+           CASE WHEN bomitem_expires > bomwork_expires THEN
+             bomwork_expires
+           ELSE bomitem_expires END,
+           'N',
+           stdcost(item_id), actcost(item_id)
+    FROM bomwork JOIN bomitem ON ( (bomitem_item_id=bomwork_item_id)
+                               AND (CURRENT_DATE BETWEEN bomitem_effective AND (bomitem_expires - 1))
+                               AND (bomitem_rev_id=getActiveRevId('BOM',bomitem_parent_item_id)) )
+                 JOIN item ON (item_id=bomitem_parent_item_id)
+    WHERE ((bomwork_status='U')
+      AND  (bomwork_item_type IN ('M', 'F')));
+
+    UPDATE bomwork
+    SET bomwork_status='C'
+    WHERE ((bomwork_status='U')
+      AND (bomwork_set_id=_indexid));
+
+    UPDATE bomwork
+    SET bomwork_status='U'
+    WHERE ((bomwork_status='N')
+      AND (bomwork_set_id=_indexid));
+
+  END LOOP;
+
+  RETURN _indexid;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/indentedwo.sql b/foundation-database/public/functions/indentedwo.sql
new file mode 100644 (file)
index 0000000..4efe0a0
--- /dev/null
@@ -0,0 +1,258 @@
+
+CREATE OR REPLACE FUNCTION indentedwo(integer, boolean, boolean, boolean) RETURNS SETOF wodata AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+   pwoid ALIAS FOR $1;
+   pshowops ALIAS FOR $2;
+   pshowmatl ALIAS FOR $3; 
+   pshowindent ALIAS FOR $4;   
+  _row wodata%ROWTYPE;
+  _subrow wodata%ROWTYPE;  
+  _opx wodata%ROWTYPE;
+  _x RECORD;
+  _level INTEGER;
+   
+BEGIN 
+    --The wodata_id_type column is used to indicate the source of the wodata_id
+    --there are three different tables used wo, womatl and womatlvar
+    --wodata_id_type = 1 = wo_id
+    --wodata_id_type = 2 = womatl_id
+    --wodata_id_type = 3 = wooper_id
+    --initialise values
+    _level := 0;   
+    --get top level works orders
+    FOR _x IN
+       SELECT wo_id,wo_number,wo_subnumber,wo_status,wo_startdate,
+         wo_duedate,wo_adhoc,wo_itemsite_id,itemsite_qtyonhand,
+         wo_qtyord,wo_qtyrcv,wo_prodnotes, item_number,
+         item_descrip1, item_descrip2, uom_name
+       FROM wo, itemsite, item, uom     
+       WHERE ((wo_id = pwoid)
+         AND (itemsite_id = wo_itemsite_id)
+         AND (itemsite_item_id=item_id)
+         AND (item_inv_uom_id=uom_id))      
+       ORDER BY wo_number, wo_subnumber
+    LOOP
+        _row.wodata_id := _x.wo_id;
+        _row.wodata_id_type := 1;            
+        _row.wodata_number := _x.wo_number;
+        _row.wodata_subnumber := _x.wo_subnumber;
+        _row.wodata_itemnumber := _x.item_number;
+        _row.wodata_descrip := _x.item_descrip1 || '-' || _x.item_descrip2;
+        _row.wodata_status := _x.wo_status;
+        _row.wodata_startdate := _x.wo_startdate;
+        _row.wodata_duedate := _x.wo_duedate;
+        _row.wodata_adhoc := _x.wo_adhoc;     
+        _row.wodata_itemsite_id := _x.wo_itemsite_id;         
+        _row.wodata_qoh := _x.itemsite_qtyonhand;
+        _row.wodata_short := noneg(_x.wo_qtyord - _x.wo_qtyrcv);
+        _row.wodata_qtyrcv := _x.wo_qtyrcv;   
+        _row.wodata_qtyordreq := _x.wo_qtyord;   
+        _row.wodata_qtyuom := _x.uom_name;    
+        _row.wodata_scrap := 0;        
+        _row.wodata_notes := _x.wo_prodnotes;     
+        _row.wodata_level := _level;                
+        RETURN NEXT _row;
+        IF (pshowmatl AND NOT pshowops) THEN
+          --expand materials      
+          FOR _subrow IN
+             SELECT * FROM indentedwomatl(pwoid, _level)
+          LOOP                                                  
+            RETURN NEXT _subrow;
+          END LOOP;
+        END IF;
+        
+        IF ((pshowmatl OR pshowindent) AND NOT pshowops) THEN
+          --expand next level down               
+          FOR _subrow IN
+           SELECT * FROM indentedwo(_x.wo_id, NULL, _level + 1, pshowmatl, pshowindent) 
+          LOOP                                           
+            RETURN NEXT _subrow; 
+          END LOOP;
+        END IF;
+          
+        IF (pshowops) THEN
+         --expand materials not on operations   
+         IF (pshowmatl) THEN   
+           FOR _subrow IN
+             SELECT * FROM indentedwomatl(pwoid, -1, _level)
+           LOOP                                                  
+             RETURN NEXT _subrow;
+           END LOOP;
+         END IF;
+
+         IF (pshowmatl OR pshowindent) THEN
+           --expand next level down             
+           FOR _subrow IN
+             SELECT * FROM indentedwo(_x.wo_id, -1, _level + 1,  pshowmatl, pshowindent) 
+           LOOP                                           
+             RETURN NEXT _subrow; 
+           END LOOP;
+         END IF;
+
+         --expand opeartions
+         FOR _opx IN
+           SELECT * FROM xtmfg.indentedwoops(pwoid,_level)
+         LOOP
+           RETURN NEXT _opx;
+
+           IF (pshowmatl) THEN  
+              --expand materials on operations      
+              FOR _subrow IN
+                 SELECT * FROM indentedwomatl(pwoid, _opx.wodata_id, _level + 1)
+              LOOP                                                  
+                RETURN NEXT _subrow;
+              END LOOP;
+           END IF;
+
+           IF (pshowmatl OR pshowindent) THEN
+              --expand next level down             
+              FOR _subrow IN
+                SELECT * FROM indentedwo(_x.wo_id, _opx.wodata_id, _level + 2,  pshowmatl, pshowindent) 
+              LOOP                                           
+                RETURN NEXT _subrow; 
+              END LOOP;
+           END IF; 
+         END LOOP; 
+       END IF;                           
+  END LOOP;                     
+  RETURN;
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION indentedwo(integer, integer, integer, boolean, boolean) RETURNS SETOF wodata AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+   pwoid ALIAS FOR $1;   
+   pwooperid ALIAS FOR $2;
+   plevel ALIAS FOR $3;
+   pshowmatl ALIAS FOR $4; 
+   pshowindent ALIAS FOR $5;  
+  _row wodata%ROWTYPE;   
+  _opx wodata%ROWTYPE; 
+  _x RECORD;
+  _subx RECORD;
+  _index INTEGER;
+  _level INTEGER;
+  _qry TEXT;
+   
+BEGIN 
+    --The wodata id column is used to indicate the source of the id
+    --there are three different tables used wo, womatl and womatlvar
+    --wodata_id_type = 1 = wo_id
+    --wodata_id_type = 2 = womatl_id
+    --wodata_id_type = 3 = wooper_id
+    _level := (plevel + 1);    
+    --find all WO with the ordid of the next level up
+    _qry := 'SELECT wo_id,wo_number,wo_subnumber,wo_status,wo_startdate,wo_duedate,
+         wo_adhoc,wo_itemsite_id,itemsite_qtyonhand,wo_qtyord,wo_qtyrcv, wo_prodnotes,
+         item_number,item_descrip1, item_descrip2, uom_name,
+         womatl_qtyiss, womatl_scrap, womatl_wooper_id
+       FROM itemsite,  wo, item, uom, womatl 
+       WHERE ((wo_ordid = ' || pwoid || ')
+         AND (wo_ordtype = ''W'')
+         AND (itemsite_item_id=item_id)
+         AND (item_inv_uom_id=uom_id)   
+         AND (wo_womatl_id=womatl_id)   
+         AND (wo_itemsite_id = itemsite_id) ';
+
+    IF (pwooperid IS NOT NULL) THEN
+      _qry := _qry || ' AND (womatl_wooper_id=' || pwooperid || ') ';
+    END IF;               
+
+    _qry := _qry || ') ORDER BY wo_number, wo_subnumber';
+ /* if (pwooperid IS NOT NULL) THEN
+    raise exception 'stop %',_qry;
+  END IF;*/
+    FOR _x IN
+      EXECUTE _qry
+    LOOP 
+        _row.wodata_id := _x.wo_id;
+        _row.wodata_id_type := 1;                     
+        _row.wodata_number := _x.wo_number;
+        _row.wodata_subnumber := _x.wo_subnumber;
+        _row.wodata_itemnumber := _x.item_number;
+        _row.wodata_descrip := _x.item_descrip1 || '-' || _x.item_descrip2;
+        _row.wodata_status := _x.wo_status;
+        _row.wodata_startdate := _x.wo_startdate;
+        _row.wodata_duedate := _x.wo_duedate;
+        _row.wodata_adhoc := _x.wo_adhoc;      
+        _row.wodata_itemsite_id := _x.wo_itemsite_id;        
+        _row.wodata_qoh := _x.itemsite_qtyonhand;
+        _row.wodata_short := noneg(_x.wo_qtyord - _x.wo_qtyrcv);
+        _row.wodata_qtyiss := _x.womatl_qtyiss;  
+        _row.wodata_qtyrcv := _x.wo_qtyrcv;   
+        _row.wodata_qtyordreq := _x.wo_qtyord; 
+       _row.wodata_scrap := _x.womatl_scrap;  
+        _row.wodata_notes := _x.wo_prodnotes;       
+        _row.wodata_level := plevel;                
+        RETURN NEXT _row;  
+        --if indentation require expand next level
+        IF (pshowindent AND pwooperid IS NULL) THEN
+          IF (pshowmatl AND pshowindent) THEN    
+           --get materials for this level
+            FOR _subx IN
+              SELECT * FROM indentedwomatl(_x.wo_id, plevel) 
+           LOOP                                            
+             RETURN NEXT _subx;
+           END LOOP;
+          END IF;
+
+          IF (pshowindent) THEN  
+            --expand lower levels 
+            FOR _subx IN
+              SELECT * FROM indentedwo(_x.wo_id, NULL, _level, pshowmatl, pshowindent )
+            LOOP                                           
+             RETURN NEXT _subx; 
+            END LOOP; 
+          END IF;    
+            
+        ELSIF (pshowindent) THEN --Handle operations
+          --expand materials not on operations   
+          IF (pshowmatl) THEN   
+            FOR _subx IN
+              SELECT * FROM indentedwomatl(_x.wo_id, -1, plevel)
+            LOOP                                                  
+              RETURN NEXT _subx;
+            END LOOP;
+          END IF;
+
+          --expand next level down not on operations
+          FOR _subx IN
+            SELECT * FROM indentedwo(_x.wo_id, -1, _level,  pshowmatl, pshowindent) 
+          LOOP                                           
+            RETURN NEXT _subx; 
+          END LOOP;
+          
+          --expand operations
+          FOR _opx IN
+            SELECT * FROM xtmfg.indentedwoops(_x.wo_id,plevel)
+          LOOP
+            RETURN NEXT _opx;
+
+            IF (pshowmatl) THEN  
+              --expand materials on operations      
+              FOR _subx IN
+                 SELECT * FROM indentedwomatl(_x.wo_id, _opx.wodata_id, _level)
+              LOOP                                                  
+                RETURN NEXT _subx;
+                --     raise exception 'stop %',_opx.wodata_id;
+              END LOOP;
+            END IF;
+              
+            --expand next level down   
+            FOR _subx IN
+              SELECT * FROM indentedwo(_x.wo_id, _opx.wodata_id, _level + 2,  pshowmatl, pshowindent) 
+            LOOP                                        
+              RETURN NEXT _subx; 
+            END LOOP;
+              
+          END LOOP;
+        END IF;                      
+      END LOOP;                         
+  RETURN;
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/indentedwomatl.sql b/foundation-database/public/functions/indentedwomatl.sql
new file mode 100644 (file)
index 0000000..9471efb
--- /dev/null
@@ -0,0 +1,98 @@
+
+CREATE OR REPLACE FUNCTION indentedwomatl(integer, integer) RETURNS SETOF wodata AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+   pwoid ALIAS FOR $1;   
+   plevel ALIAS FOR $2;
+  _subx RECORD;
+   
+BEGIN
+  FOR _subx IN
+    SELECT * FROM indentedwomatl(pwoid, NULL::integer, plevel)
+  LOOP
+    RETURN NEXT _subx;
+  END LOOP;        
+  RETURN;
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION indentedwomatl(integer, integer, integer) RETURNS SETOF wodata AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+   pwoid ALIAS FOR $1;  
+   pwooperid ALIAS FOR $2; 
+   plevel ALIAS FOR $3;
+  _status TEXT; 
+  _subrow wodata%ROWTYPE;  
+  _subx RECORD;
+  _level INTEGER;
+  _qry TEXT;
+   
+BEGIN
+  --The wodata id column is used to indicate the source of the id
+  --there are three different tables used wo, womatl and womatlvar
+  --wodata_id_type = 1 = wo_id
+  --wodata_id_type = 2 = womatl_id
+  --wodata_id_type = 3 = wooper_id
+
+  _qry := 'SELECT womatl_id, wo_number, wo_subnumber, 
+      wo_startdate, womatl_duedate, womatl_itemsite_id,
+      itemsite_qtyonhand, womatl_qtyreq, womatl_qtyiss,
+      womatl_qtyper, womatl_qtyreq, womatl_scrap,
+      womatl_ref, womatl_notes, item_number,
+      item_descrip1, item_descrip2, uom_name
+    FROM womatl, wo, itemsite, item, uom
+    WHERE ((wo_id = womatl_wo_id)
+     AND (wo_id = ' || pwoid || ')
+     AND (womatl_itemsite_id = itemsite_id)
+     AND (itemsite_item_id=item_id)
+     AND (womatl_uom_id=uom_id) ';
+-- Need to display in case child w/o is deleted
+--     AND (NOT womatl_createwo OR womatl_createwo IS NULL) ';
+
+  IF (pwooperid IS NOT NULL) THEN
+    _qry := _qry || 'AND (womatl_wooper_id=' || pwooperid  || ')';
+  END IF;
+
+  _qry := _qry || ') ORDER BY item_number;';
+  
+  _level := plevel + 1;    
+  SELECT wo_status FROM wo WHERE wo_id = pwoid  LIMIT 1 INTO _status;
+  FOR _subx IN
+    EXECUTE _qry
+  LOOP
+    _subrow.wodata_id := _subx.womatl_id;
+    _subrow.wodata_id_type  := 2;       
+    _subrow.wodata_number := _subx.wo_number;
+    _subrow.wodata_subnumber := _subx.wo_subnumber;
+    _subrow.wodata_itemnumber := _subx.item_number;
+    _subrow.wodata_descrip := _subx.item_descrip1 || '-' || _subx.item_descrip2;
+    _subrow.wodata_status := _status;
+    _subrow.wodata_startdate := _subx.wo_startdate;
+    _subrow.wodata_duedate := _subx.womatl_duedate;
+    _subrow.wodata_itemsite_id := _subx.womatl_itemsite_id;    
+    _subrow.wodata_qoh := _subx.itemsite_qtyonhand;
+    IF((_subx.itemsite_qtyonhand > (_subx.womatl_qtyreq - _subx.womatl_qtyiss))) THEN
+      _subrow.wodata_short := 0;
+    ELSE
+      _subrow.wodata_short := (_subx.womatl_qtyreq - _subx.womatl_qtyiss) -  _subx.itemsite_qtyonhand;
+    END IF;
+    _subrow.wodata_qtyper := _subx.womatl_qtyper;
+    _subrow.wodata_qtyiss := _subx.womatl_qtyiss;         
+    _subrow.wodata_qtyordreq := _subx.womatl_qtyreq;     
+    _subrow.wodata_qtyuom := _subx.uom_name;   
+    _subrow.wodata_scrap := _subx.womatl_scrap;        
+    _subrow.wodata_notes := _subx.womatl_notes;
+    _subrow.wodata_ref := _subx.womatl_ref;       
+    _subrow.wodata_level := _level;                                   
+    RETURN NEXT _subrow; 
+  END LOOP;     
+        
+  RETURN;
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/initeffectivextuser.sql b/foundation-database/public/functions/initeffectivextuser.sql
new file mode 100644 (file)
index 0000000..893cd14
--- /dev/null
@@ -0,0 +1,23 @@
+
+CREATE OR REPLACE FUNCTION initEffectiveXtUser() RETURNS VOID AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  -- Effective users use a temporary table to store the user information
+  -- and this function, called by the other functions, makes sure the temp
+  -- tables exist first.
+  PERFORM *
+     FROM pg_catalog.pg_class
+    WHERE relname = 'effective_user'
+      AND relnamespace = pg_catalog.pg_my_temp_schema();
+
+  IF NOT FOUND THEN
+    CREATE TEMPORARY TABLE effective_user (
+      effective_key TEXT,
+      effective_value TEXT
+    );
+    CREATE UNIQUE INDEX effective_user_pkey ON effective_user (effective_key);
+  END IF;
+END;
+$$ LANGUAGE 'plpgsql' VOLATILE;
+
diff --git a/foundation-database/public/functions/initialdistribution.sql b/foundation-database/public/functions/initialdistribution.sql
new file mode 100644 (file)
index 0000000..50d930e
--- /dev/null
@@ -0,0 +1,186 @@
+CREATE OR REPLACE FUNCTION initialDistribution(INTEGER, INTEGER) RETURNS INTEGER  AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  pLocationid ALIAS FOR $2;
+  _itemlocid INTEGER;
+  _invhistid INTEGER;
+  _itemlocSeries INTEGER;
+  _r RECORD;
+
+BEGIN
+
+--  Make sure the passed itemsite points to a real item
+  IF ( (SELECT (item_type IN ('R', 'F') OR itemsite_costmethod = 'J')
+         FROM itemsite, item
+         WHERE ( (itemsite_item_id=item_id)
+          AND (itemsite_id=pItemsiteid) ) ) ) THEN
+    RETURN 0;
+  END IF;
+
+  _itemlocSeries := NEXTVAL('itemloc_series_seq');
+
+--  Reassign the location_id for all existing itemlocs if
+--  the passed itemsite is already lot/serial controlled
+  IF ( ( SELECT (itemsite_controlmethod IN ('L', 'S'))
+         FROM itemsite
+         WHERE (itemsite_id=pItemsiteid) ) ) THEN
+
+    FOR _r IN SELECT itemloc_id, itemloc_ls_id, itemloc_qty
+              FROM itemloc
+              WHERE (itemloc_itemsite_id=pItemsiteid) LOOP
+
+--  Create the RL transaction
+      SELECT NEXTVAL('invhist_invhist_id_seq') INTO _invhistid;
+      INSERT INTO invhist
+      ( invhist_id, invhist_itemsite_id, invhist_series,
+        invhist_transtype, invhist_invqty,
+        invhist_qoh_before, invhist_qoh_after,
+        invhist_comments,
+        invhist_invuom, invhist_unitcost, invhist_hasdetail,
+        invhist_costmethod, invhist_value_before, invhist_value_after ) 
+      SELECT _invhistid, itemsite_id, _itemlocSeries,
+             'RL', 0,
+             _r.itemloc_qty, _r.itemloc_qty,
+             'Initial Distribution',
+             uom_name, stdCost(item_id), TRUE,
+             itemsite_costmethod, itemsite_value, itemsite_value
+      FROM item, itemsite, uom
+      WHERE ( (itemsite_item_id=item_id)
+       AND (item_inv_uom_id=uom_id)
+       AND (itemsite_controlmethod <> 'N')
+       AND (itemsite_id=pItemsiteid) );
+
+--  Update the itemloc
+      UPDATE itemloc
+      SET itemloc_location_id=pLocationid
+      WHERE (itemloc_id=_r.itemloc_id);
+
+--  Record the detail transaction
+      INSERT INTO invdetail
+      ( invdetail_invhist_id, invdetail_location_id, invdetail_ls_id,
+        invdetail_qty, invdetail_qty_before, invdetail_qty_after )
+      VALUES
+      ( _invhistid, pLocationid, _r.itemloc_ls_id,
+        _r.itemloc_qty, 0, _r.itemloc_qty );
+
+--  Adjust QOH if this itemlocdist is to/from a non-netable location
+      IF ( SELECT (NOT location_netable)
+           FROM location
+           WHERE (location_id=pLocationid) ) THEN
+
+        INSERT INTO invhist
+        ( invhist_itemsite_id, invhist_series,
+          invhist_transtype, invhist_invqty,
+          invhist_qoh_before, invhist_qoh_after,
+          invhist_comments,
+          invhist_invuom, invhist_unitcost,
+          invhist_costmethod, invhist_value_before, invhist_value_after  ) 
+        SELECT itemsite_id, _itemlocSeries,
+               'NN', (_r.itemloc_qty * -1),
+               _r.itemloc_qty, 0,
+               'Initial Distribution',
+               uom_name, stdCost(item_id),
+               itemsite_costmethod, itemsite_value, itemsite_value
+        FROM itemsite, item, uom
+        WHERE ( (itemsite_item_id=item_id)
+         AND (item_inv_uom_id=uom_id)
+         AND (itemsite_controlmethod <> 'N')
+         AND (itemsite_id=pItemsiteid) );
+
+        UPDATE itemsite
+        SET itemsite_nnqoh = (itemsite_nnqoh + _r.itemloc_qty),
+            itemsite_qtyonhand = (itemsite_qtyonhand - _r.itemloc_qty) 
+        WHERE (itemsite_id=pItemsiteid);
+
+      END IF;
+
+    END LOOP;
+
+  ELSE
+--  The passed itemsite is not lot/serial controlled
+--  Make sure that there are not any stagnent itemlocs
+    DELETE FROM itemloc
+    WHERE (itemloc_itemsite_id=pItemsiteid);
+
+--  Create the RL transaction
+    SELECT NEXTVAL('invhist_invhist_id_seq') INTO _invhistid;
+    INSERT INTO invhist
+    ( invhist_id, invhist_itemsite_id, invhist_series,
+      invhist_transtype, invhist_invqty,
+      invhist_qoh_before, invhist_qoh_after,
+      invhist_comments,
+      invhist_invuom, invhist_unitcost, invhist_hasdetail,
+      invhist_costmethod, invhist_value_before, invhist_value_after  ) 
+    SELECT _invhistid, itemsite_id, _itemlocSeries,
+           'RL', 0,
+           itemsite_qtyonhand, itemsite_qtyonhand,
+           'Initial Distribution',
+           uom_name, stdCost(item_id), TRUE,
+           itemsite_costmethod, itemsite_value, itemsite_value
+    FROM item, itemsite, uom
+    WHERE ( (itemsite_item_id=item_id)
+     AND (item_inv_uom_id=uom_id)
+     AND (itemsite_controlmethod <> 'N')
+     AND (itemsite_id=pItemsiteid) );
+
+--  Create the itemloc
+    SELECT NEXTVAL('itemloc_itemloc_id_seq') INTO _itemlocid;
+    INSERT INTO itemloc
+    ( itemloc_id, itemloc_itemsite_id, itemloc_location_id,
+      itemloc_expiration, itemloc_qty )
+    SELECT _itemlocid, itemsite_id, pLocationid,
+           endOfTime(), itemsite_qtyonhand
+    FROM itemsite
+    WHERE (itemsite_id=pItemsiteid);
+
+--  Record the detail transaction
+    INSERT INTO invdetail
+    ( invdetail_invhist_id, invdetail_location_id,
+      invdetail_qty, invdetail_qty_before, invdetail_qty_after )
+    SELECT _invhistid, pLocationid,
+           itemsite_qtyonhand, 0, itemsite_qtyonhand
+    FROM itemsite
+    WHERE (itemsite_id=pItemsiteid);
+
+--  Adjust QOH if this itemlocdist is to/from a non-netable location
+    IF ( SELECT (NOT location_netable)
+         FROM location
+         WHERE (location_id=pLocationid) ) THEN
+
+      INSERT INTO invhist
+      ( invhist_itemsite_id, invhist_series,
+        invhist_transtype, invhist_invqty,
+        invhist_qoh_before, invhist_qoh_after,
+        invhist_comments,
+        invhist_invuom, invhist_unitcost,
+        invhist_costmethod, invhist_value_before, invhist_value_after  ) 
+      SELECT itemsite_id, _itemlocSeries,
+             'NN', (itemloc_qty * -1),
+             itemloc_qty, 0,
+             'Initial Distribution',
+             uom_name, stdCost(item_id),
+             itemsite_costmethod, itemsite_value, itemsite_value
+      FROM itemloc, itemsite, item, uom
+      WHERE ( (itemsite_item_id=item_id)
+       AND (item_inv_uom_id=uom_id)
+       AND (itemsite_controlmethod <> 'N')
+       AND (itemloc_itemsite_id=itemsite_id)
+       AND (itemloc_id=_itemlocid) );
+
+      UPDATE itemsite
+      SET itemsite_nnqoh = itemsite_qtyonhand,
+          itemsite_qtyonhand = 0 
+      FROM itemloc
+      WHERE ( (itemloc_itemsite_id=itemsite_id)
+       AND (itemloc_id=_itemlocid) );
+
+    END IF;
+
+  END IF;
+
+  RETURN _itemlocid;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/insertIntoGLSeries.sql b/foundation-database/public/functions/insertIntoGLSeries.sql
new file mode 100644 (file)
index 0000000..2e19046
--- /dev/null
@@ -0,0 +1,143 @@
+
+CREATE OR REPLACE FUNCTION insertIntoGLSeries(INTEGER, TEXT, TEXT, TEXT, INTEGER, NUMERIC) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSequence ALIAS FOR $1;
+  pSource ALIAS FOR $2;
+  pDocType ALIAS FOR $3;
+  pDocNumber ALIAS FOR $4;
+  pAccntid ALIAS FOR $5;
+  pAmount ALIAS FOR $6;
+  _returnValue INTEGER;
+
+BEGIN
+
+  SELECT insertIntoGLSeries( pSequence, pSource, pDocType, pDocNumber,
+                             pAccntid, pAmount, CURRENT_DATE, '' ) INTO _returnValue;
+
+  RETURN _returnValue;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION insertIntoGLSeries(INTEGER, TEXT, TEXT, TEXT, INTEGER, NUMERIC, DATE) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSequence ALIAS FOR $1;
+  pSource ALIAS FOR $2;
+  pDocType ALIAS FOR $3;
+  pDocNumber ALIAS FOR $4;
+  pAccntid ALIAS FOR $5;
+  pAmount ALIAS FOR $6;
+  pDistDate ALIAS FOR $7;
+  _returnValue INTEGER;
+
+BEGIN
+
+  SELECT insertIntoGLSeries( pSequence, pSource, pDocType, pDocNumber,
+                             pAccntid, pAmount, pDistDate, '' ) INTO _returnValue;
+
+  RETURN _returnValue;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION insertIntoGLSeries(INTEGER, TEXT, TEXT, TEXT, INTEGER, NUMERIC, DATE, TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSequence ALIAS FOR $1;
+  pSource ALIAS FOR $2;
+  pDocType ALIAS FOR $3;
+  pDocNumber ALIAS FOR $4;
+  pAccntid ALIAS FOR $5;
+  pAmount ALIAS FOR $6;
+  pDistDate ALIAS FOR $7;
+  pNotes ALIAS FOR $8;
+  _returnValue INTEGER;
+
+BEGIN
+
+  SELECT insertIntoGLSeries( pSequence, pSource, pDocType, pDocNumber,
+                             pAccntid, pAmount, pDistDate, pNotes, NULL ) INTO _returnValue;
+
+  RETURN _returnValue;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION insertIntoGLSeries(INTEGER, TEXT, TEXT, TEXT, INTEGER, NUMERIC, DATE, TEXT, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSequence ALIAS FOR $1;
+  pSource ALIAS FOR $2;
+  pDocType ALIAS FOR $3;
+  pDocNumber ALIAS FOR $4;
+  pAccntid ALIAS FOR $5;
+  pAmount ALIAS FOR $6;
+  pDistDate ALIAS FOR $7;
+  pNotes ALIAS FOR $8;
+  pMiscid ALIAS FOR $9;
+  _glseriesid INTEGER;
+
+BEGIN
+
+--  Check GL Interface metric
+  IF (fetchMetricBool('InterfaceToGL') = false AND pSource IN ('I/M', 'P/D', 'S/R', 'W/O')) THEN
+    RETURN 0;
+  END IF;
+  IF (fetchMetricBool('InterfaceAPToGL') = false AND pSource = 'A/P') THEN
+    RETURN 0;
+  END IF;
+  IF (fetchMetricBool('InterfaceARToGL') = false AND pSource IN ('A/R', 'S/O', 'S/R')) THEN
+    RETURN 0;
+  END IF;
+
+--  Verify the target accnt
+  IF ( (pAccntid IS NULL) OR (pAccntid = -1) ) THEN
+    RETURN -1;
+  END IF;
+
+-- refuse to accept postings into closed periods
+  IF (SELECT BOOL_AND(COALESCE(period_closed, FALSE))
+      FROM accnt LEFT OUTER JOIN
+           period ON (pDistDate BETWEEN period_start AND period_end)
+      WHERE (accnt_id = pAccntid)) THEN
+    RAISE EXCEPTION 'Cannot post to closed period (%).', pDistDate;
+    RETURN -4;  -- remove raise exception when all callers check return code
+  END IF;
+
+-- refuse to accept postings into frozen periods without proper priv
+  IF (SELECT NOT BOOL_AND(checkPrivilege('PostFrozenPeriod')) AND
+             BOOL_AND(COALESCE(period_freeze, FALSE))
+      FROM accnt LEFT OUTER JOIN
+           period ON (pDistDate BETWEEN period_start AND period_end)
+      WHERE (accnt_id = pAccntid)) THEN
+    RAISE EXCEPTION 'Cannot post to frozen period (%).', pDistDate;
+    RETURN -4;  -- remove raise exception when all callers check return code
+  END IF;
+
+-- refuse to accept postings into nonexistent periods
+  IF NOT EXISTS(SELECT period_id
+                FROM period
+                WHERE (pDistDate BETWEEN period_start AND period_end)) THEN
+    RAISE EXCEPTION 'Cannot post to nonexistent period (%).', pDistDate;
+  END IF;
+
+-- Insert into the glseries
+  SELECT NEXTVAL('glseries_glseries_id_seq') INTO _glseriesid;
+  INSERT INTO glseries
+  ( glseries_id, glseries_sequence, glseries_source, glseries_doctype, glseries_docnumber,
+    glseries_accnt_id, glseries_amount, glseries_distdate, glseries_notes, glseries_misc_id )
+  VALUES
+  ( _glseriesid, pSequence, pSource, pDocType, pDocNumber,
+    pAccntid, pAmount, pDistDate, pNotes, pMiscid );
+
+  RETURN _glseriesid;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/insertccard.sql b/foundation-database/public/functions/insertccard.sql
new file mode 100644 (file)
index 0000000..4616ae3
--- /dev/null
@@ -0,0 +1,174 @@
+CREATE OR REPLACE FUNCTION insertccard(text,boolean,text,bytea,bytea,bytea,bytea,bytea,bytea,bytea,bytea,bytea,bytea,text) RETURNS boolean AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCustomer ALIAS FOR $1;
+  pActive ALIAS FOR $2;
+  pType ALIAS FOR $3;
+  pNumber ALIAS FOR $4;
+  pName ALIAS FOR $5;
+  pAddr1 ALIAS FOR $6;
+  pAddr2 ALIAS FOR $7;
+  pCity ALIAS FOR $8;
+  pState ALIAS FOR $9;
+  pPostal ALIAS FOR $10;
+  pCountry ALIAS FOR $11;
+  pMonth ALIAS FOR $12;
+  pYear ALIAS FOR $13;
+  pKey ALIAS FOR $14;
+  _type CHAR;
+  _number TEXT;
+  _month INTEGER;
+  _year INTEGER;
+  _result INTEGER;
+
+BEGIN
+  --Initialize
+  _number = CAST(encode(pNumber, 'escape') AS text);
+  _month = CAST(encode(pMonth, 'escape') AS integer);
+  _year = CAST(encode(pYear, 'escape') AS integer);
+
+  -- Check Card holder info
+  IF (pName IS NULL) THEN
+      RAISE EXCEPTION 'The name of the card holder must be entered';
+  END IF;
+  IF (pAddr1 IS NULL OR pAddr1 = '') THEN
+      RAISE EXCEPTION 'The first address line must be entered';
+  END IF;
+  IF (pCity IS NULL OR pCity = '') THEN
+      RAISE EXCEPTION 'The city must be entered';
+  END IF;
+  IF (pState IS NULL OR pState = '') THEN
+      RAISE EXCEPTION 'The state must be entered';
+  END IF;
+  IF (pPostal IS NULL OR pPostal = '') THEN
+      RAISE EXCEPTION 'The zip code must be entered';
+  END IF;
+  IF (pCountry IS NULL OR pCountry = '') THEN
+      RAISE EXCEPTION 'The country must be entered';
+  END IF;
+  IF (pMonth IS NULL OR pMonth = '') THEN
+      RAISE EXCEPTION 'The Expiration Month must be entered';
+  END IF;
+  IF (_month < 1 OR _month > 12) THEN
+      RAISE EXCEPTION 'Valid Expiration Months are 01 through 12';
+  END IF;
+  IF (LENGTH(_year::text) <> 4) THEN
+      RAISE EXCEPTION 'Valid Expiration Years are CCYY in format';
+  END IF;
+  IF (_year < 1970 OR _year > 2100) THEN
+      RAISE EXCEPTION 'Valid Expiration Years are 1970 through 2100';
+  END IF;
+  
+  -- Check Number Length
+  IF ((NOT _number ~  '[0-9]{13,16}') OR (LENGTH(_number) = 14) OR (LENGTH(_number) > 16)) THEN
+    RAISE EXCEPTION 'The credit card number must be all numeric (no spaces or hyphens) and must be 13, 15 or 16 characters in length';
+  END IF;
+  -- Convert Type
+  IF (pType = 'Visa') THEN
+    _type  = 'V';
+  ELSE
+    IF (pType = 'Master Card') THEN
+      _type  = 'M';
+    ELSE
+      IF (pType = 'American Express') THEN
+        _type  = 'A';
+      ELSE
+        IF (pType = 'Discover') THEN
+          _type  = 'D';
+        ELSE
+          RAISE EXCEPTION 'You must select Master Card, Visa, American 
+                            Express or Discover as the credit card type.';
+        END IF;
+      END IF;
+    END IF;
+  END IF;
+  
+  -- Check Card Specific Data
+  SELECT editccnumber(_number, _type) INTO _result;
+
+  IF (_result = -1) THEN
+    RAISE EXCEPTION 'You must select Master Card, Visa, American 
+                      Express or Discover as the credit card type.';
+  END IF;
+  IF (_result = -2) THEN
+    RAISE EXCEPTION 'The length of a Master Card credit card number
+                      has to be 16 digits.';
+  END IF;
+  IF (_result = -3) THEN
+    RAISE EXCEPTION 'The length of a Visa credit card number
+                      has to be either 13 or 16 digits.';
+  END IF;
+  IF (_result = -4) THEN
+    RAISE EXCEPTION 'The length of an American Express credit card
+                      number has to be 15 digits.';
+  END IF;
+  IF (_result = -5) THEN
+    RAISE EXCEPTION 'The length of a Discover credit card number 
+                      has to be 16 digits.';
+  END IF;
+   IF (_result = -6) THEN
+    RAISE EXCEPTION 'The first two digits for a valid Master Card
+                      number must be between 51 and 55';
+  END IF;
+  IF (_result = -7) THEN
+    RAISE EXCEPTION 'The first digit for a valid Visa number must
+                      be 4';
+  END IF;
+   IF (_result = -8) THEN
+    RAISE EXCEPTION 'The first two digits for a valid American
+                      Express number must be 34 or 37.';
+  END IF;
+  IF (_result = -9) THEN
+    RAISE EXCEPTION 'The first four digits for a valid Discover
+                      Express number must be 6011.';
+  END IF;
+  IF ((_result = -10) AND NOT fetchmetricbool('CCTest')) THEN
+    RAISE EXCEPTION 'The credit card number that you have provided
+                      is not valid.';
+  END IF;
+  IF (_result < -10) THEN
+    RAISE EXCEPTION 'Invalid Credit Card Information';
+  END IF;
+
+  -- Insert Record
+
+  INSERT INTO ccard ( 
+    ccard_seq, 
+    ccard_cust_id,
+    ccard_active, 
+    ccard_name, 
+    ccard_address1,
+    ccard_address2,
+    ccard_city, 
+    ccard_state, 
+    ccard_zip,
+    ccard_country, 
+    ccard_number,
+    ccard_month_expired, 
+    ccard_year_expired, 
+    ccard_type)
+    VALUES 
+    ((SELECT COALESCE(MAX(ccard_seq), 0) + 10
+      FROM ccard 
+      WHERE (ccard_cust_id =getCustId(pCustomer))),
+     getCustId(pCustomer),
+     COALESCE(pActive),
+     encrypt(setbytea(pName), setbytea(pKey), 'bf'),
+     encrypt(setbytea(pAddr1), setbytea(pKey), 'bf'),
+     encrypt(setbytea(pAddr2), setbytea(pKey), 'bf'),
+     encrypt(setbytea(pCity), setbytea(pKey), 'bf'),
+     encrypt(setbytea(pState), setbytea(pKey), 'bf'),
+     encrypt(setbytea(pPostal), setbytea(pKey), 'bf'),
+     encrypt(setbytea(pCountry), setbytea(pKey), 'bf'),
+     encrypt(setbytea(pNumber), setbytea(pKey), 'bf'),
+     encrypt(setbytea(pMonth), setbytea(pKey), 'bf'),
+     encrypt(setbytea(pYear), setbytea(pKey), 'bf'),
+     _type );
+
+  RETURN true;
+END;
+$$ LANGUAGE 'plpgsql';
+COMMENT ON FUNCTION insertccard(text,boolean,text,bytea,bytea,bytea,bytea,bytea,bytea,bytea,bytea,bytea,bytea,text)  IS 'This function is generally used to support the _custcreditcard API view';
+
diff --git a/foundation-database/public/functions/insertflgroup.sql b/foundation-database/public/functions/insertflgroup.sql
new file mode 100644 (file)
index 0000000..3c70818
--- /dev/null
@@ -0,0 +1,520 @@
+
+
+CREATE OR REPLACE FUNCTION insertFlGroup(INTEGER, INTEGER, INTEGER, INTEGER, BOOLEAN) RETURNS BOOLEAN AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pFlheadid  ALIAS FOR $1;
+  pPeriodid  ALIAS FOR $2;
+  pFlgrpid   ALIAS FOR $3;
+  pLevel     ALIAS FOR $4;
+  pSummarize ALIAS FOR $5;
+
+BEGIN
+  RETURN insertFlGroup(pFlheadid, pPeriodid, pFlgrpid, pLevel, pSummarize, NULL);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION insertFlGroup(INTEGER, INTEGER, INTEGER, INTEGER, BOOLEAN, CHAR) RETURNS BOOLEAN AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pFlheadid  ALIAS FOR $1;
+  pPeriodid  ALIAS FOR $2;
+  pFlgrpid   ALIAS FOR $3;
+  pLevel     ALIAS FOR $4;
+  pSummarize ALIAS FOR $5;
+  pInterval  ALIAS FOR $6;
+
+BEGIN
+  RETURN insertFlGroup(pFlheadid, pPeriodid, pFlgrpid, pLevel, pSummarize, pInterval, NULL);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION insertflgroup(INTEGER, INTEGER, INTEGER, INTEGER, BOOLEAN, CHAR, INTEGER) RETURNS BOOLEAN AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pFlheadid  ALIAS FOR $1;
+  pPeriodid  ALIAS FOR $2;
+  pFlgrpid   ALIAS FOR $3;
+  pLevel     ALIAS FOR $4;
+  pSummarize ALIAS FOR $5;
+  pInterval  ALIAS FOR $6;
+  pPrjid     ALIAS FOR $7;
+
+  _subtotal BOOLEAN;
+  _r RECORD;
+  _g RECORD;
+  _all BOOLEAN;
+
+BEGIN
+
+  _all = COALESCE(pPrjid,-1) = -1;
+  
+-- Check to see if this group wants a subtotal
+  _subtotal := FALSE;
+  IF (pFlgrpid != -1) THEN
+    SELECT COALESCE(flgrp_subtotal, FALSE) INTO _subtotal
+      FROM flgrp
+     WHERE ((flgrp_flhead_id=pFlheadid)
+       AND  (flgrp_id=pFlgrpid));
+  END IF;
+
+  FOR _r IN SELECT 'G' AS type, flgrp_id AS type_id,
+                   flgrp_order AS orderby,
+                   flgrp_summarize AS summarize,
+                   flgrp_subtract AS subtract,
+                   CASE WHEN(flgrp_summarize AND (NOT flgrp_showstart)) THEN NULL
+                        ELSE 0.00
+                   END AS beginning,
+                   CASE WHEN(flgrp_summarize AND (NOT flgrp_showend)) THEN NULL
+                        ELSE 0.00
+                   END AS ending,
+                   CASE WHEN(flgrp_summarize AND (NOT flgrp_showdelta)) THEN NULL
+                        ELSE 0.00
+                   END AS debits,
+                   CASE WHEN(flgrp_summarize AND (NOT flgrp_showdelta)) THEN NULL
+                        ELSE 0.00
+                   END AS credits,
+                   CASE WHEN(flgrp_summarize AND (NOT flgrp_showbudget)) THEN NULL
+                        ELSE 0.00
+                   END AS budget,
+                   CASE WHEN(flgrp_summarize AND (NOT flgrp_showdiff)) THEN NULL
+                        ELSE 0.00
+                   END AS diff,
+                   CASE WHEN(flgrp_summarize AND (NOT flgrp_showcustom)) THEN NULL
+                        ELSE 0.00
+                   END AS custom,
+                   CASE WHEN(flgrp_showstartprcnt) THEN 0.00
+                        ELSE NULL
+                   END AS beginningprcnt,
+                   CASE WHEN(flgrp_showendprcnt) THEN 0.00
+                        ELSE NULL
+                   END AS endingprcnt,
+                   CASE WHEN(flgrp_showdeltaprcnt) THEN 0.00
+                        ELSE NULL
+                   END AS debitsprcnt,
+                   CASE WHEN(flgrp_showdeltaprcnt) THEN 0.00
+                        ELSE NULL
+                   END AS creditsprcnt,
+                   CASE WHEN(flgrp_showbudgetprcnt) THEN 0.00
+                        ELSE NULL
+                   END AS budgetprcnt,
+                   CASE WHEN(flgrp_showdiffprcnt) THEN 0.00
+                        ELSE NULL
+                   END AS diffprcnt,
+                   CASE WHEN(flgrp_showcustomprcnt) THEN 0.00
+                        ELSE NULL
+                   END AS customprcnt,
+                   -1 AS accnt_id,
+                   '' AS accnt_number
+              FROM flgrp
+             WHERE ((flgrp_flgrp_id=pFlgrpid)
+               AND  (flgrp_flhead_id=pFlheadid))
+             UNION ALL
+            SELECT 'I' AS type, flitem_id AS type_id,
+                   flitem_order AS orderby,
+                   FALSE AS summarize,
+                   flitem_subtract AS subtract,
+                   CASE WHEN (flitem_showstart AND (first_trialbal_id IS NULL)) THEN 0.00
+                        WHEN (flitem_showstart) THEN normalizeTrialBal(first_trialbal_id, 'B')
+                        ELSE NULL
+                   END AS beginning,
+                   CASE WHEN (flitem_showend AND (last_trialbal_id IS NULL)) THEN 0.00
+                        WHEN (flitem_showend) THEN normalizeTrialBal(last_trialbal_id, 'E')
+                        ELSE NULL
+                   END AS ending,
+                   CASE WHEN (flitem_showdelta) THEN sum_trialbal_debits
+                        ELSE NULL
+                   END AS debits,
+                   CASE WHEN (flitem_showdelta) THEN sum_trialbal_credits
+                        ELSE NULL
+                   END AS credits,
+                   CASE WHEN ((flitem_showbudget) AND (accnt_type IN ('R','E')) AND flhead_type IN ('I','C','A')) THEN COALESCE(sum_budget_amount,0)
+                        WHEN ((flitem_showbudget) AND (accnt_type IN ('R','E')) AND flhead_type = 'B' ) THEN
+                                (SELECT COALESCE(SUM(b.budget_amount),0)
+                                FROM budget b,
+                                        (SELECT ytd.period_id AS ytd_period_id
+                                        FROM period cp, period ytd
+                                        WHERE ((cp.period_id = last_flitem_period_id)
+                                        AND (ytd.period_start <= cp.period_start)
+                                AND (ytd.period_yearperiod_id = cp.period_yearperiod_id))) AS periods
+                                WHERE ((b.budget_accnt_id=accnt_id)
+                                AND (b.budget_period_id=ytd_period_id)))
+                        WHEN ((flitem_showbudget) AND (accnt_type IN ('A','L','Q')) AND flhead_type = 'C') THEN calccashbudget(accnt_id,last_flitem_period_id,pInterval)
+                        ELSE COALESCE(last_budget_amount,0)
+                   END AS budget,
+                   CASE WHEN (flitem_showdiff AND (first_trialbal_id IS NULL)) THEN 0.00
+                        WHEN (flitem_showdiff) THEN COALESCE(normalizeTrialBal(last_trialbal_id, 'E') - normalizeTrialBal(first_trialbal_id, 'B'), 0.00)
+                        ELSE NULL
+                   END AS diff,
+                   CASE WHEN (NOT flitem_showcustom) THEN NULL
+                        WHEN (flitem_custom_source='S' AND (first_trialbal_id IS NOT NULL)) THEN normalizeTrialBal(first_trialbal_id, 'B')
+                        WHEN (flitem_custom_source='E' AND (first_trialbal_id IS NOT NULL)) THEN normalizeTrialBal(last_trialbal_id, 'E')
+                        WHEN (flitem_custom_source='D') THEN sum_trialbal_debits
+                        WHEN (flitem_custom_source='C') THEN sum_trialbal_credits
+                        WHEN (flitem_custom_source='B') THEN (
+                                CASE
+                                  WHEN (accnt_type IN ('R','E')) THEN sum_budget_amount
+                                  ELSE last_budget_amount
+                                END)
+                        WHEN (flitem_custom_source='F' AND  (first_trialbal_id IS NOT NULL)) THEN COALESCE(normalizeTrialBal(last_trialbal_id, 'E') - normalizeTrialBal(first_trialbal_id, 'B'), 0.00)
+                        ELSE 0.00
+                   END AS custom,
+                   CASE WHEN(flitem_showstartprcnt) THEN 0.00
+                        ELSE NULL
+                   END AS beginningprcnt,
+                   CASE WHEN(flitem_showendprcnt) THEN 0.00
+                        ELSE NULL
+                   END AS endingprcnt,
+                   CASE WHEN(flitem_showdeltaprcnt) THEN 0.00
+                        ELSE NULL
+                   END AS debitsprcnt,
+                   CASE WHEN(flitem_showdeltaprcnt) THEN 0.00
+                        ELSE NULL
+                   END AS creditsprcnt,
+                   CASE WHEN(flitem_showbudgetprcnt) THEN 0.00
+                        ELSE NULL
+                   END AS budgetprcnt,
+                   CASE WHEN(flitem_showdiffprcnt) THEN 0.00
+                        ELSE NULL
+                   END AS diffprcnt,
+                   CASE WHEN(flitem_showcustomprcnt) THEN 0.00
+                        ELSE NULL
+                   END AS customprcnt,
+                   accnt_id,
+                   public.formatglaccount(accnt_id) AS accnt_number
+              FROM
+                (SELECT 
+                  flhead_type,flitem_id,flitem_order,flitem_subtract,flitem_showstart,flitem_showend,
+                  flitem_showdelta,flitem_showbudget,flitem_showdiff,flitem_showcustom,
+                  flitem_custom_source,flitem_showstartprcnt,flitem_showendprcnt,flitem_showdeltaprcnt,
+                  flitem_showbudgetprcnt,flitem_showdiffprcnt,flitem_showcustomprcnt,
+                  accnt_id,accnt_type,
+                  FIRST(trialbal_id) AS first_trialbal_id, LAST(trialbal_id) AS last_trialbal_id,
+                  SUM(trialbal_debits) AS sum_trialbal_debits, SUM(trialbal_credits) AS sum_trialbal_credits, 
+                  LAST(flitem_period_id) AS last_flitem_period_id,
+                  SUM(budget_amount) AS sum_budget_amount, LAST(budget_amount) AS last_budget_amount
+                  FROM
+                (SELECT period_id AS flitem_period_id, period_start,flhead_type,flitem_id,flitem_order,flitem_subtract,flitem_showstart,flitem_showend,
+                        flitem_showdelta,flitem_showbudget,flitem_showdiff,flitem_showcustom,
+                        flitem_custom_source,flitem_showstartprcnt,flitem_showendprcnt,flitem_showdeltaprcnt,
+                        flitem_showbudgetprcnt,flitem_showdiffprcnt,flitem_showcustomprcnt,
+                        accnt_id,accnt_type,COALESCE(trialbal_id,getlasttrialbalid(accnt_id,period_id)) as trialbal_id,COALESCE(trialbal_debits,0) as trialbal_debits,
+                        COALESCE(trialbal_credits,0) AS trialbal_credits,COALESCE(budget_amount,0) AS budget_amount
+                   FROM (SELECT period_id, period_start, flhead_type, flitem_id,flitem_order,flitem_subtract,flitem_showstart,flitem_showend,
+                                flitem_showdelta,flitem_showbudget,flitem_showdiff,flitem_showcustom,flitem_custom_source,flitem_showstartprcnt,
+                                flitem_showendprcnt,flitem_showdeltaprcnt,flitem_showbudgetprcnt,flitem_showdiffprcnt,flitem_showcustomprcnt,
+                                accnt_id, accnt_type
+                        FROM  period,flaccnt
+                        WHERE ((flitem_flhead_id=pFlheadid)
+                        AND (flitem_flgrp_id=pFlgrpid)
+                        AND (_all OR prj_id=pPrjId)
+                        AND (period_id IN  (SELECT * FROM getperiodid(pPeriodId,pInterval))))
+                        ORDER BY flitem_id
+                        ) AS flitem
+                   LEFT OUTER JOIN trialbal
+                     ON ((trialbal_accnt_id=accnt_id)
+                     AND (trialbal_period_id=period_id))
+                   LEFT OUTER JOIN budget
+                     ON ((budget_accnt_id=accnt_id)
+                     AND (budget_period_id=period_id))
+             ORDER BY accnt_id, period_start) AS data
+             GROUP BY flhead_type,flitem_id,flitem_order,flitem_subtract,flitem_showstart,flitem_showend,
+                flitem_showdelta,flitem_showbudget,flitem_showdiff,flitem_showcustom,
+                flitem_custom_source,flitem_showstartprcnt,flitem_showendprcnt,flitem_showdeltaprcnt,
+                flitem_showbudgetprcnt,flitem_showdiffprcnt,flitem_showcustomprcnt,accnt_id,accnt_type) AS agg
+             UNION ALL
+            SELECT 'S' AS type, flspec_id AS type_id,
+                   flspec_order AS orderby,
+                   FALSE AS summarize,
+                   flspec_subtract AS subtract,
+                   CASE WHEN (flspec_showstart) THEN findSpecialFinancial('S', flspec_type, pPeriodid)
+                        ELSE NULL
+                   END AS beginning,
+                   CASE WHEN (flspec_showend) THEN findSpecialFinancial('E', flspec_type, pPeriodid)
+                        ELSE NULL
+                   END AS ending,
+                   CASE WHEN (flspec_showdelta) THEN findSpecialFinancial('D', flspec_type, pPeriodid)
+                        ELSE NULL
+                   END AS debits,
+                   CASE WHEN (flspec_showdelta) THEN findSpecialFinancial('C', flspec_type, pPeriodid)
+                        ELSE NULL
+                   END AS credits,
+                   CASE WHEN (flspec_showbudget) THEN findSpecialFinancial('B', flspec_type, pPeriodid)
+                        ELSE NULL
+                   END AS budget,
+                   CASE WHEN (flspec_showdiff) THEN findSpecialFinancial('E', flspec_type, pPeriodid) - findSpecialFinancial('S', flspec_type, pPeriodid)
+                        ELSE NULL
+                   END AS diff,
+                   CASE WHEN (NOT flspec_showcustom) THEN NULL
+                        WHEN (flspec_custom_source='F') THEN findSpecialFinancial('E', flspec_type, pPeriodid) - findSpecialFinancial('S', flspec_type, pPeriodid)
+                        WHEN (flspec_custom_source IN ('S', 'E', 'D', 'C', 'B')) THEN findSpecialFinancial(flspec_custom_source, flspec_type, pPeriodid)
+                        ELSE 0.00
+                   END AS custom,
+                   CASE WHEN(flspec_showstartprcnt) THEN 0.00
+                        ELSE NULL
+                   END AS beginningprcnt,
+                   CASE WHEN(flspec_showendprcnt) THEN 0.00
+                        ELSE NULL
+                   END AS endingprcnt,
+                   CASE WHEN(flspec_showdeltaprcnt) THEN 0.00
+                        ELSE NULL
+                   END AS debitsprcnt,
+                   CASE WHEN(flspec_showdeltaprcnt) THEN 0.00
+                        ELSE NULL
+                   END AS creditsprcnt,
+                   CASE WHEN(flspec_showbudgetprcnt) THEN 0.00
+                        ELSE NULL
+                   END AS budgetprcnt,
+                   CASE WHEN(flspec_showdiffprcnt) THEN 0.00
+                        ELSE NULL
+                   END AS diffprcnt,
+                   CASE WHEN(flspec_showcustomprcnt) THEN 0.00
+                        ELSE NULL
+                   END AS customprcnt,
+                   -1 AS accnt_id,
+                   '' AS accnt_number
+              FROM flspec
+             WHERE ((flspec_flgrp_id=pFlgrpid)
+               AND  (flspec_flhead_id=pFlheadid))
+          ORDER BY orderby, accnt_number LOOP
+
+    IF (_r.type = 'G') THEN
+
+-- Create a record for the items sub items to be attached to and be able to update the total
+      INSERT INTO flrpt
+             (flrpt_flhead_id, flrpt_period_id, flrpt_username,
+              flrpt_order,
+              flrpt_level, flrpt_type, flrpt_type_id,
+              flrpt_beginning, flrpt_ending,
+              flrpt_debits, flrpt_credits, flrpt_budget, flrpt_diff, flrpt_custom,
+              flrpt_beginningprcnt, flrpt_endingprcnt,
+              flrpt_debitsprcnt, flrpt_creditsprcnt, flrpt_budgetprcnt, flrpt_diffprcnt, flrpt_customprcnt,
+              flrpt_parent_id, flrpt_interval)
+      VALUES (pFlheadid, pPeriodid, getEffectiveXtUser(),
+              (COALESCE(( SELECT MAX(flrpt_order)
+                            FROM flrpt
+                           WHERE ((flrpt_flhead_id=pFlheadid)
+                             AND  (flrpt_period_id=pPeriodid)
+                             AND (flrpt_interval=pInterval)
+                             AND  (flrpt_username=getEffectiveXtUser()))
+                        ), 1) + 1),
+              pLevel, _r.type, _r.type_id,
+              _r.beginning, _r.ending,
+              _r.debits, _r.credits, _r.budget, _r.diff, _r.custom,
+              _r.beginningprcnt, _r.endingprcnt,
+              _r.debitsprcnt, _r.creditsprcnt, _r.budgetprcnt, _r.diffprcnt, _r.customprcnt,
+              pFlgrpid, pInterval);
+
+      PERFORM insertFlGroup(pFlheadid, pPeriodid, _r.type_id, (pLevel + 1), (pSummarize OR _r.summarize), pInterval, pPrjid);
+
+-- Update the parent item
+      SELECT COALESCE(flrpt_beginning, 0.00) AS beginning,
+             COALESCE(flrpt_ending, 0.00) AS ending,
+             COALESCE(flrpt_debits, 0.00) AS debits,
+             COALESCE(flrpt_credits, 0.00) AS credits,
+             COALESCE(flrpt_budget, 0.00) AS budget,
+             COALESCE(flrpt_diff, 0.00) AS diff,
+             COALESCE(flrpt_custom, 0.00) AS custom INTO _g
+        FROM flrpt
+       WHERE ((flrpt_flhead_id=pFlheadid)
+         AND  (flrpt_period_id=pPeriodid)
+          AND (flrpt_interval=pInterval)
+         AND  (flrpt_username=getEffectiveXtUser())
+         AND  (flrpt_type=_r.type)
+         AND  (flrpt_type_id=_r.type_id));
+      IF (_r.subtract) THEN
+        UPDATE flrpt
+           SET flrpt_beginning = flrpt_beginning - _g.beginning,
+               flrpt_ending    = flrpt_ending    - _g.ending,
+               flrpt_debits    = flrpt_debits    - _g.debits,
+               flrpt_credits   = flrpt_credits   - _g.credits,
+               flrpt_budget    = flrpt_budget    - _g.budget,
+               flrpt_diff      = flrpt_diff      - _g.diff,
+               flrpt_custom    = flrpt_custom    - _g.custom
+         WHERE ((flrpt_flhead_id=pFlheadid)
+           AND  (flrpt_period_id=pPeriodid)
+           AND  (flrpt_interval=pInterval)
+           AND  (flrpt_username=getEffectiveXtUser())
+           AND  (flrpt_type='G')
+           AND  (flrpt_type_id=pFlgrpid));
+      ELSE
+        UPDATE flrpt
+           SET flrpt_beginning = flrpt_beginning + _g.beginning,
+               flrpt_ending    = flrpt_ending    + _g.ending,
+               flrpt_debits    = flrpt_debits    + _g.debits,
+               flrpt_credits   = flrpt_credits   + _g.credits,
+               flrpt_budget    = flrpt_budget    + _g.budget,
+               flrpt_diff      = flrpt_diff      + _g.diff,
+               flrpt_custom    = flrpt_custom    + _g.custom
+         WHERE ((flrpt_flhead_id=pFlheadid)
+           AND  (flrpt_period_id=pPeriodid)
+           AND  (flrpt_interval=pInterval)
+           AND  (flrpt_username=getEffectiveXtUser())
+           AND  (flrpt_type='G')
+           AND  (flrpt_type_id=pFlgrpid));
+      END IF;
+
+-- If we are summarizing then we need to remove the record we created now that we have updated the total
+      IF (pSummarize) THEN
+        DELETE FROM flrpt
+         WHERE ((flrpt_flhead_id=pFlheadid)
+          AND  (flrpt_period_id=pPeriodid)
+          AND  (flrpt_interval=pInterval)
+          AND  (flrpt_username=getEffectiveXtUser())
+          AND  (flrpt_type=_r.type)
+          AND  (flrpt_type_id=_r.type_id));
+      END IF;
+
+    ELSE
+      IF (_r.type = 'I' OR _r.type = 'S' ) THEN
+
+-- If we are not summarizing then create a new entry for this record
+        IF (NOT pSummarize) THEN
+          INSERT INTO flrpt
+                 (flrpt_flhead_id, flrpt_period_id, flrpt_username,
+                  flrpt_order,
+                  flrpt_level, flrpt_type, flrpt_type_id,
+                  flrpt_beginning, flrpt_ending,
+                  flrpt_debits, flrpt_credits, flrpt_budget, flrpt_diff, flrpt_custom,
+                  flrpt_beginningprcnt, flrpt_endingprcnt,
+                  flrpt_debitsprcnt, flrpt_creditsprcnt, flrpt_budgetprcnt, flrpt_diffprcnt, flrpt_customprcnt,
+                  flrpt_parent_id,flrpt_accnt_id,flrpt_interval)
+          VALUES (pFlheadid, pPeriodid, getEffectiveXtUser(),
+                  (COALESCE(( SELECT MAX(flrpt_order)
+                               FROM flrpt
+                              WHERE ((flrpt_flhead_id=pFlheadid)
+                                AND  (flrpt_period_id=pPeriodid)
+                                AND  (flrpt_interval=pInterval)
+                                AND  (flrpt_username=getEffectiveXtUser()))
+                            ), 1) + 1),
+                  pLevel, _r.type, _r.type_id,
+                  _r.beginning, _r.ending,
+                  _r.debits, _r.credits, _r.budget, _r.diff, _r.custom,
+                  _r.beginningprcnt, _r.endingprcnt,
+                  _r.debitsprcnt, _r.creditsprcnt, _r.budgetprcnt, _r.diffprcnt, _r.customprcnt,
+                  pFlgrpid,_r.accnt_id,pInterval);
+        END IF;
+
+-- Update the parent item
+        IF (_r.subtract) THEN
+          UPDATE flrpt
+             SET flrpt_beginning = flrpt_beginning - COALESCE(_r.beginning, 0.00),
+                 flrpt_ending    = flrpt_ending    - COALESCE(_r.ending, 0.00),
+                 flrpt_debits    = flrpt_debits    - COALESCE(_r.debits, 0.00),
+                 flrpt_credits   = flrpt_credits   - COALESCE(_r.credits, 0.00),
+                 flrpt_budget    = flrpt_budget    - COALESCE(_r.budget, 0.00),
+                 flrpt_diff      = flrpt_diff      - COALESCE(_r.diff, 0.00),
+                 flrpt_custom    = flrpt_custom    - COALESCE(_r.custom, 0.00)
+           WHERE ((flrpt_flhead_id=pFlheadid)
+             AND  (flrpt_period_id=pPeriodid)
+             AND  (flrpt_interval=pInterval)
+             AND  (flrpt_username=getEffectiveXtUser())
+             AND  (flrpt_type='G')
+             AND  (flrpt_type_id=pFlgrpid));
+        ELSE
+          UPDATE flrpt
+             SET flrpt_beginning = flrpt_beginning + COALESCE(_r.beginning, 0.00),
+                 flrpt_ending    = flrpt_ending    + COALESCE(_r.ending, 0.00),
+                 flrpt_debits    = flrpt_debits    + COALESCE(_r.debits, 0.00),
+                 flrpt_credits   = flrpt_credits   + COALESCE(_r.credits, 0.00),
+                 flrpt_budget    = flrpt_budget    + COALESCE(_r.budget, 0.00),
+                 flrpt_diff      = flrpt_diff      + COALESCE(_r.diff, 0.00),
+                 flrpt_custom    = flrpt_custom    + COALESCE(_r.custom, 0.00)
+           WHERE ((flrpt_flhead_id=pFlheadid)
+             AND  (flrpt_interval=pInterval)
+             AND  (flrpt_period_id=pPeriodid)
+             AND  (flrpt_username=getEffectiveXtUser())
+             AND  (flrpt_type='G')
+             AND  (flrpt_type_id=pFlgrpid));
+        END IF;
+
+      END IF;
+    END IF;
+
+  END LOOP;
+
+  IF (NOT pSummarize) THEN
+-- If this group wants a summarized line create it here.
+    IF (_subtotal) THEN
+      INSERT INTO flrpt
+             (flrpt_flhead_id, flrpt_period_id, flrpt_username,
+              flrpt_order,
+              flrpt_level, flrpt_type, flrpt_type_id,
+              flrpt_beginning, flrpt_ending,
+              flrpt_debits, flrpt_credits, flrpt_budget, flrpt_diff, flrpt_custom,
+              flrpt_beginningprcnt, flrpt_endingprcnt,
+              flrpt_debitsprcnt, flrpt_creditsprcnt, flrpt_budgetprcnt, flrpt_diffprcnt, flrpt_customprcnt,
+              flrpt_parent_id, flrpt_altname,flrpt_interval )
+      SELECT pFlheadid, pPeriodid, getEffectiveXtUser(),
+             (COALESCE(( SELECT MAX(flrpt_order)
+                           FROM flrpt
+                          WHERE ((flrpt_flhead_id=pFlheadid)
+                            AND  (flrpt_period_id=pPeriodid)
+                            AND  (flrpt_interval=pInterval)
+                            AND  (flrpt_username=getEffectiveXtUser()))
+                       ), 1) + 1),
+             pLevel, 'T', -1,
+             CASE WHEN (flgrp_showstart) THEN flrpt_beginning
+                  ELSE NULL
+             END,
+             CASE WHEN (flgrp_showend) THEN flrpt_ending
+                  ELSE NULL
+             END,
+             CASE WHEN (flgrp_showdelta) THEN flrpt_debits
+                  ELSE NULL
+             END,
+             CASE WHEN (flgrp_showdelta) THEN flrpt_credits
+                  ELSE NULL
+             END,
+             CASE WHEN (flgrp_showbudget) THEN flrpt_budget
+                  ELSE NULL
+             END,
+             CASE WHEN (flgrp_showdiff) THEN flrpt_diff
+                  ELSE NULL
+             END,
+             CASE WHEN (flgrp_showcustom) THEN flrpt_custom
+                  ELSE NULL
+             END,
+             CASE WHEN (flgrp_showstartprcnt) THEN flrpt_beginningprcnt
+                  ELSE NULL
+             END,
+             CASE WHEN (flgrp_showendprcnt) THEN flrpt_endingprcnt
+                  ELSE NULL
+             END,
+             CASE WHEN (flgrp_showdeltaprcnt) THEN flrpt_debitsprcnt
+                  ELSE NULL
+             END,
+             CASE WHEN (flgrp_showdeltaprcnt) THEN flrpt_creditsprcnt
+                  ELSE NULL
+             END,
+             CASE WHEN (flgrp_showbudgetprcnt) THEN flrpt_budgetprcnt
+                  ELSE NULL
+             END,
+             CASE WHEN (flgrp_showdiffprcnt) THEN flrpt_diffprcnt
+                  ELSE NULL
+             END,
+             CASE WHEN (flgrp_showcustomprcnt) THEN flrpt_customprcnt
+                  ELSE NULL
+             END,
+             pFlgrpid,
+             CASE WHEN (flgrp_usealtsubtotal) THEN flgrp_altsubtotal
+                  ELSE NULL
+             END, pInterval
+        FROM flrpt, flgrp
+       WHERE ((flrpt_flhead_id=flgrp_flhead_id)
+         AND  (flrpt_type_id=flgrp_id)
+         AND  (flrpt_flhead_id=pFlheadid)
+         AND  (flrpt_period_id=pPeriodid)
+         AND  (flrpt_interval=pInterval)
+         AND  (flrpt_username=getEffectiveXtUser())
+         AND  (flrpt_type='G')
+         AND  (flrpt_type_id=pFlgrpid));
+    END IF;
+  END IF;
+
+  return TRUE;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/insertgltransaction.sql b/foundation-database/public/functions/insertgltransaction.sql
new file mode 100644 (file)
index 0000000..8f24435
--- /dev/null
@@ -0,0 +1,271 @@
+
+CREATE OR REPLACE FUNCTION insertGLTransaction(TEXT, TEXT, TEXT, TEXT, INTEGER, INTEGER, INTEGER, NUMERIC(12,2), DATE) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSource ALIAS FOR $1;
+  pDocType ALIAS FOR $2;
+  pDocNumber ALIAS FOR $3;
+  pNotes ALIAS FOR $4;
+  pCreditid ALIAS FOR $5;
+  pDebitid ALIAS FOR $6;
+  pMiscid ALIAS FOR $7;
+  pAmount ALIAS FOR $8;
+  pDistDate ALIAS FOR $9;
+  _return INTEGER;
+
+BEGIN
+
+  SELECT insertGLTransaction( fetchJournalNumber('GL-MISC'),
+                              pSource, pDocType, pDocNumber, pNotes,
+                              pCreditid, pDebitid, pMiscid, pAmount, pDistDate) INTO _return;
+
+  RETURN _return;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION insertGLTransaction(TEXT, TEXT, TEXT, TEXT, INTEGER, INTEGER, INTEGER, NUMERIC(12,2), DATE, BOOLEAN) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSource ALIAS FOR $1;
+  pDocType ALIAS FOR $2;
+  pDocNumber ALIAS FOR $3;
+  pNotes ALIAS FOR $4;
+  pCreditid ALIAS FOR $5;
+  pDebitid ALIAS FOR $6;
+  pMiscid ALIAS FOR $7;
+  pAmount ALIAS FOR $8;
+  pDistDate ALIAS FOR $9;
+  pPostTrialBal ALIAS FOR $10;
+  _return INTEGER;
+
+BEGIN
+
+  SELECT insertGLTransaction( fetchJournalNumber('GL-MISC'),
+                              pSource, pDocType, pDocNumber, pNotes,
+                              pCreditid, pDebitid, pMiscid, pAmount, pDistDate, pPostTrialBal) INTO _return;
+
+  RETURN _return;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION insertGLTransaction(INTEGER, TEXT, TEXT, TEXT, TEXT, INTEGER, INTEGER, INTEGER, NUMERIC(12,2), DATE) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pJournalNumber ALIAS FOR $1;
+  pSource ALIAS FOR $2;
+  pDocType ALIAS FOR $3;
+  pDocNumber ALIAS FOR $4;
+  pNotes ALIAS FOR $5;
+  pCreditid ALIAS FOR $6;
+  pDebitid ALIAS FOR $7;
+  pMiscid ALIAS FOR $8;
+  pAmount ALIAS FOR $9;
+  pDistDate ALIAS FOR $10;
+  _return INTEGER;
+
+BEGIN
+
+  SELECT insertGLTransaction( pJournalNumber, pSource, pDocType, pDocNumber, pNotes,
+                              pCreditid, pDebitid, pMiscid, pAmount, pDistDate, TRUE) INTO _return;
+
+  RETURN _return;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION insertGLTransaction(INTEGER, TEXT, TEXT, TEXT, TEXT, INTEGER, INTEGER, INTEGER, NUMERIC(12,2), DATE, BOOLEAN) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pJournalNumber ALIAS FOR $1;
+  pSource ALIAS FOR $2;
+  pDocType ALIAS FOR $3;
+  pDocNumber ALIAS FOR $4;
+  pNotes ALIAS FOR $5;
+  pCreditid ALIAS FOR $6;
+  pDebitid ALIAS FOR $7;
+  pMiscid ALIAS FOR $8;
+  pAmount ALIAS FOR $9;
+  pDistDate ALIAS FOR $10;
+  pPostTrialBal ALIAS FOR $11;
+  
+  _return INTEGER;
+
+BEGIN
+
+  SELECT insertGLTransaction( pJournalNumber, pSource, pDocType, pDocNumber, pNotes,
+                              pCreditid, pDebitid, pMiscid, pAmount, pDistDate, pPostTrialBal, false) INTO _return;
+
+  RETURN _return;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION insertGLTransaction(INTEGER, TEXT, TEXT, TEXT, TEXT, INTEGER, INTEGER, INTEGER, NUMERIC(12,2), DATE, BOOLEAN, BOOLEAN) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pJournalNumber ALIAS FOR $1;
+  pSource ALIAS FOR $2;
+  pDocType ALIAS FOR $3;
+  pDocNumber ALIAS FOR $4;
+  pNotes ALIAS FOR $5;
+  pCreditid ALIAS FOR $6;
+  pDebitid ALIAS FOR $7;
+  pMiscid ALIAS FOR $8;
+  pAmount ALIAS FOR $9;
+  pDistDate ALIAS FOR $10;
+  pPostTrialBal ALIAS FOR $11;
+  pOnlyGL ALIAS FOR $12;
+  _debitid INTEGER;
+  _creditid INTEGER;
+  _sequence INTEGER;
+  _check INTEGER;
+
+BEGIN
+
+--  Check GL Interface metric
+  IF (fetchMetricBool('InterfaceToGL') = false AND pSource IN ('I/M', 'P/D', 'S/R', 'W/O')) THEN
+    RETURN 0;
+  END IF;
+  IF (fetchMetricBool('InterfaceAPToGL') = false AND pSource = 'A/P') THEN
+    RETURN 0;
+  END IF;
+  IF (fetchMetricBool('InterfaceARToGL') = false AND pSource IN ('A/R', 'S/O', 'S/R')) THEN
+    RETURN 0;
+  END IF;
+
+--  Is there anything to post?
+--  ToDo - 2 should really be the scale of the base currency
+  IF (round(pAmount, 2) = 0) THEN
+    RETURN -3;
+  END IF;
+
+/*  Make sure we don't create an imbalance across companies.
+    The 'IgnoreCompanyBalance' metric is a back door mechanism to
+    allow legacy users to create transactions accross companies if
+    they have been using the company segment for something else
+    and they MUST continue to be able to do so.  It can only be 
+    implemented by direct sql update to the metric table and should 
+    otherwise be discouraged.
+*/ 
+  IF (COALESCE(fetchMetricValue('GLCompanySize'),0) > 0 
+    AND fetchMetricBool('IgnoreCompany') = false)  THEN
+
+    IF (SELECT (COALESCE(d.accnt_company,'') != COALESCE(c.accnt_company,''))
+       FROM accnt d, accnt c
+       WHERE ((d.accnt_id=pDebitid)
+        AND (c.accnt_id=pCreditid))) THEN
+      RAISE EXCEPTION 'G/L Transaction can not be posted because accounts % and % reference two differnt companies.',
+        formatGlaccount(pDebitid), formatGlaccount(pCreditid);
+    END IF;
+  END IF;
+
+--  Validate pDebitid
+  IF (pDebitid IN (SELECT accnt_id FROM accnt)) THEN
+    _debitid := pDebitid;
+  ELSE
+    SELECT getUnassignedAccntId() INTO _debitid;
+  END IF;
+
+--  Validate pCreditid
+  IF (pCreditid IN (SELECT accnt_id FROM accnt)) THEN
+    _creditid := pCreditid;
+  ELSE
+    SELECT getUnassignedAccntId() INTO _creditid;
+  END IF;
+
+-- refuse to accept postings into closed periods
+  IF (SELECT BOOL_AND(COALESCE(period_closed, FALSE))
+      FROM accnt LEFT OUTER JOIN
+           period ON (pDistDate BETWEEN period_start AND period_end)
+      WHERE (accnt_id IN (_creditid, _debitid))) THEN
+    RAISE EXCEPTION 'Cannot post to closed period (%).', pDistDate;
+    RETURN -4;  -- remove raise exception when all callers check return code
+  END IF;
+
+-- refuse to accept postings into frozen periods without proper priv
+  IF (SELECT NOT BOOL_AND(checkPrivilege('PostFrozenPeriod')) AND
+             BOOL_AND(COALESCE(period_freeze, FALSE))
+      FROM accnt LEFT OUTER JOIN
+           period ON (pDistDate BETWEEN period_start AND period_end)
+      WHERE (accnt_id IN (_creditid, _debitid))) THEN
+    RAISE EXCEPTION 'Cannot post to frozen period (%).', pDistDate;
+    RETURN -4;  -- remove raise exception when all callers check return code
+  END IF;
+
+-- refuse to accept postings into nonexistent periods
+  IF NOT EXISTS(SELECT period_id
+                FROM period
+                WHERE (pDistDate BETWEEN period_start AND period_end)) THEN
+    RAISE EXCEPTION 'Cannot post to nonexistent period (%).', pDistDate;
+  END IF;
+
+--  Grab a sequence for the pair
+  SELECT fetchGLSequence() INTO _sequence;
+
+  IF (NOT pOnlyGL AND fetchMetricBool('UseJournals')) THEN
+  --  First the credit 
+    INSERT INTO sltrans
+    ( sltrans_journalnumber, sltrans_posted, sltrans_created, sltrans_date,
+      sltrans_sequence, sltrans_accnt_id, sltrans_source,
+      sltrans_doctype, sltrans_docnumber, sltrans_notes,
+      sltrans_misc_id, sltrans_amount )
+    VALUES
+    ( pJournalNumber, FALSE, CURRENT_TIMESTAMP, pDistDate,
+      _sequence, _creditid, pSource,
+      pDocType, pDocNumber, pNotes,
+      pMiscid, pAmount );
+
+  --  Now the debit
+    INSERT INTO sltrans
+    ( sltrans_journalnumber, sltrans_posted, sltrans_created, sltrans_date,
+      sltrans_sequence, sltrans_accnt_id, sltrans_source,
+      sltrans_doctype, sltrans_docnumber, sltrans_notes,
+      sltrans_misc_id, sltrans_amount )
+    VALUES
+    ( pJournalNumber, FALSE, CURRENT_TIMESTAMP, pDistDate,
+      _sequence, _debitid, pSource,
+      pDocType, pDocNumber, pNotes,
+      pMiscid, (pAmount * -1) );
+  ELSE
+  --  First the credit
+    INSERT INTO gltrans
+    ( gltrans_journalnumber, gltrans_posted, gltrans_exported, gltrans_created, gltrans_date,
+      gltrans_sequence, gltrans_accnt_id, gltrans_source,
+      gltrans_doctype, gltrans_docnumber, gltrans_notes,
+      gltrans_misc_id, gltrans_amount )
+    VALUES
+    ( pJournalNumber, FALSE, FALSE, CURRENT_TIMESTAMP, pDistDate,
+      _sequence, _creditid, pSource,
+      pDocType, pDocNumber, pNotes,
+      pMiscid, pAmount );
+
+  --  Now the debit
+    INSERT INTO gltrans
+    ( gltrans_journalnumber, gltrans_posted, gltrans_exported, gltrans_created, gltrans_date,
+      gltrans_sequence, gltrans_accnt_id, gltrans_source,
+      gltrans_doctype, gltrans_docnumber, gltrans_notes,
+      gltrans_misc_id, gltrans_amount )
+    VALUES
+    ( pJournalNumber, FALSE, FALSE, CURRENT_TIMESTAMP, pDistDate,
+      _sequence, _debitid, pSource,
+      pDocType, pDocNumber, pNotes,
+      pMiscid, (pAmount * -1) );
+
+    IF (pPostTrialBal) THEN
+      PERFORM postIntoTrialBalance(_sequence);
+    END IF;
+  END IF;
+
+  RETURN _sequence;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/insertitemcost.sql b/foundation-database/public/functions/insertitemcost.sql
new file mode 100644 (file)
index 0000000..735763c
--- /dev/null
@@ -0,0 +1,78 @@
+CREATE OR REPLACE FUNCTION insertItemCost(INTEGER, INTEGER, INTEGER, NUMERIC, BOOLEAN) RETURNS INTEGER AS $BODY$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+pItemId ALIAS FOR $1;
+pCostElemId ALIAS FOR $2;
+pCurrId ALIAS FOR $3;
+pCost ALIAS FOR $4;
+pPostToStandard ALIAS FOR $5;
+_itemcost_id INTEGER;
+_update_return INTEGER;
+_postcost_return BOOLEAN;
+
+--This function is used with the api.itemcost View for updating and inserting
+--into the itemcosts table
+
+BEGIN
+  IF (pCost IS NULL OR pCost < 0) THEN
+    RAISE EXCEPTION 'itemcost Actual Cost Invalid ', pCost;
+  END IF;
+
+-- Check for uniqueness
+  SELECT itemcost_id INTO _itemcost_id
+  FROM itemcost
+  WHERE ( (itemcost_item_id = pItemId)
+    AND   (itemcost_costelem_id = pCostElemId)
+    AND   (NOT itemcost_lowlevel) );
+  IF (FOUND) THEN
+    RAISE EXCEPTION 'itemcost already exists for this Item and Cost Element';
+  END IF;
+
+-- Check for valid combination of item_type and costelem_type
+  IF (SELECT (COUNT(*) > 0)
+      FROM item, costelem
+      WHERE (item_id=pItemId)
+        AND (costelem_id=pCostElemId)
+        AND (item_type IN ('M', 'F', 'B', 'C', 'T'))
+        AND (costelem_type IN ('Material'))) THEN
+    RAISE EXCEPTION 'itemcost of this type is invalid for Manufactured Item';
+  END IF;
+  
+  IF (SELECT (COUNT(*) > 0)
+      FROM item, costelem
+      WHERE (item_id=pItemId)
+        AND (costelem_id=pCostElemId)
+        AND (item_type IN ('O', 'P'))
+        AND (costelem_type IN ('Direct Labor', 'Overhead', 'Machine Overhead'))) THEN
+    RAISE EXCEPTION 'itemcost of this type is invalid for Purchased Item';
+  END IF;
+  
+  IF (pCost > 0) THEN
+    SELECT NEXTVAL('itemcost_itemcost_id_seq') INTO _itemcost_id;
+    INSERT INTO itemcost
+      ( itemcost_id, itemcost_item_id, itemcost_costelem_id, itemcost_lowlevel,
+        itemcost_stdcost, itemcost_posted, itemcost_actcost, itemcost_updated, itemcost_curr_id )
+    VALUES
+      ( _itemcost_id, pItemId, pCostElemId, FALSE,
+        0, startOfTime(), pCost, CURRENT_DATE, pCurrId );
+
+    --Only Post Cost to standard if the parameter is set to true
+    IF (pPostToStandard) THEN
+      IF (NOT checkPrivilege('PostStandardCosts')) THEN
+        RAISE EXCEPTION 'You do not have privileges to poststandard itemcosts. Set api.itemcost post_to_standard to false';
+      END IF;
+      SELECT postcost(_itemcost_id) INTO _postcost_return;       
+      IF (NOT _postcost_return) THEN
+        RETURN -2;
+      END IF;
+    END IF;
+  ELSE 
+    RETURN -1;
+  END IF;
+
+  RETURN _itemcost_id;
+  
+END;
+$BODY$
+  LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/intervaltominutes.sql b/foundation-database/public/functions/intervaltominutes.sql
new file mode 100644 (file)
index 0000000..8a20788
--- /dev/null
@@ -0,0 +1,10 @@
+
+CREATE OR REPLACE FUNCTION intervalToMinutes(INTERVAL) RETURNS NUMERIC AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple.
+-- See www.xtuple.com/CPAL for the full text of the software license.
+  SELECT (EXTRACT(DAY FROM $1) * 24 * 60 +
+          EXTRACT(HOUR FROM $1) * 60 +
+          EXTRACT(MINUTE FROM $1) +
+          ROUND((EXTRACT(SECOND FROM $1) / 60)::NUMERIC, 4))::NUMERIC AS result
+$$ LANGUAGE 'sql';
+
diff --git a/foundation-database/public/functions/invadjustment.sql b/foundation-database/public/functions/invadjustment.sql
new file mode 100644 (file)
index 0000000..9914a3c
--- /dev/null
@@ -0,0 +1,54 @@
+CREATE OR REPLACE FUNCTION invAdjustment(INTEGER, NUMERIC, TEXT, TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN invAdjustment($1, $2, $3, $4, CURRENT_TIMESTAMP, NULL);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION invAdjustment(INTEGER, NUMERIC, TEXT, TEXT, TIMESTAMP WITH TIME ZONE) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN invAdjustment($1, $2, $3, $4, $5, NULL);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION invAdjustment(INTEGER, NUMERIC, TEXT, TEXT, TIMESTAMP WITH TIME ZONE, NUMERIC) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid     ALIAS FOR $1;
+  pQty            ALIAS FOR $2;
+  pDocumentNumber ALIAS FOR $3;
+  pComments       ALIAS FOR $4;
+  pGlDistTS       ALIAS FOR $5;
+  pCostValue      ALIAS FOR $6;
+  _invhistid      INTEGER;
+  _itemlocSeries  INTEGER;
+
+BEGIN
+
+--  Make sure the passed itemsite points to a real item
+  IF ( ( SELECT (item_type IN ('R', 'F') OR itemsite_costmethod = 'J')
+         FROM itemsite, item
+         WHERE ( (itemsite_item_id=item_id)
+          AND (itemsite_id=pItemsiteid) ) ) ) THEN
+    RETURN 0;
+  END IF;
+
+  SELECT NEXTVAL('itemloc_series_seq') INTO _itemlocSeries;
+  SELECT postInvTrans( itemsite_id, 'AD', pQty,
+                       'I/M', 'AD', pDocumentNumber, '',
+                       ('Miscellaneous Adjustment for item ' || item_number || E'\n' ||  pComments),
+                       costcat_asset_accnt_id, costcat_adjustment_accnt_id,
+                       _itemlocSeries, pGlDistTS, pCostValue) INTO _invhistid
+  FROM itemsite, item, costcat
+  WHERE ( (itemsite_item_id=item_id)
+   AND (itemsite_costcat_id=costcat_id)
+   AND (itemsite_id=pItemsiteid) );
+
+  RETURN _itemlocSeries;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/invexpense.sql b/foundation-database/public/functions/invexpense.sql
new file mode 100644 (file)
index 0000000..810c435
--- /dev/null
@@ -0,0 +1,46 @@
+CREATE OR REPLACE FUNCTION invExpense(pItemsiteid     INTEGER,
+                                      pQty            NUMERIC,
+                                      pExpcatid       INTEGER,
+                                      pDocumentNumber TEXT,
+                                      pComments       TEXT,
+                                      pGlDistTS       TIMESTAMP WITH TIME ZONE
+                                                      DEFAULT CURRENT_TIMESTAMP,
+                                      pPrjid          INTEGER DEFAULT NULL)
+  RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _invhistid INTEGER;
+  _itemlocSeries INTEGER;
+
+BEGIN
+
+  --  Make sure the passed itemsite points to a real item
+  IF ( ( SELECT (item_type IN ('R', 'F') OR itemsite_costmethod = 'J')
+         FROM itemsite, item
+         WHERE ( (itemsite_item_id=item_id)
+          AND (itemsite_id=pItemsiteid) ) ) ) THEN
+    RETURN 0;
+  END IF;
+
+  SELECT NEXTVAL('itemloc_series_seq') INTO _itemlocSeries;
+  SELECT postInvTrans( itemsite_id, 'EX', pQty,
+                       'I/M', 'EX', pDocumentNumber, '',
+                       CASE WHEN (pQty < 0) THEN ('Reverse Material Expense for item ' || item_number || E'\n' ||  pComments)
+                            ELSE  ('Material Expense for item ' || item_number || E'\n' ||  pComments)
+                       END,
+                       getPrjAccntId(pPrjid, expcat_exp_accnt_id), costcat_asset_accnt_id,
+                       _itemlocSeries, pGlDistTS) INTO _invhistid
+  FROM itemsite, item, costcat, expcat
+  WHERE ( (itemsite_item_id=item_id)
+   AND (itemsite_costcat_id=costcat_id)
+   AND (itemsite_id=pItemsiteid)
+   AND (expcat_id=pExpcatid) );
+
+  INSERT INTO invhistexpcat (invhistexpcat_invhist_id, invhistexpcat_expcat_id)
+                     VALUES (_invhistid,               pExpcatid);
+
+  RETURN _itemlocSeries;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/invhistsense.sql b/foundation-database/public/functions/invhistsense.sql
new file mode 100644 (file)
index 0000000..22637a2
--- /dev/null
@@ -0,0 +1,47 @@
+CREATE OR REPLACE FUNCTION invhistSense(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pInvhistId ALIAS FOR $1;
+  _count INTEGER;
+  _row RECORD;
+  _sense INTEGER;
+BEGIN
+
+  SELECT invhist_transtype, invhist_ordnumber, itemsite_warehous_id
+  INTO _row 
+  FROM invhist 
+    JOIN itemsite ON (itemsite_id=invhist_itemsite_id)
+  WHERE (invhist_id=pInvhistId);
+
+  GET DIAGNOSTICS _count = ROW_COUNT;
+  IF (_count = 0) THEN
+    RAISE EXCEPTION 'Record not found for invhist_id=%',pInvhistId;
+  END IF;
+  
+  -- increase inventory: AD RM RT RP RR RS RX RB TR
+  -- decrease inventory: IM IB IT SH SI EX RI
+  -- TS and TR are special: shipShipment and recallShipment should not change
+  -- QOH at the Transfer Order src whs (as this was done by issueToShipping)
+  -- but postReceipt should change QOH at the transit whs
+  IF (_row.invhist_transtype='TS') THEN
+       _sense := CASE WHEN (SELECT tohead_trns_warehous_id=_row.itemsite_warehous_id
+                                  FROM tohead
+                                  WHERE (tohead_number=_row.invhist_ordnumber)) THEN -1
+                ELSE 0
+                END;
+  ELSIF (_row.invhist_transtype='TR') THEN
+      _sense := CASE WHEN (SELECT tohead_src_warehous_id=_row.itemsite_warehous_id
+                                  FROM tohead
+                                  WHERE (tohead_number=_row.invhist_ordnumber)) THEN 0
+                ELSE 1
+                END;
+  ELSIF (_row.invhist_transtype IN ('IM', 'IB', 'IT', 'SH', 'SI', 'EX', 'RI')) THEN
+      _sense := -1;
+    ELSE
+      _sense := 1;
+    END IF;
+
+  RETURN _sense;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/invoicetotal.sql b/foundation-database/public/functions/invoicetotal.sql
new file mode 100644 (file)
index 0000000..00b03b9
--- /dev/null
@@ -0,0 +1,67 @@
+CREATE OR REPLACE FUNCTION invoiceTotal(INTEGER) RETURNS NUMERIC AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pInvoiceId ALIAS FOR $1;
+  _linesum   NUMERIC;
+  _linetax   NUMERIC;
+  _result    NUMERIC;
+  _allocated NUMERIC;
+  _posted    BOOLEAN;
+
+BEGIN
+
+  SELECT SUM(ROUND(COALESCE((invcitem_billed * invcitem_qty_invuomratio) *
+                             (invcitem_price / COALESCE(invcitem_price_invuomratio,1)), 0),2))
+                          INTO _linesum
+  FROM invcitem
+  WHERE (invcitem_invchead_id=pInvoiceId);
+
+  -- TODO: why sum on the result of select round(sum(), 2)?
+  SELECT SUM(tax) INTO _linetax
+    FROM (SELECT ROUND(SUM(COALESCE(taxdetail_tax, 0)),2) AS tax 
+            FROM tax 
+            JOIN calculateTaxDetailSummary('I', pInvoiceId, 'T') ON (taxdetail_tax_id=tax_id)
+         GROUP BY tax_id) AS data;
+
+  SELECT noNeg(invchead_freight + invchead_misc_amount + COALESCE(_linetax, 0) + COALESCE(_linesum, 0)),
+         invchead_posted INTO _result, _posted
+  FROM invchead
+  WHERE (invchead_id=pInvoiceId);
+
+  IF NOT FOUND THEN
+    RETURN 0;
+  END IF;
+
+  IF (_posted) THEN
+    SELECT COALESCE(SUM(currToCurr(arapply_curr_id, aropen_curr_id,
+                                   arapply_applied, aropen_docdate)),0) INTO _allocated
+     FROM arapply, aropen, invchead
+    WHERE ( (invchead_posted)
+      AND   (invchead_id=pInvoiceId)
+      AND   (aropen_docnumber=invchead_invcnumber)
+      AND   (aropen_doctype='I')
+      AND   (arapply_target_aropen_id=aropen_id)
+      AND   (arapply_reftype='S')
+      AND   (invchead_posted) ) ;
+  ELSE
+    SELECT COALESCE(SUM(CASE WHEN((aropen_amount - aropen_paid) >=
+                       currToCurr(aropenalloc_curr_id, aropen_curr_id,
+                          aropenalloc_amount, aropen_docdate))
+           THEN currToCurr(aropenalloc_curr_id, invchead_curr_id,
+                   aropenalloc_amount, aropen_docdate)
+           ELSE currToCurr(aropen_curr_id, invchead_curr_id,
+                   aropen_amount - aropen_paid, aropen_docdate)
+           END),0) INTO _allocated
+     FROM invchead LEFT OUTER JOIN cohead ON (cohead_number=invchead_ordernumber)
+                   JOIN aropenalloc ON ((aropenalloc_doctype='I' AND aropenalloc_doc_id=invchead_id) OR
+                                        (aropenalloc_doctype='S' AND aropenalloc_doc_id=cohead_id))
+                   JOIN aropen ON (aropen_id=aropenalloc_aropen_id AND (aropen_amount - aropen_paid) > 0.0)
+    WHERE ( (NOT invchead_posted)
+      AND   (invchead_id=pInvoiceId) );
+  END IF;
+  
+  RETURN _result - COALESCE(_allocated, 0);
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/invreceipt.sql b/foundation-database/public/functions/invreceipt.sql
new file mode 100644 (file)
index 0000000..3994b3f
--- /dev/null
@@ -0,0 +1,55 @@
+CREATE OR REPLACE FUNCTION invReceipt(INTEGER, NUMERIC, TEXT, TEXT, TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN invReceipt($1, $2, $3, $4, $5, CURRENT_TIMESTAMP, NULL);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION invReceipt(INTEGER, NUMERIC, TEXT, TEXT, TEXT, TIMESTAMP WITH TIME ZONE) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN invReceipt($1, $2, $3, $4, $5, $6, NULL);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION invReceipt(INTEGER, NUMERIC, TEXT, TEXT, TEXT, TIMESTAMP WITH TIME ZONE, NUMERIC) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  pQty ALIAS FOR $2;
+  pOrdernumber ALIAS FOR $3;
+  pDocumentNumber ALIAS FOR $4;
+  pComments ALIAS FOR $5;
+  pGlDistTS     ALIAS FOR $6;
+  pCostValue ALIAS FOR $7;
+  _invhistid INTEGER;
+  _itemlocSeries INTEGER;
+
+BEGIN
+
+--  Make sure the passed itemsite points to a real item
+  IF ( ( SELECT (item_type IN ('R', 'F') OR itemsite_costmethod = 'J')
+         FROM itemsite, item
+         WHERE ( (itemsite_item_id=item_id)
+          AND (itemsite_id=pItemsiteid) ) ) ) THEN
+    RETURN 0;
+  END IF;
+
+  SELECT NEXTVAL('itemloc_series_seq') INTO _itemlocSeries;
+  SELECT postInvTrans( itemsite_id, 'RX', pQty,
+                       'I/M', 'RX', pDocumentNumber, '',
+                       ('Miscellaneous Receipt for item ' || item_number || E'\n' ||  pComments),
+                       costcat_asset_accnt_id, costcat_liability_accnt_id,
+                       _itemlocSeries, pGlDistTS, pCostValue) INTO _invhistid
+  FROM itemsite, item, costcat
+  WHERE ( (itemsite_item_id=item_id)
+   AND (itemsite_costcat_id=costcat_id)
+   AND (itemsite_id=pItemsiteid) );
+
+  RETURN _itemlocSeries;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/invscrap.sql b/foundation-database/public/functions/invscrap.sql
new file mode 100644 (file)
index 0000000..0eee1fd
--- /dev/null
@@ -0,0 +1,72 @@
+CREATE OR REPLACE FUNCTION invScrap(INTEGER, NUMERIC, TEXT, TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN invScrap($1, $2, $3, $4, CURRENT_TIMESTAMP);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION invScrap(INTEGER, NUMERIC, TEXT, TEXT, TIMESTAMP WITH TIME ZONE) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN invScrap($1, $2, $3, $4, $5, NULL);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION invScrap(INTEGER, NUMERIC, TEXT, TEXT, TIMESTAMP WITH TIME ZONE, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN invScrap($1, $2, $3, $4, $5, $6, NULL);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION invScrap(INTEGER, NUMERIC, TEXT, TEXT, TIMESTAMP WITH TIME ZONE, INTEGER, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  pQty ALIAS FOR $2;
+  pDocumentNumber ALIAS FOR $3;
+  pComments ALIAS FOR $4;
+  pGlDistTS ALIAS FOR $5;
+  pInvHistId ALIAS FOR $6;
+  pPrjid ALIAS FOR $7;
+  _invhistid INTEGER;
+  _itemlocSeries INTEGER;
+
+BEGIN
+
+--  Make sure the passed itemsite points to a real item
+  IF ( ( SELECT (item_type IN ('R', 'F') OR itemsite_costmethod = 'J')
+         FROM itemsite, item
+         WHERE ( (itemsite_item_id=item_id)
+          AND (itemsite_id=pItemsiteid) ) ) ) THEN
+    RETURN 0;
+  END IF;
+
+  IF (pInvHistId IS NOT NULL) THEN
+    SELECT invhist_series INTO _itemlocSeries
+    FROM invhist
+    WHERE invhist_id=pInvHistId;
+  ELSE
+    SELECT NEXTVAL('itemloc_series_seq') INTO _itemlocSeries;
+  END IF;
+  
+  SELECT postInvTrans( itemsite_id, 'SI', pQty,
+                       'I/M', 'SI', pDocumentNumber, '',
+                       CASE WHEN (pQty < 0) THEN ('Reverse Material Scrap for item ' || item_number || E'\n' ||  pComments)
+                            ELSE ('Material Scrap for item ' || item_number || E'\n' ||  pComments)
+                       END,
+                       getPrjAccntId(pPrjid, costcat_scrap_accnt_id), costcat_asset_accnt_id,
+                       _itemlocSeries, pGlDistTS, NULL, pInvHistId) INTO _invhistid
+  FROM itemsite, item, costcat
+  WHERE ( (itemsite_item_id=item_id)
+   AND (itemsite_costcat_id=costcat_id)
+   AND (itemsite_id=pItemsiteid) );
+
+  RETURN _itemlocSeries;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/isdba.sql b/foundation-database/public/functions/isdba.sql
new file mode 100644 (file)
index 0000000..cad0cb6
--- /dev/null
@@ -0,0 +1,6 @@
+CREATE OR REPLACE FUNCTION isDBA(TEXT DEFAULT NULL) RETURNS BOOLEAN AS $$
+  SELECT (datdba=pg_roles.oid OR rolsuper) AS issuper
+    FROM pg_database, pg_roles
+  WHERE ((datname=current_database())
+     AND (rolname=COALESCE($1, getEffectiveXtUser())));
+$$ LANGUAGE SQL;
diff --git a/foundation-database/public/functions/ismulticurr.sql b/foundation-database/public/functions/ismulticurr.sql
new file mode 100644 (file)
index 0000000..b515a69
--- /dev/null
@@ -0,0 +1,10 @@
+
+CREATE OR REPLACE FUNCTION isMultiCurr() RETURNS BOOL IMMUTABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN (SELECT (count(*) > 1)
+          FROM curr_symbol);
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/isnumeric.sql b/foundation-database/public/functions/isnumeric.sql
new file mode 100644 (file)
index 0000000..26173e4
--- /dev/null
@@ -0,0 +1,26 @@
+
+CREATE OR REPLACE FUNCTION isNumeric(TEXT) RETURNS BOOLEAN IMMUTABLE AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pText ALIAS FOR $1;
+  _cursor INTEGER;
+
+BEGIN
+
+  IF ( (LENGTH(pText) = 0) OR (pText IS NULL) ) THEN
+    RETURN FALSE;
+  END IF;
+
+  FOR _cursor IN 1..LENGTH(pText) LOOP
+    IF (SUBSTRING(pText FROM _cursor FOR 1) NOT IN ( ''0'', ''1'', ''2'', ''3'', ''4'',
+                                                     ''5'' ,''6'' ,''7'' ,''8'' ,''9'' )) THEN
+      RETURN FALSE;
+    END IF;
+  END LOOP;
+
+  RETURN TRUE;
+
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/issueallbalancetoshipping.sql b/foundation-database/public/functions/issueallbalancetoshipping.sql
new file mode 100644 (file)
index 0000000..28bb05a
--- /dev/null
@@ -0,0 +1,70 @@
+CREATE OR REPLACE FUNCTION issueAllBalanceToShipping(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN issueAllBalanceToShipping('SO', $1, 0, NULL);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION issueAllBalanceToShipping(TEXT, INTEGER, INTEGER, TIMESTAMP WITH TIME ZONE) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pordertype           ALIAS FOR $1;
+  pheadid              ALIAS FOR $2;
+  _itemlocSeries       INTEGER                  := $3;
+  _timestamp           TIMESTAMP WITH TIME ZONE := $4;
+  _s                   RECORD;
+
+BEGIN
+  IF (pordertype = 'SO') THEN
+    FOR _s IN SELECT coitem_id,
+                    noNeg(coitem_qtyord - coitem_qtyshipped + coitem_qtyreturned -
+                          ( SELECT COALESCE(SUM(shipitem_qty), 0)
+                            FROM shipitem, shiphead
+                            WHERE ( (shipitem_orderitem_id=coitem_id)
+                             AND (shipitem_shiphead_id=shiphead_id)
+                             AND (NOT shiphead_shipped)
+                             AND (shiphead_order_type=pordertype) ) ) ) AS balance
+             FROM coitem LEFT OUTER JOIN (itemsite JOIN item ON (itemsite_item_id=item_id)) ON (coitem_itemsite_id=itemsite_id)
+             WHERE ( (coitem_status NOT IN ('C','X'))
+                AND (item_type != 'K')
+              AND (coitem_cohead_id=pheadid) ) LOOP
+
+      IF (_s.balance <> 0) THEN
+       _itemlocSeries := issueToShipping(pordertype, _s.coitem_id, _s.balance, _itemlocSeries, _timestamp);
+       IF (_itemlocSeries < 0) THEN
+         EXIT;
+       END IF;
+      END IF;
+    END LOOP;
+
+  ELSEIF (pordertype = 'TO') THEN
+    FOR _s IN SELECT toitem_id,
+                    noNeg( toitem_qty_ordered - toitem_qty_shipped -
+                          ( SELECT COALESCE(SUM(shipitem_qty), 0)
+                            FROM shipitem, shiphead
+                            WHERE ( (shipitem_orderitem_id=toitem_id)
+                             AND (shipitem_shiphead_id=shiphead_id)
+                             AND (NOT shiphead_shipped)
+                             AND (shiphead_order_type=pordertype) ) ) ) AS balance
+             FROM toitem
+             WHERE ( (toitem_status NOT IN ('C','X'))
+              AND (toitem_tohead_id=pheadid) ) LOOP
+
+      IF (_s.balance <> 0) THEN
+       _itemlocSeries := issueToShipping(pordertype, _s.toitem_id, _s.balance, _itemlocSeries, _timestamp);
+       IF (_itemlocSeries < 0) THEN
+         EXIT;
+       END IF;
+      END IF;
+    END LOOP;
+
+  ELSE
+    RETURN -1;
+  END IF;
+
+  RETURN _itemlocSeries;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/issuelinebalancetoshipping.sql b/foundation-database/public/functions/issuelinebalancetoshipping.sql
new file mode 100644 (file)
index 0000000..9ef5420
--- /dev/null
@@ -0,0 +1,61 @@
+CREATE OR REPLACE FUNCTION issueLineBalanceToShipping(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN issueLineBalanceToShipping('SO', $1, NULL);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION issueLineBalanceToShipping(TEXT, INTEGER, TIMESTAMP WITH TIME ZONE) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN issueLineBalanceToShipping($1, $2, $3, 0, NULL);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION issueLineBalanceToShipping(TEXT, INTEGER, TIMESTAMP WITH TIME ZONE, INTEGER, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pordertype           ALIAS FOR $1;
+  pitemid              ALIAS FOR $2;
+  ptimestamp           ALIAS FOR $3;
+  pitemlocseries               ALIAS FOR $4;
+  pinvhistid           ALIAS FOR $5;
+  _itemlocSeries       INTEGER := 0;
+  _qty                 NUMERIC;
+
+BEGIN
+  _itemlocSeries := COALESCE(pitemlocseries,0);
+  
+  IF (pordertype = 'SO') THEN
+    SELECT noNeg( coitem_qtyord - coitem_qtyshipped + coitem_qtyreturned - 
+                ( SELECT COALESCE(SUM(shipitem_qty), 0)
+                  FROM shipitem, shiphead
+                  WHERE ((shipitem_orderitem_id=coitem_id)
+                    AND  (shipitem_shiphead_id=shiphead_id)
+                    AND  (NOT shiphead_shipped) ) ) ) INTO _qty
+    FROM coitem
+    WHERE (coitem_id=pitemid);
+  ELSEIF (pordertype = 'TO') THEN
+    SELECT noNeg( toitem_qty_ordered - toitem_qty_shipped - 
+                ( SELECT COALESCE(SUM(shipitem_qty), 0)
+                  FROM shipitem, shiphead
+                  WHERE ( (shipitem_orderitem_id=toitem_id)
+                   AND (shipitem_shiphead_id=shiphead_id)
+                   AND (NOT shiphead_shipped) ) ) ) INTO _qty
+    FROM toitem
+    WHERE (toitem_id=pitemid);
+  ELSE
+    RETURN -1;
+  END IF;
+
+  IF (_qty > 0) THEN
+    _itemlocSeries := issueToShipping(pordertype, pitemid, _qty, _itemlocSeries, ptimestamp, pinvhistid);
+  END IF;
+
+  RETURN _itemlocSeries;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/issues.sql b/foundation-database/public/functions/issues.sql
new file mode 100644 (file)
index 0000000..ec92436
--- /dev/null
@@ -0,0 +1,15 @@
+CREATE OR REPLACE FUNCTION issues(TEXT) RETURNS BOOLEAN AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pTransType ALIAS FOR $1;
+
+BEGIN
+  IF (pTransType IN (''IM'', ''IB'', ''IT'')) THEN
+    RETURN TRUE;
+  ELSE
+    RETURN FALSE;
+  END IF;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/issuetoshipping.sql b/foundation-database/public/functions/issuetoshipping.sql
new file mode 100644 (file)
index 0000000..3eea2a4
--- /dev/null
@@ -0,0 +1,294 @@
+CREATE OR REPLACE FUNCTION issueToShipping(INTEGER, NUMERIC) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN issueToShipping('SO', $1, $2, 0, CURRENT_TIMESTAMP);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION issueToShipping(INTEGER, NUMERIC, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN issueToShipping('SO', $1, $2, $3, CURRENT_TIMESTAMP);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION issueToShipping(TEXT, INTEGER, NUMERIC, INTEGER, TIMESTAMP WITH TIME ZONE) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN issueToShipping($1, $2, $3, $4, $5, NULL);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION issueToShipping(TEXT, INTEGER, NUMERIC, INTEGER, TIMESTAMP WITH TIME ZONE, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pordertype           ALIAS FOR $1;
+  pitemid              ALIAS FOR $2;
+  pQty                 ALIAS FOR $3;
+  _itemlocSeries       INTEGER := $4;
+  _timestamp           TIMESTAMP WITH TIME ZONE := $5;
+  pinvhistid           ALIAS FOR $6;
+  _coholdtype          TEXT;
+  _invhistid           INTEGER;
+  _shipheadid          INTEGER;
+  _shipnumber          INTEGER;
+  _cntctid              INTEGER;
+  _p                    RECORD;
+  _m                    RECORD;
+  _value                NUMERIC;
+  _warehouseid         INTEGER;
+  _shipitemid          INTEGER;
+  _freight              NUMERIC;
+
+BEGIN
+
+  IF (_timestamp IS NULL) THEN
+    _timestamp := CURRENT_TIMESTAMP;
+  END IF;
+
+  IF (_itemlocSeries = 0) THEN
+    _itemlocSeries := NEXTVAL('itemloc_series_seq');
+  END IF;
+
+  IF (pordertype = 'SO') THEN
+
+    -- Check site security
+    SELECT warehous_id INTO _warehouseid
+    FROM coitem,itemsite,site()
+    WHERE ((coitem_id=pitemid)
+    AND (itemsite_id=coitem_itemsite_id)
+    AND (warehous_id=itemsite_warehous_id));
+          
+    IF (NOT FOUND) THEN
+      RETURN 0;
+    END IF;
+
+    -- Check for average cost items going negative
+    IF ( SELECT ( (itemsite_costmethod='A') AND
+                  ((itemsite_qtyonhand - round(pQty * coitem_qty_invuomratio, 6)) < 0.0) )
+         FROM coitem JOIN itemsite ON (itemsite_id=coitem_itemsite_id)
+         WHERE (coitem_id=pitemid) ) THEN
+      RETURN -20;
+    END IF;
+
+    -- Check auto registration
+    IF ( SELECT COALESCE(itemsite_autoreg, FALSE)
+         FROM coitem JOIN itemsite ON (itemsite_id=coitem_itemsite_id)
+         WHERE (coitem_id=pitemid) ) THEN
+      SELECT COALESCE(crmacct_cntct_id_1, -1) INTO _cntctid
+      FROM coitem JOIN cohead ON (cohead_id=coitem_cohead_id)
+                  JOIN crmacct ON (crmacct_cust_id=cohead_cust_id)
+      WHERE (coitem_id=pitemid);
+      IF (_cntctid = -1) THEN
+        RETURN -15;
+      END IF;
+    END IF; 
+  
+    SELECT shiphead_id INTO _shipheadid
+    FROM shiphead, coitem
+    WHERE ((shiphead_order_id=coitem_cohead_id)
+      AND  (NOT shiphead_shipped)
+      AND  (coitem_id=pitemid)
+      AND  (shiphead_order_type=pordertype));
+    IF (NOT FOUND) THEN
+      SELECT NEXTVAL('shiphead_shiphead_id_seq') INTO _shipheadid;
+
+      _shipnumber := fetchShipmentNumber();
+      IF (_shipnumber < 0) THEN
+       RETURN -10;
+      END IF;
+
+      SELECT cohead_holdtype INTO _coholdtype
+      FROM cohead, coitem
+      WHERE ((cohead_id=coitem_cohead_id)
+        AND  (coitem_id=pitemid));
+
+      IF (_coholdtype = 'C') THEN
+       RETURN -12;
+      ELSIF (_coholdtype = 'P') THEN
+       RETURN -13;
+      ELSIF (_coholdtype = 'R') THEN
+       RETURN -14;
+      END IF;
+
+      INSERT INTO shiphead
+      ( shiphead_id, shiphead_number, shiphead_order_id, shiphead_order_type,
+       shiphead_shipped,
+       shiphead_sfstatus, shiphead_shipvia, shiphead_shipchrg_id,
+       shiphead_freight, shiphead_freight_curr_id,
+       shiphead_shipdate, shiphead_notes, shiphead_shipform_id )
+      SELECT _shipheadid, _shipnumber, coitem_cohead_id, pordertype,
+            FALSE,
+            'N', cohead_shipvia,
+            CASE WHEN (cohead_shipchrg_id <= 0) THEN NULL
+                 ELSE cohead_shipchrg_id
+            END,
+            cohead_freight, cohead_curr_id,
+            _timestamp::DATE, cohead_shipcomments,
+            CASE WHEN cohead_shipform_id = -1 THEN NULL
+                 ELSE cohead_shipform_id
+            END
+      FROM cohead, coitem
+      WHERE ((coitem_cohead_id=cohead_id)
+         AND (coitem_id=pitemid) );
+
+      UPDATE pack
+      SET pack_shiphead_id = _shipheadid,
+         pack_printed = FALSE
+      FROM coitem
+      WHERE ((pack_head_id=coitem_cohead_id)
+       AND  (pack_shiphead_id IS NULL)
+       AND  (pack_head_type='SO')
+       AND  (coitem_id=pitemid));
+
+    ELSE
+      UPDATE pack
+      SET pack_printed = FALSE
+      FROM coitem
+      WHERE ((pack_head_id=coitem_cohead_id)
+       AND  (pack_shiphead_id=_shipheadid)
+       AND  (pack_head_type='SO')
+       AND  (coitem_id=pitemid));
+    END IF;
+
+    -- Handle g/l transaction
+    SELECT postInvTrans( itemsite_id, 'SH', (pQty * coitem_qty_invuomratio),
+                          'S/R', porderType,
+                          formatSoNumber(coitem_id), shiphead_number,
+                           ('Issue ' || item_number || ' to Shipping for customer ' || cohead_billtoname),
+                          getPrjAccntId(cohead_prj_id, costcat_shipasset_accnt_id), costcat_asset_accnt_id,
+                          _itemlocSeries, _timestamp, NULL, pinvhistid ) INTO _invhistid
+    FROM coitem, cohead, itemsite, item, costcat, shiphead
+    WHERE ( (coitem_cohead_id=cohead_id)
+     AND (coitem_itemsite_id=itemsite_id)
+     AND (itemsite_item_id=item_id)
+     AND (itemsite_costcat_id=costcat_id)
+     AND (coitem_id=pitemid)
+     AND (shiphead_id=_shipheadid) );
+
+    SELECT (invhist_unitcost * invhist_invqty) INTO _value
+    FROM invhist
+    WHERE (invhist_id=_invhistid);
+
+    _shipitemid := nextval('shipitem_shipitem_id_seq');
+    INSERT INTO shipitem
+    ( shipitem_id, shipitem_shiphead_id, shipitem_orderitem_id, shipitem_qty,
+      shipitem_transdate, shipitem_trans_username, shipitem_invoiced,
+      shipitem_value, shipitem_invhist_id )
+    VALUES
+    ( _shipitemid, _shipheadid, pitemid, pQty,
+      _timestamp, getEffectiveXtUser(), FALSE,
+      _value, 
+      CASE WHEN _invhistid = -1 THEN
+        NULL
+      ELSE 
+        _invhistid
+      END );
+
+    -- Handle reservation
+    IF (fetchmetricbool('EnableSOReservations')) THEN
+      -- Remember what was reserved so we can re-reserve if this issue is returned
+      INSERT INTO shipitemrsrv 
+        (shipitemrsrv_shipitem_id, shipitemrsrv_qty)
+      SELECT _shipitemid, least(pQty,coitem_qtyreserved)
+      FROM coitem
+      WHERE ((coitem_id=pitemid)
+      AND (coitem_qtyreserved > 0));
+
+      -- Update sales order
+      UPDATE coitem
+        SET coitem_qtyreserved = noNeg(coitem_qtyreserved - pQty)
+      WHERE(coitem_id=pitemid);
+    END IF;
+
+    -- Calculate shipment freight
+    SELECT calcShipFreight(_shipheadid) INTO _freight;
+    UPDATE shiphead SET shiphead_freight=_freight
+    WHERE (shiphead_id=_shipheadid);
+
+  ELSEIF (pordertype = 'TO') THEN
+
+    -- Check site security
+    IF (fetchMetricBool('MultiWhs')) THEN
+      SELECT warehous_id INTO _warehouseid
+      FROM toitem, tohead, site()
+      WHERE ( (toitem_id=pitemid)
+        AND   (tohead_id=toitem_tohead_id)
+        AND   (warehous_id=tohead_src_warehous_id) );
+          
+      IF (NOT FOUND) THEN
+        RETURN 0;
+      END IF;
+    END IF;
+
+    SELECT postInvTrans( itemsite_id, 'SH', pQty, 'S/R',
+                        pordertype, formatToNumber(toitem_id), '', 'Issue to Shipping',
+                        costcat_shipasset_accnt_id, costcat_asset_accnt_id,
+                        _itemlocSeries, _timestamp) INTO _invhistid
+    FROM tohead, toitem, itemsite, costcat
+    WHERE ((tohead_id=toitem_tohead_id)
+      AND  (itemsite_item_id=toitem_item_id)
+      AND  (itemsite_warehous_id=tohead_src_warehous_id)
+      AND  (itemsite_costcat_id=costcat_id)
+      AND  (toitem_id=pitemid) );
+
+    SELECT shiphead_id INTO _shipheadid
+    FROM shiphead, toitem
+    WHERE ((shiphead_order_id=toitem_tohead_id)
+      AND  (NOT shiphead_shipped)
+      AND  (toitem_id=pitemid)
+      AND  (shiphead_order_type=pordertype));
+
+    IF (NOT FOUND) THEN
+      _shipheadid := NEXTVAL('shiphead_shiphead_id_seq');
+
+      _shipnumber := fetchShipmentNumber();
+      IF (_shipnumber < 0) THEN
+       RETURN -10;
+      END IF;
+
+      INSERT INTO shiphead
+      ( shiphead_id, shiphead_number, shiphead_order_id, shiphead_order_type,
+       shiphead_shipped,
+       shiphead_sfstatus, shiphead_shipvia, shiphead_shipchrg_id,
+       shiphead_freight, shiphead_freight_curr_id,
+       shiphead_shipdate, shiphead_notes, shiphead_shipform_id )
+      SELECT _shipheadid, _shipnumber, tohead_id, pordertype,
+            FALSE,
+            'N', tohead_shipvia, tohead_shipchrg_id,
+            tohead_freight + SUM(toitem_freight), tohead_freight_curr_id,
+            _timestamp::DATE, tohead_shipcomments, tohead_shipform_id
+      FROM tohead, toitem
+      WHERE ((toitem_tohead_id=tohead_id)
+         AND (tohead_id IN (SELECT toitem_tohead_id
+                           FROM toitem
+                           WHERE (toitem_id=pitemid))) )
+      GROUP BY tohead_id, tohead_shipvia, tohead_shipchrg_id, tohead_freight,
+              tohead_freight_curr_id, tohead_shipcomments, tohead_shipform_id;
+    END IF;
+
+    INSERT INTO shipitem
+    ( shipitem_shiphead_id, shipitem_orderitem_id, shipitem_qty,
+      shipitem_transdate, shipitem_trans_username, shipitem_value,
+      shipitem_invhist_id )
+    SELECT
+      _shipheadid, pitemid, pQty,
+      _timestamp, getEffectiveXtUser(), invhist_invqty * invhist_unitcost,
+      _invhistid
+    FROM toitem, item, invhist
+    WHERE ((toitem_id=pitemid)
+    AND (item_id=toitem_item_id)
+    AND (invhist_id=_invhistid));
+
+  ELSE
+    RETURN -11;
+  END IF;
+
+  RETURN _itemlocSeries;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/issuewomaterial.sql b/foundation-database/public/functions/issuewomaterial.sql
new file mode 100644 (file)
index 0000000..1b6ad0c
--- /dev/null
@@ -0,0 +1,210 @@
+CREATE OR REPLACE FUNCTION issueWoMaterial(INTEGER, NUMERIC, INTEGER, BOOLEAN) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWomatlid ALIAS FOR $1;
+  pQty ALIAS FOR $2;
+  pItemlocSeries ALIAS FOR $3;
+  pMarkPush ALIAS FOR $4;
+  _itemlocSeries INTEGER;
+
+BEGIN
+  RETURN issueWoMaterial(pWomatlid, pQty, pItemlocSeries,now());
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION issueWoMaterial(INTEGER, NUMERIC, INTEGER, BOOLEAN, TIMESTAMP WITH TIME ZONE) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWomatlid ALIAS FOR $1;
+  pQty ALIAS FOR $2;
+  pItemlocSeries ALIAS FOR $3;
+  pMarkPush ALIAS FOR $4;
+  pGlDistTS ALIAS FOR $5;
+  _itemlocSeries INTEGER;
+
+BEGIN
+
+  SELECT issueWoMaterial(pWomatlid, pQty, pItemlocSeries, pGlDistTS) INTO _itemlocSeries;
+
+  IF (pMarkPush) THEN
+    UPDATE womatl
+    SET womatl_issuemethod='S'
+    WHERE ((womatl_issuemethod='M')
+     AND (womatl_id=pWomatlid));
+  END IF;
+
+  RETURN _itemlocSeries;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION issueWoMaterial(INTEGER, NUMERIC) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWomatlid ALIAS FOR $1;
+  pQty ALIAS FOR $2;
+  _itemlocSeries INTEGER;
+
+BEGIN
+
+  SELECT NEXTVAL('itemloc_series_seq') INTO _itemlocSeries;
+  RETURN issueWoMaterial(pWomatlid, pQty, _itemlocSeries);
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION issueWoMaterial(INTEGER, NUMERIC, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWomatlid ALIAS FOR $1;
+  pQty ALIAS FOR $2;
+  pItemlocSeries ALIAS FOR $3;
+  _p RECORD;
+  _invhistid INTEGER;
+  _itemlocSeries INTEGER;
+
+BEGIN
+  RETURN issueWoMaterial(pWomatlid, pQty, pItemlocSeries, now());
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION issueWoMaterial(INTEGER, NUMERIC, INTEGER, TIMESTAMP WITH TIME ZONE) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN issueWoMaterial($1, $2, $3, $4, NULL);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION issueWoMaterial(INTEGER, NUMERIC, INTEGER, TIMESTAMP WITH TIME ZONE, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWomatlid ALIAS FOR $1;
+  pQty ALIAS FOR $2;
+  pItemlocSeries ALIAS FOR $3;
+  pGlDistTS ALIAS FOR $4;
+  pInvhistid ALIAS FOR $5;
+  _p RECORD;
+  _invhistid INTEGER;
+  _itemlocSeries INTEGER;
+
+BEGIN
+
+  SELECT item_id,
+         itemsite_id AS c_itemsite_id,
+         wo_itemsite_id AS p_itemsite_id,
+         itemsite_loccntrl, itemsite_controlmethod,
+         womatl_wo_id, womatl_qtyreq, itemsite_item_id, womatl_uom_id, wo_prj_id,
+         roundQty(item_fractional, itemuomtouom(itemsite_item_id, womatl_uom_id, NULL, pQty)) AS qty,
+         formatWoNumber(wo_id) AS woNumber,
+         CASE WHEN(itemsite_costmethod='J' AND item_type='P' AND poitem_id IS NOT NULL) THEN poitem_unitprice
+              WHEN(itemsite_costmethod IN ('A','J')) THEN avgcost(itemsite_id)
+              WHEN(itemsite_costmethod='S') THEN stdcost(itemsite_item_id)
+              ELSE 0.0
+         END AS cost,
+         womatl_issuemethod AS issueMethod INTO _p
+  FROM womatl JOIN wo ON (wo_id=womatl_wo_id)
+              JOIN itemsite ON (itemsite_id=womatl_itemsite_id)
+              JOIN item ON (item_id=itemsite_item_id)
+              LEFT OUTER JOIN poitem ON (poitem_order_id=womatl_id AND poitem_order_type='W')
+  WHERE (womatl_id=pWomatlid);
+
+  IF (pQty < 0) THEN
+    RETURN pItemlocSeries;
+  END IF;
+
+  IF (pItemlocSeries <> 0) THEN
+    _itemlocSeries := pItemlocSeries;
+  ELSE
+    SELECT NEXTVAL('itemloc_series_seq') INTO _itemlocSeries;
+  END IF;
+  SELECT postInvTrans( ci.itemsite_id, 'IM', _p.qty,
+                      'W/O', 'WO', _p.woNumber, '',
+                      ('Material ' || item_number || ' Issue to Work Order'),
+                      getPrjAccntId(_p.wo_prj_id, pc.costcat_wip_accnt_id),
+                      cc.costcat_asset_accnt_id, _itemlocSeries, pGlDistTS,
+                      NULL, pInvhistid ) INTO _invhistid
+  FROM itemsite AS ci, itemsite AS pi,
+       costcat AS cc, costcat AS pc,
+       item
+  WHERE ( (ci.itemsite_costcat_id=cc.costcat_id)
+   AND (pi.itemsite_costcat_id=pc.costcat_id)
+   AND (ci.itemsite_id=_p.c_itemsite_id)
+   AND (pi.itemsite_id=_p.p_itemsite_id)
+   AND (ci.itemsite_item_id=item_id) );
+
+--  Create linkage to the transaction created
+  IF (_invhistid != -1) THEN
+    INSERT INTO womatlpost (womatlpost_womatl_id,womatlpost_invhist_id)
+                VALUES (pWomatlid,_invhistid);
+  END IF;
+
+--  Increase the parent W/O's WIP value by the value of the issued components
+  UPDATE wo
+  SET wo_wipvalue = (wo_wipvalue + (_p.cost * _p.qty)),
+      wo_postedvalue = (wo_postedvalue + (_p.cost * _p.qty))
+  WHERE (wo_id=_p.womatl_wo_id);
+
+  UPDATE womatl
+  SET womatl_qtyiss = (womatl_qtyiss + itemuomtouom(_p.itemsite_item_id, NULL, _p.womatl_uom_id, _p.qty)),
+      womatl_lastissue = pGlDistTS::DATE
+  WHERE (womatl_id=pWomatlid);
+
+  UPDATE wo
+  SET wo_status='I'
+  WHERE ( (wo_status <> 'I')
+   AND (wo_id=_p.womatl_wo_id) );
+
+  RETURN _itemlocSeries;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION issueWoMaterial(INTEGER, NUMERIC, BOOLEAN) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWomatlid ALIAS FOR $1;
+  pQty ALIAS FOR $2;
+  pMarkPush ALIAS FOR $3;
+  _itemlocSeries INTEGER;
+
+BEGIN
+  RETURN issueWoMaterial(pWomatlid, pQty, pMarkPush, now());
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION issueWoMaterial(INTEGER, NUMERIC, BOOLEAN, TIMESTAMP WITH TIME ZONE) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWomatlid ALIAS FOR $1;
+  pQty ALIAS FOR $2;
+  pMarkPush ALIAS FOR $3;
+  pGlDistTS ALIAS FOR $4;
+  _itemlocSeries INTEGER;
+
+BEGIN
+
+  SELECT issueWoMaterial(pWomatlid, pQty, 0, pGlDistTS) INTO _itemlocSeries;
+  IF (_itemlocSeries < 0) THEN
+    RETURN _itemlocSeries;
+  END IF;
+
+  IF (pMarkPush) THEN
+    UPDATE womatl
+    SET womatl_issuemethod='S'
+    WHERE ((womatl_issuemethod='M')
+     AND (womatl_id=pWomatlid));
+  END IF;
+
+  RETURN _itemlocSeries;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/issuewomaterialbatch.sql b/foundation-database/public/functions/issuewomaterialbatch.sql
new file mode 100644 (file)
index 0000000..d6466d3
--- /dev/null
@@ -0,0 +1,35 @@
+CREATE OR REPLACE FUNCTION issueWoMaterialBatch(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWoid ALIAS FOR $1;
+  _itemlocSeries INTEGER;
+  _r RECORD;
+  _woNumber TEXT;
+
+BEGIN
+
+  SELECT NEXTVAL('itemloc_series_seq') INTO _itemlocSeries;
+
+  FOR _r IN SELECT womatl_id, 
+              CASE WHEN (womatl_qtyreq >= 0) THEN
+                roundQty(itemuomfractionalbyuom(item_id, womatl_uom_id), noNeg(womatl_qtyreq - womatl_qtyiss))
+              ELSE
+                roundQty(itemuomfractionalbyuom(item_id, womatl_uom_id), noNeg(womatl_qtyiss * -1)) 
+              END AS qty
+            FROM womatl, itemsite, item
+            WHERE ( (womatl_itemsite_id=itemsite_id)
+             AND (itemsite_item_id=item_id)
+             AND (womatl_issuemethod IN ('S', 'M'))
+             AND (womatl_wo_id=pWoid) ) LOOP
+
+    IF (_r.qty > 0) THEN
+      SELECT issueWoMaterial(_r.womatl_id, _r.qty, _itemlocSeries, TRUE) INTO _itemlocSeries;
+    END IF;
+
+  END LOOP;
+
+  RETURN _itemlocSeries;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/itemaltcapinvrat.sql b/foundation-database/public/functions/itemaltcapinvrat.sql
new file mode 100644 (file)
index 0000000..2fdd558
--- /dev/null
@@ -0,0 +1,10 @@
+CREATE OR REPLACE FUNCTION itemAltCapInvRat(INTEGER) RETURNS NUMERIC STABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemid ALIAS FOR $1;
+
+BEGIN
+  RETURN itemUOMRatioByType(pItemid, 'AltCapacity');
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/itemaltcapuom.sql b/foundation-database/public/functions/itemaltcapuom.sql
new file mode 100644 (file)
index 0000000..0174aff
--- /dev/null
@@ -0,0 +1,10 @@
+CREATE OR REPLACE FUNCTION itemAltCapUOM(INTEGER) RETURNS TEXT STABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemid ALIAS FOR $1;
+
+BEGIN
+  RETURN itemUOMByType(pItemid, 'AltCapacity');
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/itemcapinvrat.sql b/foundation-database/public/functions/itemcapinvrat.sql
new file mode 100644 (file)
index 0000000..0eff3e7
--- /dev/null
@@ -0,0 +1,10 @@
+CREATE OR REPLACE FUNCTION itemCapInvRat(INTEGER) RETURNS NUMERIC STABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemid ALIAS FOR $1;
+
+BEGIN
+  RETURN itemUOMRatioByType(pItemid, 'Capacity');
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/itemcapuom.sql b/foundation-database/public/functions/itemcapuom.sql
new file mode 100644 (file)
index 0000000..6279406
--- /dev/null
@@ -0,0 +1,10 @@
+CREATE OR REPLACE FUNCTION itemCapUOM(INTEGER) RETURNS TEXT STABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemid ALIAS FOR $1;
+
+BEGIN
+  RETURN itemUOMByType(pItemid, 'Capacity');
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/itemcharprice.sql b/foundation-database/public/functions/itemcharprice.sql
new file mode 100644 (file)
index 0000000..d40fe81
--- /dev/null
@@ -0,0 +1,304 @@
+CREATE OR REPLACE FUNCTION itemCharPrice(INTEGER, INTEGER, TEXT, INTEGER, NUMERIC) RETURNS NUMERIC AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemid ALIAS FOR $1;
+  pCharid ALIAS FOR $2;
+  pCharValue ALIAS FOR $3;
+  pCustid ALIAS FOR $4;
+  pQty ALIAS FOR $5;
+
+BEGIN
+  RETURN itemCharPrice(pItemid, pCharid, pCharValue, pCustid, -1, pQty, baseCurrId(), CURRENT_DATE);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION itemCharPrice(INTEGER, INTEGER, TEXT, INTEGER, INTEGER, NUMERIC) RETURNS NUMERIC AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemid ALIAS FOR $1;
+  pCharid ALIAS FOR $2;
+  pCharValue ALIAS FOR $3;
+  pCustid ALIAS FOR $4;
+  pShiptoid ALIAS FOR $5;
+  pQty ALIAS FOR $6;
+
+BEGIN
+  RETURN itemCharPrice(pItemid, pCharid, pCharValue, pCustid, pShiptoid, pQty, baseCurrId(), CURRENT_DATE);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION itemCharPrice(INTEGER, INTEGER, TEXT, INTEGER, INTEGER, NUMERIC, INTEGER) RETURNS NUMERIC AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemid ALIAS FOR $1;
+  pCharid ALIAS FOR $2;
+  pCharValue ALIAS FOR $3;
+  pCustid ALIAS FOR $4;
+  pShiptoid ALIAS FOR $5;
+  pQty ALIAS FOR $6;
+  pCurrid ALIAS FOR $7;
+
+BEGIN
+  RETURN itemCharPrice(pItemid, pCharid, pCharValue, pCustid, pShiptoid, pQty, pCurrid, CURRENT_DATE);
+END;
+$$ LANGUAGE plpgsql;
+
+CREATE OR REPLACE FUNCTION itemCharPrice(INTEGER, INTEGER, TEXT, INTEGER, INTEGER, NUMERIC, INTEGER, DATE) RETURNS NUMERIC AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemid ALIAS FOR $1;
+  pCharid ALIAS FOR $2;
+  pCharValue ALIAS FOR $3;
+  pCustid ALIAS FOR $4;
+  pShiptoid ALIAS FOR $5;
+  pQty ALIAS FOR $6;
+  pCurrid ALIAS FOR $7;
+  pEffective ALIAS FOR $8;
+
+BEGIN
+  RETURN itemCharPrice(pItemid, pCharid, pCharValue, pCustid, pShiptoid, pQty, pCurrid, CURRENT_DATE, CURRENT_DATE);
+END;
+$$ LANGUAGE plpgsql;
+
+CREATE OR REPLACE FUNCTION itemCharPrice(INTEGER, INTEGER, TEXT, INTEGER, INTEGER, NUMERIC, INTEGER, DATE, DATE) RETURNS NUMERIC AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemid ALIAS FOR $1;
+  pCharid ALIAS FOR $2;
+  pCharValue ALIAS FOR $3;
+  pCustid ALIAS FOR $4;
+  pShiptoid ALIAS FOR $5;
+  pQty ALIAS FOR $6;
+  pCurrid ALIAS FOR $7;
+  pEffective ALIAS FOR $8;
+  pAsOf ALIAS FOR $9;
+  _price NUMERIC;
+  _sales NUMERIC;
+  _item RECORD;
+  _iteminvpricerat NUMERIC;
+
+BEGIN
+-- If the charass_value passed in is NULL, we can skip this function
+  IF (pCharValue IS NULL) THEN 
+    RETURN 0;
+  END IF;
+
+-- Return the itemCharPrice in the currency passed in as pCurrid
+
+-- Get a value here so we do not have to call the function several times
+  SELECT iteminvpricerat(pItemid)
+    INTO _iteminvpricerat;
+
+-- First get a sales price if any so we when we find other prices
+-- we can determine if we want that price or this price.
+--  Check for a Sale Price
+  SELECT currToCurr(ipshead_curr_id, pCurrid,
+                      ipsprice_price - (ipsprice_price * cust_discntprcnt),
+                      pEffective) INTO _sales
+  FROM (
+  SELECT ipsitem_ipshead_id AS ipsprice_ipshead_id,
+         itemuomtouom(ipsitem_item_id, ipsitem_qty_uom_id, NULL, ipsitem_qtybreak) AS ipsprice_qtybreak,
+         (ipsitemchar_price * itemuomtouomratio(ipsitem_item_id, NULL, ipsitem_price_uom_id)) * _iteminvpricerat AS ipsprice_price
+    FROM ipsiteminfo,ipsitemchar
+   WHERE((ipsitem_item_id=pItemid)
+    AND (ipsitemchar_char_id=pCharid)
+    AND (ipsitemchar_value=pCharValue)
+    AND (ipsitemchar_ipsitem_id=ipsitem_id))
+       ) AS
+        ipsprice, ipshead, sale, custinfo
+  WHERE ( (ipsprice_ipshead_id=ipshead_id)
+   AND (sale_ipshead_id=ipshead_id)
+   AND (pAsOf BETWEEN sale_startdate AND sale_enddate)
+   AND (ipsprice_qtybreak <= pQty)
+   AND (cust_id=pCustid) )
+  ORDER BY ipsprice_qtybreak DESC, ipsprice_price ASC
+  LIMIT 1;
+
+--  Check for a Customer Shipto Price
+  SELECT currToCurr(ipshead_curr_id, pCurrid, ipsprice_price, pEffective) INTO _price
+  FROM (
+  SELECT ipsitem_ipshead_id AS ipsprice_ipshead_id,
+         itemuomtouom(ipsitem_item_id, ipsitem_qty_uom_id, NULL, ipsitem_qtybreak) AS ipsprice_qtybreak,
+         (ipsitemchar_price * itemuomtouomratio(ipsitem_item_id, NULL, ipsitem_price_uom_id)) * _iteminvpricerat AS ipsprice_price
+    FROM ipsiteminfo,ipsitemchar
+   WHERE ((ipsitem_item_id=pItemid)
+    AND (ipsitemchar_char_id=pCharid)
+    AND (ipsitemchar_value=pCharValue)
+    AND (ipsitemchar_ipsitem_id=ipsitem_id))
+       ) AS
+        ipsprice, ipshead, ipsass
+  WHERE ( (ipsprice_ipshead_id=ipshead_id)
+   AND (ipsass_ipshead_id=ipshead_id)
+   AND (pAsOf BETWEEN ipshead_effective AND (ipshead_expires - 1))
+   AND (ipsprice_qtybreak <= pQty)
+   AND (ipsass_shipto_id != -1)
+   AND (ipsass_shipto_id=pShiptoid) )
+  ORDER BY ipsprice_qtybreak DESC, ipsprice_price ASC
+  LIMIT 1;
+
+  IF (_price IS NOT NULL) THEN
+    IF ((_sales IS NOT NULL) AND (_sales < _price)) THEN
+      RETURN _sales;
+    END IF;
+    RETURN _price;
+  END IF;
+
+--  Check for a Customer Shipto Pattern Price
+  SELECT currToCurr(ipshead_curr_id, pCurrid, ipsprice_price, pEffective) INTO _price
+  FROM (
+  SELECT ipsitem_ipshead_id AS ipsprice_ipshead_id,
+         itemuomtouom(ipsitem_item_id, ipsitem_qty_uom_id, NULL, ipsitem_qtybreak) AS ipsprice_qtybreak,
+         (ipsitemchar_price * itemuomtouomratio(ipsitem_item_id, NULL, ipsitem_price_uom_id)) * _iteminvpricerat AS ipsprice_price
+    FROM ipsiteminfo,ipsitemchar
+   WHERE ((ipsitem_item_id=pItemid)
+    AND (ipsitemchar_char_id=pCharid)
+    AND (ipsitemchar_value=pCharValue)
+    AND (ipsitemchar_ipsitem_id=ipsitem_id))
+       ) AS
+        ipsprice, ipshead, ipsass, shiptoinfo
+  WHERE ( (ipsprice_ipshead_id=ipshead_id)
+   AND (ipsass_ipshead_id=ipshead_id)
+   AND (pAsOf BETWEEN ipshead_effective AND (ipshead_expires - 1))
+   AND (ipsprice_qtybreak <= pQty)
+   AND (COALESCE(length(ipsass_shipto_pattern), 0) > 0)
+   AND (shipto_num ~ ipsass_shipto_pattern)
+   AND (ipsass_cust_id=pCustid)
+   AND (shipto_id=pShiptoid) )
+  ORDER BY ipsprice_qtybreak DESC, ipsprice_price ASC
+  LIMIT 1;
+
+  IF (_price IS NOT NULL) THEN
+    IF ((_sales IS NOT NULL) AND (_sales < _price)) THEN
+      RETURN _sales;
+    END IF;
+    RETURN _price;
+  END IF;
+
+--  Check for a Customer Price
+  SELECT currToCurr(ipshead_curr_id, pCurrid, ipsprice_price, pEffective) INTO _price
+  FROM (
+  SELECT ipsitem_ipshead_id AS ipsprice_ipshead_id,
+         itemuomtouom(ipsitem_item_id, ipsitem_qty_uom_id, NULL, ipsitem_qtybreak) AS ipsprice_qtybreak,
+         (ipsitemchar_price * itemuomtouomratio(ipsitem_item_id, NULL, ipsitem_price_uom_id)) * _iteminvpricerat AS ipsprice_price
+    FROM ipsiteminfo,ipsitemchar
+   WHERE ((ipsitem_item_id=pItemid)
+    AND (ipsitemchar_char_id=pCharid)
+    AND (ipsitemchar_value=pCharValue)
+    AND (ipsitemchar_ipsitem_id=ipsitem_id))
+       ) AS
+        ipsprice, ipshead, ipsass
+  WHERE ( (ipsprice_ipshead_id=ipshead_id)
+   AND (ipsass_ipshead_id=ipshead_id)
+   AND (pAsOf BETWEEN ipshead_effective AND (ipshead_expires - 1))
+   AND (ipsprice_qtybreak <= pQty)
+   AND (COALESCE(length(ipsass_shipto_pattern), 0) = 0)
+   AND (ipsass_cust_id=pCustid) )
+  ORDER BY ipsprice_qtybreak DESC, ipsprice_price ASC
+  LIMIT 1;
+
+  IF (_price IS NOT NULL) THEN
+    IF ((_sales IS NOT NULL) AND (_sales < _price)) THEN
+      RETURN _sales;
+    END IF;
+    RETURN _price;
+  END IF;
+
+--  Check for a Customer Type Price
+  SELECT currToCurr(ipshead_curr_id, pCurrid, ipsprice_price, pEffective) INTO _price
+  FROM (
+  SELECT ipsitem_ipshead_id AS ipsprice_ipshead_id,
+         itemuomtouom(ipsitem_item_id, ipsitem_qty_uom_id, NULL, ipsitem_qtybreak) AS ipsprice_qtybreak,
+         (ipsitemchar_price * itemuomtouomratio(ipsitem_item_id, NULL, ipsitem_price_uom_id)) * _iteminvpricerat AS ipsprice_price
+    FROM ipsiteminfo,ipsitemchar
+   WHERE((ipsitem_item_id=pItemid)
+    AND (ipsitemchar_char_id=pCharid)
+    AND (ipsitemchar_value=pCharValue)
+    AND (ipsitemchar_ipsitem_id=ipsitem_id))
+       ) AS
+        ipsprice, ipshead, ipsass, custinfo
+  WHERE ( (ipsprice_ipshead_id=ipshead_id)
+   AND (ipsass_ipshead_id=ipshead_id)
+   AND (ipsass_custtype_id=cust_custtype_id)
+   AND (pAsOf BETWEEN ipshead_effective AND (ipshead_expires - 1))
+   AND (ipsprice_qtybreak <= pQty)
+   AND (cust_id=pCustid) )
+  ORDER BY ipsprice_qtybreak DESC, ipsprice_price ASC
+  LIMIT 1;
+
+  IF (_price IS NOT NULL) THEN
+    IF ((_sales IS NOT NULL) AND (_sales < _price)) THEN
+      RETURN _sales;
+    END IF;
+    RETURN _price;
+  END IF;
+
+--  Check for a Customer Type Pattern Price
+  SELECT currToCurr(ipshead_curr_id, pCurrid, ipsprice_price, pEffective) INTO _price
+  FROM (
+  SELECT ipsitem_ipshead_id AS ipsprice_ipshead_id,
+         itemuomtouom(ipsitem_item_id, ipsitem_qty_uom_id, NULL, ipsitem_qtybreak) AS ipsprice_qtybreak,
+         (ipsitemchar_price * itemuomtouomratio(ipsitem_item_id, NULL, ipsitem_price_uom_id)) * _iteminvpricerat AS ipsprice_price
+    FROM ipsiteminfo,ipsitemchar
+   WHERE ((ipsitem_item_id=pItemid)
+    AND (ipsitemchar_char_id=pCharid)
+    AND (ipsitemchar_value=pCharValue)
+    AND (ipsitemchar_ipsitem_id=ipsitem_id))
+       ) AS
+        ipsprice, ipshead, ipsass, custtype, custinfo
+  WHERE ( (ipsprice_ipshead_id=ipshead_id)
+   AND (ipsass_ipshead_id=ipshead_id)
+   AND (coalesce(length(ipsass_custtype_pattern), 0) > 0)
+   AND (custtype_code ~ ipsass_custtype_pattern)
+   AND (cust_custtype_id=custtype_id)
+   AND (pAsOf BETWEEN ipshead_effective AND (ipshead_expires - 1))
+   AND (ipsprice_qtybreak <= pQty)
+   AND (cust_id=pCustid) )
+  ORDER BY ipsprice_qtybreak DESC, ipsprice_price ASC
+  LIMIT 1;
+
+  IF (_price IS NOT NULL) THEN
+    IF ((_sales IS NOT NULL) AND (_sales < _price)) THEN
+      RETURN _sales;
+    END IF;
+    RETURN _price;
+  END IF;
+
+-- If we have not found another price yet and we have a
+-- sales price we will use that.
+  IF (_sales IS NOT NULL) THEN
+    RETURN _sales;
+  END IF;
+
+--  Check for a list price
+  SELECT MIN(currToLocal(pCurrid,
+                       charass_price - (charass_price * COALESCE(cust_discntprcnt, 0)),
+                       pEffective)) AS price,
+         item_exclusive INTO _item
+  FROM charass,item LEFT OUTER JOIN custinfo ON (cust_id=pCustid)
+  WHERE ((item_id=pItemid)
+   AND (charass_char_id=pCharid)
+   AND (charass_value=pCharValue)
+   AND (charass_target_type='I')
+   AND (charass_target_id=item_id))
+  GROUP BY item_exclusive;
+  IF (FOUND) THEN
+    IF (NOT _item.item_exclusive) THEN
+      IF (_item.price < 0) THEN
+        RETURN 0;
+      ELSE
+        RETURN _item.price;
+      END IF;
+    ELSE
+      RETURN 0;
+    END IF;
+  ELSE
+    RETURN 0;
+  END IF;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/itemcharvalue.sql b/foundation-database/public/functions/itemcharvalue.sql
new file mode 100644 (file)
index 0000000..8dcf74c
--- /dev/null
@@ -0,0 +1,26 @@
+
+CREATE OR REPLACE FUNCTION itemCharValue(INTEGER, TEXT) RETURNS TEXT AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemid ALIAS FOR $1;
+  pCharName ALIAS FOR $2;
+  _value TEXT;
+
+BEGIN
+
+  SELECT charass_value INTO _value
+  FROM charass, char
+  WHERE ( (charass_char_id=char_id)
+   AND (charass_target_type=''I'')
+   AND (charass_target_id=pItemid)
+   AND (char_name=pCharName) );
+  IF (NOT FOUND) THEN
+    _value = '''';
+  END IF;
+
+  RETURN _value;
+
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/itemcost.sql b/foundation-database/public/functions/itemcost.sql
new file mode 100644 (file)
index 0000000..d6eba74
--- /dev/null
@@ -0,0 +1,18 @@
+CREATE OR REPLACE FUNCTION itemCost(INTEGER) RETURNS NUMERIC STABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  _cost NUMERIC;
+BEGIN
+  SELECT CASE WHEN (itemsite_costmethod='A' AND itemsite_qtyonhand != 0.0) THEN (itemsite_value / itemsite_qtyonhand)
+              WHEN (itemsite_costmethod='A' AND itemsite_qtyonhand = 0.0) THEN 0.0
+              WHEN (itemsite_costmethod='N') THEN 0.0
+              ELSE stdCost(itemsite_item_id)
+         END INTO _cost
+    FROM itemsite
+   WHERE(itemsite_id=pItemsiteid);
+  RETURN _cost;
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/iteminventoryuominuse.sql b/foundation-database/public/functions/iteminventoryuominuse.sql
new file mode 100644 (file)
index 0000000..27bc0d5
--- /dev/null
@@ -0,0 +1,31 @@
+CREATE OR REPLACE FUNCTION itemInventoryUOMInUse(INTEGER) RETURNS BOOLEAN AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemid ALIAS FOR $1;
+  _uomid INTEGER;
+  _result INTEGER;
+BEGIN
+  SELECT item_inv_uom_id INTO _uomid
+    FROM item
+   WHERE(item_id=pItemid);
+
+  SELECT itemuomconv_id INTO _result
+    FROM itemuomconv
+   WHERE(itemuomconv_item_id=pItemid)
+   LIMIT 1;
+  IF (FOUND) THEN
+    RETURN TRUE;
+  END IF;
+
+  SELECT itemsite_id INTO _result
+  FROM itemsite WHERE ( (itemsite_item_id=pItemid)
+                  AND   ((itemsite_qtyonhand <> 0) OR (itemsite_nnqoh <> 0)) )
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN TRUE;
+  END IF;
+
+  RETURN FALSE;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/iteminvpricerat.sql b/foundation-database/public/functions/iteminvpricerat.sql
new file mode 100644 (file)
index 0000000..7db6830
--- /dev/null
@@ -0,0 +1,44 @@
+CREATE OR REPLACE FUNCTION itemInvPriceRat(INTEGER) RETURNS NUMERIC AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemid ALIAS FOR $1;
+  _fromUomid INTEGER;
+  _toUomid INTEGER;
+  _ratio NUMERIC;
+BEGIN
+
+  IF(pItemid IS NULL) THEN
+    RETURN 1.0;
+  END IF;
+
+  SELECT item_inv_uom_id, item_price_uom_id
+    INTO _fromUomid, _toUomid
+    FROM item
+   WHERE(item_id=pItemid);
+
+  IF(NOT FOUND) THEN
+    RAISE EXCEPTION ''No item record found for item_id %'', pItemid;
+  END IF;
+
+  IF(_fromUomid = _toUomid) THEN
+    RETURN 1.0;
+  END IF;
+
+  -- Return the ration as inventory / price
+  SELECT CASE WHEN(itemuomconv_from_uom_id=_fromUomid) THEN itemuomconv_from_value / itemuomconv_to_value
+              ELSE itemuomconv_to_value / itemuomconv_from_value
+         END
+    INTO _ratio
+    FROM itemuomconv
+   WHERE((((itemuomconv_from_uom_id=_fromUomid) AND (itemuomconv_to_uom_id=_toUomid))
+       OR ((itemuomconv_from_uom_id=_toUomid) AND (itemuomconv_to_uom_id=_fromUomid)))
+     AND (itemuomconv_item_id=pItemid));
+
+  IF(NOT FOUND) THEN
+    RAISE EXCEPTION ''No itemuomconv record found for item_id % to item_price_uomid %'', pItemid, _toUomid;
+  END IF;
+  
+  RETURN _ratio;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/itemipsprice.sql b/foundation-database/public/functions/itemipsprice.sql
new file mode 100644 (file)
index 0000000..b05a18b
--- /dev/null
@@ -0,0 +1,156 @@
+
+CREATE OR REPLACE FUNCTION itemIpsPrice(pItemid INTEGER,
+                                        pCustid INTEGER,
+                                        pShiptoid INTEGER,
+                                        pQty NUMERIC,
+                                        pQtyUOM INTEGER,
+                                        pPriceUOM INTEGER,
+                                        pCurrid INTEGER,
+                                        pEffective DATE,
+                                        pAsOf DATE,
+                                        pSiteid INTEGER) RETURNS SETOF itemprice AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _row  itemprice%ROWTYPE;
+  _sale RECORD;
+  _ips RECORD;
+  _item RECORD;
+  _cust RECORD;
+  _shipto RECORD;
+  _iteminvpricerat NUMERIC := 1.0;
+  _listprice NUMERIC := 0.0;
+  _qty NUMERIC;
+  _asof DATE;
+
+BEGIN
+-- Return the itemPrice in the currency passed in as pCurrid
+  _qty := itemuomtouom(pItemid, pQtyUOM, NULL, pQty);
+
+-- If no as of passed, use current date
+  _asof := COALESCE(pAsOf, CURRENT_DATE);
+
+--  Cache Item, Customer and Shipto
+  SELECT * INTO _item
+  FROM item
+  WHERE (item_id=pItemid);
+
+  SELECT * INTO _cust
+  FROM custinfo JOIN custtype ON (custtype_id=cust_custtype_id)
+  WHERE (cust_id=pCustid);
+
+  SELECT * INTO _shipto
+  FROM shiptoinfo
+  WHERE (shipto_id=pShiptoid);
+
+-- Get a value here so we do not have to call the function several times
+  SELECT itemuomtouomratio(pItemid, pPriceUOM, _item.item_price_uom_id) AS ratio
+    INTO _iteminvpricerat;
+
+-- First get a sales price if any so we when we find other prices
+-- we can determine if we want that price or this price.
+--  Check for a Sale Price
+  SELECT INTO _sale
+    currToCurr(ipshead_curr_id, pCurrid, ipsprice_price, pEffective) AS rightprice, ipsitem_type AS righttype
+  FROM (
+  SELECT ipsitem_ipshead_id AS ipsprice_ipshead_id, ipsitem_type,
+         CASE WHEN ipsitem_type = 'N' THEN (ipsitem_price * itemuomtouomratio(_item.item_id, pPriceUOM, ipsitem_price_uom_id))
+              WHEN ipsitem_type = 'D' THEN noNeg(_item.item_listprice - (_item.item_listprice * ipsitem_discntprcnt) - ipsitem_fixedamtdiscount) * _iteminvpricerat
+              WHEN ipsitem_type = 'M' THEN (_item.item_listcost + (_item.item_listcost * ipsitem_discntprcnt) + ipsitem_fixedamtdiscount) * _iteminvpricerat
+              ELSE 0.00
+         END AS ipsprice_price,
+         CASE WHEN (ipsitem_item_id=_item.item_id) THEN itemuomtouom(ipsitem_item_id, ipsitem_qty_uom_id, NULL, ipsitem_qtybreak)
+              ELSE ipsitem_qtybreak
+         END AS ipsprice_qtybreak,
+         (ipsitem_price_uom_id=COALESCE(pPriceUOM,-1)) AS uommatched
+    FROM ipsiteminfo
+   WHERE(ipsitem_item_id=_item.item_id) OR (ipsitem_prodcat_id=_item.item_prodcat_id) ) AS
+        ipsprice, ipshead, ipsass, sale
+  WHERE ( (ipsprice_ipshead_id=ipshead_id)
+    AND   (sale_ipshead_id=ipsprice_ipshead_id)
+    AND   (_asof BETWEEN sale_startdate AND sale_enddate)
+    AND   (ipsprice_qtybreak <= _qty)
+    AND   (ipsass_ipshead_id=ipshead_id)
+    AND ( (ipsass_shipto_id=_shipto.shipto_id)
+     OR   ((COALESCE(LENGTH(ipsass_shipto_pattern), 0) > 0) AND (_shipto.shipto_num ~ ipsass_shipto_pattern))
+     OR   (ipsass_cust_id=_cust.cust_id)
+     OR   (ipsass_custtype_id=_cust.cust_custtype_id)
+     OR   ((COALESCE(LENGTH(ipsass_custtype_pattern), 0) > 0) AND (_cust.custtype_code ~ ipsass_custtype_pattern)) )
+        )
+  ORDER BY uommatched DESC, ipsprice_qtybreak DESC, ipsprice_price ASC
+  LIMIT 1;
+
+-- Find the best Price Schedule Price
+  SELECT INTO _ips
+    currToCurr(ipshead_curr_id, pCurrid, protoprice, pEffective) AS rightprice, ipsitem_type AS righttype
+  
+  FROM (
+    SELECT *,
+           CASE WHEN (COALESCE(ipsass_shipto_id, -1) > 0) THEN 1
+             WHEN (COALESCE(LENGTH(ipsass_shipto_pattern), 0) > 0) THEN 2
+             WHEN (COALESCE(ipsass_cust_id, -1) > 0) THEN 3
+             WHEN (COALESCE(ipsass_custtype_id, -1) > 0) THEN 4
+             WHEN (COALESCE(LENGTH(ipsass_custtype_pattern), 0) > 0) THEN 5
+             ELSE 99
+           END AS assignseq,
+           CASE WHEN ipsitem_type = 'N' THEN (ipsitem_price * itemuomtouomratio(_item.item_id, pPriceUOM, ipsitem_price_uom_id))
+                WHEN ipsitem_type = 'D' THEN noNeg(_item.item_listprice - (_item.item_listprice * ipsitem_discntprcnt) - ipsitem_fixedamtdiscount) * _iteminvpricerat
+                WHEN ipsitem_type = 'M' THEN (_item.item_listcost + (_item.item_listcost * ipsitem_discntprcnt) + ipsitem_fixedamtdiscount) * _iteminvpricerat
+                ELSE 0.00
+           END AS protoprice,
+           CASE WHEN (ipsitem_item_id=_item.item_id) THEN itemuomtouom(ipsitem_item_id, ipsitem_qty_uom_id, NULL, ipsitem_qtybreak)
+                ELSE ipsitem_qtybreak
+           END AS protoqtybreak,
+           (COALESCE(ipsitem_price_uom_id, -1)=COALESCE(pPriceUOM, -1)) AS uommatched
+    FROM ipsass JOIN ipshead ON (ipshead_id=ipsass_ipshead_id)
+                JOIN ipsiteminfo ON (ipsitem_ipshead_id=ipshead_id)
+    WHERE ((ipsitem_item_id=_item.item_id) OR (ipsitem_prodcat_id=_item.item_prodcat_id))
+      AND (_asof BETWEEN ipshead_effective AND ipshead_expires)
+      AND ((ipsitem_warehous_id=pSiteid) OR (ipsitem_warehous_id IS NULL))
+      AND ( (ipsass_shipto_id=_shipto.shipto_id)
+       OR   ((COALESCE(LENGTH(ipsass_shipto_pattern), 0) > 0) AND (_shipto.shipto_num ~ ipsass_shipto_pattern))
+       OR   (ipsass_cust_id=_cust.cust_id)
+       OR   (ipsass_custtype_id=_cust.cust_custtype_id)
+       OR   ((COALESCE(LENGTH(ipsass_custtype_pattern), 0) > 0) AND (_cust.custtype_code ~ ipsass_custtype_pattern))
+          )
+  ) AS proto
+  WHERE (protoqtybreak <= pQty)
+  ORDER BY assignseq, protoqtybreak DESC, rightprice
+  LIMIT 1;
+  IF (_ips.rightprice IS NOT NULL) THEN
+    IF ((_sale.rightprice IS NOT NULL) AND (_sale.rightprice < _ips.rightprice)) THEN
+      RAISE DEBUG 'itemprice, item=%, cust=%, shipto=%, sale price= %', pItemid, pCustid, pShiptoid, _sale.rightprice;
+      _row.itemprice_price := _sale.rightprice;
+      _row.itemprice_type := _sale.righttype;
+      RETURN NEXT _row;
+    END IF;
+    RAISE DEBUG 'itemprice, item=%, cust=%, shipto=%, schedule price= %', pItemid, pCustid, pShiptoid, _ips.rightprice;
+    _row.itemprice_price := _ips.rightprice;
+    _row.itemprice_type := _ips.righttype;
+    RETURN NEXT _row;
+  END IF;
+
+--  If item is exclusive then list list price does not apply
+  IF (_item.item_exclusive) THEN
+    RAISE DEBUG 'itemprice, item=%, cust=%, shipto=%, item exclusive, price=-9999', pItemid, pCustid, pShiptoid;
+    _row.itemprice_price := -9999.0;
+    _row.itemprice_type := '';
+    RETURN NEXT _row;
+  END IF;
+
+--  Check for a list price
+  _listprice := noNeg(currToLocal(pCurrid, _item.item_listprice - (_item.item_listprice * COALESCE(_cust.cust_discntprcnt, 0.0)), pEffective)
+                      * itemuomtouomratio(pItemid, pPriceUOM, _item.item_price_uom_id));
+
+  RAISE DEBUG 'itemprice, item=%, cust=%, shipto=%, list price= %', pItemid, pCustid, pShiptoid, _listprice;
+
+  _row.itemprice_price := _listprice;
+  _row.itemprice_type := 'P';
+  RETURN NEXT _row;
+
+  RETURN;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/itemlocdistqty.sql b/foundation-database/public/functions/itemlocdistqty.sql
new file mode 100644 (file)
index 0000000..2763aba
--- /dev/null
@@ -0,0 +1,69 @@
+CREATE OR REPLACE FUNCTION itemlocdistQty(TEXT, INTEGER, INTEGER) RETURNS NUMERIC AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pTypes ALIAS FOR $1;
+  pLocationid ALIAS FOR $2;
+  pParentid ALIAS FOR $3;
+  _qty NUMERIC := 0;
+  _tempQty NUMERIC;
+
+BEGIN
+
+  IF (strpos(pTypes, ''L'') > 0) THEN
+    SELECT COALESCE(SUM(itemlocdist_qty), 0) INTO _tempQty
+    FROM itemlocdist
+    WHERE ( (itemlocdist_source_type=''L'')
+     AND (itemlocdist_source_id=pLocationid)
+     AND (itemlocdist_itemlocdist_id=pParentid) );
+
+    _qty := (_qty + _tempQty);
+  END IF;
+
+  IF (strpos(pTypes, ''I'') > 0) THEN
+    SELECT COALESCE(SUM(itemlocdist_qty), 0) INTO _tempQty
+    FROM itemlocdist, itemloc
+    WHERE ( (itemlocdist_source_type=''I'')
+     AND (itemlocdist_source_id=itemloc_id)
+     AND (itemloc_location_id=pLocationid)
+     AND (itemlocdist_itemlocdist_id=pParentid) );
+
+    _qty := (_qty + _tempQty);
+  END IF;
+
+  RETURN _qty;
+
+END;
+' LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION itemlocdistQty(INTEGER, INTEGER) RETURNS NUMERIC AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pLocationid ALIAS FOR $1;
+  pParentid ALIAS FOR $2;
+  _qty NUMERIC;
+  _tempQty NUMERIC;
+
+BEGIN
+
+  SELECT COALESCE(SUM(itemlocdist_qty), 0) INTO _qty
+  FROM itemlocdist
+  WHERE ( (itemlocdist_source_type=''L'')
+   AND (itemlocdist_source_id=pLocationid)
+   AND (itemlocdist_itemlocdist_id=pParentid) );
+
+  SELECT COALESCE(SUM(itemlocdist_qty), 0) INTO _tempQty
+  FROM itemlocdist, itemloc
+  WHERE ( (itemlocdist_source_type=''I'')
+   AND (itemlocdist_source_id=itemloc_id)
+   AND (itemloc_location_id=pLocationid)
+   AND (itemlocdist_itemlocdist_id=pParentid) );
+
+  _qty := (_qty + _tempQty);
+
+  RETURN _qty;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/itemprice.sql b/foundation-database/public/functions/itemprice.sql
new file mode 100644 (file)
index 0000000..dba85dd
--- /dev/null
@@ -0,0 +1,80 @@
+
+CREATE OR REPLACE FUNCTION itemPrice(pItemid INTEGER,
+                                     pCustid INTEGER,
+                                     pShiptoid INTEGER,
+                                     pQty NUMERIC,
+                                     pCurrid INTEGER,
+                                     pEffective DATE) RETURNS NUMERIC AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _item RECORD;
+
+BEGIN
+  SELECT item_inv_uom_id, item_price_uom_id
+    INTO _item
+    FROM item
+   WHERE(item_id=pItemid);
+  IF (FOUND) THEN
+    RETURN itemPrice(pItemid, pCustid, pShiptoid, pQty, _item.item_inv_uom_id, _item.item_price_uom_id, pCurrid, pEffective);
+  END IF;
+  RETURN -9999;
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION itemPrice(pItemid INTEGER,
+                                     pCustid INTEGER,
+                                     pShiptoid INTEGER,
+                                     pQty NUMERIC,
+                                     pQtyUOM INTEGER,
+                                     pPriceUOM INTEGER,
+                                     pCurrid INTEGER,
+                                     pEffective DATE) RETURNS NUMERIC AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+
+BEGIN
+  RETURN itemPrice(pItemid, pCustid, pShiptoid, pQty, pQtyUOM, pPriceUOM, pCurrid, pEffective, CURRENT_DATE);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION itemPrice(pItemid INTEGER,
+                                     pCustid INTEGER,
+                                     pShiptoid INTEGER,
+                                     pQty NUMERIC,
+                                     pQtyUOM INTEGER,
+                                     pPriceUOM INTEGER,
+                                     pCurrid INTEGER,
+                                     pEffective DATE,
+                                     pAsOf DATE) RETURNS NUMERIC AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+
+BEGIN
+  RETURN itemPrice(pItemid, pCustid, pShiptoid, pQty, pQtyUOM, pPriceUOM, pCurrid, pEffective, pAsOf, NULL);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION itemPrice(pItemid INTEGER,
+                                     pCustid INTEGER,
+                                     pShiptoid INTEGER,
+                                     pQty NUMERIC,
+                                     pQtyUOM INTEGER,
+                                     pPriceUOM INTEGER,
+                                     pCurrid INTEGER,
+                                     pEffective DATE,
+                                     pAsOf DATE,
+                                     pSiteid INTEGER) RETURNS NUMERIC AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _r RECORD;
+
+BEGIN
+  SELECT * FROM itemIpsPrice(pItemid, pCustid, pShiptoid, pQty, pQtyUOM, pPriceUOM,
+                             pCurrid, pEffective, pAsOf, pSiteid) INTO _r;
+  RETURN _r.itemprice_price;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/itemsellinguom.sql b/foundation-database/public/functions/itemsellinguom.sql
new file mode 100644 (file)
index 0000000..1fbe126
--- /dev/null
@@ -0,0 +1,10 @@
+CREATE OR REPLACE FUNCTION itemSellingUOM(INTEGER) RETURNS TEXT AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemid ALIAS FOR $1;
+
+BEGIN
+  RETURN itemUOMByType(pItemid, ''Selling'');
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/itemsrcprice.sql b/foundation-database/public/functions/itemsrcprice.sql
new file mode 100644 (file)
index 0000000..e5646e7
--- /dev/null
@@ -0,0 +1,67 @@
+
+CREATE OR REPLACE FUNCTION itemsrcPrice(pItemsrcid INTEGER,
+                                        pQty NUMERIC,
+                                        pCurrid INTEGER,
+                                        pEffective DATE) RETURNS NUMERIC AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _price NUMERIC := 0.0;
+
+BEGIN
+
+  SELECT itemsrcPrice(pItemsrcid, -1, FALSE, pQty, pCurrid, pEffective) INTO _price;
+
+  RETURN _price;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION itemsrcPrice(pItemsrcid INTEGER,
+                                        pSiteid INTEGER,
+                                        pDropship BOOLEAN,
+                                        pQty NUMERIC,
+                                        pCurrid INTEGER,
+                                        pEffective DATE) RETURNS NUMERIC AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _price NUMERIC := 0.0;
+  _r RECORD;
+  _effective DATE;
+
+BEGIN
+-- If no pEffective passed, use current date
+  _effective := COALESCE(pEffective, CURRENT_DATE);
+
+--  Cache Itemsrc and Item
+  SELECT *
+  INTO _r
+  FROM itemsrc JOIN item ON (item_id=itemsrc_item_id)
+  WHERE (itemsrc_id=pItemsrcid);
+  IF (NOT FOUND) THEN
+    RAISE EXCEPTION 'itemsrc % not found.', pItemsrcid;
+  END IF;
+
+--  Determine price
+  SELECT currToCurr(itemsrcp_curr_id, pCurrid, price, _effective) INTO _price
+  FROM (
+    SELECT *,
+           CASE itemsrcp_type WHEN ('N') THEN itemsrcp_price
+                              WHEN ('D') THEN (_r.item_listcost - (_r.item_listcost * itemsrcp_discntprcnt) - itemsrcp_fixedamtdiscount)
+                              ELSE 0.0
+           END AS price
+    FROM itemsrcp
+    WHERE ( (itemsrcp_itemsrc_id=_r.itemsrc_id)
+      AND   ((itemsrcp_warehous_id=pSiteid) OR (itemsrcp_warehous_id=-1))
+      AND   ((itemsrcp_dropship=pDropship) OR (NOT itemsrcp_dropship))
+      AND   (itemsrcp_qtybreak <= pQty) )
+    ORDER BY itemsrcp_qtybreak DESC
+    LIMIT 1
+       ) AS data
+  ;
+
+  RETURN _price;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/itemuombytype.sql b/foundation-database/public/functions/itemuombytype.sql
new file mode 100644 (file)
index 0000000..36f04e6
--- /dev/null
@@ -0,0 +1,41 @@
+CREATE OR REPLACE FUNCTION itemUOMByType(INTEGER, TEXT) RETURNS TEXT STABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemid ALIAS FOR $1;
+  pUomtype ALIAS FOR $2;
+  _uom TEXT;
+BEGIN
+  SELECT uom_name INTO _uom FROM (
+  SELECT uom_name
+    FROM item
+    JOIN itemuomconv ON (itemuomconv_item_id=item_id)
+    JOIN itemuom ON (itemuom_itemuomconv_id=itemuomconv_id)
+    JOIN uomtype ON (itemuom_uomtype_id=uomtype_id)
+    JOIN uom ON (itemuomconv_to_uom_id=uom_id)
+   WHERE((item_id=pItemid)
+     AND (uomtype_name=pUomtype)
+     AND (item_inv_uom_id != itemuomconv_to_uom_id))
+  UNION
+  SELECT uom_name
+    FROM item
+    JOIN itemuomconv ON (itemuomconv_item_id=item_id)
+    JOIN itemuom ON (itemuom_itemuomconv_id=itemuomconv_id)
+    JOIN uomtype ON (itemuom_uomtype_id=uomtype_id)
+    JOIN uom ON (itemuomconv_from_uom_id=uom_id)
+   WHERE((item_id=pItemid)
+     AND (uomtype_name=pUomtype)
+     AND (item_inv_uom_id != itemuomconv_from_uom_id))) data
+   LIMIT 1;
+
+  IF (NOT FOUND) THEN
+    SELECT uom_name
+      INTO _uom
+      FROM item
+      JOIN uom ON (item_inv_uom_id=uom_id)
+     WHERE(item_id=pItemid);
+  END IF;
+
+  RETURN _uom;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/itemuomfractionalbytype.sql b/foundation-database/public/functions/itemuomfractionalbytype.sql
new file mode 100644 (file)
index 0000000..f2c590b
--- /dev/null
@@ -0,0 +1,29 @@
+CREATE OR REPLACE FUNCTION itemUOMFractionalByType(INTEGER, TEXT) RETURNS BOOL AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemid ALIAS FOR $1;
+  pUomtype ALIAS FOR $2;
+  _frac BOOLEAN;
+BEGIN
+  SELECT itemuomconv_fractional
+    INTO _frac
+    FROM item
+    JOIN itemuomconv ON (itemuomconv_item_id=item_id)
+    JOIN itemuom ON (itemuom_itemuomconv_id=itemuomconv_id)
+    JOIN uomtype ON (itemuom_uomtype_id=uomtype_id)
+   WHERE((item_id=pItemid)
+     AND (uomtype_name=pUomtype))
+   LIMIT 1;
+
+  IF (NOT FOUND) THEN
+    SELECT item_fractional
+      INTO _frac
+      FROM item
+      JOIN uom ON (item_inv_uom_id=uom_id)
+     WHERE(item_id=pItemid);
+  END IF;
+
+  RETURN _frac;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/itemuomfractionalbyuom.sql b/foundation-database/public/functions/itemuomfractionalbyuom.sql
new file mode 100644 (file)
index 0000000..2715839
--- /dev/null
@@ -0,0 +1,28 @@
+CREATE OR REPLACE FUNCTION itemUOMFractionalByUOM(INTEGER, INTEGER) RETURNS BOOL AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemid ALIAS FOR $1;
+  pUomid ALIAS FOR $2;
+  _frac BOOLEAN;
+BEGIN
+  SELECT itemuomconv_fractional
+    INTO _frac
+    FROM item
+    JOIN itemuomconv ON (itemuomconv_item_id=item_id)
+   WHERE((item_id=pItemid)
+     AND ((itemuomconv_from_uom_id=item_inv_uom_id AND itemuomconv_to_uom_id=pUomid)
+       OR (itemuomconv_to_uom_id=item_inv_uom_id AND itemuomconv_from_uom_id=pUomid)))
+   LIMIT 1;
+
+  IF (NOT FOUND) THEN
+    SELECT item_fractional
+      INTO _frac
+      FROM item
+      JOIN uom ON (item_inv_uom_id=uom_id)
+     WHERE(item_id=pItemid);
+  END IF;
+
+  RETURN _frac;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/itemuomratiobytype.sql b/foundation-database/public/functions/itemuomratiobytype.sql
new file mode 100644 (file)
index 0000000..cd0f67a
--- /dev/null
@@ -0,0 +1,28 @@
+CREATE OR REPLACE FUNCTION itemUOMRatioByType(INTEGER, TEXT) RETURNS NUMERIC STABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemid ALIAS FOR $1;
+  pUomtype ALIAS FOR $2;
+  _ratio NUMERIC;
+BEGIN
+  -- Return the ration as alternate / inventory uom
+  SELECT CASE WHEN(itemuomconv_from_uom_id=item_inv_uom_id) THEN itemuomconv_to_value / itemuomconv_from_value
+              ELSE itemuomconv_from_value / itemuomconv_to_value
+         END
+    INTO _ratio
+    FROM item
+    JOIN itemuomconv ON (itemuomconv_item_id=item_id)
+    JOIN itemuom ON (itemuom_itemuomconv_id=itemuomconv_id)
+    JOIN uomtype ON (itemuom_uomtype_id=uomtype_id)
+   WHERE((item_id=pItemid)
+     AND (uomtype_name=pUomtype))
+   LIMIT 1;
+
+  IF (NOT FOUND) THEN
+    _ratio := 1.0;
+  END IF;
+
+  RETURN _ratio;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/itemuomtouom.sql b/foundation-database/public/functions/itemuomtouom.sql
new file mode 100644 (file)
index 0000000..8c042d7
--- /dev/null
@@ -0,0 +1,132 @@
+CREATE OR REPLACE FUNCTION itemuomtouom(INTEGER, INTEGER, INTEGER, NUMERIC) RETURNS NUMERIC STABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN itemuomtouom($1, $2, $3, $4, 'qty');
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION itemuomtouom(pItemid INTEGER,
+                                        pUomidFrom INTEGER,
+                                        pUomidTo INTEGER,
+                                        pQtyFrom NUMERIC,
+                                        pLocale TEXT) RETURNS NUMERIC STABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _uomidFrom INTEGER;
+  _uomidTo   INTEGER;
+  _uomidInv  INTEGER;
+  _valueFrom NUMERIC := 0.0;
+  _valueTo   NUMERIC := 0.0;
+  _value     NUMERIC := 0.0;
+  _item      RECORD;
+  _conv      RECORD;
+  _frac      BOOLEAN := FALSE;
+BEGIN
+
+  SELECT item_inv_uom_id, item_fractional
+    INTO _item
+    FROM item
+   WHERE(item_id=pItemid);
+  IF(NOT FOUND) THEN
+    RAISE EXCEPTION 'No item record was found for item id %', pItemid;
+  END IF;
+
+  _uomidFrom := COALESCE(pUomidFrom, _item.item_inv_uom_id);
+  _uomidTo   := COALESCE(pUomidTo,   _item.item_inv_uom_id);
+  _uomidInv  := _item.item_inv_uom_id;
+
+  -- Should we round the qty here or not?
+  IF(_uomidFrom = _uomidTo) THEN
+    -- Both from/to are the same. If it is the item inv uom
+    -- then use the item fractional value otherwise assume
+    -- it is fractional for now so the user gets the same value back.
+    IF(_uomidFrom = _item.item_inv_uom_id) THEN
+      _frac := _item.item_fractional;
+    ELSE
+      _frac := true;
+    END IF;
+    RETURN roundLocale(_frac, pQtyFrom, pLocale);
+  END IF;
+
+  -- Try a direct conversion
+  SELECT itemuomconv_from_uom_id, itemuomconv_from_value,
+         itemuomconv_to_uom_id, itemuomconv_to_value,
+         itemuomconv_fractional
+    INTO _conv
+    FROM itemuomconv
+   WHERE(((itemuomconv_from_uom_id=_uomidFrom AND itemuomconv_to_uom_id=_uomidTo)
+       OR (itemuomconv_from_uom_id=_uomidTo AND itemuomconv_to_uom_id=_uomidFrom))
+     AND (itemuomconv_item_id=pItemid));
+  IF(FOUND) THEN
+    IF(_conv.itemuomconv_from_uom_id=_uomidFrom) THEN
+      _valueFrom := _conv.itemuomconv_from_value;
+      _valueTo := _conv.itemuomconv_to_value;
+    ELSE
+      _valueFrom := _conv.itemuomconv_to_value;
+      _valueTo := _conv.itemuomconv_from_value;
+    END IF;
+
+    -- If we are converting to the item inv uom use the item fractional value
+    -- otherwise use the conversion fractional value.
+    if(_uomidTo = _uomidInv) THEN
+      _frac := _item.item_fractional;
+    ELSE
+      _frac := _conv.itemuomconv_fractional;
+    END IF;
+    _value := roundLocale(_frac, ((_valueTo/_valueFrom) * pQtyFrom), pLocale);
+  ELSE
+    -- Try to convert the from uom to the inventory uom
+    SELECT itemuomconv_from_uom_id, itemuomconv_from_value,
+           itemuomconv_to_uom_id, itemuomconv_to_value,
+           itemuomconv_fractional
+      INTO _conv
+      FROM itemuomconv
+     WHERE(((itemuomconv_from_uom_id=_uomidFrom AND itemuomconv_to_uom_id=_uomidInv)
+         OR (itemuomconv_from_uom_id=_uomidInv AND itemuomconv_to_uom_id=_uomidFrom))
+       AND (itemuomconv_item_id=pItemid));
+    IF(NOT FOUND) THEN
+      RAISE EXCEPTION 'A conversion for item_id % from uom_id % to inv_uom_id % was not found.', pItemid, _uomidFrom, _uomidInv;
+    END IF;
+    IF(_conv.itemuomconv_from_uom_id=_uomidInv) THEN
+      _valueFrom := _conv.itemuomconv_from_value;
+      _valueTo := _conv.itemuomconv_to_value;
+    ELSE
+      _valueFrom := _conv.itemuomconv_to_value;
+      _valueTo := _conv.itemuomconv_from_value;
+    END IF;
+    _value := (_valueTo / _valueFrom);
+    IF (_conv.itemuomconv_fractional OR _item.item_fractional) THEN
+      _frac := TRUE;
+    END IF;
+    -- Try to convert the to uom to the inventory uom
+    SELECT itemuomconv_from_uom_id, itemuomconv_from_value,
+           itemuomconv_to_uom_id, itemuomconv_to_value,
+           itemuomconv_fractional
+      INTO _conv
+      FROM itemuomconv
+     WHERE(((itemuomconv_from_uom_id=_uomidInv AND itemuomconv_to_uom_id=_uomidTo)
+         OR (itemuomconv_from_uom_id=_uomidTo AND itemuomconv_to_uom_id=_uomidInv))
+       AND (itemuomconv_item_id=pItemid));
+    IF(NOT FOUND) THEN
+      RAISE EXCEPTION 'A conversion for item_id % from uom_id % to inv_uom_id % was not found.', pItemid, _uomidTo, _uomidInv;
+    END IF;
+    IF(_conv.itemuomconv_from_uom_id=_uomidInv) THEN
+      _valueFrom := _conv.itemuomconv_from_value;
+      _valueTo := _conv.itemuomconv_to_value;
+    ELSE
+      _valueFrom := _conv.itemuomconv_to_value;
+      _valueTo := _conv.itemuomconv_from_value;
+    END IF;
+    _value := _value * (_valueTo / _valueFrom);
+    IF (_conv.itemuomconv_fractional OR _item.item_fractional) THEN
+      _frac := TRUE;
+    END IF;
+    _value := roundLocale(_frac, (_value * pQtyFrom), pLocale);
+  END IF;
+
+  RETURN _value;
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/itemuomtouomratio.sql b/foundation-database/public/functions/itemuomtouomratio.sql
new file mode 100644 (file)
index 0000000..c1ccdee
--- /dev/null
@@ -0,0 +1,94 @@
+CREATE OR REPLACE FUNCTION itemuomtouomratio(pItemid INTEGER,
+                                             pUomidFrom INTEGER,
+                                             pUomidTo INTEGER) RETURNS NUMERIC STABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _uomidFrom INTEGER;
+  _uomidTo   INTEGER;
+  _uomidInv  INTEGER;
+  _valueFrom NUMERIC := 0.0;
+  _valueTo   NUMERIC := 0.0;
+  _value     NUMERIC := 0.0;
+  _item      RECORD;
+  _conv      RECORD;
+BEGIN
+
+  SELECT item_inv_uom_id
+    INTO _item
+    FROM item
+   WHERE(item_id=pItemid);
+  IF(NOT FOUND) THEN
+    RAISE EXCEPTION 'No item record was found for item id %', pItemid;
+  END IF;
+
+  _uomidFrom := COALESCE(pUomidFrom, _item.item_inv_uom_id);
+  _uomidTo   := COALESCE(pUomidTo,   _item.item_inv_uom_id);
+  _uomidInv  := _item.item_inv_uom_id;
+
+  IF(_uomidFrom = _uomidTo) THEN
+    RETURN 1.0;
+  END IF;
+
+  -- Try a direct conversion
+  SELECT itemuomconv_from_uom_id, itemuomconv_from_value,
+         itemuomconv_to_uom_id, itemuomconv_to_value
+    INTO _conv
+    FROM itemuomconv
+   WHERE(((itemuomconv_from_uom_id=_uomidFrom AND itemuomconv_to_uom_id=_uomidTo)
+       OR (itemuomconv_from_uom_id=_uomidTo AND itemuomconv_to_uom_id=_uomidFrom))
+     AND (itemuomconv_item_id=pItemid));
+  IF(FOUND) THEN
+    IF(_conv.itemuomconv_from_uom_id=_uomidFrom) THEN
+      _valueFrom := _conv.itemuomconv_from_value;
+      _valueTo := _conv.itemuomconv_to_value;
+    ELSE
+      _valueFrom := _conv.itemuomconv_to_value;
+      _valueTo := _conv.itemuomconv_from_value;
+    END IF;
+    _value := (_valueTo / _valueFrom);
+  ELSE
+    -- Try to convert the from uom to the inventory uom
+    SELECT itemuomconv_from_uom_id, itemuomconv_from_value,
+           itemuomconv_to_uom_id, itemuomconv_to_value
+      INTO _conv
+      FROM itemuomconv
+     WHERE(((itemuomconv_from_uom_id=_uomidFrom AND itemuomconv_to_uom_id=_uomidInv)
+         OR (itemuomconv_from_uom_id=_uomidInv AND itemuomconv_to_uom_id=_uomidFrom))
+       AND (itemuomconv_item_id=pItemid));
+    IF(NOT FOUND) THEN
+      RAISE EXCEPTION 'A conversion for item_id % from uom_id % to inv_uom_id % was not found.', pItemid, _uomidFrom, _uomidInv;
+    END IF;
+    IF(_conv.itemuomconv_from_uom_id=_uomidInv) THEN
+      _valueFrom := _conv.itemuomconv_from_value;
+      _valueTo := _conv.itemuomconv_to_value;
+    ELSE
+      _valueFrom := _conv.itemuomconv_to_value;
+      _valueTo := _conv.itemuomconv_from_value;
+    END IF;
+    _value := (_valueTo / _valueFrom);
+    -- Try to convert the to uom to the inventory uom
+    SELECT itemuomconv_from_uom_id, itemuomconv_from_value,
+           itemuomconv_to_uom_id, itemuomconv_to_value
+      INTO _conv
+      FROM itemuomconv
+     WHERE(((itemuomconv_from_uom_id=_uomidInv AND itemuomconv_to_uom_id=_uomidTo)
+         OR (itemuomconv_from_uom_id=_uomidTo AND itemuomconv_to_uom_id=_uomidInv))
+       AND (itemuomconv_item_id=pItemid));
+    IF(NOT FOUND) THEN
+      RAISE EXCEPTION 'A conversion for item_id % from uom_id % to inv_uom_id % was not found.', pItemid, _uomidTo, _uomidInv;
+    END IF;
+    IF(_conv.itemuomconv_from_uom_id=_uomidInv) THEN
+      _valueFrom := _conv.itemuomconv_from_value;
+      _valueTo := _conv.itemuomconv_to_value;
+    ELSE
+      _valueFrom := _conv.itemuomconv_to_value;
+      _valueTo := _conv.itemuomconv_from_value;
+    END IF;
+    _value := _value * (_valueTo / _valueFrom);
+  END IF;
+
+  RETURN _value;
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/last_agg.sql b/foundation-database/public/functions/last_agg.sql
new file mode 100644 (file)
index 0000000..ea2c372
--- /dev/null
@@ -0,0 +1,15 @@
+
+-- Create a function that always returns the last non-NULL item
+CREATE OR REPLACE FUNCTION public.last_agg ( anyelement, anyelement )
+RETURNS anyelement AS $$
+  SELECT $2;
+$$ LANGUAGE SQL STABLE;
+
+-- And then wrap an aggreagate around it
+DROP AGGREGATE public.last(anyelement);
+CREATE AGGREGATE public.last (
+        sfunc    = public.last_agg,
+        basetype = anyelement,
+        stype    = anyelement
+);
+
diff --git a/foundation-database/public/functions/login.sql b/foundation-database/public/functions/login.sql
new file mode 100644 (file)
index 0000000..3c481c6
--- /dev/null
@@ -0,0 +1,67 @@
+
+CREATE OR REPLACE FUNCTION public.login() RETURNS integer AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE 
+  _p RECORD;
+
+BEGIN
+
+  RETURN login(false);
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION public.login(boolean) RETURNS integer AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE 
+  _setSearchPath ALIAS FOR $1;
+  _p RECORD;
+
+BEGIN
+
+  PERFORM pg_try_advisory_lock(datid::integer, procpid)
+     FROM pg_stat_activity
+    WHERE(procpid = pg_backend_pid());
+
+  -- This is new to version 9.0 and higher and will error on older versions
+  IF (select CAST(split_part(split_part(version(), ' ', 2),'.',1) AS integer) >= 9) THEN
+    SET bytea_output TO escape;
+  END IF;
+
+  -- this is temporary until either qt fixes the postgres driver or we find &
+  -- fix all of the places in our app that can write strings with backslashes
+  SET standard_conforming_strings TO false;
+
+  SELECT usr_id, userCanLogin(usr_username) AS usr_active INTO _p
+  FROM usr
+  WHERE (usr_username=getEffectiveXtUser());
+
+  IF (NOT FOUND) THEN
+    RETURN -1;
+
+  ELSIF (NOT _p.usr_active) THEN
+    IF(SELECT metric_value='AdminOnly'
+         FROM metric
+        WHERE metric_name='AllowedUserLogins') THEN
+      RETURN -3;
+    END IF;
+    RETURN -2;
+  END IF;
+
+  IF (_setSearchPath) THEN
+    IF EXISTS(SELECT 1
+                FROM pg_proc
+                JOIN pg_namespace ON (pronamespace=pg_namespace.oid)
+               WHERE nspname='public'
+                 AND proname='buildsearchpath') THEN
+      EXECUTE 'SET SEARCH_PATH TO ' || public.buildSearchPath();
+    END IF;
+  END IF;
+
+  RETURN 1;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/logout.sql b/foundation-database/public/functions/logout.sql
new file mode 100644 (file)
index 0000000..b62539f
--- /dev/null
@@ -0,0 +1,14 @@
+
+CREATE OR REPLACE FUNCTION logout() RETURNS integer AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  PERFORM pg_advisory_unlock(datid::integer, procpid)
+     FROM pg_stat_activity
+    WHERE(procpid = pg_backend_pid());
+
+  RETURN 0;
+END;
+$$ LANGUAGE 'plpgsql';
+
+
diff --git a/foundation-database/public/functions/lowercost.sql b/foundation-database/public/functions/lowercost.sql
new file mode 100644 (file)
index 0000000..f0eb316
--- /dev/null
@@ -0,0 +1,143 @@
+CREATE OR REPLACE FUNCTION lowerCost(INTEGER, TEXT) RETURNS NUMERIC AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemid      ALIAS FOR $1;
+  pCosttype    ALIAS FOR $2;
+
+BEGIN
+    RETURN lowerCost(pItemid, pCosttype, TRUE);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION lowerCost(INTEGER, TEXT, BOOLEAN) RETURNS NUMERIC AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemid ALIAS FOR $1;
+  pCosttype ALIAS FOR $2;
+  pActual      ALIAS FOR $3;
+  _type CHAR(1);
+  _actCost     NUMERIC;
+  _actCost1    NUMERIC;
+  _actCost2    NUMERIC;
+  _stdCost     NUMERIC;
+  _stdCost1    NUMERIC;
+  _stdCost2    NUMERIC;
+  _cost                NUMERIC;
+  _cost1       NUMERIC;
+  _cost2       NUMERIC;
+  _batchsize   NUMERIC;
+
+BEGIN
+
+  SELECT item_type INTO _type
+  FROM item
+  WHERE (item_id=pItemid);
+
+  _batchsize := COALESCE( (
+    SELECT bomhead_batchsize
+    FROM bomhead
+    WHERE ((bomhead_item_id=pItemId)
+     AND  (bomhead_rev_id=getActiveRevId('BOM',pItemId))) LIMIT 1), 1);
+
+  -- find the lowercost in the base currency at the current conversion rate
+  IF (_type IN ('M', 'F', 'B', 'T')) THEN
+
+    IF (pActual) THEN
+      SELECT SUM( CASE WHEN (bomitemcost_id IS NOT NULL AND bc.costelem_id IS NOT NULL) THEN
+                  round(currToBase(bomitemcost_curr_id, bomitemcost_actcost, CURRENT_DATE),6) *
+                    itemuomtouom(bomitem_item_id, bomitem_uom_id, NULL, (bomitem_qtyfxd/_batchsize + bomitem_qtyper) * (1 + bomitem_scrap), 'qtyper')
+                  ELSE
+                  round(currToBase(itemcost_curr_id, itemcost_actcost, CURRENT_DATE),6) *
+                    itemuomtouom(bomitem_item_id, bomitem_uom_id, NULL, (bomitem_qtyfxd/_batchsize + bomitem_qtyper) * (1 + bomitem_scrap), 'qtyper')
+                  END )
+         INTO _cost
+      FROM bomitem(pItemid)
+        JOIN item ON (item_id=bomitem_item_id AND item_type <> 'T')
+        JOIN itemcost ON (itemcost_item_id=bomitem_item_id)
+        JOIN costelem ic ON (ic.costelem_id=itemcost_costelem_id AND ic.costelem_type=pCosttype)
+        LEFT OUTER JOIN bomitemcost ON (bomitemcost_bomitem_id=bomitem_id)
+        LEFT OUTER JOIN costelem bc ON (bc.costelem_id=bomitemcost_costelem_id AND bc.costelem_type=pCosttype)
+      WHERE ( CURRENT_DATE BETWEEN bomitem_effective AND (bomitem_expires - 1) );
+    ELSE
+      SELECT SUM( CASE WHEN (bomitemcost_id IS NOT NULL AND bc.costelem_id IS NOT NULL) THEN
+                  bomitemcost_stdcost *
+                    itemuomtouom(bomitem_item_id, bomitem_uom_id, NULL, (bomitem_qtyfxd/_batchsize + bomitem_qtyper) * (1 + bomitem_scrap), 'qtyper')
+                  ELSE
+                  itemcost_stdcost *
+                    itemuomtouom(bomitem_item_id, bomitem_uom_id, NULL, (bomitem_qtyfxd/_batchsize + bomitem_qtyper) * (1 + bomitem_scrap), 'qtyper')
+                  END )
+         INTO _cost
+      FROM bomitem(pItemid)
+        JOIN item ON (item_id=bomitem_item_id AND item_type <> 'T')
+        JOIN itemcost ON (itemcost_item_id=bomitem_item_id)
+        JOIN costelem ON (costelem_id=itemcost_costelem_id AND costelem_type=pCosttype)
+        LEFT OUTER JOIN bomitemcost ON (bomitemcost_bomitem_id=bomitem_id)
+        LEFT OUTER JOIN costelem bc ON (bc.costelem_id=bomitemcost_costelem_id AND bc.costelem_type=pCosttype)
+      WHERE ( CURRENT_DATE BETWEEN bomitem_effective AND (bomitem_expires - 1) ); 
+    END IF;
+    
+    IF (NOT FOUND) THEN
+      _cost := NULL;
+    END IF;
+
+  ELSIF (_type IN ('C')) THEN
+    SELECT SUM(CASE WHEN (bbomitem_qtyper = 0) THEN 0
+                    ELSE currToBase(itemcost_curr_id, itemcost_actcost, CURRENT_DATE) / bbomitem_qtyper * bbomitem_costabsorb
+               END),
+          SUM(CASE WHEN (bbomitem_qtyper = 0) THEN 0
+                    ELSE itemcost_stdcost / bbomitem_qtyper * bbomitem_costabsorb
+               END)
+       INTO _actCost1, _stdCost1
+    FROM itemcost
+         JOIN costelem       ON (itemcost_costelem_id=costelem_id)
+         JOIN xtmfg.bbomitem ON (bbomitem_parent_item_id=itemcost_item_id)
+    WHERE ( (bbomitem_item_id=pItemid)
+     AND (CURRENT_DATE BETWEEN bbomitem_effective AND (bbomitem_expires - 1))
+     AND (costelem_type=pCosttype) );
+
+    SELECT SUM(CASE WHEN (t.bbomitem_qtyper = 0) THEN 0
+                    ELSE currToBase(itemcost_curr_id, itemcost_actcost, CURRENT_DATE) * s.bbomitem_qtyper / t.bbomitem_qtyper * t.bbomitem_costabsorb
+               END),
+          SUM(CASE WHEN (t.bbomitem_qtyper = 0) THEN 0
+                    ELSE itemcost_stdcost * s.bbomitem_qtyper / t.bbomitem_qtyper * t.bbomitem_costabsorb
+               END)
+       INTO _actCost2, _stdCost2
+    FROM costelem
+         JOIN itemcost            ON (costelem_id=itemcost_costelem_id)
+         JOIN xtmfg.bbomitem AS s ON (itemcost_item_id=s.bbomitem_item_id)
+         JOIN xtmfg.bbomitem AS t ON (s.bbomitem_parent_item_id=t.bbomitem_parent_item_id)
+         JOIN  item               ON (s.bbomitem_item_id=item_id)
+    WHERE ( (t.bbomitem_item_id=pItemid)
+     AND ( CURRENT_DATE BETWEEN s.bbomitem_effective
+                        AND (s.bbomitem_expires - 1) )
+     AND ( CURRENT_DATE BETWEEN t.bbomitem_effective
+                        AND (t.bbomitem_expires - 1) )
+     AND (item_type='Y')
+     AND (costelem_type=pCosttype) );
+
+    IF (pActual) THEN
+       _cost  = _actCost;
+       _cost1 = _actCost1;
+       _cost2 = _actCost2;
+    ELSE
+       _cost  = _stdCost;
+       _cost1 = _stdCost1;
+       _cost2 = _stdCost2;     -- should this be std or act?
+    END IF;
+
+    IF (_cost1 IS NULL AND _cost2 IS NULL) THEN
+       _cost = NULL;
+    ELSE
+        _cost = COALESCE(_cost1, 0) + COALESCE(_cost2, 0);
+    END IF;
+
+  ELSE
+    RETURN NULL;
+  END IF;
+
+  RETURN round(_cost,6);
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/maintainbomworkspace.sql b/foundation-database/public/functions/maintainbomworkspace.sql
new file mode 100644 (file)
index 0000000..4f9d0a7
--- /dev/null
@@ -0,0 +1,27 @@
+CREATE OR REPLACE FUNCTION maintainBOMWorkspace() RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _test TEXT;
+
+BEGIN
+
+  SELECT tablename INTO _test
+  FROM pg_tables
+  WHERE (tablename=''bomwork'');
+  IF (NOT FOUND) THEN
+    CREATE TEMPORARY TABLE bomwork
+    ( bomwork_id INTEGER, bomwork_set_id INTEGER, bomwork_parent_id INTEGER,
+      bomwork_seqnumber INTEGER, bomwork_parent_seqnumber INTEGER,
+      bomwork_item_id INTEGER, bomwork_item_type CHARACTER(1), bomwork_status CHARACTER(1),
+      bomwork_qtyper NUMERIC(20, 8), bomwork_scrap NUMERIC(20, 10),
+      bomwork_level INTEGER, bomwork_effective DATE, bomwork_expires DATE,
+      bomwork_stdunitcost NUMERIC(16, 4), bomwork_actunitcost NUMERIC(16, 4),
+      bomwork_createwo BOOLEAN, bomwork_issuemethod CHARACTER(1) );
+    CREATE INDEX bomwork_set_id_idx ON bomwork(bomwork_set_id);
+  END IF;
+
+  RETURN 1;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/markapcheckasposted.sql b/foundation-database/public/functions/markapcheckasposted.sql
new file mode 100644 (file)
index 0000000..96e1ba3
--- /dev/null
@@ -0,0 +1,9 @@
+CREATE OR REPLACE FUNCTION markAPCheckASPosted(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RAISE NOTICE ''markAPCheckAsPosted() is deprecated - use markCheckAsPosted() instead'';
+  RETURN markCheckAsPosted($1);
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/markapcheckasprinted.sql b/foundation-database/public/functions/markapcheckasprinted.sql
new file mode 100644 (file)
index 0000000..378b322
--- /dev/null
@@ -0,0 +1,8 @@
+CREATE OR REPLACE FUNCTION markAPCheckAsPrinted(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RAISE NOTICE ''markAPCheckAsPrinted() is deprecated - use markCheckAsPrinted()'';
+  RETURN markCheckAsPrinted($1);
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/markcheckasposted.sql b/foundation-database/public/functions/markcheckasposted.sql
new file mode 100644 (file)
index 0000000..153ce89
--- /dev/null
@@ -0,0 +1,16 @@
+CREATE OR REPLACE FUNCTION markCheckASPosted(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCheckid ALIAS FOR $1;
+
+BEGIN
+
+  UPDATE checkhead
+  SET checkhead_posted=TRUE
+  WHERE (checkhead_id=pCheckid);
+
+  RETURN 1;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/markcheckasprinted.sql b/foundation-database/public/functions/markcheckasprinted.sql
new file mode 100644 (file)
index 0000000..877550e
--- /dev/null
@@ -0,0 +1,16 @@
+CREATE OR REPLACE FUNCTION markCheckAsPrinted(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCheckid ALIAS FOR $1;
+
+BEGIN
+
+  UPDATE checkhead
+  SET checkhead_printed=TRUE
+  WHERE (checkhead_id=pCheckid);
+
+  RETURN 1;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/massexpirebomitem.sql b/foundation-database/public/functions/massexpirebomitem.sql
new file mode 100644 (file)
index 0000000..972751d
--- /dev/null
@@ -0,0 +1,19 @@
+CREATE OR REPLACE FUNCTION massExpireBomitem(INTEGER, DATE, TEXT) RETURNS BOOLEAN AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemid ALIAS FOR $1;
+  pExpireDate ALIAS FOR $2;
+  pECN ALIAS FOR $3;
+
+BEGIN
+
+  UPDATE bomitem
+  SET bomitem_expires=pExpireDate
+  WHERE ( (bomitem_expires >= CURRENT_DATE)
+   AND (bomitem_item_id=pItemid)
+   AND (bomitem_rev_id=getActiveRevId('BOM',bomitem_parent_item_id)) );
+
+  RETURN TRUE;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/massreplacebomitem.sql b/foundation-database/public/functions/massreplacebomitem.sql
new file mode 100644 (file)
index 0000000..8e6507b
--- /dev/null
@@ -0,0 +1,47 @@
+CREATE OR REPLACE FUNCTION massReplaceBomitem(INTEGER, INTEGER, DATE, TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pNewItemid ALIAS FOR $1;
+  pOriginalItemid ALIAS FOR $2;
+  pEffectiveDate ALIAS FOR $3;
+  pECN ALIAS FOR $4;
+  _effectiveDate DATE;
+  _result              INTEGER;
+
+BEGIN
+
+  _effectiveDate := COALESCE(pEffectiveDate, CURRENT_DATE);
+
+  IF (BOMContains(pOriginalItemid, pNewItemid) OR
+      BOMContains(pNewItemid, pOriginalItemid)) THEN
+    RETURN -1;
+  END IF;
+
+  INSERT INTO bomitem
+  ( bomitem_parent_item_id, bomitem_seqnumber,
+    bomitem_item_id, bomitem_qtyfxd, bomitem_qtyper, bomitem_uom_id,
+    bomitem_scrap, bomitem_effective, bomitem_expires, bomitem_ecn,
+    bomitem_createwo, bomitem_issuemethod, bomitem_subtype,
+    bomitem_booitem_seq_id, bomitem_schedatwooper, bomitem_moddate, bomitem_rev_id,
+    bomitem_char_id, bomitem_value )
+  SELECT bomitem_parent_item_id, bomitem_seqnumber,
+         pNewItemid, bomitem_qtyfxd, bomitem_qtyper, bomitem_uom_id,
+         bomitem_scrap, _effectiveDate, endOfTime(), pECN,
+         bomitem_createwo, bomitem_issuemethod, 'I',
+         bomitem_booitem_seq_id, bomitem_schedatwooper, CURRENT_DATE, getActiveRevId('BOM',bomitem_parent_item_id),
+         bomitem_char_id, bomitem_value
+  FROM bomitem
+  WHERE ( (_effectiveDate < bomitem_expires)
+   AND (bomitem_item_id=pOriginalItemid)
+   AND (bomitem_rev_id=getActiveRevId('BOM',bomitem_parent_item_id)) );
+
+  UPDATE bomitem
+  SET bomitem_expires=_effectiveDate
+  WHERE ( (_effectiveDate < bomitem_expires)
+   AND (bomitem_item_id=pOriginalItemid)
+   AND (bomitem_rev_id=getActiveRevid('BOM',bomitem_parent_item_id)) );
+
+  RETURN 1;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/merge2crmaccts.sql b/foundation-database/public/functions/merge2crmaccts.sql
new file mode 100644 (file)
index 0000000..4251ce5
--- /dev/null
@@ -0,0 +1,256 @@
+CREATE OR REPLACE FUNCTION merge2crmaccts(INTEGER, INTEGER, BOOLEAN) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSourceId ALIAS FOR $1;
+  pTargetId ALIAS FOR $2;
+  _purge    BOOLEAN := COALESCE($3, FALSE);
+
+  _coldesc    RECORD;
+  _count      INTEGER := 0;
+  _hassubtype BOOLEAN;
+  _mrgcol     BOOLEAN;
+  _result     INTEGER := 0;
+  _sel        RECORD;
+  _colname    TEXT;
+  _tmpid      INTEGER;
+
+BEGIN
+  -- Validate
+  IF (pSourceId = pTargetId) THEN
+    RAISE NOTICE 'Tried to merge a CRM Account with itself: %.', pSourceId;
+    RETURN 0;
+  ELSIF (pSourceId IS NULL) THEN
+    RAISE EXCEPTION 'Merge source id cannot be null [xtuple: merge, -1]';
+  ELSIF NOT(EXISTS(SELECT 1 FROM crmacct WHERE crmacct_id=pSourceId)) THEN
+    RAISE EXCEPTION 'Merge source % not found [xtuple: merge, -2, %]',
+                    pSourceId, pSourceId;
+  ELSIF (pTargetId IS NULL) THEN
+    RAISE EXCEPTION 'Merge target id cannot be null [xtuple: merge, -3]';
+  ELSIF NOT(EXISTS(SELECT 1 FROM crmacct WHERE crmacct_id=pTargetId)) THEN
+    RAISE EXCEPTION 'Merge target % not found [xtuple: merge, -4, %]',
+                    pTargetId, pTargetId;
+  ELSIF NOT(EXISTS(SELECT 1
+                     FROM crmacctsel
+                    WHERE (crmacctsel_src_crmacct_id=pSourceId)
+                      AND (crmacctsel_dest_crmacct_id=pTargetId))) THEN
+    RAISE EXCEPTION 'Source % and target % have not been selected for merging [xtuple: merge, -5, %, %]',
+                    pSourceId, pTargetId, pSourceId, pTargetId;
+  END IF;
+
+  _result:= changeFkeyPointers('public', 'crmacct', pSourceId, pTargetId,
+                               ARRAY[ 'crmacctsel', 'crmacctmrgd' ], _purge)
+          + changePseudoFKeyPointers('public', 'alarm', 'alarm_source_id',
+                                     pSourceId, 'public', 'crmacct', pTargetId,
+                                     'alarm_source', 'CRMA', _purge)
+          + changePseudoFKeyPointers('public', 'charass', 'charass_target_id',
+                                     pSourceId, 'public', 'crmacct', pTargetId,
+                                     'charass_target_type', 'CRMACCT', _purge)
+          + changePseudoFKeyPointers('public', 'comment', 'comment_source_id',
+                                     pSourceId, 'public', 'crmacct', pTargetId,
+                                     'comment_source', 'CRMA', _purge)
+          + changePseudoFKeyPointers('public', 'docass', 'docass_source_id',
+                                     pSourceId, 'public', 'crmacct', pTargetId,
+                                     'docass_source_type', 'CRMA', _purge)
+          + changePseudoFKeyPointers('public', 'docass', 'docass_target_id',
+                                     pSourceId, 'public', 'crmacct', pTargetId,
+                                     'docass_target_type', 'CRMA', _purge)
+          + changePseudoFKeyPointers('public', 'imageass', 'imageass_source_id',
+                                     pSourceId, 'public', 'crmacct', pTargetId,
+                                     'imageass_source', 'CRMA', _purge)
+          ;
+
+  -- TODO: find a generic way to handle pseudofkeys in packages - see 9401
+  IF (fetchMetricBool('EnableBatchManager') AND packageIsEnabled('xtbatch')) THEN
+    _result:= _result
+            + changePseudoFKeyPointers('xtbatch', 'emlassc', 'emlassc_assc_id',
+                                       pSourceId, 'public', 'crmacct', pTargetId,
+                                       'emlassc_type', 'CRMA', _purge);
+  END IF;
+
+  -- back up all of the values in the target record that are about to be changed
+  FOR _coldesc IN SELECT attname, typname
+                    FROM pg_attribute
+                    JOIN pg_type      ON (atttypid=pg_type.oid)
+                    JOIN pg_class     ON (attrelid=pg_class.oid)
+                    JOIN pg_namespace ON (relnamespace=pg_namespace.oid)
+                   WHERE (attnum >= 0)
+                     AND (relname='crmacct')
+                     AND (nspname='public')
+                     AND (attname NOT IN ('crmacct_id', 'crmacct_number'))
+  LOOP
+
+    -- if we're supposed to merge this column at all
+    EXECUTE 'SELECT ' || quote_ident('crmacctsel_mrg_' || _coldesc.attname) || '
+               FROM crmacctsel
+              WHERE ((crmacctsel_src_crmacct_id='  || pSourceId || ')
+                 AND (crmacctsel_dest_crmacct_id=' || pTargetId || '))' INTO _mrgcol;
+
+    IF (_mrgcol) THEN
+      _colname := REPLACE(_coldesc.attname, 'crmacctsel_mrg_', '');
+
+      -- optionally back up the old value from the destination
+      -- we'll back up the old value from the source further down
+      IF (NOT _purge) THEN
+        BEGIN
+          EXECUTE 'INSERT INTO mrgundo (
+                       mrgundo_schema,      mrgundo_table,
+                       mrgundo_pkey_col,    mrgundo_pkey_id,
+                       mrgundo_col,         mrgundo_value,      mrgundo_type,
+                       mrgundo_base_schema, mrgundo_base_table, mrgundo_base_id
+                 ) SELECT ''public'',     ''crmacct'',
+                          ''crmacct_id'', crmacct_id, '   ||
+                          quote_literal(_colname)         || ', ' ||
+                          quote_ident(_colname)           || ', ' ||
+                          quote_literal(_coldesc.typname) || ',
+                          ''public'', ''crmacct'', crmacct_id
+                     FROM crmacct
+                    WHERE (crmacct_id=' || pTargetId || ');' ;
+        EXCEPTION WHEN unique_violation THEN
+          RAISE EXCEPTION 'Could not make a backup copy of % when merging % into % [xtuple: merge, -8, %, %, public, crmacct, %]',
+                       _colname, pSourceId, pTargetId,
+                       _colname, pSourceId, pTargetId;
+        END;
+      END IF;
+
+      -- TODO: what do we do about users?
+      /* update the destination crmacct in one of 3 different ways:
+         - crmacct_notes might be concatenated from more than one source record
+        - foreign keys to crm account subtype records (e.g. crmacct_cust_id)
+           must not leave orphaned records and must avoid uniqueness violations
+         - some fields can simply be updated in place
+       */
+      IF (_colname = 'crmacct_notes') THEN
+        EXECUTE 'UPDATE crmacct dest
+                    SET '      || quote_ident(_colname) ||
+                      '=dest.' || quote_ident(_colname) ||
+                      E' || E''\\n'' || src.' || _colname || '
+                  FROM crmacct src
+                  JOIN crmacctsel ON (src.crmacct_id=crmacctsel_src_crmacct_id)
+                 WHERE ((dest.crmacct_id=crmacctsel_dest_crmacct_id)
+                    AND (dest.crmacct_id!=crmacctsel_src_crmacct_id));';
+
+      ELSIF (_colname IN ('crmacct_cust_id', 'crmacct_prospect_id', 
+                          'crmacct_vend_id', 'crmacct_taxauth_id',
+                          'crmacct_emp_id',  'crmacct_salesrep_id')) THEN
+        IF (_colname IN ('crmacct_cust_id', 'crmacct_prospect_id')) THEN
+          EXECUTE 'SELECT src.' || quote_ident(_colname) || ' IS NOT NULL
+                      AND (dest.crmacct_prospect_id IS NOT NULL OR
+                           dest.crmacct_cust_id IS NOT NULL)
+                     FROM crmacct src
+                     JOIN crmacctsel ON (src.crmacct_id=crmacctsel_src_crmacct_id)
+                     JOIN crmacct dest ON (crmacctsel_dest_crmacct_id=dest.crmacct_id)
+                    WHERE ((src.crmacct_id='  || pSourceId || ')
+                       AND (dest.crmacct_id=' || pTargetId || '))' INTO _hassubtype;
+          IF (_hassubtype) THEN
+            RAISE EXCEPTION 'Cannot merge two CRM Accounts that both refer to Customers and/or Prospects [xtuple: merge, -6, %, %]',
+                            pSourceId, pTargetId;
+          END IF;
+        ELSE
+          EXECUTE 'SELECT src.' || quote_ident(_colname) || ' IS NOT NULL
+                      AND dest.'|| quote_ident(_colname) || ' IS NOT NULL
+                     FROM crmacct src
+                     JOIN crmacctsel ON (src.crmacct_id=crmacctsel_src_crmacct_id)
+                     JOIN crmacct dest ON (crmacctsel_dest_crmacct_id=dest.crmacct_id)
+                    WHERE ((src.crmacct_id='  || pSourceId || ')
+                       AND (dest.crmacct_id=' || pTargetId || '))' INTO _hassubtype;
+
+          IF (_hassubtype) THEN
+            RAISE EXCEPTION 'Cannot merge CRM Accounts until the % child records have been merged [xtuple: merge, -7, %, %, %]',
+                            _colname, _colname, pSourceId, pTargetId;
+          END IF;
+
+        END IF;
+
+        /* clearing the source separately from setting the target avoids
+           problems with triggers updating the wrong records */
+        EXECUTE 'SELECT ' || quote_ident(_colname) || ' FROM crmacct
+                  WHERE crmacct_id=' || pSourceId
+        INTO _tmpid;
+
+        -- now we have the data to back up the source
+        IF (NOT _purge) THEN
+          BEGIN
+            EXECUTE 'INSERT INTO mrgundo (
+                         mrgundo_schema,      mrgundo_table,
+                         mrgundo_pkey_col,    mrgundo_pkey_id,
+                         mrgundo_col,         mrgundo_value,      mrgundo_type,
+                         mrgundo_base_schema, mrgundo_base_table, mrgundo_base_id
+                   ) SELECT ''public'',     ''crmacct'',
+                            ''crmacct_id'', crmacct_id, '   ||
+                            quote_literal(_colname)         || ', ' ||
+                            quote_ident(_colname)           || ', ' ||
+                            quote_literal(_coldesc.typname) || ',
+                            ''public'', ''crmacct'', '      || pTargetId || '
+                       FROM crmacct
+                      WHERE (crmacct_id=' || pSourceId || ');' ;
+          EXCEPTION WHEN unique_violation THEN
+            RAISE EXCEPTION 'Could not make a backup copy of % when merging % into % [xtuple: merge, -8, %, %, public, crmacct, %]',
+                         _colname, pSourceId, pTargetId,
+                         _colname, pSourceId, pTargetId;
+          END;
+        END IF;
+
+        EXECUTE 'UPDATE crmacct SET ' || quote_ident(_colname) || '=NULL
+              WHERE (crmacct_id=' || pSourceId || ');';
+
+        EXECUTE 'UPDATE crmacct
+                    SET ' || quote_ident(_colname) || '=' || quote_literal(_tmpid) || '
+              WHERE (crmacct_id=' || pTargetId || ');';
+
+      ELSE
+        EXECUTE 'UPDATE crmacct dest
+                    SET '      || quote_ident(_colname) || '
+                        =src.' || quote_ident(_colname) || '
+                  FROM crmacct src
+                 WHERE ((dest.crmacct_id=' || pTargetId || ')
+                    AND (src.crmacct_id='  || pSourceId || '));';
+      END IF;
+
+      GET DIAGNOSTICS _count = ROW_COUNT;
+      _result := _result + _count;
+    END IF;
+
+  END LOOP;
+
+  IF (_purge) THEN
+    DELETE FROM crmacct WHERE crmacct = pSourceId;
+  ELSE
+    INSERT INTO mrgundo (
+           mrgundo_schema,      mrgundo_table,
+           mrgundo_pkey_col,    mrgundo_pkey_id,
+           mrgundo_col,         mrgundo_value,      mrgundo_type,
+           mrgundo_base_schema, mrgundo_base_table, mrgundo_base_id
+    ) SELECT 'public',         'crmacct',
+             'crmacct_id',     pSourceId,
+             'crmacct_active', crmacct_active, 'bool',
+             'public',         'crmacct',       pTargetId
+        FROM crmacct
+       WHERE crmacct_active AND (crmacct_id = pSourceId);
+    GET DIAGNOSTICS _count = ROW_COUNT;
+    IF (_count > 0) THEN
+      _result := _result + _count;
+      UPDATE crmacct SET crmacct_active = false WHERE (crmacct_id=pSourceId);
+    END IF;
+
+    -- make a special record of the source crm account so we can delete it later
+    INSERT INTO mrgundo (
+           mrgundo_schema,      mrgundo_table,
+           mrgundo_pkey_col,    mrgundo_pkey_id,
+           mrgundo_col,         mrgundo_value,      mrgundo_type,
+           mrgundo_base_schema, mrgundo_base_table, mrgundo_base_id
+     ) VALUES (
+           'public',     'crmacct',
+           'crmacct_id', pSourceId,
+           NULL,         NULL,       NULL,
+           'public',     'crmacct', pTargetId);
+  END IF;
+
+  DELETE FROM crmacctsel WHERE (crmacctsel_src_crmacct_id=pSourceId);
+
+  RETURN _result;
+END;
+$$ LANGUAGE 'plpgsql';
+
+COMMENT ON FUNCTION merge2crmaccts(INTEGER, INTEGER, BOOLEAN) IS
+'This function merges two crmacct records as decribed in crmacctsel records. For each field in the crmacctsel record marked TRUE, the data are copied from the crmacct record with crmacct_id=pSourceId to the record with crmacct_id=pTargetId. If the purge argument is TRUE, the source record is deleted. If it is FALSE, then mrgundo records are created so the merge can later be undone.';
diff --git a/foundation-database/public/functions/mergecrmaccts.sql b/foundation-database/public/functions/mergecrmaccts.sql
new file mode 100644 (file)
index 0000000..a5b71ee
--- /dev/null
@@ -0,0 +1,54 @@
+CREATE OR REPLACE FUNCTION mergecrmaccts(INTEGER, BOOLEAN) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pTargetId     ALIAS FOR $1;
+  _purge        BOOLEAN := COALESCE($2, FALSE);
+
+  _retval       INTEGER;
+BEGIN
+
+  /* if crmacctsel says the target should not keep its original
+     notes, clear them.  notes are special because the merge allows
+     concatenating them from multiple sources. this needs to be
+     kept in sync with merge2crmaccts' similar check.
+   */
+  IF NOT (SELECT crmacctsel_mrg_crmacct_notes
+            FROM crmacctsel
+           WHERE crmacctsel_src_crmacct_id=crmacctsel_dest_crmacct_id
+             AND crmacctsel_dest_crmacct_id=pTargetId) THEN
+    IF (NOT _purge) THEN
+      INSERT INTO mrgundo (
+             mrgundo_schema,      mrgundo_table,
+             mrgundo_pkey_col,    mrgundo_pkey_id,
+             mrgundo_col,         mrgundo_value,      mrgundo_type,
+             mrgundo_base_schema, mrgundo_base_table, mrgundo_base_id)
+      SELECT 'public', 'crmacct', crmacct_id,
+             'public', 'crmacct', 'crmacct_id', crmacct_id,
+             'crmacct_notes', crmacct_notes, 'text',
+             'public', 'crmacct', crmacct_id
+        FROM crmacct
+       WHERE (crmacct_id=pTargetId);
+    END IF;
+
+    UPDATE crmacct
+       SET crmacct_notes = ''
+     WHERE (crmacct_id=pTargetId);
+  END IF;
+
+  -- merge the data from the various source records
+  SELECT SUM(merge2crmaccts(crmacctsel_src_crmacct_id, pTargetId, _purge))
+         INTO _retval
+    FROM crmacctsel
+   WHERE ((crmacctsel_dest_crmacct_id=pTargetId)
+      AND (crmacctsel_dest_crmacct_id!=crmacctsel_src_crmacct_id));
+
+  DELETE FROM crmacctsel WHERE crmacctsel_dest_crmacct_id=pTargetId;
+
+  RETURN COALESCE(_retval, 0);
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+COMMENT ON FUNCTION mergecrmaccts(INTEGER, BOOLEAN) IS
+'This function uses the crmacctsel table to merge multiple crmacct records together. Only the merges into the specified target account are performed. Most of the work is done by repeated calls to the merge2crmaccts function. If the purge argument is FALSE, data are kept to allow reversing the merge.';
diff --git a/foundation-database/public/functions/movebomitemdown.sql b/foundation-database/public/functions/movebomitemdown.sql
new file mode 100644 (file)
index 0000000..9f6f0dc
--- /dev/null
@@ -0,0 +1,49 @@
+CREATE OR REPLACE FUNCTION moveBomitemDown(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pBomitemid ALIAS FOR $1;
+  _nextBomitem RECORD;
+
+BEGIN
+
+  SELECT nextbomitem.bomitem_seqnumber AS next_seqnumber,
+         thisbomitem.bomitem_seqnumber AS this_seqnumber,
+         thisbomitem.bomitem_parent_item_id AS parent_item_id,
+         thisbomitem.bomitem_rev_id AS rev_id
+          INTO _nextBomitem
+  FROM bomitem AS nextbomitem, bomitem AS thisbomitem
+  WHERE ((nextbomitem.bomitem_seqnumber > thisbomitem.bomitem_seqnumber)
+   AND (nextbomitem.bomitem_parent_item_id=thisbomitem.bomitem_parent_item_id)
+   AND (nextbomitem.bomitem_rev_id=thisbomitem.bomitem_rev_id)
+   AND (thisbomitem.bomitem_id=pBomitemid))
+  ORDER BY next_seqnumber
+  LIMIT 1;
+
+  IF (FOUND) THEN
+--  Swap the seqnumber of the current bomitem and the next bomitem
+--  There is the potential for multiple bomitems with the same seqnumber
+
+    UPDATE bomitem
+    SET bomitem_seqnumber=0
+    WHERE (bomitem_seqnumber=_nextBomitem.next_seqnumber)
+      AND (bomitem_parent_item_id=_nextBomitem.parent_item_id)
+      AND (bomitem_rev_id=_nextBomitem.rev_id);
+
+    UPDATE bomitem 
+    SET bomitem_seqnumber=_nextBomitem.next_seqnumber
+    WHERE (bomitem_seqnumber=_nextBomitem.this_seqnumber)
+      AND (bomitem_parent_item_id=_nextBomitem.parent_item_id)
+      AND (bomitem_rev_id=_nextBomitem.rev_id);
+
+    UPDATE bomitem
+    SET bomitem_seqnumber=_nextBomitem.this_seqnumber
+    WHERE (bomitem_seqnumber=0)
+      AND (bomitem_parent_item_id=_nextBomitem.parent_item_id)
+      AND (bomitem_rev_id=_nextBomitem.rev_id);
+  END IF;
+
+  RETURN 1;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/movebomitemup.sql b/foundation-database/public/functions/movebomitemup.sql
new file mode 100644 (file)
index 0000000..c509912
--- /dev/null
@@ -0,0 +1,50 @@
+CREATE OR REPLACE FUNCTION moveBomitemUp(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pBomitemid ALIAS FOR $1;
+  _nextBomitem RECORD;
+
+BEGIN
+
+  SELECT nextbomitem.bomitem_seqnumber AS next_seqnumber,
+         thisbomitem.bomitem_seqnumber AS this_seqnumber,
+         thisbomitem.bomitem_parent_item_id AS parent_item_id,
+         thisbomitem.bomitem_rev_id AS rev_id
+          INTO _nextBomitem
+  FROM bomitem AS nextbomitem, bomitem AS thisbomitem
+  WHERE ((nextbomitem.bomitem_seqnumber < thisbomitem.bomitem_seqnumber)
+   AND (nextbomitem.bomitem_parent_item_id=thisbomitem.bomitem_parent_item_id)
+   AND (nextbomitem.bomitem_rev_id=thisbomitem.bomitem_rev_id)
+   AND (thisbomitem.bomitem_id=pBomitemid))
+  ORDER BY next_seqnumber DESC
+  LIMIT 1;
+
+  IF (FOUND) THEN
+--  Swap the seqnumber of the current bomitem and the next bomitem
+--  There is the potential for multiple bomitems with the same seqnumber
+
+    UPDATE bomitem
+    SET bomitem_seqnumber=0
+    WHERE (bomitem_seqnumber=_nextBomitem.next_seqnumber)
+      AND (bomitem_parent_item_id=_nextBomitem.parent_item_id)
+      AND (bomitem_rev_id=_nextBomitem.rev_id);
+
+    UPDATE bomitem 
+    SET bomitem_seqnumber=_nextBomitem.next_seqnumber
+    WHERE (bomitem_seqnumber=_nextBomitem.this_seqnumber)
+      AND (bomitem_parent_item_id=_nextBomitem.parent_item_id)
+      AND (bomitem_rev_id=_nextBomitem.rev_id);
+
+    UPDATE bomitem
+    SET bomitem_seqnumber=_nextBomitem.this_seqnumber
+    WHERE (bomitem_seqnumber=0)
+      AND (bomitem_parent_item_id=_nextBomitem.parent_item_id)
+      AND (bomitem_rev_id=_nextBomitem.rev_id);
+  END IF;
+
+  RETURN 1;
+
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/moveccarddown.sql b/foundation-database/public/functions/moveccarddown.sql
new file mode 100644 (file)
index 0000000..8ad0109
--- /dev/null
@@ -0,0 +1,37 @@
+CREATE OR REPLACE FUNCTION moveccarddown(int4)
+  RETURNS int4 AS
+'
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCcardid ALIAS FOR $1;
+  _nextCcard RECORD;
+
+BEGIN
+
+  SELECT nextCcard.ccard_id, nextCcard.ccard_seq AS next_seqnumber,
+         thisCcard.ccard_seq AS this_seqnumber INTO _nextCcard
+  FROM Ccard AS nextCcard, Ccard AS thisCcard
+  WHERE ((nextCcard.ccard_seq > thisCcard.ccard_seq)
+   AND (nextCcard.ccard_cust_id=thisCcard.ccard_cust_id)
+   AND (thisCcard.ccard_id=pCcardid))
+  ORDER BY next_seqnumber
+  LIMIT 1;
+
+  IF (FOUND) THEN
+--  Swap the seqnumber of the current Ccard and the next Ccard
+
+    UPDATE Ccard
+    SET ccard_seq=_nextCcard.next_seqnumber
+    WHERE (ccard_id=pCcardid);
+
+    UPDATE Ccard
+    SET ccard_seq=_nextCcard.this_seqnumber
+    WHERE (ccard_id=_nextCcard.ccard_id);
+  END IF;
+
+  RETURN 1;
+
+END;
+'
+  LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/moveccardup.sql b/foundation-database/public/functions/moveccardup.sql
new file mode 100644 (file)
index 0000000..91e7a40
--- /dev/null
@@ -0,0 +1,37 @@
+CREATE OR REPLACE FUNCTION moveccardup(int4)
+  RETURNS int4 AS
+'
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCcardid ALIAS FOR $1;
+  _nextCcard RECORD;
+
+BEGIN
+
+  SELECT nextCcard.ccard_id AS ccard_id, nextCcard.ccard_seq AS next_seqnumber,
+         thisCcard.ccard_seq AS this_seqnumber INTO _nextCcard
+  FROM ccard AS nextCcard, ccard AS thisCcard
+  WHERE ((nextCcard.ccard_seq < thisCcard.ccard_seq)
+   AND (nextCcard.ccard_cust_id=thisCcard.ccard_cust_id)
+   AND (thisCcard.ccard_id=pCcardid))
+  ORDER BY next_seqnumber DESC
+  LIMIT 1;
+
+  IF (FOUND) THEN
+--  Swap the seqnumber of the current Ccard and the next Ccard
+
+    UPDATE Ccard 
+    SET ccard_seq=_nextCcard.next_seqnumber
+    WHERE (ccard_id=pCcardid);
+
+    UPDATE Ccard
+    SET ccard_seq=_nextCcard.this_seqnumber
+    WHERE (ccard_id=_nextCcard.ccard_id);
+  END IF;
+
+  RETURN 1;
+
+END;
+'
+  LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/moveflgroupdown.sql b/foundation-database/public/functions/moveflgroupdown.sql
new file mode 100644 (file)
index 0000000..361939b
--- /dev/null
@@ -0,0 +1,66 @@
+CREATE OR REPLACE FUNCTION moveFlGroupDown(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pFlgrpid ALIAS FOR $1;
+  _from RECORD;
+  _to RECORD;
+
+BEGIN
+
+  SELECT flgrp_id AS id,
+         flgrp_flhead_id AS flhead_id,
+         flgrp_flgrp_id AS flgrp_id,
+         flgrp_order AS ord INTO _from
+    FROM flgrp
+   WHERE (flgrp_id=pFlgrpid);
+  IF (NOT FOUND) THEN
+    RETURN -1;
+  END IF;
+
+  SELECT id, type, ord INTO _to
+    FROM (SELECT flitem_id AS id, ''I'' AS type, flitem_order AS ord
+            FROM flitem
+           WHERE ((flitem_flgrp_id=_from.flgrp_id)
+             AND  (flitem_flhead_id=_from.flhead_id))
+           UNION
+          SELECT flgrp_id AS id, ''G'' AS type, flgrp_order AS ord
+            FROM flgrp
+           WHERE ((flgrp_flgrp_id=_from.flgrp_id)
+             AND  (flgrp_flhead_id=_from.flhead_id))
+           UNION
+          SELECT flspec_id AS id, ''S'' AS type, flspec_order AS ord
+            FROM flspec
+           WHERE ((flspec_flgrp_id=_from.flgrp_id)
+             AND  (flspec_flhead_id=_from.flhead_id)) ) AS data
+   WHERE (ord > _from.ord)
+   ORDER BY ord
+   LIMIT 1;
+  IF (FOUND) THEN
+    UPDATE flgrp
+       SET flgrp_order=_to.ord
+     WHERE (flgrp_id=_from.id);
+
+    IF (_to.type=''I'') THEN
+      UPDATE flitem
+         SET flitem_order=_from.ord
+       WHERE (flitem_id=_to.id);
+    ELSE
+      IF (_to.type=''G'') THEN
+        UPDATE flgrp
+           SET flgrp_order=_from.ord
+         WHERE (flgrp_id=_to.id);
+      ELSE
+        IF (_to.type=''S'') THEN
+          UPDATE flspec
+             SET flspec_order=_from.ord
+           WHERE (flspec_id=_to.id);
+        END IF;
+      END IF;
+    END IF;
+  END IF;
+
+  RETURN 0;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/moveflgroupup.sql b/foundation-database/public/functions/moveflgroupup.sql
new file mode 100644 (file)
index 0000000..6f28386
--- /dev/null
@@ -0,0 +1,66 @@
+CREATE OR REPLACE FUNCTION moveFlGroupUp(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pFlgrpid ALIAS FOR $1;
+  _from RECORD;
+  _to RECORD;
+
+BEGIN
+
+  SELECT flgrp_id AS id,
+         flgrp_flhead_id AS flhead_id,
+         flgrp_flgrp_id AS flgrp_id,
+         flgrp_order AS ord INTO _from
+    FROM flgrp
+   WHERE (flgrp_id=pFlgrpid);
+  IF (NOT FOUND) THEN
+    RETURN -1;
+  END IF;
+
+  SELECT id, type, ord INTO _to
+    FROM (SELECT flitem_id AS id, ''I'' AS type, flitem_order AS ord
+            FROM flitem
+           WHERE ((flitem_flgrp_id=_from.flgrp_id)
+             AND  (flitem_flhead_id=_from.flhead_id))
+           UNION
+          SELECT flgrp_id AS id, ''G'' AS type, flgrp_order AS ord
+            FROM flgrp
+           WHERE ((flgrp_flgrp_id=_from.flgrp_id)
+             AND  (flgrp_flhead_id=_from.flhead_id))
+           UNION
+          SELECT flspec_id AS id, ''S'' AS type, flspec_order AS ord
+            FROM flspec
+           WHERE ((flspec_flgrp_id=_from.flgrp_id)
+             AND  (flspec_flhead_id=_from.flhead_id)) ) AS data
+   WHERE (ord < _from.ord)
+   ORDER BY ord DESC
+   LIMIT 1;
+  IF (FOUND) THEN
+    UPDATE flgrp
+       SET flgrp_order=_to.ord
+     WHERE (flgrp_id=_from.id);
+
+    IF (_to.type=''I'') THEN
+      UPDATE flitem
+         SET flitem_order=_from.ord
+       WHERE (flitem_id=_to.id);
+    ELSE
+      IF (_to.type=''G'') THEN
+        UPDATE flgrp
+           SET flgrp_order=_from.ord
+         WHERE (flgrp_id=_to.id);
+      ELSE
+        IF (_to.type=''S'') THEN
+          UPDATE flspec
+             SET flspec_order=_from.ord
+           WHERE (flspec_id=_to.id);
+        END IF;
+      END IF;
+    END IF;
+  END IF;
+
+  RETURN 0;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/moveflitemdown.sql b/foundation-database/public/functions/moveflitemdown.sql
new file mode 100644 (file)
index 0000000..ce62691
--- /dev/null
@@ -0,0 +1,66 @@
+CREATE OR REPLACE FUNCTION moveFlItemDown(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pFlitemid ALIAS FOR $1;
+  _from RECORD;
+  _to RECORD;
+
+BEGIN
+
+  SELECT flitem_id AS id,
+         flitem_flhead_id AS flhead_id,
+         flitem_flgrp_id AS flgrp_id,
+         flitem_order AS ord INTO _from
+    FROM flitem
+   WHERE (flitem_id=pFlitemid);
+  IF (NOT FOUND) THEN
+    RETURN -1;
+  END IF;
+
+  SELECT id, type, ord INTO _to
+    FROM (SELECT flitem_id AS id, ''I'' AS type, flitem_order AS ord
+            FROM flitem
+           WHERE ((flitem_flgrp_id=_from.flgrp_id)
+             AND  (flitem_flhead_id=_from.flhead_id))
+           UNION
+          SELECT flgrp_id AS id, ''G'' AS type, flgrp_order AS ord
+            FROM flgrp
+           WHERE ((flgrp_flgrp_id=_from.flgrp_id)
+             AND  (flgrp_flhead_id=_from.flhead_id))
+           UNION
+          SELECT flspec_id AS id, ''S'' AS type, flspec_order AS ord
+            FROM flspec
+           WHERE ((flspec_flgrp_id=_from.flgrp_id)
+             AND  (flspec_flhead_id=_from.flhead_id)) ) AS data
+   WHERE (ord > _from.ord)
+   ORDER BY ord
+   LIMIT 1;
+  IF (FOUND) THEN
+    UPDATE flitem
+       SET flitem_order=_to.ord
+     WHERE (flitem_id=_from.id);
+
+    IF (_to.type=''I'') THEN
+      UPDATE flitem
+         SET flitem_order=_from.ord
+       WHERE (flitem_id=_to.id);
+    ELSE
+      IF (_to.type=''G'') THEN
+        UPDATE flgrp
+           SET flgrp_order=_from.ord
+         WHERE (flgrp_id=_to.id);
+      ELSE
+        IF (_to.type=''S'') THEN
+          UPDATE flspec
+             SET flspec_order=_from.ord
+           WHERE (flspec_id=_to.id);
+        END IF;
+      END IF;
+    END IF;
+  END IF;
+
+  RETURN 0;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/moveflitemup.sql b/foundation-database/public/functions/moveflitemup.sql
new file mode 100644 (file)
index 0000000..40b61af
--- /dev/null
@@ -0,0 +1,66 @@
+CREATE OR REPLACE FUNCTION moveFlItemUp(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pFlitemid ALIAS FOR $1;
+  _from RECORD;
+  _to RECORD;
+
+BEGIN
+
+  SELECT flitem_id AS id,
+         flitem_flhead_id AS flhead_id,
+         flitem_flgrp_id AS flgrp_id,
+         flitem_order AS ord INTO _from
+    FROM flitem
+   WHERE (flitem_id=pFlitemid);
+  IF (NOT FOUND) THEN
+    RETURN -1;
+  END IF;
+
+  SELECT id, type, ord INTO _to
+    FROM (SELECT flitem_id AS id, ''I'' AS type, flitem_order AS ord
+            FROM flitem
+           WHERE ((flitem_flgrp_id=_from.flgrp_id)
+             AND  (flitem_flhead_id=_from.flhead_id))
+           UNION
+          SELECT flgrp_id AS id, ''G'' AS type, flgrp_order AS ord
+            FROM flgrp
+           WHERE ((flgrp_flgrp_id=_from.flgrp_id)
+             AND  (flgrp_flhead_id=_from.flhead_id))
+           UNION
+          SELECT flspec_id AS id, ''S'' AS type, flspec_order AS ord
+            FROM flspec
+           WHERE ((flspec_flgrp_id=_from.flgrp_id)
+             AND  (flspec_flhead_id=_from.flhead_id)) ) AS data
+   WHERE (ord < _from.ord)
+   ORDER BY ord DESC
+   LIMIT 1;
+  IF (FOUND) THEN
+    UPDATE flitem
+       SET flitem_order=_to.ord
+     WHERE (flitem_id=_from.id);
+
+    IF (_to.type=''I'') THEN
+      UPDATE flitem
+         SET flitem_order=_from.ord
+       WHERE (flitem_id=_to.id);
+    ELSE
+      IF (_to.type=''G'') THEN
+        UPDATE flgrp
+           SET flgrp_order=_from.ord
+         WHERE (flgrp_id=_to.id);
+      ELSE
+        IF (_to.type=''S'') THEN
+          UPDATE flspec
+             SET flspec_order=_from.ord
+           WHERE (flspec_id=_to.id);
+        END IF;
+      END IF;
+    END IF;
+  END IF;
+
+  RETURN 0;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/moveflspecdown.sql b/foundation-database/public/functions/moveflspecdown.sql
new file mode 100644 (file)
index 0000000..5e9f2e9
--- /dev/null
@@ -0,0 +1,66 @@
+CREATE OR REPLACE FUNCTION moveFlSpecDown(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pFlspecid ALIAS FOR $1;
+  _from RECORD;
+  _to RECORD;
+
+BEGIN
+
+  SELECT flspec_id AS id,
+         flspec_flhead_id AS flhead_id,
+         flspec_flgrp_id AS flgrp_id,
+         flspec_order AS ord INTO _from
+    FROM flspec
+   WHERE (flspec_id=pFlspecid);
+  IF (NOT FOUND) THEN
+    RETURN -1;
+  END IF;
+
+  SELECT id, type, ord INTO _to
+    FROM (SELECT flitem_id AS id, ''I'' AS type, flitem_order AS ord
+            FROM flitem
+           WHERE ((flitem_flgrp_id=_from.flgrp_id)
+             AND  (flitem_flhead_id=_from.flhead_id))
+           UNION
+          SELECT flgrp_id AS id, ''G'' AS type, flgrp_order AS ord
+            FROM flgrp
+           WHERE ((flgrp_flgrp_id=_from.flgrp_id)
+             AND  (flgrp_flhead_id=_from.flhead_id))
+           UNION
+          SELECT flspec_id AS id, ''S'' AS type, flspec_order AS ord
+            FROM flspec
+           WHERE ((flspec_flgrp_id=_from.flgrp_id)
+             AND  (flspec_flhead_id=_from.flhead_id)) ) AS data
+   WHERE (ord > _from.ord)
+   ORDER BY ord
+   LIMIT 1;
+  IF (FOUND) THEN
+    UPDATE flspec
+       SET flspec_order=_to.ord
+     WHERE (flspec_id=_from.id);
+
+    IF (_to.type=''I'') THEN
+      UPDATE flitem
+         SET flitem_order=_from.ord
+       WHERE (flitem_id=_to.id);
+    ELSE
+      IF (_to.type=''G'') THEN
+        UPDATE flgrp
+           SET flgrp_order=_from.ord
+         WHERE (flgrp_id=_to.id);
+      ELSE
+        IF (_to.type=''S'') THEN
+          UPDATE flspec
+             SET flspec_order=_from.ord
+           WHERE (flspec_id=_to.id);
+        END IF;
+      END IF;
+    END IF;
+  END IF;
+
+  RETURN 0;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/moveflspecup.sql b/foundation-database/public/functions/moveflspecup.sql
new file mode 100644 (file)
index 0000000..f0d61ad
--- /dev/null
@@ -0,0 +1,66 @@
+CREATE OR REPLACE FUNCTION moveFlSpecUp(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pFlspecid ALIAS FOR $1;
+  _from RECORD;
+  _to RECORD;
+
+BEGIN
+
+  SELECT flspec_id AS id,
+         flspec_flhead_id AS flhead_id,
+         flspec_flgrp_id AS flgrp_id,
+         flspec_order AS ord INTO _from
+    FROM flspec
+   WHERE (flspec_id=pFlspecid);
+  IF (NOT FOUND) THEN
+    RETURN -1;
+  END IF;
+
+  SELECT id, type, ord INTO _to
+    FROM (SELECT flitem_id AS id, ''I'' AS type, flitem_order AS ord
+            FROM flitem
+           WHERE ((flitem_flgrp_id=_from.flgrp_id)
+             AND  (flitem_flhead_id=_from.flhead_id))
+           UNION
+          SELECT flgrp_id AS id, ''G'' AS type, flgrp_order AS ord
+            FROM flgrp
+           WHERE ((flgrp_flgrp_id=_from.flgrp_id)
+             AND  (flgrp_flhead_id=_from.flhead_id))
+           UNION
+          SELECT flspec_id AS id, ''S'' AS type, flspec_order AS ord
+            FROM flspec
+           WHERE ((flspec_flgrp_id=_from.flgrp_id)
+             AND  (flspec_flhead_id=_from.flhead_id)) ) AS data
+   WHERE (ord < _from.ord)
+   ORDER BY ord DESC
+   LIMIT 1;
+  IF (FOUND) THEN
+    UPDATE flspec
+       SET flspec_order=_to.ord
+     WHERE (flspec_id=_from.id);
+
+    IF (_to.type=''I'') THEN
+      UPDATE flitem
+         SET flitem_order=_from.ord
+       WHERE (flitem_id=_to.id);
+    ELSE
+      IF (_to.type=''G'') THEN
+        UPDATE flgrp
+           SET flgrp_order=_from.ord
+         WHERE (flgrp_id=_to.id);
+      ELSE
+        IF (_to.type=''S'') THEN
+          UPDATE flspec
+             SET flspec_order=_from.ord
+           WHERE (flspec_id=_to.id);
+        END IF;
+      END IF;
+    END IF;
+  END IF;
+
+  RETURN 0;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/movescript.sql b/foundation-database/public/functions/movescript.sql
new file mode 100644 (file)
index 0000000..3f317cd
--- /dev/null
@@ -0,0 +1,85 @@
+CREATE OR REPLACE FUNCTION moveScript(INTEGER, INTEGER, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pscriptid ALIAS FOR $1;
+  poldpkgid ALIAS FOR $2;
+  pnewpkgid ALIAS FOR $3;
+
+  _deletestr    TEXT;
+  _destination  TEXT;
+  _insertstr    TEXT;
+  _rows         INTEGER;
+  _selectstr    TEXT;
+  _source       TEXT;
+  _record       RECORD;
+
+BEGIN
+  IF (poldpkgid = pnewpkgid) THEN
+    RETURN 0;
+  END IF;
+
+  IF (poldpkgid = -1) THEN
+    _source = 'public.script';
+  ELSE
+    SELECT pkghead_name || '.pkgscript' INTO _source
+    FROM pkghead
+    WHERE pkghead_id=poldpkgid;
+
+    IF NOT FOUND THEN
+      RETURN -1;
+    END IF;
+  END IF;
+
+  IF (pnewpkgid = -1) THEN
+    _destination = 'public.script';
+  ELSE
+    SELECT pkghead_name || '.pkgscript' INTO _destination
+    FROM pkghead
+    WHERE pkghead_id=pnewpkgid;
+
+    IF NOT FOUND THEN
+      RETURN -2;
+    END IF;
+  END IF;
+
+  _selectstr := ' SELECT * FROM ' || _source ||
+                ' WHERE script_id = ' || pscriptid;
+  EXECUTE _selectstr INTO _record;
+
+  _deletestr := 'DELETE FROM ONLY ' || _source || 
+                ' WHERE script_id = ' || pscriptid;
+  EXECUTE _deletestr;
+  GET DIAGNOSTICS _rows = ROW_COUNT;
+  RAISE NOTICE '% rows from %', _rows, _deletestr;
+  IF (_rows < 1) THEN
+    RETURN -3;
+  ELSIF (_rows > 1) THEN
+    RAISE EXCEPTION 'Tried to delete % scripts with the id % when there should be exactly 1',
+                    _rows, pscriptid;
+  END IF;
+
+  _insertstr := 'INSERT INTO ' || _destination ||
+                ' (script_id, script_name, script_order, script_enabled, ' ||
+                '  script_source, script_notes) VALUES ('
+                || _record.script_id      || ','
+                || quote_literal(_record.script_name)    || ','
+                || _record.script_order   || ','
+                || _record.script_enabled || ','
+                || quote_literal(_record.script_source)  || ','
+                || quote_literal(_record.script_notes )  || ');'
+                ;
+  EXECUTE _insertstr;
+  GET DIAGNOSTICS _rows = ROW_COUNT;
+  RAISE NOTICE '% rows from %', _rows, _insertstr;
+  IF (_rows < 1) THEN
+    RETURN -4;
+  ELSIF (_rows > 1) THEN
+    RAISE EXCEPTION 'Tried to insert % scripts with the id % when there should be exactly 1',
+                    _rows, pscriptid;
+  END IF;
+
+  RETURN pscriptid;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/moveuiform.sql b/foundation-database/public/functions/moveuiform.sql
new file mode 100644 (file)
index 0000000..94a234f
--- /dev/null
@@ -0,0 +1,85 @@
+CREATE OR REPLACE FUNCTION moveUiform(INTEGER, INTEGER, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  puiformid ALIAS FOR $1;
+  poldpkgid ALIAS FOR $2;
+  pnewpkgid ALIAS FOR $3;
+
+  _deletestr    TEXT;
+  _destination  TEXT;
+  _insertstr    TEXT;
+  _rows         INTEGER;
+  _selectstr    TEXT;
+  _source       TEXT;
+  _record       RECORD;
+
+BEGIN
+  IF (poldpkgid = pnewpkgid) THEN
+    RETURN 0;
+  END IF;
+
+  IF (poldpkgid = -1) THEN
+    _source = 'public.uiform';
+  ELSE
+    SELECT pkghead_name || '.pkguiform' INTO _source
+    FROM pkghead
+    WHERE pkghead_id=poldpkgid;
+
+    IF NOT FOUND THEN
+      RETURN -1;
+    END IF;
+  END IF;
+
+  IF (pnewpkgid = -1) THEN
+    _destination = 'public.uiform';
+  ELSE
+    SELECT pkghead_name || '.pkguiform' INTO _destination
+    FROM pkghead
+    WHERE pkghead_id=pnewpkgid;
+
+    IF NOT FOUND THEN
+      RETURN -2;
+    END IF;
+  END IF;
+
+  _selectstr := ' SELECT * FROM ' || _source ||
+                ' WHERE uiform_id = ' || puiformid;
+  EXECUTE _selectstr INTO _record;
+
+  _deletestr := 'DELETE FROM ONLY ' || _source || 
+                ' WHERE uiform_id = ' || puiformid;
+  EXECUTE _deletestr;
+  GET DIAGNOSTICS _rows = ROW_COUNT;
+  RAISE NOTICE '% rows from %', _rows, _deletestr;
+  IF (_rows < 1) THEN
+    RETURN -3;
+  ELSIF (_rows > 1) THEN
+    RAISE EXCEPTION 'Tried to delete % uiforms with the id % when there should be exactly 1',
+                    _rows, puiformid;
+  END IF;
+
+  _insertstr := 'INSERT INTO ' || _destination ||
+                ' (uiform_id, uiform_name, uiform_order, uiform_enabled, ' ||
+                '  uiform_source, uiform_notes) VALUES ('
+                || _record.uiform_id      || ','
+                || quote_literal(_record.uiform_name)    || ','
+                || _record.uiform_order   || ','
+                || _record.uiform_enabled || ','
+                || quote_literal(_record.uiform_source)  || ','
+                || quote_literal(_record.uiform_notes )  || ');'
+                ;
+  EXECUTE _insertstr;
+  GET DIAGNOSTICS _rows = ROW_COUNT;
+  RAISE NOTICE '% rows from %', _rows, _insertstr;
+  IF (_rows < 1) THEN
+    RETURN -4;
+  ELSIF (_rows > 1) THEN
+    RAISE EXCEPTION 'Tried to insert % uiforms with the id % when there should be exactly 1',
+                    _rows, puiformid;
+  END IF;
+
+  RETURN puiformid;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/moveupdown.sql b/foundation-database/public/functions/moveupdown.sql
new file mode 100644 (file)
index 0000000..93e9c21
--- /dev/null
@@ -0,0 +1,104 @@
+CREATE OR REPLACE FUNCTION moveUpDown(pId      INTEGER,
+                                      pSchema  TEXT,
+                                      pTable   TEXT,
+                                      pSeqCol  TEXT, 
+                                      pJoinCol TEXT,
+                                      pExtra   TEXT,
+                                      pDir     TEXT) RETURNS INTEGER AS $$
+DECLARE
+  _keyfield TEXT;
+  _keysize  INTEGER;
+  _qry      TEXT;
+  _r        RECORD;
+  _rowcnt   INTEGER;
+  _schema   TEXT := COALESCE(pSchema, 'public');
+BEGIN
+  RAISE DEBUG 'moveUpDown(%, %, %, %, %, %, %) entered',
+              pId, pSchema, pTable, pSeqCol, pJoinCol, pExtra, pDir;
+
+  IF (UPPER(pDir) NOT IN ('UP', 'DOWN')) THEN
+     RAISE EXCEPTION 'Cannot change the order of records; unsure what % means for sequencing [xtuple: moveUpDown, -1, %, %.%]',
+                     pDir, pDir, _schema, pTable;
+  END IF;
+
+  SELECT attname, ARRAY_UPPER(conkey, 1) INTO _keyfield, _keysize
+    FROM pg_attribute
+    JOIN pg_constraint ON (attrelid=conrelid AND attnum=conkey[1])
+    JOIN pg_class      ON (conrelid=pg_class.oid)
+    JOIN pg_namespace  ON (relnamespace=pg_namespace.oid)
+   WHERE ((contype='p')
+      AND (nspname=_schema)
+      AND (relname=pTable));
+
+  RAISE DEBUG 'SELECT attname... returned %, %', _keyfield, _keysize;
+
+  IF (_keysize > 1) THEN
+    RAISE EXCEPTION 'Cannot change the order of records because %.% has a composite primary key [xtuple: moveUpDown, -2, %.%]',
+                     _schema, pTable,
+                     _schema, pTable;
+  END IF;
+
+  /* SELECT next._keyfield AS nextid,
+            next.pSeqCol   AS nextseq,
+            this.pSeqCol   AS thisseq
+       FROM _schema.pTable AS next,
+            _schema.pTable AS this
+      WHERE (this._keyfield=$1)
+          AND (next.pSeqCol [> or <] this.pSeqCol)
+        [ AND (next.pJoinCol=this.pJoinCol) ]
+        [ AND (pExtra) ]
+      ORDER BY nextseq [ DESC or ASC ]
+      LIMIT 1;
+  */
+
+  _qry := 'SELECT next.' || quote_ident(_keyfield) || ' AS nextid,
+                  next.' || quote_ident(pSeqCol)   || ' AS nextseq,
+                  this.' || quote_ident(pSeqCol)   || ' AS thisseq
+             FROM ' || _schema || '.' || quote_ident(pTable) || ' AS next,
+                  ' || _schema || '.' || quote_ident(pTable) || ' AS this
+            WHERE ((this.' || quote_ident(_keyfield)  || '=$1)
+               AND (next.' || quote_ident(pSeqCol)    ||
+                    CASE pDir WHEN 'UP' THEN ' < ' ELSE ' > ' END ||
+                   'this.' || quote_ident(pSeqCol) || ')' ||
+               CASE WHEN pJoinCol IS NULL THEN ''
+                    ELSE ' AND (next.' || quote_ident(pJoinCol)  ||
+                         '=this.' || quote_ident(pJoinCol) || ')'
+               END ||
+          '     AND (' || COALESCE(pExtra, 'TRUE') || '))
+            ORDER BY nextseq ' ||
+               CASE pDir WHEN 'UP' THEN 'DESC' ELSE 'ASC' END ||
+          ' LIMIT 1;';
+  RAISE DEBUG 'moveUpDown about to use % when running %', pId, _qry;
+
+  EXECUTE _qry INTO _r USING pId;
+  GET DIAGNOSTICS _rowcnt = ROW_COUNT;
+  RAISE DEBUG 'next id %, next seq %, this id %, this seq %',
+               _r.nextid, _r.nextseq, pId, _r.thisseq;
+
+  IF (_rowcnt > 0) THEN
+    _qry := 'UPDATE ' || _schema || '.' || quote_ident(pTable)  ||
+              ' SET ' || pSeqCol || '=CAST($1 AS INTEGER)
+             WHERE (' || quote_ident(_keyfield) || '=$2);';
+
+    EXECUTE _qry USING -1,         _r.nextid;
+    EXECUTE _qry USING _r.nextseq, pId;
+    EXECUTE _qry USING _r.thisseq, _r.nextid;
+    RETURN _r.nextid;
+  END IF;
+
+  RETURN pId;
+END;
+$$
+LANGUAGE 'plpgsql';
+
+COMMENT ON FUNCTION moveUpDown(INTEGER, TEXT, TEXT, TEXT, TEXT, TEXT, TEXT) IS
+'moveUpDown moves a particular record up or down in an ordered list.
+      pId argument names the record to move.
+      pSchema (uses public if NULL) and pTable name the table holding the list.
+      pSeqCol is the column that holds the sequence number.
+      pJoinCol is the column that distinguishes one list from another in the same table, or NULL if the table holds only one list.
+      pExtra is an extra join clause that may be required, or NULL.
+      pDir is either UP, meaning move the pId record closer to the beginning,
+                or DOWN.
+Returns the id of the record with which pId was swapped,
+      or pId if the record was already at the end in the specified direction.';
diff --git a/foundation-database/public/functions/nextperiodbyinterval.sql b/foundation-database/public/functions/nextperiodbyinterval.sql
new file mode 100644 (file)
index 0000000..a81757a
--- /dev/null
@@ -0,0 +1,22 @@
+
+CREATE OR REPLACE FUNCTION nextPeriodByInterval(INTEGER, INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pPeriodid ALIAS FOR $1;
+  pInterval ALIAS FOR $2;
+  _periodid INTEGER;
+BEGIN
+  SELECT b.period_id INTO _periodid
+    FROM period AS a, period AS b
+   WHERE ((a.period_id=pPeriodid)
+     AND  (b.period_start >= a.period_start))
+   ORDER BY b.period_start
+   LIMIT 1 OFFSET pInterval;
+  IF (NOT FOUND) THEN
+    RETURN -1;
+  END IF;
+  RETURN _periodid;
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/nextprsubnumber.sql b/foundation-database/public/functions/nextprsubnumber.sql
new file mode 100644 (file)
index 0000000..6fdb0b5
--- /dev/null
@@ -0,0 +1,21 @@
+CREATE OR REPLACE FUNCTION nextPrSubnumber(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pPrNumber ALIAS FOR $1;
+  _subNumber INTEGER;
+
+BEGIN
+
+  SELECT MAX(pr_subnumber) INTO _subNumber
+  FROM pr
+  WHERE (pr_number=pPrNumber);
+
+  IF (_subNumber IS NULL)
+    THEN _subNumber := 0;
+  END IF;
+
+  RETURN (_subNumber + 1);
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/nextwosubnumber.sql b/foundation-database/public/functions/nextwosubnumber.sql
new file mode 100644 (file)
index 0000000..95e600f
--- /dev/null
@@ -0,0 +1,7 @@
+CREATE OR REPLACE FUNCTION nextWoSubnumber(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+SELECT COALESCE((MAX(wo_subnumber) + 1), 1)
+FROM wo
+WHERE (wo_number=($1));
+$$ LANGUAGE 'sql';
diff --git a/foundation-database/public/functions/noneg.sql b/foundation-database/public/functions/noneg.sql
new file mode 100644 (file)
index 0000000..e114cbd
--- /dev/null
@@ -0,0 +1,18 @@
+
+CREATE OR REPLACE FUNCTION noNeg(NUMERIC) RETURNS NUMERIC IMMUTABLE AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pValue ALIAS FOR $1;
+
+BEGIN
+
+  IF (pValue < 0) THEN
+    RETURN 0;
+  ELSE
+   RETURN pValue;
+  END IF;
+
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/nopos.sql b/foundation-database/public/functions/nopos.sql
new file mode 100644 (file)
index 0000000..efcd388
--- /dev/null
@@ -0,0 +1,18 @@
+
+CREATE OR REPLACE FUNCTION noPos(NUMERIC) RETURNS NUMERIC IMMUTABLE AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pValue ALIAS FOR $1;
+
+BEGIN
+
+  IF (pValue > 0) THEN
+    RETURN 0;
+  ELSE
+   RETURN pValue;
+  END IF;
+
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/normalizetrialbal.sql b/foundation-database/public/functions/normalizetrialbal.sql
new file mode 100644 (file)
index 0000000..7919c46
--- /dev/null
@@ -0,0 +1,39 @@
+
+CREATE OR REPLACE FUNCTION normalizeTrialBal(INTEGER, CHARACTER(1)) RETURNS NUMERIC STABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pTrialbalid ALIAS FOR $1;
+  pSide ALIAS FOR $2;
+  _value NUMERIC;
+  _r RECORD;
+
+BEGIN
+
+  SELECT accnt_type, trialbal_beginning, trialbal_ending INTO _r
+  FROM trialbal, accnt
+  WHERE ( (trialbal_accnt_id=accnt_id)
+   AND (trialbal_id=pTrialbalid) );
+  IF (NOT FOUND) THEN
+    RETURN -1;
+  END IF;
+
+-- If we are looking for the Ending Balance, cache it
+  IF (pSide = 'E') THEN
+    _value = _r.trialbal_ending;
+
+--  We had better been looking for the Beginning Balance!
+  ELSE
+    _value = _r.trialbal_beginning;
+  END IF;
+
+--  If the accnt_type is Asset or Expense, swap the sense
+  IF (_r.accnt_type IN ('A', 'E')) THEN
+    _value := (_value * -1);
+  END IF;
+
+  RETURN _value;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/numofdatabaseusers.sql b/foundation-database/public/functions/numofdatabaseusers.sql
new file mode 100644 (file)
index 0000000..0c36b99
--- /dev/null
@@ -0,0 +1,25 @@
+
+CREATE OR REPLACE FUNCTION numOfDatabaseUsers() RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _count INTEGER;
+
+BEGIN
+
+  SELECT count(*)
+    INTO _count
+    FROM pg_stat_activity, pg_locks
+   WHERE((database=datid)
+     AND (classid=datid)
+     AND (objsubid=2)
+     AND (procpid = pg_backend_pid()));
+  IF (_count IS NULL) THEN
+    _count := 0;
+  END IF;
+
+  RETURN _count;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/numofserverusers.sql b/foundation-database/public/functions/numofserverusers.sql
new file mode 100644 (file)
index 0000000..2206289
--- /dev/null
@@ -0,0 +1,20 @@
+
+CREATE OR REPLACE FUNCTION numOfServerUsers() RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _count INTEGER;
+
+BEGIN
+
+  SELECT COUNT(*) INTO _count
+  FROM pg_stat_activity;
+  IF (_count IS NULL) THEN
+    _count := 0;
+  END IF;
+
+  RETURN _count;
+
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/openaccountingperiod.sql b/foundation-database/public/functions/openaccountingperiod.sql
new file mode 100644 (file)
index 0000000..b3913d0
--- /dev/null
@@ -0,0 +1,55 @@
+
+CREATE OR REPLACE FUNCTION openAccountingPeriod(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pPeriodid ALIAS FOR $1;
+  _r RECORD;
+
+BEGIN
+
+--  Check to make use that the period is closed
+  IF ( ( SELECT (NOT period_closed)
+         FROM period
+         WHERE (period_id=pPeriodid) ) ) THEN
+    RETURN -1;
+  END IF;
+
+  IF ( ( SELECT (count(period_id) > 0)
+           FROM period
+          WHERE ((period_end > (
+            SELECT period_end 
+            FROM period 
+            WHERE (period_id=pPeriodId))
+          )
+           AND (period_closed)) ) ) THEN
+    RETURN -3;
+  END IF;
+  
+--  Make sure the year is open
+  IF ( ( SELECT (yearperiod_closed)
+         FROM yearperiod
+           JOIN period ON (period_yearperiod_id=yearperiod_id)
+         WHERE (period_id=pPeriodid) ) ) THEN
+    RETURN -4;
+  END IF;
+
+--  Reset the period_closed flag
+  UPDATE period
+  SET period_closed=FALSE
+  WHERE (period_id=pPeriodid);
+
+--  Post any unposted G/L Transactions into the new period
+  FOR _r IN SELECT DISTINCT gltrans_sequence
+            FROM gltrans, period
+            WHERE ( (NOT gltrans_posted)
+             AND (gltrans_date BETWEEN period_start AND period_end)
+             AND (period_id=pPeriodid) ) LOOP
+    PERFORM postIntoTrialBalance(_r.gltrans_sequence);
+  END LOOP;
+
+  RETURN pPeriodid;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/openaccountingyearperiod.sql b/foundation-database/public/functions/openaccountingyearperiod.sql
new file mode 100644 (file)
index 0000000..e7ecb6d
--- /dev/null
@@ -0,0 +1,38 @@
+
+CREATE OR REPLACE FUNCTION openAccountingYearPeriod(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pYearPeriodId ALIAS FOR $1;
+  _r RECORD;
+
+BEGIN
+
+--  Check to make use that the yearperiod is closed
+  IF ( ( SELECT (NOT yearperiod_closed)
+         FROM yearperiod
+         WHERE (yearperiod_id=pYearPeriodId) ) ) THEN
+    RETURN -1;
+  END IF;
+
+  IF ( ( SELECT (count(yearperiod_id) > 0)
+           FROM yearperiod
+          WHERE ((yearperiod_end> (
+            SELECT yearperiod_end 
+            FROM yearperiod 
+            WHERE (yearperiod_id=pYearPeriodId))
+          )
+           AND (yearperiod_closed)) ) ) THEN
+    RETURN -2;
+  END IF;
+
+--  Reset the yearperiod_closed flag
+  UPDATE yearperiod
+  SET yearperiod_closed=FALSE
+  WHERE (yearperiod_id=pYearPeriodId);
+
+  RETURN pYearPeriodid;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/openapitemsvalue.sql b/foundation-database/public/functions/openapitemsvalue.sql
new file mode 100644 (file)
index 0000000..d6036dd
--- /dev/null
@@ -0,0 +1,21 @@
+CREATE OR REPLACE FUNCTION openAPItemsValue(pVendid    INTEGER,
+                                            pPeriodid  INTEGER) RETURNS NUMERIC AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _value NUMERIC;
+
+BEGIN
+
+  SELECT SUM( (apopen_amount - apopen_paid) / apopen_curr_rate *
+               CASE WHEN (apopen_doctype IN ('D', 'V')) THEN 1 ELSE -1 END )
+               INTO _value
+  FROM apopen
+  WHERE ( (apopen_open)
+    AND   (apopen_vend_id=pVendid)
+    AND   (apopen_duedate BETWEEN findPeriodStart(pPeriodid) AND findPeriodEnd(pPeriodid)) );
+
+  RETURN COALESCE(_value, 0.0);
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/openaritemsvalue.sql b/foundation-database/public/functions/openaritemsvalue.sql
new file mode 100644 (file)
index 0000000..a9813b7
--- /dev/null
@@ -0,0 +1,28 @@
+
+CREATE OR REPLACE FUNCTION openARItemsValue(INTEGER, INTEGER) RETURNS NUMERIC AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCustid ALIAS FOR $1;
+  pPeriodid ALIAS FOR $2;
+  _value NUMERIC;
+
+BEGIN
+
+  SELECT SUM( CASE WHEN (aropen_doctype IN (''C'', ''R'')) THEN ((aropen_amount - aropen_paid) * -1)
+                   ELSE (aropen_amount - aropen_paid)
+              END )  INTO _value
+  FROM aropen
+  WHERE ( (aropen_open)
+    AND (aropen_cust_id=pCustid)
+    AND (aropen_duedate BETWEEN findPeriodStart(pPeriodid) AND findPeriodEnd(pPeriodid)) );
+
+  IF (_value IS NULL) THEN
+    _value := 0;
+  END IF;
+
+  RETURN _value;
+
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/openrecurringitems.sql b/foundation-database/public/functions/openrecurringitems.sql
new file mode 100644 (file)
index 0000000..4f8265d
--- /dev/null
@@ -0,0 +1,40 @@
+CREATE OR REPLACE FUNCTION openRecurringItems(INTEGER, TEXT, TIMESTAMP WITH TIME ZONE) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pParentid  ALIAS FOR $1;
+  pType      TEXT := UPPER($2);
+  pDatetime  TIMESTAMP WITH TIME ZONE := COALESCE($3, CURRENT_TIMESTAMP);
+
+  _count     INTEGER := -1;
+  _countstmt TEXT;
+  _rt        RECORD;
+
+BEGIN
+  IF (pParentid IS NULL) THEN
+    RETURN -11;
+  END IF;
+  
+  SELECT * INTO _rt FROM recurtype WHERE (UPPER(recurtype_type)=pType);
+  GET DIAGNOSTICS _count = ROW_COUNT;
+  IF (_count <= 0) THEN
+    RETURN -10;
+  END IF;
+
+  _countstmt := 'SELECT COUNT(*) FROM [fulltable]'
+             || ' WHERE (NOT ([done])'
+             || '    AND ([schedcol]>=''$1'')'
+             || '    AND ([table]_recurring_[table]_id=''$2''));';
+  _countstmt := REPLACE(_countstmt, '[fulltable]',    _rt.recurtype_table);
+  _countstmt := REPLACE(_countstmt, '[table]',
+                        REGEXP_REPLACE(_rt.recurtype_table, E'.*\\.', ''));
+  _countstmt := REPLACE(_countstmt, '[done]',     _rt.recurtype_donecheck);
+  _countstmt := REPLACE(_countstmt, '[schedcol]', _rt.recurtype_schedcol);
+
+  -- 8.4+: EXECUTE _countstmt INTO _count USING pDatetime, pParentid;
+  EXECUTE REPLACE(REPLACE(_countstmt, '$1', pDatetime::TEXT),
+                                      '$2', pParentid::TEXT) INTO _count;
+
+  RETURN _count;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/ophead.sql b/foundation-database/public/functions/ophead.sql
new file mode 100644 (file)
index 0000000..a02f348
--- /dev/null
@@ -0,0 +1,39 @@
+CREATE OR REPLACE FUNCTION ophead() RETURNS SETOF ophead AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _row ophead%ROWTYPE;
+  _priv TEXT;
+  _grant BOOLEAN;
+
+BEGIN
+  -- This query will give us the most permissive privilege the user has been granted
+  SELECT privilege, granted INTO _priv, _grant
+  FROM privgranted 
+  WHERE privilege IN ('MaintainAllOpportunities','ViewAllOpportunities','MaintainPersonalOpportunities','ViewPersonalOpportunities')
+  ORDER BY granted DESC, sequence
+  LIMIT 1;
+
+  -- If have an 'All' privilege return all results
+  IF (_priv ~ 'All' AND _grant) THEN
+    FOR _row IN 
+      SELECT * FROM ophead
+    LOOP
+      RETURN NEXT _row;
+    END LOOP;
+  -- Otherwise if have any other grant, must be personal privilege.
+  ELSIF (_grant) THEN
+    FOR _row IN 
+      SELECT * FROM ophead 
+      WHERE getEffectiveXtUser() IN (ophead_owner_username, ophead_username)
+    LOOP
+      RETURN NEXT _row;
+    END LOOP;
+  END IF;
+
+  RETURN;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+COMMENT ON FUNCTION ophead() IS 'A table function that returns Opportunity results according to privilege settings.';
diff --git a/foundation-database/public/functions/orderedbypo.sql b/foundation-database/public/functions/orderedbypo.sql
new file mode 100644 (file)
index 0000000..3c9ae42
--- /dev/null
@@ -0,0 +1,38 @@
+CREATE OR REPLACE FUNCTION orderedByPo(INTEGER, INTEGER) RETURNS NUMERIC AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  pLookAhead ALIAS FOR $2;
+  _qty NUMERIC;
+
+BEGIN
+
+  SELECT orderedByPo(pItemsiteid, startOfTime(), (CURRENT_DATE + pLookAhead)) INTO _qty;
+  RETURN _qty;
+
+END;
+' LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION orderedByPo(INTEGER, DATE, DATE) RETURNS NUMERIC AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  pStartDate ALIAS FOR $2;
+  pEndDate ALIAS FOR $3;
+  _qty NUMERIC;
+
+BEGIN
+
+  SELECT COALESCE(SUM(noNeg(poitem_qty_ordered - poitem_qty_received) * poitem_invvenduomratio), 0.0) INTO _qty
+  FROM poitem
+  WHERE ( (poitem_itemsite_id=pItemsiteid)
+    AND (poitem_status <> ''C'')
+    AND (poitem_duedate BETWEEN pStartDate AND pEndDate) );
+
+  RETURN _qty;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/orderedbywo.sql b/foundation-database/public/functions/orderedbywo.sql
new file mode 100644 (file)
index 0000000..fabe1e3
--- /dev/null
@@ -0,0 +1,96 @@
+CREATE OR REPLACE FUNCTION orderedByWo(INTEGER, INTEGER) RETURNS NUMERIC AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  pLookAheadDays ALIAS FOR $2;
+
+BEGIN
+
+  RETURN orderedByWo(pItemsiteid, startOfTime(), (CURRENT_DATE + pLookAheadDays));
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION orderedByWo(INTEGER, DATE, DATE) RETURNS NUMERIC AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  pStartDate ALIAS FOR $2;
+  pEndDate ALIAS FOR $3;
+  _itemType CHARACTER(1);
+  _qty NUMERIC := 0;
+
+BEGIN
+  SELECT item_type INTO _itemType
+  FROM itemsite, item
+  WHERE ( (itemsite_item_id=item_id)
+   AND (itemsite_id=pItemsiteid) );
+  
+  IF (_itemType NOT IN ('C','T')) THEN
+    SELECT COALESCE(SUM(noNeg(wo_qtyord - wo_qtyrcv)), 0.0) INTO _qty
+    FROM wo
+    WHERE ( (wo_status <> 'C')
+     AND (wo_itemsite_id=pItemsiteid)
+     AND (wo_duedate BETWEEN pStartDate AND pEndDate) );
+  ELSIF (_itemType = 'C') THEN
+    SELECT COALESCE(SUM((noNeg(wo_qtyord - wo_qtyrcv) * brddist_stdqtyper)), 0.0) INTO _qty
+    FROM wo, xtmfg.brddist
+    WHERE ( (wo_status <> 'C')
+     AND (brddist_wo_id=wo_id)
+     AND (brddist_itemsite_id=pItemsiteid)
+     AND (wo_duedate BETWEEN pStartDate AND pEndDate) );
+  ELSIF (_itemType = 'T' AND fetchMetricBool('Routings')) THEN -- Tooling:  Determine quantity already returned
+    SELECT
+      -- Qty Required
+      COALESCE(SUM(noNeg(womatl_qtyreq)),0)  - 
+      -- Qty Returned
+     (SELECT COALESCE(SUM(abs(invhist_invqty)),0) 
+      FROM wo
+        JOIN womatl ON (womatl_wo_id=wo_id)
+        JOIN womatlpost ON (womatl_id=womatlpost_womatl_id)
+        JOIN invhist ON ((womatlpost_invhist_id=invhist_id)
+                     AND (invhist_invqty < 0))   
+      LEFT OUTER JOIN xtmfg.wooper ON (womatl_wooper_id=wooper_id)
+    WHERE ( NOT (COALESCE(wooper_rncomplete,wo_status = 'C'))
+     AND (womatl_itemsite_id=pItemsiteid)
+     AND (wo_duedate BETWEEN pStartDate AND pEndDate) )
+       )
+   INTO _qty
+    FROM wo
+      JOIN womatl ON (womatl_wo_id=wo_id)
+      LEFT OUTER JOIN xtmfg.wooper ON (womatl_wooper_id=wooper_id)
+    WHERE ( NOT (COALESCE(wooper_rncomplete,wo_status = 'C'))
+     AND (womatl_itemsite_id=pItemsiteid)
+     AND (wo_duedate BETWEEN pStartDate AND pEndDate) )
+    GROUP BY womatl_qtyreq;   
+  ELSIF (_itemType = 'T') THEN -- Tooling:  Determine quantity already returned
+    SELECT
+      -- Qty Required
+      COALESCE(SUM(noNeg(womatl_qtyreq)),0)  - 
+      -- Qty Returned
+     (SELECT COALESCE(SUM(abs(invhist_invqty)),0) 
+      FROM wo
+        JOIN womatl ON (womatl_wo_id=wo_id)
+        JOIN womatlpost ON (womatl_id=womatlpost_womatl_id)
+        JOIN invhist ON ((womatlpost_invhist_id=invhist_id)
+                     AND (invhist_invqty < 0))
+    WHERE ( NOT (wo_status = 'C')
+     AND (womatl_itemsite_id=pItemsiteid)
+     AND (wo_duedate BETWEEN pStartDate AND pEndDate) )
+       )
+   INTO _qty
+    FROM wo
+      JOIN womatl ON (womatl_wo_id=wo_id)
+    WHERE ( NOT (wo_status = 'C')
+     AND (womatl_itemsite_id=pItemsiteid)
+     AND (wo_duedate BETWEEN pStartDate AND pEndDate) )
+    GROUP BY womatl_qtyreq;   
+  END IF;
+
+  RETURN COALESCE(_qty,0);
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/orderhead.sql b/foundation-database/public/functions/orderhead.sql
new file mode 100644 (file)
index 0000000..c6ef6d1
--- /dev/null
@@ -0,0 +1,102 @@
+
+CREATE OR REPLACE FUNCTION orderhead() RETURNS SETOF ordhead AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _row ordhead%ROWTYPE;
+  _query TEXT;
+BEGIN
+
+  _query := '
+  SELECT DISTINCT * FROM (
+  SELECT pohead_id             AS orderhead_id,
+        ''PO''                 AS orderhead_type,
+        pohead_number          AS orderhead_number,
+        pohead_status          AS orderhead_status,
+        pohead_orderdate       AS orderhead_orderdate,
+        (SELECT count(*)
+          FROM poitem
+          WHERE poitem_pohead_id=pohead_id) AS orderhead_linecount,
+        pohead_vend_id         AS orderhead_from_id,
+        vend_name              AS orderhead_from,
+        NULL                   AS orderhead_to_id,
+        ''''                   AS orderhead_to,
+        pohead_curr_id         AS orderhead_curr_id,
+        pohead_agent_username  AS orderhead_agent_username,
+        pohead_shipvia         AS orderhead_shipvia
+  FROM pohead LEFT OUTER JOIN vendinfo ON (pohead_vend_id=vend_id)
+  UNION
+  SELECT cohead_id             AS orderhead_id,
+        ''SO''                 AS orderhead_type,
+        cohead_number          AS orderhead_number,
+        COALESCE(coitem_status,''C'') AS orderhead_status,
+        cohead_orderdate       AS orderhead_orderdate,
+        (SELECT count(*)
+          FROM coitem
+          WHERE coitem_cohead_id=cohead_id) AS orderhead_linecount,
+        NULL                   AS orderhead_from_id,
+        ''''                   AS orderhead_from,
+        cohead_cust_id         AS orderhead_to_id,
+        cust_name              AS orderhead_to,
+        cohead_curr_id         AS orderhead_curr_id,
+        ''''                   AS orderhead_agent_username,
+        cohead_shipvia         AS orderhead_shipvia
+  FROM cohead LEFT OUTER JOIN custinfo ON (cohead_cust_id=cust_id)
+              LEFT OUTER JOIN coitem ON ((cohead_id=coitem_cohead_id)
+                                     AND (coitem_status=''O''))';
+
+  IF (fetchmetricbool('MultiWhs')) THEN
+    _query := _query || '
+    UNION
+    SELECT tohead_id           AS orderhead_id,
+        ''TO''                 AS orderhead_type,
+        tohead_number          AS orderhead_number,
+        tohead_status          AS orderhead_status,
+        tohead_orderdate       AS orderhead_orderdate,
+        (SELECT count(*)
+          FROM toitem
+          WHERE toitem_tohead_id=tohead_id) AS orderhead_linecount,
+        tohead_src_warehous_id  AS orderhead_from_id,
+        tohead_srcname         AS orderhead_from,
+        tohead_dest_warehous_id AS orderhead_to_id,
+        tohead_destname        AS orderhead_to,
+        tohead_freight_curr_id AS orderhead_curr_id,
+        tohead_agent_username  AS orderhead_agent_username,
+        tohead_shipvia         AS orderhead_shipvia
+    FROM tohead';
+  END IF;
+
+  IF (fetchmetricbool('EnableReturnAuth')) THEN
+    _query := _query || '
+  UNION
+    SELECT rahead_id           AS orderhead_id,
+        ''RA''                 AS orderhead_type,
+        rahead_number          AS orderhead_number,
+        COALESCE(raitem_status,''C'') AS orderhead_status,
+        rahead_authdate        AS orderhead_orderdate,
+        (SELECT count(*)
+          FROM raitem
+          WHERE raitem_rahead_id=rahead_id) AS orderhead_linecount,
+        rahead_cust_id         AS orderhead_from_id,
+        cust_name              AS orderhead_from,
+        NULL                   AS orderhead_to_id,
+        ''''                   AS orderhead_to,
+        rahead_curr_id         AS orderhead_curr_id,
+        ''''                   AS orderhead_agent_username,
+        ''''                   AS orderhead_shipvia
+    FROM rahead LEFT OUTER JOIN custinfo ON (rahead_cust_id=cust_id)
+              LEFT OUTER JOIN raitem ON ((rahead_id=raitem_rahead_id)
+                                     AND (raitem_status=''O''))';
+  END IF;
+
+  _query := _query || ') AS data ORDER BY orderhead_type, orderhead_number ;';
+  
+  FOR _row IN EXECUTE _query
+  LOOP
+    RETURN NEXT _row;
+  END LOOP;
+
+  RETURN;
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/orderitem.sql b/foundation-database/public/functions/orderitem.sql
new file mode 100644 (file)
index 0000000..906dc59
--- /dev/null
@@ -0,0 +1,108 @@
+
+CREATE OR REPLACE FUNCTION orderitem() RETURNS SETOF orditem AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _row orditem%ROWTYPE;
+  _query TEXT;
+BEGIN
+
+  _query := '
+  SELECT poitem_id             AS orderitem_id,
+        ''PO''                 AS orderitem_orderhead_type,
+        poitem_pohead_id       AS orderitem_orderhead_id,
+        poitem_linenumber      AS orderitem_linenumber,
+        poitem_status          AS orderitem_status,
+        poitem_itemsite_id     AS orderitem_itemsite_id,
+        poitem_duedate         AS orderitem_scheddate,
+        poitem_qty_ordered     AS orderitem_qty_ordered,
+        poitem_qty_returned    AS orderitem_qty_shipped,
+        poitem_qty_received    AS orderitem_qty_received,
+        uom_id                 AS orderitem_qty_uom_id,
+        poitem_invvenduomratio AS orderitem_qty_invuomratio,
+        poitem_unitprice       AS orderitem_unitcost,
+        pohead_curr_id         AS orderitem_unitcost_curr_id,
+        poitem_freight         AS orderitem_freight,
+        poitem_freight_received AS orderitem_freight_received,
+        pohead_curr_id         AS orderitem_freight_curr_id
+
+  FROM poitem LEFT OUTER JOIN pohead ON (poitem_pohead_id=pohead_id)
+              LEFT OUTER JOIN uom ON (uom_name=poitem_vend_uom)
+  UNION
+  SELECT coitem_id             AS orderitem_id,
+        ''SO''                 AS orderitem_orderhead_type,
+        coitem_cohead_id       AS orderitem_orderhead_id,
+        coitem_linenumber      AS orderitem_linenumber,
+        coitem_status          AS orderitem_status,
+        coitem_itemsite_id     AS orderitem_itemsite_id,
+        coitem_scheddate       AS orderitem_scheddate,
+        coitem_qtyord          AS orderitem_qty_ordered,
+        coitem_qtyshipped      AS orderitem_qty_shipped,
+        coitem_qtyreturned     AS orderitem_qty_received,
+        coitem_qty_uom_id      AS orderitem_qty_uom_id,
+        coitem_qty_invuomratio AS orderitem_qty_invuomratio,
+        coitem_unitcost        AS orderitem_unitcost,
+        basecurrid()           AS orderitem_unitcost_curr_id,
+        NULL                   AS orderitem_freight,
+        NULL                   AS orderitem_freight_received,
+        basecurrid()           AS orderitem_freight_curr_id
+  FROM coitem';
+
+  IF (fetchmetricbool('MultiWhs')) THEN
+    _query := _query || '
+    UNION
+    SELECT toitem_id           AS orderitem_id,
+      ''TO''                   AS orderitem_orderhead_type,
+      toitem_tohead_id AS orderitem_orderhead_id,
+      toitem_linenumber        AS orderitem_linenumber,
+      toitem_status            AS orderitem_status,
+      itemsite_id              AS orderitem_itemsite_id,
+      toitem_duedate           AS orderitem_scheddate,
+      toitem_qty_ordered       AS orderitem_qty_ordered,
+      toitem_qty_shipped       AS orderitem_qty_shipped,
+      toitem_qty_received      AS orderitem_qty_received,
+      uom_id                   AS orderitem_qty_uom_id,
+      1                        AS orderitem_qty_invuomratio,
+      toitem_stdcost           AS orderitem_unitcost,
+      basecurrid()             AS orderitem_unitcost_curr_id,
+      toitem_freight           AS orderitem_freight,
+      toitem_freight_received AS orderitem_freight_received,
+      toitem_freight_curr_id   AS orderitem_freight_curr_id
+    FROM tohead, itemsite, toitem LEFT OUTER JOIN uom ON (uom_name=toitem_uom)
+    WHERE ((toitem_tohead_id=tohead_id)
+     AND  (tohead_src_warehous_id=itemsite_warehous_id)
+     AND  (toitem_item_id=itemsite_item_id)) ';
+  END IF;
+
+  IF (fetchmetricbool('EnableReturnAuth')) THEN
+    _query := _query || '
+    UNION
+    SELECT raitem_id           AS orderitem_id,
+      ''RA''                   AS orderitem_orderhead_type,
+      raitem_rahead_id AS orderitem_orderhead_id,
+      raitem_linenumber        AS orderitem_linenumber,
+      raitem_status            AS orderitem_status,
+      raitem_itemsite_id       AS orderitem_itemsite_id,
+      raitem_scheddate AS orderitem_scheddate,
+      raitem_qtyauthorized     AS orderitem_qty_ordered,
+      0                        AS orderitem_qty_shipped,
+      raitem_qtyreceived       AS orderitem_qty_received,
+      raitem_qty_uom_id        AS orderitem_qty_uom_id,
+      raitem_qty_invuomratio   AS orderitem_qty_invuomratio,
+      raitem_unitprice AS orderitem_unitcost,
+      basecurrid()             AS orderitem_unitcost_curr_id,
+      NULL                     AS orderitem_freight,
+      NULL                     AS orderitem_freight_received,
+      basecurrid()             AS orderitem_freight_curr_id
+    FROM raitem';
+  END IF;
+  
+  FOR _row IN EXECUTE _query
+  LOOP
+    RETURN NEXT _row;
+  END LOOP;
+
+  RETURN;
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/orderitemdata.sql b/foundation-database/public/functions/orderitemdata.sql
new file mode 100644 (file)
index 0000000..685a328
--- /dev/null
@@ -0,0 +1,197 @@
+CREATE OR REPLACE FUNCTION orderitemData(TEXT, INTEGER, INTEGER) RETURNS SETOF orderitemtype
+AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pOrdertype ALIAS FOR $1;
+  pOrderheadid ALIAS FOR $2;
+  pOrderitemid ALIAS FOR $3;
+  _row orderitemtype%ROWTYPE;
+  _set RECORD;
+
+BEGIN
+
+  IF(UPPER(pOrdertype)='PO') THEN
+    FOR _set IN 
+      SELECT poitem_id              AS orderitem_id,
+             'PO'                   AS orderitem_orderhead_type,
+             poitem_pohead_id       AS orderitem_orderhead_id,
+             poitem_linenumber      AS orderitem_linenumber,
+             poitem_status          AS orderitem_status,
+             poitem_itemsite_id     AS orderitem_itemsite_id,
+             poitem_duedate         AS orderitem_scheddate,
+             poitem_qty_ordered     AS orderitem_qty_ordered,
+             poitem_qty_returned    AS orderitem_qty_shipped,
+             poitem_qty_received    AS orderitem_qty_received,
+             uom_id                 AS orderitem_qty_uom_id,
+             poitem_invvenduomratio AS orderitem_qty_invuomratio,
+             poitem_unitprice       AS orderitem_unitcost,
+             (SELECT pohead_curr_id FROM pohead WHERE pohead_id=poitem_pohead_id)
+                                    AS orderitem_unitcost_curr_id,
+             poitem_freight         AS orderitem_freight,
+             poitem_freight_received AS orderitem_freight_received,
+             (SELECT pohead_curr_id FROM pohead WHERE pohead_id=poitem_pohead_id)
+                                    AS orderitem_freight_curr_id
+        FROM poitem LEFT OUTER JOIN uom ON (uom_name=poitem_vend_uom)
+       WHERE(((pOrderheadid IS NULL) OR (poitem_pohead_id=pOrderheadid))
+         AND ((pOrderitemid IS NULL) OR (poitem_id=pOrderitemid))) LOOP
+  
+      _row.orderitem_id := _set.orderitem_id;
+      _row.orderitem_orderhead_type := _set.orderitem_orderhead_type;
+      _row.orderitem_orderhead_id := _set.orderitem_orderhead_id;
+      _row.orderitem_linenumber := _set.orderitem_linenumber;
+      _row.orderitem_status := _set.orderitem_status;
+      _row.orderitem_itemsite_id := _set.orderitem_itemsite_id;
+      _row.orderitem_scheddate := _set.orderitem_scheddate;
+      _row.orderitem_qty_ordered := _set.orderitem_qty_ordered;
+      _row.orderitem_qty_shipped := _set.orderitem_qty_shipped;
+      _row.orderitem_qty_received := _set.orderitem_qty_received;
+      _row.orderitem_qty_uom_id := _set.orderitem_qty_uom_id;
+      _row.orderitem_qty_invuomratio := _set.orderitem_qty_invuomratio;
+      _row.orderitem_unitcost := _set.orderitem_unitcost;
+      _row.orderitem_unitcost_curr_id := _set.orderitem_unitcost_curr_id;
+      _row.orderitem_freight := _set.orderitem_freight;
+      _row.orderitem_freight_received := _set.orderitem_freight_received;
+      _row.orderitem_freight_curr_id := _set.orderitem_freight_curr_id;
+
+      RETURN NEXT _row;
+    END LOOP;
+  ELSEIF(UPPER(pOrdertype)='SO') THEN
+    FOR _set IN 
+      SELECT coitem_id              AS orderitem_id,
+             'SO'                   AS orderitem_orderhead_type,
+             coitem_cohead_id       AS orderitem_orderhead_id,
+             coitem_linenumber      AS orderitem_linenumber,
+             coitem_status          AS orderitem_status,
+             coitem_itemsite_id     AS orderitem_itemsite_id,
+             coitem_scheddate       AS orderitem_scheddate,
+             coitem_qtyord          AS orderitem_qty_ordered,
+             coitem_qtyshipped      AS orderitem_qty_shipped,
+             coitem_qtyreturned     AS orderitem_qty_received,
+             coitem_qty_uom_id      AS orderitem_qty_uom_id,
+             coitem_qty_invuomratio AS orderitem_qty_invuomratio,
+             coitem_unitcost        AS orderitem_unitcost,
+             basecurrid()           AS orderitem_unitcost_curr_id,
+             NULL                   AS orderitem_freight,
+             NULL                   AS orderitem_freight_received,
+             basecurrid()           AS orderitem_freight_curr_id
+        FROM coitem
+       WHERE(((pOrderheadid IS NULL) OR (coitem_cohead_id=pOrderheadid))
+         AND ((pOrderitemid IS NULL) OR (coitem_id=pOrderitemid))) LOOP
+  
+      _row.orderitem_id := _set.orderitem_id;
+      _row.orderitem_orderhead_type := _set.orderitem_orderhead_type;
+      _row.orderitem_orderhead_id := _set.orderitem_orderhead_id;
+      _row.orderitem_linenumber := _set.orderitem_linenumber;
+      _row.orderitem_status := _set.orderitem_status;
+      _row.orderitem_itemsite_id := _set.orderitem_itemsite_id;
+      _row.orderitem_scheddate := _set.orderitem_scheddate;
+      _row.orderitem_qty_ordered := _set.orderitem_qty_ordered;
+      _row.orderitem_qty_shipped := _set.orderitem_qty_shipped;
+      _row.orderitem_qty_received := _set.orderitem_qty_received;
+      _row.orderitem_qty_uom_id := _set.orderitem_qty_uom_id;
+      _row.orderitem_qty_invuomratio := _set.orderitem_qty_invuomratio;
+      _row.orderitem_unitcost := _set.orderitem_unitcost;
+      _row.orderitem_unitcost_curr_id := _set.orderitem_unitcost_curr_id;
+      _row.orderitem_freight := _set.orderitem_freight;
+      _row.orderitem_freight_received := _set.orderitem_freight_received;
+      _row.orderitem_freight_curr_id := _set.orderitem_freight_curr_id;
+
+      RETURN NEXT _row;
+    END LOOP;
+  ELSEIF(UPPER(pOrdertype)='RA') THEN
+    FOR _set IN 
+      SELECT raitem_id              AS orderitem_id,
+             'RA'                   AS orderitem_orderhead_type,
+             raitem_rahead_id       AS orderitem_orderhead_id,
+             raitem_linenumber      AS orderitem_linenumber,
+             raitem_status          AS orderitem_status,
+             raitem_itemsite_id     AS orderitem_itemsite_id,
+             raitem_scheddate       AS orderitem_scheddate,
+             raitem_qtyauthorized   AS orderitem_qty_ordered,
+             0                      AS orderitem_qty_shipped,
+             raitem_qtyreceived     AS orderitem_qty_received,
+             raitem_qty_uom_id      AS orderitem_qty_uom_id,
+             raitem_qty_invuomratio AS orderitem_qty_invuomratio,
+             raitem_unitprice       AS orderitem_unitcost,
+             basecurrid()           AS orderitem_unitcost_curr_id,
+             NULL                   AS orderitem_freight,
+             NULL                   AS orderitem_freight_received,
+             basecurrid()           AS orderitem_freight_curr_id
+        FROM raitem
+       WHERE(((pOrderheadid IS NULL) OR (raitem_rahead_id=pOrderheadid))
+         AND ((pOrderitemid IS NULL) OR (raitem_id=pOrderitemid))) LOOP
+  
+      _row.orderitem_id := _set.orderitem_id;
+      _row.orderitem_orderhead_type := _set.orderitem_orderhead_type;
+      _row.orderitem_orderhead_id := _set.orderitem_orderhead_id;
+      _row.orderitem_linenumber := _set.orderitem_linenumber;
+      _row.orderitem_status := _set.orderitem_status;
+      _row.orderitem_itemsite_id := _set.orderitem_itemsite_id;
+      _row.orderitem_scheddate := _set.orderitem_scheddate;
+      _row.orderitem_qty_ordered := _set.orderitem_qty_ordered;
+      _row.orderitem_qty_shipped := _set.orderitem_qty_shipped;
+      _row.orderitem_qty_received := _set.orderitem_qty_received;
+      _row.orderitem_qty_uom_id := _set.orderitem_qty_uom_id;
+      _row.orderitem_qty_invuomratio := _set.orderitem_qty_invuomratio;
+      _row.orderitem_unitcost := _set.orderitem_unitcost;
+      _row.orderitem_unitcost_curr_id := _set.orderitem_unitcost_curr_id;
+      _row.orderitem_freight := _set.orderitem_freight;
+      _row.orderitem_freight_received := _set.orderitem_freight_received;
+      _row.orderitem_freight_curr_id := _set.orderitem_freight_curr_id;
+
+      RETURN NEXT _row;
+    END LOOP;
+  ELSEIF(UPPER(pOrdertype)='TO') THEN
+    FOR _set IN 
+      SELECT toitem_id              AS orderitem_id,
+             'TO'                   AS orderitem_orderhead_type,
+             toitem_tohead_id       AS orderitem_orderhead_id,
+             toitem_linenumber      AS orderitem_linenumber,
+             toitem_status          AS orderitem_status,
+             itemsite_id            AS orderitem_itemsite_id,
+             toitem_duedate         AS orderitem_scheddate,
+             toitem_qty_ordered     AS orderitem_qty_ordered,
+             toitem_qty_shipped     AS orderitem_qty_shipped,
+             toitem_qty_received    AS orderitem_qty_received,
+             uom_id                 AS orderitem_qty_uom_id,
+             1                      AS orderitem_qty_invuomratio,
+             toitem_stdcost         AS orderitem_unitcost,
+             basecurrid()           AS orderitem_unitcost_curr_id,
+             toitem_freight         AS orderitem_freight,
+             toitem_freight_received AS orderitem_freight_received,
+             toitem_freight_curr_id AS orderitem_freight_curr_id
+        FROM tohead, itemsite, toitem LEFT OUTER JOIN uom ON (uom_name=toitem_uom)
+       WHERE((toitem_tohead_id=tohead_id)
+         AND (tohead_src_warehous_id=itemsite_warehous_id)
+         AND (toitem_item_id=itemsite_item_id)
+         AND ((pOrderheadid IS NULL) OR (toitem_tohead_id=pOrderheadid))
+         AND ((pOrderitemid IS NULL) OR (toitem_id=pOrderitemid))) LOOP
+  
+      _row.orderitem_id := _set.orderitem_id;
+      _row.orderitem_orderhead_type := _set.orderitem_orderhead_type;
+      _row.orderitem_orderhead_id := _set.orderitem_orderhead_id;
+      _row.orderitem_linenumber := _set.orderitem_linenumber;
+      _row.orderitem_status := _set.orderitem_status;
+      _row.orderitem_itemsite_id := _set.orderitem_itemsite_id;
+      _row.orderitem_scheddate := _set.orderitem_scheddate;
+      _row.orderitem_qty_ordered := _set.orderitem_qty_ordered;
+      _row.orderitem_qty_shipped := _set.orderitem_qty_shipped;
+      _row.orderitem_qty_received := _set.orderitem_qty_received;
+      _row.orderitem_qty_uom_id := _set.orderitem_qty_uom_id;
+      _row.orderitem_qty_invuomratio := _set.orderitem_qty_invuomratio;
+      _row.orderitem_unitcost := _set.orderitem_unitcost;
+      _row.orderitem_unitcost_curr_id := _set.orderitem_unitcost_curr_id;
+      _row.orderitem_freight := _set.orderitem_freight;
+      _row.orderitem_freight_received := _set.orderitem_freight_received;
+      _row.orderitem_freight_curr_id := _set.orderitem_freight_curr_id;
+
+      RETURN NEXT _row;
+    END LOOP;
+  END IF;
+
+  RETURN;
+END;
+$$
+LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/packageisenabled.sql b/foundation-database/public/functions/packageisenabled.sql
new file mode 100644 (file)
index 0000000..e323965
--- /dev/null
@@ -0,0 +1,22 @@
+CREATE OR REPLACE FUNCTION packageIsEnabled(INTEGER) RETURNS BOOLEAN AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+  SELECT COUNT(*) >= 8
+  FROM pg_inherits, pg_class, pg_namespace, pkghead
+  WHERE ((inhrelid=pg_class.oid)
+     AND (relnamespace=pg_namespace.oid)
+     AND  (nspname=lower(pkghead_name))
+     AND  (pkghead_id=$1));
+$$
+LANGUAGE 'sql';
+
+CREATE OR REPLACE FUNCTION packageIsEnabled(TEXT) RETURNS BOOLEAN AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+  SELECT COUNT(*) >= 8
+  FROM pg_inherits, pg_class, pg_namespace
+  WHERE ((inhrelid=pg_class.oid)
+     AND (relnamespace=pg_namespace.oid)
+     AND  (nspname=lower($1)));
+$$
+LANGUAGE 'sql';
diff --git a/foundation-database/public/functions/pkgmaybemodified.sql b/foundation-database/public/functions/pkgmaybemodified.sql
new file mode 100644 (file)
index 0000000..51bb8d1
--- /dev/null
@@ -0,0 +1,17 @@
+CREATE OR REPLACE FUNCTION pkgMayBeModified(NAME) RETURNS BOOLEAN AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pschemaname ALIAS FOR $1;
+  _returnval  BOOLEAN;
+BEGIN
+  SELECT pkghead_indev INTO _returnval
+  FROM pkghead
+  WHERE (pkghead_name=pschemaname);
+  IF (NOT FOUND) THEN
+    RETURN FALSE;
+  END IF;
+  RETURN _returnval;
+END;
+$$
+LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/postapcheck.sql b/foundation-database/public/functions/postapcheck.sql
new file mode 100644 (file)
index 0000000..d6dfb46
--- /dev/null
@@ -0,0 +1,18 @@
+CREATE OR REPLACE FUNCTION postAPCheck(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RAISE NOTICE ''postAPCheck() is deprecated - use postCheck() instead'';
+  RETURN postCheck($1, fetchJournalNumber(''AP-CK''));
+END;
+' LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION postAPCheck(INTEGER, INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RAISE NOTICE ''postAPCheck() is deprecated - use postCheck() instead'';
+  RETURN postCheck($1, $2);
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/postapchecks.sql b/foundation-database/public/functions/postapchecks.sql
new file mode 100644 (file)
index 0000000..5c1b7c3
--- /dev/null
@@ -0,0 +1,10 @@
+CREATE OR REPLACE FUNCTION postAPChecks(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+
+  RAISE NOTICE ''postAPChecks() is deprecated - use postChecks() instead'';
+  RETURN postChecks($1);
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/postapcreditmemoapplication.sql b/foundation-database/public/functions/postapcreditmemoapplication.sql
new file mode 100644 (file)
index 0000000..bdfc09b
--- /dev/null
@@ -0,0 +1,129 @@
+CREATE OR REPLACE FUNCTION postAPCreditMemoApplication(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pApopenid ALIAS FOR $1;
+  _src RECORD;
+  _r RECORD;
+  _totalAmount NUMERIC := 0.0;
+  _exchGain NUMERIC := 0.0;
+  _apaccntid INTEGER;
+
+BEGIN
+
+  SELECT apopen_docnumber, (apopen_amount - apopen_paid) AS balance,
+--         SUM(currtocurr(apcreditapply_curr_id, apopen_curr_id,
+--                              apcreditapply_amount, CURRENT_DATE)) AS toApply,
+        SUM(apcreditapply_amount) AS toApply,
+        apopen_curr_rate INTO _src
+  FROM apopen, apcreditapply
+  WHERE ( (apcreditapply_source_apopen_id=apopen_id)
+   AND (apopen_id=pApopenid) )
+  GROUP BY apopen_docnumber, apopen_amount, apopen_paid,
+          apopen_curr_rate;
+  IF (NOT FOUND) THEN
+    RETURN -1;
+  ELSIF (_src.toApply = 0) THEN
+    RETURN -2;
+  ELSIF (_src.toApply > _src.balance) THEN
+    RETURN -3;
+--  ELSIF (_src.toApply IS NULL AND _src.junk IS NOT NULL) THEN
+--    RETURN -4;               -- missing exchange rate
+  ELSIF (_src.toApply IS NULL) THEN
+    RETURN -6;         -- amount to apply is NULL for some unknown reason
+  END IF;
+
+  SELECT apopen_id, apopen_vend_id, apopen_docnumber, apopen_doctype, apopen_amount,
+         apopen_curr_id, apopen_curr_rate, apopen_docdate, apopen_accnt_id INTO _src
+  FROM apopen
+  WHERE (apopen_id=pApopenid);
+  IF (NOT FOUND) THEN
+    RETURN -5;
+  END IF;
+
+  FOR _r IN SELECT apcreditapply_id, apcreditapply_target_apopen_id,
+                  apcreditapply_amount AS apply_amountSource,
+                   currToCurr(apcreditapply_curr_id, apopen_curr_id,
+                              apcreditapply_amount, CURRENT_DATE) AS apply_amountTarget,
+                   apopen_id, apopen_doctype, apopen_docnumber, apopen_curr_id, apopen_curr_rate, apopen_docdate
+            FROM apcreditapply, apopen
+            WHERE ( (apcreditapply_source_apopen_id=pApopenid)
+             AND (apcreditapply_target_apopen_id=apopen_id) ) LOOP
+
+    IF (_r.apply_amountTarget IS NULL) THEN
+      RETURN -4;       -- missing exchange rate
+    END IF;
+
+    IF (_r.apply_amountTarget > 0) THEN
+
+--  Update the apopen item to post the paid amount
+      UPDATE apopen
+      SET apopen_paid = (apopen_paid + _r.apply_amountTarget)
+      WHERE (apopen_id=_r.apcreditapply_target_apopen_id);
+
+      UPDATE apopen
+      SET apopen_open = false,
+        apopen_closedate = current_date
+      WHERE ( (apopen_id=_r.apcreditapply_target_apopen_id)
+        AND (apopen_amount <= apopen_paid) );
+
+--  Cache the running amount posted
+      _totalAmount := (_totalAmount + _r.apply_amountSource);
+
+--  Record the application
+      INSERT INTO apapply
+      ( apapply_vend_id, apapply_amount,
+        apapply_source_apopen_id, apapply_source_doctype, apapply_source_docnumber,
+        apapply_target_apopen_id, apapply_target_doctype, apapply_target_docnumber,
+        apapply_postdate, apapply_journalnumber, apapply_username, apapply_curr_id )
+      VALUES
+      ( _src.apopen_vend_id, round(_r.apply_amountSource, 2),
+        pApopenid, 'C', _src.apopen_docnumber,
+        _r.apopen_id, _r.apopen_doctype, _r.apopen_docnumber,
+        CURRENT_DATE, 0, getEffectiveXtUser(), _src.apopen_curr_id );
+
+    END IF;
+
+--  Delete the posted apcreditapply record
+    DELETE FROM apcreditapply
+    WHERE (apcreditapply_id=_r.apcreditapply_id);
+
+  END LOOP;
+
+--  Record the amount posted and mark the source apopen as closed if it is completely posted
+  UPDATE apopen
+  SET apopen_paid = (apopen_paid + _totalAmount)
+  WHERE (apopen_id=pApopenid);
+
+  UPDATE apopen
+  SET apopen_open = false,
+    apopen_closedate = current_date
+  WHERE ( (apopen_id=pApopenid)
+    AND (apopen_amount <= apopen_paid) );
+
+  IF (_r.apopen_curr_id = _src.apopen_curr_id) THEN
+    IF (_r.apopen_docdate > _src.apopen_docdate) THEN
+      _exchGain := (_totalAmount / _r.apopen_curr_rate - _totalAmount / _src.apopen_curr_rate) * -1;
+    ELSE
+      _exchGain := _totalAmount / _src.apopen_curr_rate - _totalAmount / _r.apopen_curr_rate;
+    END IF;
+  END IF;
+
+  IF (_src.apopen_accnt_id > -1) THEN
+    _apaccntid := _src.apopen_accnt_id;
+  ELSE 
+    _apaccntid := findAPAccount(_src.apopen_vend_id);
+  END IF;
+
+  PERFORM insertGLTransaction(fetchJournalNumber('AP-MISC'), 'A/P', 'CM',
+                            _src.apopen_docnumber, 'CM Application',
+                            _apaccntid,
+                           getGainLossAccntId(_apaccntid), -1,
+                            _exchGain,
+                            CURRENT_DATE);
+
+
+  RETURN pApopenid;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/postapopenitems.sql b/foundation-database/public/functions/postapopenitems.sql
new file mode 100644 (file)
index 0000000..829467d
--- /dev/null
@@ -0,0 +1,13 @@
+CREATE OR REPLACE FUNCTION postApopenItems() RETURNS BOOLEAN AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+
+  UPDATE apopen
+  SET apopen_posted=TRUE
+  WHERE (NOT apopen_posted);
+
+  RETURN TRUE;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/postarcreditmemoapplication.sql b/foundation-database/public/functions/postarcreditmemoapplication.sql
new file mode 100644 (file)
index 0000000..d013184
--- /dev/null
@@ -0,0 +1,158 @@
+
+CREATE OR REPLACE FUNCTION postARCreditMemoApplication(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pAropenid ALIAS FOR $1;
+BEGIN
+  RETURN postARCreditMemoApplication(pAropenid, CURRENT_DATE);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION postARCreditMemoApplication(INTEGER, DATE) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pAropenid ALIAS FOR $1;
+  _applyDate DATE := COALESCE($2, CURRENT_DATE);
+  _p RECORD;
+  _r RECORD;
+  _totalAmount NUMERIC := 0;
+  _totalTarget NUMERIC := 0;
+  _exchGain NUMERIC := 0;
+  _result NUMERIC;
+  _araccntid INTEGER;
+
+BEGIN
+
+  SELECT aropen_docnumber,
+         ROUND(aropen_amount - aropen_paid, 2) AS balance,
+         aropen_open, aropen_curr_rate,
+         ROUND(SUM(currToCurr(arcreditapply_curr_id, aropen_curr_id,
+              COALESCE(arcreditapply_amount, 0), _applyDate)), 2) AS toApply INTO _p
+  FROM aropen, arcreditapply
+  WHERE ( (arcreditapply_source_aropen_id=aropen_id)
+   AND (aropen_id=pAropenid) )
+  GROUP BY aropen_docnumber, aropen_amount, aropen_paid, aropen_open, aropen_curr_rate;
+  IF (NOT FOUND) THEN
+    RETURN -1;
+  ELSIF (_p.toApply = 0) THEN
+    RETURN -2;
+  ELSIF (_p.toApply > _p.balance) THEN
+    RETURN -3;
+  END IF;
+
+  SELECT aropen_cust_id, aropen_docnumber, aropen_doctype, aropen_amount,
+         aropen_curr_id, aropen_docdate, aropen_accnt_id, aropen_cust_id,
+         aropen_curr_rate INTO _p
+  FROM aropen
+  WHERE (aropen_id=pAropenid);
+  IF (NOT FOUND) THEN
+    RETURN -5;
+  END IF;
+
+  FOR _r IN SELECT arcreditapply_id, arcreditapply_target_aropen_id,
+                   arcreditapply_amount AS arcreditapply_amountSource,
+                   arcreditapply_reftype, arcreditapply_ref_id,
+                   currToCurr(arcreditapply_curr_id, aropen_curr_id,
+                              arcreditapply_amount, _applyDate) AS arcreditapply_amountTarget,
+                   aropen_id, aropen_doctype, aropen_docnumber, aropen_docdate, aropen_curr_rate
+            FROM arcreditapply, aropen
+            WHERE ( (arcreditapply_source_aropen_id=pAropenid)
+             AND (arcreditapply_target_aropen_id=aropen_id) ) LOOP
+
+    IF (_r.arcreditapply_amountTarget IS NULL) THEN
+      RETURN -4;
+    END IF;
+
+    IF (_r.arcreditapply_amountTarget > 0) THEN
+
+--  Update the aropen item to post the paid amount
+      UPDATE aropen
+      SET aropen_paid = round(aropen_paid + _r.arcreditapply_amountTarget, 2)
+      WHERE (aropen_id=_r.arcreditapply_target_aropen_id);
+
+      UPDATE aropen
+      SET aropen_open = (round(aropen_amount, 2) > round(aropen_paid, 2))
+      WHERE (aropen_id=_r.arcreditapply_target_aropen_id);
+
+--  Cache the running amount posted
+      _totalAmount := (_totalAmount + _r.arcreditapply_amountSource);
+      _totalTarget := (_totalTarget + _r.arcreditapply_amountTarget);
+
+--  Record the application
+      INSERT INTO arapply
+      ( arapply_cust_id,
+        arapply_source_aropen_id, arapply_source_doctype, arapply_source_docnumber,
+        arapply_target_aropen_id, arapply_target_doctype, arapply_target_docnumber,
+        arapply_fundstype, arapply_refnumber,
+        arapply_applied, arapply_closed, arapply_postdate, arapply_distdate,
+        arapply_journalnumber, arapply_username, arapply_curr_id,
+        arapply_reftype, arapply_ref_id )
+      VALUES
+      ( _p.aropen_cust_id,
+        pAropenid, _p.aropen_doctype, _p.aropen_docnumber,
+        _r.aropen_id, _r.aropen_doctype, _r.aropen_docnumber,
+        '', '',
+        round(_r.arcreditapply_amountSource, 2), TRUE, _applyDate, _applyDate,
+        0, getEffectiveXtUser(), _p.aropen_curr_id, 
+        _r.arcreditapply_reftype, _r.arcreditapply_ref_id );
+
+    END IF;
+
+--  Delete the posted arcreditapply record
+    DELETE FROM arcreditapply
+    WHERE (arcreditapply_id=_r.arcreditapply_id);
+
+    IF (_r.aropen_docdate > _p.aropen_docdate) THEN
+      _exchGain := (_totalTarget / _r.aropen_curr_rate - _totalAmount / _p.aropen_curr_rate) * -1;
+    ELSE
+      _exchGain := _totalAmount / _p.aropen_curr_rate - _totalTarget / _r.aropen_curr_rate;
+    END IF;
+
+    IF (_p.aropen_accnt_id > -1) THEN
+      _araccntid := _p.aropen_accnt_id;
+    ELSE 
+      _araccntid := findARAccount(_p.aropen_cust_id);
+    END IF;
+    
+    IF (_exchGain <> 0) THEN
+        PERFORM insertGLTransaction(fetchJournalNumber('AR-MISC'), 'A/R',
+                                    'CR', _p.aropen_docnumber, 'CM Application',
+                                    _araccntid, getGainLossAccntId(_araccntid),
+                                    -1, _exchGain * -1, _applyDate);
+    END IF;
+
+  END LOOP;
+
+-- TODO: If this is a Customer Deposit (aropen_doctype='R')
+--       the we need to convert the total to a base transaction
+  IF(_p.aropen_doctype='R') THEN
+    SELECT insertGLTransaction(fetchJournalNumber('AR-MISC'), 'A/R',
+                               'CD', _p.aropen_docnumber, 'CM Application',
+                               cr.accnt_id, db.accnt_id,
+                               -1, currToBase(_p.aropen_curr_id, _totalAmount, _p.aropen_docdate),
+                               _applyDate)
+      INTO _result
+      FROM accnt AS cr, accnt AS db
+     WHERE ((db.accnt_id = findDeferredAccount(_p.aropen_cust_id))
+       AND  (cr.accnt_id = findARAccount(_p.aropen_cust_id)) );
+    IF(NOT FOUND OR _result < 0) THEN
+      RAISE EXCEPTION 'There was an error posting the Customer Deposit GL Transactions.';
+    END IF;
+  END IF;
+
+--  Record the amount posted and mark the source aropen as closed if it is completely posted
+  UPDATE aropen
+  SET aropen_paid = round(aropen_paid + _totalAmount, 2)
+  WHERE (aropen_id=pAropenid);
+
+  UPDATE aropen
+  SET aropen_open = (round(aropen_amount, 2) > round(aropen_paid, 2))
+  WHERE (aropen_id=pAropenid);
+
+  RETURN pAropenid;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/postaropenitems.sql b/foundation-database/public/functions/postaropenitems.sql
new file mode 100644 (file)
index 0000000..76db92f
--- /dev/null
@@ -0,0 +1,13 @@
+CREATE OR REPLACE FUNCTION postAropenItems() RETURNS BOOLEAN AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+
+  UPDATE aropen
+  SET aropen_posted=TRUE
+  WHERE (NOT aropen_posted);
+
+  RETURN TRUE;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/postbankadjustment.sql b/foundation-database/public/functions/postbankadjustment.sql
new file mode 100644 (file)
index 0000000..8bd41b2
--- /dev/null
@@ -0,0 +1,43 @@
+
+CREATE OR REPLACE FUNCTION postBankAdjustment(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pBankadjid ALIAS FOR $1;
+  _sequence INTEGER;
+  _r RECORD;
+
+BEGIN
+
+--  Post the G/L transaction
+  SELECT insertGLTransaction( fetchJournalNumber('GL-MISC'), 'G/L', 'AD',
+                              bankadj_docnumber, (bankadjtype_name || '-' || bankadj_notes),
+                              bankadjtype_accnt_id, bankaccnt_accnt_id, bankadj_id,
+                              round(currToBase(bankaccnt_curr_id,
+                                         CASE WHEN(bankadjtype_iscredit) THEN
+                                             (bankadj_amount * -1)
+                                         ELSE bankadj_amount END,
+                                         bankadj_date), 2),
+                              bankadj_date, TRUE, TRUE ) INTO _sequence
+    FROM bankadj, bankaccnt, bankadjtype
+   WHERE ( (bankadj_bankaccnt_id=bankaccnt_id)
+     AND   (bankadj_bankadjtype_id=bankadjtype_id)
+     AND   (NOT bankadj_posted)
+     AND   (bankadj_id=pBankadjid) );
+  IF ( NOT FOUND ) THEN
+    RETURN -1;
+  END IF;
+
+  IF (_sequence >= 0) THEN
+--  Update the bankadj record with this sequence and mark it posted
+    UPDATE bankadj
+       SET bankadj_sequence = _sequence,
+           bankadj_posted = TRUE
+     WHERE bankadj_id=pBankadjid;
+  END IF;
+
+  RETURN _sequence;
+
+END;
+$$ LANGUAGE plpgsql;
+
diff --git a/foundation-database/public/functions/postbankreconciliation.sql b/foundation-database/public/functions/postbankreconciliation.sql
new file mode 100644 (file)
index 0000000..93ca86c
--- /dev/null
@@ -0,0 +1,88 @@
+
+CREATE OR REPLACE FUNCTION postBankReconciliation(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pBankrecid ALIAS FOR $1;
+  _accntid INTEGER;
+  _sequence INTEGER;
+  _gltransid INTEGER;
+  _r RECORD;
+
+BEGIN
+
+-- Check the accnt information to make sure it is valid
+  SELECT accnt_id INTO _accntid
+    FROM bankrec, bankaccnt, accnt
+   WHERE ( (bankaccnt_accnt_id=accnt_id)
+     AND   (bankrec_bankaccnt_id=bankaccnt_id)
+     AND   (bankrec_id=pBankrecid) );
+  IF ( NOT FOUND ) THEN
+    RETURN -1;
+  END IF;
+
+-- Delete any bankrecitem records that are not marked as cleared for cleanliness
+  DELETE FROM bankrecitem
+   WHERE ( (NOT bankrecitem_cleared)
+     AND   (bankrecitem_bankrec_id=pBankrecid) );
+
+-- Post any bankadj items that were marked as cleared and convert the bankrecitem
+  FOR _r IN SELECT bankrecitem_id, bankrecitem_source_id
+              FROM bankrecitem, bankadj
+             WHERE ( (bankrecitem_source = 'AD')
+               AND   (bankrecitem_source_id=bankadj_id)
+               AND   (bankrecitem_cleared)
+               AND   (NOT bankadj_posted)
+               AND   (bankrecitem_bankrec_id=pBankrecid) ) LOOP
+
+    SELECT postBankAdjustment(_r.bankrecitem_source_id) INTO _sequence;
+
+    IF (_sequence < 0) THEN
+      RETURN -10;
+    END IF;
+
+    SELECT gltrans_id INTO _gltransid
+      FROM gltrans
+     WHERE ( (gltrans_sequence=_sequence)
+       AND   (gltrans_accnt_id=_accntid) );
+    IF ( NOT FOUND ) THEN
+      RETURN -11;
+    END IF;
+
+    UPDATE bankrecitem
+       SET bankrecitem_source = 'GL',
+           bankrecitem_source_id=_gltransid
+     WHERE (bankrecitem_id=_r.bankrecitem_id);
+
+  END LOOP;
+
+-- Mark all the gltrans items that have been cleared as reconciled.
+  UPDATE gltrans
+     SET gltrans_rec = TRUE
+   WHERE ( (gltrans_id IN (SELECT bankrecitem_source_id
+                             FROM bankrecitem
+                            WHERE ((bankrecitem_source = 'GL')
+                              AND  (bankrecitem_cleared)
+                              AND  (bankrecitem_bankrec_id=pBankrecid) ) ) )
+     AND   (gltrans_accnt_id=_accntid) ) ;
+
+-- Mark all the sltrans items that have been cleared as reconciled.
+  UPDATE sltrans
+     SET sltrans_rec = TRUE
+   WHERE ( (sltrans_id IN (SELECT bankrecitem_source_id
+                             FROM bankrecitem
+                            WHERE ((bankrecitem_source = 'SL')
+                              AND  (bankrecitem_cleared)
+                              AND  (bankrecitem_bankrec_id=pBankrecid) ) ) )
+     AND   (sltrans_accnt_id=_accntid) ) ;
+
+-- Mark the bankrec record as posted
+  UPDATE bankrec SET 
+    bankrec_posted = TRUE,
+    bankrec_postdate = now()
+   WHERE (bankrec_id=pBankrecid);
+
+  RETURN pBankrecid;
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/postbillingselection.sql b/foundation-database/public/functions/postbillingselection.sql
new file mode 100644 (file)
index 0000000..ec8fd09
--- /dev/null
@@ -0,0 +1,16 @@
+CREATE OR REPLACE FUNCTION postbillingselection(integer)
+  RETURNS integer AS
+$BODY$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCobmiscid ALIAS FOR $1;
+
+BEGIN
+
+  RAISE NOTICE 'postBillingselection(integer) has been deprecated.  Please use createInvoice(integer).';
+  RETURN createInvoice(pCobmiscid);
+
+END;
+$BODY$
+  LANGUAGE 'plpgsql' VOLATILE;
diff --git a/foundation-database/public/functions/postbillingselectionconsolidated.sql b/foundation-database/public/functions/postbillingselectionconsolidated.sql
new file mode 100644 (file)
index 0000000..57add16
--- /dev/null
@@ -0,0 +1,16 @@
+CREATE OR REPLACE FUNCTION postbillingselectionconsolidated(integer)
+  RETURNS integer AS
+$$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCustid ALIAS FOR $1;
+
+BEGIN
+
+  RAISE NOTICE 'postBillingselectionConsolidated(integer) has been deprecated.  Please use createInvoiceConsolidated(integer).';
+  RETURN createInvoiceConsolidated(pCustid);
+
+END;
+$$
+  LANGUAGE 'plpgsql' VOLATILE;
diff --git a/foundation-database/public/functions/postbillingselections.sql b/foundation-database/public/functions/postbillingselections.sql
new file mode 100644 (file)
index 0000000..5965810
--- /dev/null
@@ -0,0 +1,34 @@
+CREATE OR REPLACE FUNCTION postBillingSelections() RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+
+  RAISE NOTICE 'postBillingselections() has been deprecated.  Please use createInvoices().';
+  RETURN createInvoices();
+  
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION postBillingSelections(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RAISE NOTICE 'postBillingselections(int) has been deprecated.  Please use createInvoices(int).';
+  RETURN createInvoices($1, false);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION postBillingSelections(INTEGER, BOOLEAN) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCustTypeId ALIAS FOR $1;
+  pConsolidate ALIAS FOR $2;
+
+BEGIN
+
+  RAISE NOTICE 'postBillingselections(int,bool) has been deprecated.  Please use createInvoices(int,bool).';
+  RETURN createInvoices(pCustTypeId, pConsolidate);
+  
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/postcashreceipt.sql b/foundation-database/public/functions/postcashreceipt.sql
new file mode 100644 (file)
index 0000000..5517479
--- /dev/null
@@ -0,0 +1,382 @@
+CREATE OR REPLACE FUNCTION postCashReceipt(pCashrcptid    INTEGER,
+                                           pJournalNumber INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _ccpayid  INTEGER;
+  _cctype TEXT;
+  _p RECORD;
+  _r RECORD;
+  _t RECORD;
+  _v RECORD;
+  _postToAR NUMERIC;
+  _postToMisc NUMERIC;
+  _postToCM NUMERIC;
+  _posted_base NUMERIC := 0;
+  _posted NUMERIC := 0;
+  _sequence INTEGER;
+  _aropenid INTEGER;
+  _arMemoNumber TEXT;
+  _arAccntid INTEGER;
+  _closed BOOLEAN;
+  _debitAccntid INTEGER;
+  _exchGain NUMERIC;
+  _comment TEXT;
+  _predist BOOLEAN;
+  _check INTEGER;
+
+BEGIN
+  _posted := 0;
+  _posted_base := 0;
+
+  SELECT fetchGLSequence() INTO _sequence;
+
+  SELECT accnt_id INTO _arAccntid
+  FROM cashrcpt, accnt, salescat
+  WHERE ((cashrcpt_salescat_id=salescat_id)
+    AND  (salescat_ar_accnt_id=accnt_id)
+    AND  (cashrcpt_id=pCashrcptid));
+  IF (NOT FOUND) THEN
+    SELECT accnt_id INTO _arAccntid
+    FROM cashrcpt LEFT OUTER JOIN accnt ON (accnt_id=findARAccount(cashrcpt_cust_id))
+    WHERE ( (findARAccount(cashrcpt_cust_id)=0 OR accnt_id > 0) -- G/L interface might be disabled
+     AND (cashrcpt_id=pCashrcptid) );
+    IF (NOT FOUND) THEN
+      RETURN -5;
+    END IF;
+  END IF;
+
+  SELECT cashrcpt_cust_id, (cust_number||'-'||cust_name) AS custnote,
+         cashrcpt_fundstype, cashrcpt_number, cashrcpt_docnumber,
+         cashrcpt_distdate, cashrcpt_amount, cashrcpt_discount,
+         (cashrcpt_amount / cashrcpt_curr_rate) AS cashrcpt_amount_base,
+        (cashrcpt_discount / cashrcpt_curr_rate) AS cashrcpt_discount_base,
+         cashrcpt_notes,
+         cashrcpt_bankaccnt_id AS bankaccnt_id,
+         accnt_id AS prepaid_accnt_id,
+         cashrcpt_usecustdeposit,
+         COALESCE(cashrcpt_applydate, cashrcpt_distdate) AS applydate,
+         cashrcpt_curr_id, cashrcpt_curr_rate, cashrcpt_posted, cashrcpt_void INTO _p
+  FROM cashrcpt LEFT OUTER JOIN custinfo ON (cashrcpt_cust_id=cust_id)
+                LEFT OUTER JOIN accnt ON (accnt_id=findPrepaidAccount(cashrcpt_cust_id))
+  WHERE ( (findPrepaidAccount(cashrcpt_cust_id)=0 OR accnt_id > 0) -- G/L interface might be disabled
+     AND (cashrcpt_id=pCashrcptid) );
+  IF (NOT FOUND) THEN
+    RETURN -7;
+  END IF;
+
+  IF (COALESCE(_p.cashrcpt_distdate > _p.applydate, false)) THEN
+    RAISE EXCEPTION 'Cannot post cashrcpt % because application date is before distribution date.', _p.cashrcpt_docnumber;
+  END IF;
+
+  IF (COALESCE(_p.cashrcpt_posted, false)) THEN
+    RAISE EXCEPTION 'Cannot post cashrcpt % because the document has already been posted.', _p.cashrcpt_docnumber;
+  END IF;
+
+  IF (COALESCE(_p.cashrcpt_void, false)) THEN
+    RAISE EXCEPTION 'Cannot post cashrcpt % because the document has been voided.', _p.cashrcpt_docnumber;
+  END IF;
+
+  _predist := COALESCE(_p.cashrcpt_distdate < _p.applydate, false);
+
+  IF (_p.cashrcpt_fundstype IN ('A', 'D', 'M', 'V')) THEN
+    SELECT ccpay_id, ccpay_type INTO _ccpayid, _cctype
+    FROM ccpay
+    WHERE ((ccpay_r_ordernum IN (CAST(pCashrcptid AS TEXT), _p.cashrcpt_docnumber))
+       AND (ccpay_status IN ('C', 'A')));
+
+    IF NOT FOUND THEN
+      -- the following select seems to work except for xikar - bug 8848. why?
+      -- raise warning so there is some visibility if people fall into this path.
+      SELECT ccpay_id, ccpay_type INTO _ccpayid, _cctype
+      FROM ccpay
+      WHERE ((ccpay_order_number IN (CAST(pCashrcptid AS TEXT), _p.cashrcpt_docnumber))
+         AND (ccpay_status IN ('C', 'A')));
+      IF (NOT FOUND) THEN
+        RETURN -8;
+      ELSE
+        RAISE NOTICE 'PostCashReceipt() found ccpay_id % for order number %/% (ref 8848).',
+                      _ccpayid, pCashrcptid, _p.cashrcpt_docnumber;
+      END IF;
+    END IF;
+
+-- If there is a ccpay entry and the card was charged directly, use the prepaid account
+    IF (_cctype = 'C' ) THEN
+      _debitAccntid := findPrepaidAccount(_p.cashrcpt_cust_id);
+-- If there is a ccpay entry and the card was preauthed and then charged, use the Bank account
+    ELSE
+      SELECT accnt_id INTO _debitAccntid
+      FROM cashrcpt, bankaccnt, accnt
+      WHERE ( (cashrcpt_bankaccnt_id=bankaccnt_id)
+       AND (bankaccnt_accnt_id=accnt_id)
+       AND (cashrcpt_id=pCashrcptid) );
+      IF (NOT FOUND) THEN
+        RETURN -6;
+      END IF;
+    END IF;
+  ELSE
+    SELECT accnt_id INTO _debitAccntid
+    FROM cashrcpt, bankaccnt, accnt
+    WHERE ( (cashrcpt_bankaccnt_id=bankaccnt_id)
+     AND (bankaccnt_accnt_id=accnt_id)
+     AND (cashrcpt_id=pCashrcptid) );
+    IF (NOT FOUND) THEN
+      RETURN -6;
+    END IF;
+  END IF;
+
+--  Determine the amount to post to A/R Open Items
+  SELECT COALESCE(SUM(cashrcptitem_amount), 0) INTO _postToAR
+  FROM cashrcptitem JOIN aropen ON (aropen_id=cashrcptitem_aropen_id)
+  WHERE (cashrcptitem_cashrcpt_id=pCashrcptid);
+  IF (NOT FOUND) THEN
+    _postToAR := 0;
+  END IF;
+
+--  Determine the amount to post to Misc. Distributions
+  SELECT COALESCE(SUM(cashrcptmisc_amount), 0) INTO _postToMisc
+  FROM cashrcptmisc
+  WHERE (cashrcptmisc_cashrcpt_id=pCashrcptid);
+  IF (NOT FOUND) THEN
+    _postToMisc := 0;
+  END IF;
+
+--  Determine the amount to post to Discount Credit Memo
+  SELECT COALESCE(SUM(cashrcptitem_discount), 0) INTO _postToCM
+  FROM cashrcptitem JOIN aropen ON ( (aropen_id=cashrcptitem_aropen_id) AND (aropen_doctype IN ('I', 'D')) )
+  WHERE (cashrcptitem_cashrcpt_id=pCashrcptid);
+  IF (NOT FOUND) THEN
+    _postToCM := 0;
+  END IF;
+  
+--  Check to see if the C/R is over applied
+  IF ((_postToAR + _postToMisc) > _p.cashrcpt_amount) THEN
+    RETURN -1;
+  END IF;
+
+--  Check to see if the C/R is positive amount
+  IF (_p.cashrcpt_amount <= 0) THEN
+    RETURN -2;
+  END IF;
+
+--  Distribute A/R Applications
+    FOR _r IN SELECT aropen_id, aropen_doctype, aropen_docnumber, aropen_docdate,
+                     aropen_duedate, aropen_curr_id, aropen_curr_rate, aropen_amount,
+                     round(aropen_amount - aropen_paid, 2) <=
+                        round(currToCurr(_p.cashrcpt_curr_id, aropen_curr_id,abs(cashrcptitem_amount + cashrcptitem_discount),_p.cashrcpt_distdate),2)
+                                 AS closed,
+                     cashrcptitem_id, cashrcptitem_amount, cashrcptitem_discount,
+                     (cashrcptitem_amount / _p.cashrcpt_curr_rate) AS cashrcptitem_amount_base,
+                    (cashrcptitem_discount / _p.cashrcpt_curr_rate) AS cashrcptitem_discount_base,
+                     round(aropen_paid + 
+                       currToCurr(_p.cashrcpt_curr_id, aropen_curr_id,abs(cashrcptitem_amount),_p.cashrcpt_distdate),2) AS new_paid,
+                     round(currToCurr(_p.cashrcpt_curr_id, aropen_curr_id,cashrcptitem_discount,_p.cashrcpt_distdate),2) AS new_discount
+              FROM cashrcptitem JOIN aropen ON (aropen_id=cashrcptitem_aropen_id)
+              WHERE ((cashrcptitem_cashrcpt_id=pCashrcptid)
+               AND (NOT _predist OR aropen_doctype IN ('C','R'))) LOOP
+  
+  --  Handle discount 
+      IF (_r.cashrcptitem_discount_base > 0) THEN
+        PERFORM postCashReceiptDisc(_r.cashrcptitem_id, pJournalNumber);
+      END IF;
+     
+  --  Update the aropen item to post the paid amount
+      UPDATE aropen
+      SET aropen_paid = _r.new_paid + _r.new_discount,
+          aropen_open = (NOT _r.closed),
+          aropen_closedate = CASE WHEN _r.closed THEN _p.cashrcpt_distdate END
+      WHERE (aropen_id=_r.aropen_id);
+  
+  --  Cache the running amount posted
+      _posted_base := _posted_base + _r.cashrcptitem_amount_base;
+      _posted := _posted + _r.cashrcptitem_amount;
+  --  Record the cashrcpt application
+    IF (_r.aropen_doctype IN ('I','D')) THEN
+      INSERT INTO arapply
+      ( arapply_cust_id,
+        arapply_source_aropen_id, arapply_source_doctype, arapply_source_docnumber,
+        arapply_target_aropen_id, arapply_target_doctype, arapply_target_docnumber,
+        arapply_fundstype, arapply_refnumber, arapply_reftype, arapply_ref_id,
+        arapply_applied, arapply_closed,
+        arapply_postdate, arapply_distdate, arapply_journalnumber, arapply_username,
+        arapply_curr_id )
+      VALUES
+      ( _p.cashrcpt_cust_id,
+        -1, 'K', _p.cashrcpt_number,
+        _r.aropen_id, _r.aropen_doctype, _r.aropen_docnumber,
+        _p.cashrcpt_fundstype, _p.cashrcpt_docnumber, 'CRA', _r.cashrcptitem_id,
+        round(_r.cashrcptitem_amount, 2), _r.closed,
+        _p.applydate, _p.cashrcpt_distdate, pJournalNumber, getEffectiveXtUser(), _p.cashrcpt_curr_id);
+    ELSE
+      INSERT INTO arapply
+      ( arapply_cust_id,
+        arapply_source_aropen_id, arapply_source_doctype, arapply_source_docnumber,
+        arapply_target_aropen_id, arapply_target_doctype, arapply_target_docnumber,
+        arapply_fundstype, arapply_refnumber, arapply_reftype, arapply_ref_id,
+        arapply_applied, arapply_closed, arapply_postdate, arapply_distdate,
+        arapply_journalnumber, arapply_username, arapply_curr_id )
+      VALUES
+      ( _p.cashrcpt_cust_id,
+        _r.aropen_id, _r.aropen_doctype, _r.aropen_docnumber,
+        -1, 'R', _p.cashrcpt_number,
+        '', '', 'CRA', _r.cashrcptitem_id,
+        round(abs(_r.cashrcptitem_amount), 2), _r.closed,
+        _p.applydate, _p.cashrcpt_distdate, pJournalNumber, getEffectiveXtUser(), _p.cashrcpt_curr_id );
+    END IF;
+  
+      _exchGain := arCurrGain(_r.aropen_id,_p.cashrcpt_curr_id, abs(_r.cashrcptitem_amount),
+                             _p.cashrcpt_distdate);
+
+       PERFORM insertIntoGLSeries( _sequence, 'A/R', 'CR',
+                          (_r.aropen_doctype || '-' || _r.aropen_docnumber),
+                          CASE WHEN _r.aropen_doctype != 'R' THEN _arAccntid
+                          ELSE findDeferredAccount(_p.cashrcpt_cust_id) END, 
+                          round(_r.cashrcptitem_amount_base + _exchGain, 2),
+                          _p.cashrcpt_distdate, _p.custnote, pCashrcptid );      
+                          
+      IF (_exchGain <> 0) THEN
+          PERFORM insertIntoGLSeries(_sequence, 'A/R', 'CR',
+                 _r.aropen_doctype || '-' || _r.aropen_docnumber,
+                 getGainLossAccntId(
+                   CASE WHEN _r.aropen_doctype != 'R' THEN _arAccntid
+                   ELSE findDeferredAccount(_p.cashrcpt_cust_id) END
+                 ), round(_exchGain, 2) * -1,
+                 _p.cashrcpt_distdate, _p.custnote, pCashrcptid);
+      END IF;
+
+    END LOOP;
+
+--  Distribute Misc. Applications
+  FOR _r IN SELECT cashrcptmisc_id, cashrcptmisc_accnt_id, cashrcptmisc_amount,
+                   (cashrcptmisc_amount / cashrcpt_curr_rate) AS cashrcptmisc_amount_base,
+                   cashrcptmisc_notes, cashrcpt_curr_id
+            FROM cashrcptmisc JOIN
+                 cashrcpt ON (cashrcptmisc_cashrcpt_id = cashrcpt_id)
+            WHERE (cashrcptmisc_cashrcpt_id=pCashrcptid)  LOOP
+
+--  Cache the running amount posted
+    _posted_base := (_posted_base + _r.cashrcptmisc_amount_base);
+    _posted := (_posted + _r.cashrcptmisc_amount);
+
+--  Record the cashrcpt application
+    INSERT INTO arapply
+    ( arapply_cust_id,
+      arapply_source_aropen_id, arapply_source_doctype, arapply_source_docnumber,
+      arapply_target_aropen_id, arapply_target_doctype, arapply_target_docnumber,
+      arapply_fundstype, arapply_refnumber,
+      arapply_applied, arapply_closed,
+      arapply_postdate, arapply_distdate, arapply_journalnumber, arapply_username,
+      arapply_curr_id, arapply_reftype, arapply_ref_id )
+    VALUES
+    ( _p.cashrcpt_cust_id,
+      -1, 'K', '',
+      -1, 'Misc.', '',
+      _p.cashrcpt_fundstype, _p.cashrcpt_docnumber,
+      round(_r.cashrcptmisc_amount, 2), TRUE,
+      _p.applydate, _p.cashrcpt_distdate, pJournalNumber, getEffectiveXtUser(), 
+      _r.cashrcpt_curr_id, 'CRD', _r.cashrcptmisc_id );
+    PERFORM insertIntoGLSeries( _sequence, 'A/R', 'CR', _r.cashrcptmisc_notes,
+                                _r.cashrcptmisc_accnt_id,
+                                round(_r.cashrcptmisc_amount_base, 2),
+                                _p.cashrcpt_distdate, _p.custnote, pCashrcptid );
+
+  END LOOP;
+
+--  Post any remaining Cash to an A/R Cash Despoit (Credit Memo)
+--  this credit memo may absorb an occasional currency exchange rounding error
+  IF (round(_posted_base, 2) < round(_p.cashrcpt_amount_base, 2)) THEN
+    _comment := ('Unapplied from ' || _p.cashrcpt_fundstype || '-' || _p.cashrcpt_docnumber);
+    PERFORM insertIntoGLSeries( _sequence, 'A/R', 'CR',
+                                _comment,
+                                _p.prepaid_accnt_id,
+                                round(_p.cashrcpt_amount_base, 2) -
+                                                        round(_posted_base, 2),
+                                _p.cashrcpt_distdate, _p.custnote, pCashrcptid );
+    SELECT fetchArMemoNumber() INTO _arMemoNumber;
+    IF(_p.cashrcpt_usecustdeposit) THEN
+      -- Post Customer Deposit
+      SELECT createARCashDeposit(_p.cashrcpt_cust_id, _arMemoNumber, '',
+                                 _p.cashrcpt_distdate, (_p.cashrcpt_amount - _posted),
+                                 _comment, pJournalNumber, _p.cashrcpt_curr_id) INTO _aropenid;
+    ELSE
+      -- Post A/R Credit Memo
+      _aropenid := createARCreditMemo(NULL, _p.cashrcpt_cust_id, _arMemoNumber, '',
+                                _p.cashrcpt_distdate, (_p.cashrcpt_amount - _posted),
+                                _comment, -1, -1, -1, _p.cashrcpt_distdate, -1, NULL, 0,
+                                pJournalNumber, _p.cashrcpt_curr_id, _arAccntid);
+    END IF;
+
+    IF (_ccpayid IS NOT NULL) THEN
+      INSERT INTO payaropen (payaropen_ccpay_id, payaropen_aropen_id,
+                             payaropen_amount,   payaropen_curr_id
+                   ) VALUES (_ccpayid,           _aropenid,
+                             _p.cashrcpt_amount, _p.cashrcpt_curr_id);
+    END IF;
+
+    -- Create Cash Receipt Item to capture posting
+    IF (_predist=false) THEN
+      INSERT INTO cashrcptitem
+        ( cashrcptitem_cashrcpt_id, cashrcptitem_aropen_id, cashrcptitem_amount, cashrcptitem_applied )
+      VALUES
+        ( pCashrcptid, _aropenid, (_p.cashrcpt_amount - _posted), false );
+    END IF;
+
+  ELSIF (round(_posted_base, 2) > round((_p.cashrcpt_amount_base), 2)) THEN
+    PERFORM insertIntoGLSeries(_sequence, 'A/R', 'CR',
+                   'Currency Exchange Rounding - ' || _p.cashrcpt_docnumber,
+                   getGainLossAccntId(_debitAccntid),
+                   round(_posted_base, 2) - round((_p.cashrcpt_amount_base + _p.cashrcpt_discount_base), 2),
+                   _p.cashrcpt_distdate, _p.custnote, pCashrcptid);
+  END IF;
+
+--  Debit Cash
+  PERFORM insertIntoGLSeries( _sequence, 'A/R', 'CR',
+                    (_p.cashrcpt_fundstype || '-' || _p.cashrcpt_docnumber),
+                     _debitAccntid, round(_p.cashrcpt_amount_base, 2) * -1, 
+                     _p.cashrcpt_distdate,
+                     _p.custnote, pCashrcptid );
+
+  PERFORM postGLSeries(_sequence, pJournalNumber);
+
+  -- convert the cashrcptitem records to applications against the cm/cd if we are _predist
+  IF(_predist=true) THEN
+    FOR _r IN SELECT *
+              FROM cashrcptitem
+              WHERE ((cashrcptitem_cashrcpt_id=pCashrcptid)
+                AND (cashrcptitem_amount > 0)) LOOP
+
+      -- Handle discount if applicable
+      IF (_r.cashrcptitem_discount > 0) THEN
+        PERFORM postCashReceiptDisc(_r.cashrcptitem_id, pJournalNumber);
+      END IF;
+      
+      INSERT INTO arcreditapply (arcreditapply_source_aropen_id, arcreditapply_target_aropen_id,
+                                 arcreditapply_amount, arcreditapply_curr_id)
+                          VALUES(_aropenid, _r.cashrcptitem_aropen_id,
+                                 _r.cashrcptitem_amount, _p.cashrcpt_curr_id);
+      _posted := (_posted + _r.cashrcptitem_amount);
+      
+    END LOOP;
+
+    PERFORM postArCreditMemoApplication(_aropenid, _p.applydate);
+    
+    -- If there is any left over go ahead and create an additional cashrcptitem record for it with the amount
+    IF (round(_posted, 2) < round(_p.cashrcpt_amount, 2)) THEN
+      INSERT INTO cashrcptitem
+        ( cashrcptitem_cashrcpt_id, cashrcptitem_aropen_id, cashrcptitem_amount, cashrcptitem_applied )
+      VALUES
+        ( pCashrcptid, _aropenid, (_p.cashrcpt_amount - _posted), false );
+    END IF;
+  END IF;
+
+--  Update the posted cashrcpt
+  UPDATE cashrcpt SET cashrcpt_posted=TRUE,
+                      cashrcpt_posteddate=CURRENT_DATE,
+                      cashrcpt_postedby=getEffectiveXtUser()
+  WHERE (cashrcpt_id=pCashrcptid);
+
+  RETURN 1;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/postcashreceiptdisc.sql b/foundation-database/public/functions/postcashreceiptdisc.sql
new file mode 100644 (file)
index 0000000..ecb6614
--- /dev/null
@@ -0,0 +1,121 @@
+CREATE OR REPLACE FUNCTION postCashReceiptDisc(INTEGER, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCashrcptItemId ALIAS FOR $1;
+  pJournalNumber ALIAS FOR $2;
+  _r RECORD;
+  _t RECORD;
+  _v RECORD;
+  _ardiscountid INTEGER;
+  _arMemoNumber TEXT;
+  _arAccntid INTEGER;
+  _discountAccntid INTEGER;
+  _comment      TEXT;
+  _discprcnt NUMERIC;
+  _check INTEGER;
+
+BEGIN
+
+    -- Fetch base records for processing
+    SELECT aropen_id, aropen_doctype, aropen_amount,
+           cashrcptitem_discount,
+           cashrcpt_cust_id, cashrcpt_distdate, cashrcpt_applydate,
+           cashrcpt_curr_id, cashrcpt_fundstype, cashrcpt_docnumber,
+           round(currToCurr(cashrcpt_curr_id, aropen_curr_id, cashrcptitem_discount, cashrcpt_distdate),2) AS aropen_discount
+      INTO _r
+    FROM cashrcptitem 
+      JOIN cashrcpt ON (cashrcptitem_cashrcpt_id=cashrcpt_id)
+      JOIN aropen ON ( (aropen_id=cashrcptitem_aropen_id) AND (aropen_doctype IN ('I', 'D')) )
+    WHERE (cashrcptitem_id=pCashrcptItemId);
+
+    -- Get discount account
+    _discountAccntid := findardiscountaccount(_r.cashrcpt_cust_id);
+  
+    IF (_r.cashrcptitem_discount > 0) THEN
+      --  Determine discount percentage
+      _discprcnt := _r.aropen_discount / _r.aropen_amount;
+
+      SELECT fetchArMemoNumber() INTO _arMemoNumber;
+      _comment := 'Discount Credit from ' || _r.cashrcpt_fundstype || '-' || _r.cashrcpt_docnumber;
+
+      -- Create misc credit memo record
+      _ardiscountid := nextval('aropen_aropen_id_seq');
+      INSERT INTO aropen (
+        aropen_id, aropen_docdate, aropen_duedate, aropen_doctype, 
+        aropen_docnumber, aropen_curr_id, aropen_posted, aropen_amount) 
+      VALUES (
+        _ardiscountid, _r.cashrcpt_distdate, _r.cashrcpt_distdate, 'C', 
+        _arMemoNumber, _r.cashrcpt_curr_id, false,_r.cashrcptitem_discount);
+        
+      IF (fetchMetricBool('CreditTaxDiscount')) THEN
+        --  proportional tax credits calculated and implemented for the credit memo generated by the discount
+        IF (_r.aropen_doctype  = 'I') THEN
+          -- Tax for invoices
+          SELECT aropen_cobmisc_id AS invcheadid, 
+                 invchead_curr_id, 
+                 invchead_invcdate INTO _t
+          FROM aropen
+            LEFT OUTER JOIN invchead ON (aropen_cobmisc_id = invchead_id) 
+            LEFT OUTER JOIN invcitem ON (invchead_id = invcitem_invchead_id)
+          WHERE aropen_id = _r.aropen_id;
+
+          FOR _v IN SELECT tax_sales_accnt_id,
+                           tax_id, 
+                           round(sum(taxdetail_tax), 2) AS tax,
+                           currToBase(_t.invchead_curr_id, round(sum(taxdetail_tax), 2), _t.invchead_invcdate) AS taxbasevalue
+          FROM tax 
+            JOIN calculateTaxDetailSummary('I', _t.invcheadid, 'T') ON (taxdetail_tax_id=tax_id)
+            GROUP BY tax_id, tax_sales_accnt_id 
+          LOOP
+            INSERT INTO aropentax(
+              taxhist_parent_id, taxhist_taxtype_id, taxhist_tax_id,
+              taxhist_percent, taxhist_amount, taxhist_tax, 
+              taxhist_docdate, taxhist_basis)
+            VALUES (
+              _ardiscountid, getadjustmenttaxtypeid(), _v.tax_id, 
+              0.00, 0.00, (round((_v.tax * _discprcnt), 2) * -1), 
+              _r.cashrcpt_distdate, 0.00);
+          END LOOP;
+
+        ELSIF (_r.aropen_doctype  = 'D') THEN
+          -- Tax for debit memos
+          INSERT INTO aropentax(
+            taxhist_parent_id, taxhist_taxtype_id, taxhist_tax_id,
+            taxhist_percent, taxhist_amount, taxhist_tax, 
+            taxhist_docdate, taxhist_basis)
+          SELECT
+            _ardiscountid, taxhist_taxtype_id, taxhist_tax_id, 
+            0.00, 0.00, (round((taxhist_tax * _discprcnt), 2) * -1),  
+            _r.cashrcpt_distdate, 0.00
+          FROM aropentax
+          WHERE (taxhist_parent_id=_r.aropen_id);
+              
+        END IF;
+      END IF; -- End taxes
+
+      -- Create credit memo for discount
+      SELECT createARCreditMemo(_ardiscountid, _r.cashrcpt_cust_id, _arMemoNumber, '',
+                                _r.cashrcpt_distdate, _r.cashrcptitem_discount,
+                                _comment, -1, -1, _discountAccntid, _r.cashrcpt_distdate,
+                                -1, NULL, 0,
+                                pJournalNumber, _r.cashrcpt_curr_id) INTO _ardiscountid;
+
+      -- Apply discount credit memo
+      INSERT INTO arcreditapply ( 
+        arcreditapply_source_aropen_id, arcreditapply_target_aropen_id,
+        arcreditapply_amount, arcreditapply_curr_id )
+      VALUES ( 
+        _ardiscountid, _r.aropen_id, _r.cashrcptitem_discount, _r.cashrcpt_curr_id );
+      SELECT postARCreditMemoApplication(_ardiscountid, _r.cashrcpt_applydate) INTO _check;
+      IF (_check < 0) THEN
+        RAISE EXCEPTION 'Error posting discount credit memo application. Code %', _check;
+      END IF;
+        
+   END IF; -- End handle Discount
+
+   RETURN 1;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/postcccashreceipt.sql b/foundation-database/public/functions/postcccashreceipt.sql
new file mode 100644 (file)
index 0000000..6be4654
--- /dev/null
@@ -0,0 +1,113 @@
+CREATE OR REPLACE FUNCTION postCCcashReceipt(pCCpay   INTEGER,
+                                             pdocid   INTEGER,
+                                             pdoctype TEXT    DEFAULT NULL,
+                                             pamount  NUMERIC DEFAULT NULL) RETURNS INTEGER AS
+$$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _aropenid     INTEGER;
+  _bankaccnt_id INTEGER;
+  _c            RECORD;
+  _ccOrderDesc  TEXT;
+  _journal      INTEGER;
+  _realaccnt    INTEGER;
+  _return       INTEGER := 0;
+
+BEGIN
+  SELECT * INTO _c
+     FROM ccpay, ccard, custinfo
+     WHERE ( (ccpay_id = pCCpay)
+       AND   (ccpay_ccard_id = ccard_id)
+       AND   (ccpay_cust_id = cust_id) );
+
+  IF (NOT FOUND) THEN
+    RAISE EXCEPTION 'Cannot find the Credit Card transaction information [xtuple: postCCcashReceipt, -11, %]',
+                    pCCpay;
+  END IF;
+
+  IF (pamount IS NOT NULL) THEN
+    _c.ccpay_amount = pamount;
+  END IF;
+
+  SELECT bankaccnt_id, bankaccnt_accnt_id INTO _bankaccnt_id, _realaccnt
+  FROM ccbank JOIN bankaccnt ON (ccbank_bankaccnt_id=bankaccnt_id)
+  WHERE (ccbank_ccard_type=_c.ccard_type);
+
+  IF (_bankaccnt_id IS NULL) THEN
+    RAISE EXCEPTION 'Cannot find the default Bank Account for this Credit Card [xtuple: postCCcredit, -1, %]',
+                    _c.ccard_type;
+  END IF;
+
+  _ccOrderDesc := (_c.ccard_type || '-' || _c.ccpay_order_number::TEXT ||
+                  '-' || _c.ccpay_order_number_seq::TEXT);
+
+  _journal := fetchJournalNumber('C/R');
+
+  IF (pdoctype = 'cashrcpt') THEN
+    IF (COALESCE(pdocid, -1) < 0) THEN
+      INSERT INTO cashrcpt (
+        cashrcpt_cust_id,   cashrcpt_amount,     cashrcpt_curr_id,
+        cashrcpt_fundstype, cashrcpt_docnumber,  cashrcpt_notes,
+        cashrcpt_distdate,  cashrcpt_bankaccnt_id,
+        cashrcpt_usecustdeposit
+      ) VALUES (
+        _c.ccpay_cust_id,   _c.ccpay_amount,     _c.ccpay_curr_id,
+        _c.ccard_type,      _c.ccpay_r_ordernum, _ccOrderDesc,
+        CURRENT_DATE,       _bankaccnt_id,
+        fetchMetricBool('EnableCustomerDeposits'))
+      RETURNING cashrcpt_id INTO _return;
+    ELSE
+      UPDATE cashrcpt
+      SET cashrcpt_cust_id=_c.ccpay_cust_id,
+          cashrcpt_amount=_c.ccpay_amount,
+          cashrcpt_curr_id=_c.ccpay_curr_id,
+          cashrcpt_fundstype=_c.ccard_type,
+          cashrcpt_docnumber=_c.ccpay_r_ordernum,
+          cashrcpt_notes=_ccOrderDesc,
+          cashrcpt_distdate=CURRENT_DATE,
+          cashrcpt_bankaccnt_id=_bankaccnt_id
+      WHERE (cashrcpt_id=pdocid);
+      _return := pdocid;
+    END IF;
+
+  ELSIF (pdoctype = 'cohead') THEN
+    SELECT createARCreditMemo(NULL,               _c.ccpay_cust_id,
+                             fetchArMemoNumber(), cohead_number,
+                             CURRENT_DATE,        _c.ccpay_amount,
+                             'Unapplied from ' || _ccOrderDesc,
+                             NULL,                NULL, NULL,
+                             CURRENT_DATE,        NULL,
+                             cohead_salesrep_id,  NULL,
+                             _journal,            _c.ccpay_curr_id,
+                             NULL,                pCCpay) INTO _aropenid
+      FROM cohead
+     WHERE cohead_id = pdocid;
+    IF (COALESCE(_aropenid, -1) < 0) THEN       -- coalesce handles not-found case
+      RAISE EXCEPTION '[xtuple: createARCreditMemo, %]', _aropenid;
+    END IF;
+
+    INSERT INTO payaropen (payaropen_ccpay_id, payaropen_aropen_id,
+                           payaropen_amount,   payaropen_curr_id)
+                  VALUES  (pccpay,             _aropenid,
+                           _c.ccpay_amount,    _c.ccpay_curr_id);
+    INSERT INTO aropenalloc (aropenalloc_aropen_id, aropenalloc_doctype, aropenalloc_doc_id,
+                             aropenalloc_amount,    aropenalloc_curr_id)
+                     VALUES (_aropenid, 'S',          pdocid,
+                             _c.ccpay_amount,    _c.ccpay_curr_id);
+    _return := _aropenid;
+  END IF;
+
+  PERFORM insertGLTransaction(_journal, 'A/R', 'CR', _ccOrderDesc, 
+                              ('Cash Receipt from Credit Card ' || _c.cust_name),
+                              findPrepaidAccount(_c.ccpay_cust_id),
+                              _realaccnt,
+                              NULL,
+                             ROUND(currToBase(_c.ccpay_curr_id,
+                                              _c.ccpay_amount,
+                                              _c.ccpay_transaction_datetime::DATE),2),
+                              CURRENT_DATE);
+
+  RETURN _return;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/postcccredit.sql b/foundation-database/public/functions/postcccredit.sql
new file mode 100644 (file)
index 0000000..dbe56ca
--- /dev/null
@@ -0,0 +1,146 @@
+CREATE OR REPLACE FUNCTION postCCcredit(INTEGER, TEXT, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCCpay       ALIAS FOR $1;
+  preftype      ALIAS FOR $2;
+  prefid        ALIAS FOR $3;
+  _c           RECORD;
+  _ccOrderDesc TEXT;
+  _cglaccnt     INTEGER;
+  _dglaccnt    INTEGER;
+  _glseriesres  INTEGER;
+  _notes       TEXT := 'Credit via Credit Card';
+  _r           RECORD;
+  _sequence    INTEGER;
+  _dmaropenid  INTEGER;
+
+BEGIN
+  IF ((preftype = 'cohead') AND NOT EXISTS(SELECT cohead_id
+                                            FROM cohead
+                                            WHERE (cohead_id=prefid))) THEN
+    RAISE EXCEPTION 'Cannot find original Sales Order for this Credit Card credit [xtuple: postCCcredit, -2, %, %, %]',
+                    pCCpay, preftype, prefid;
+  ELSIF ((preftype = 'aropen') AND NOT EXISTS(SELECT aropen_id
+                                                FROM aropen
+                                                WHERE (aropen_id=prefid))) THEN
+    RAISE EXCEPTION 'Cannot find original A/R Open record for this Credit Card credit [xtuple: postCCcredit, -2, %, %, %]',
+                    pCCpay, preftype, prefid;
+  ELSIF ((preftype = 'cmhead') AND NOT EXISTS(SELECT cmhead_id
+                                                FROM cmhead
+                                               WHERE cmhead_id=prefid)) THEN
+    RAISE EXCEPTION 'Cannot find original Credit Memo record for this Credit Card credit [xtuple: postCCcredit, -2, %, %, %]',
+                    pCCpay, preftype, prefid;
+  END IF;
+
+  SELECT * INTO _c
+     FROM ccpay
+     JOIN ccard  ON (ccpay_ccard_id = ccard_id)
+     JOIN ccbank ON (ccard_type=ccbank_ccard_type)
+    WHERE (ccpay_id = pCCpay);
+
+  IF (NOT FOUND) THEN
+    RAISE EXCEPTION 'Cannot find the record for this Credit Card credit [xtuple: postCCcredit, -3, %, %, %]',
+                    pCCpay, preftype, prefid;
+  END IF;
+
+  IF (preftype = 'cohead') THEN
+    _dglaccnt := findPrepaidAccount(_c.ccpay_cust_id);
+  ELSE
+    _dglaccnt := findARAccount(_c.ccpay_cust_id);
+  END IF;
+
+  SELECT bankaccnt_accnt_id INTO _cglaccnt
+  FROM bankaccnt
+  WHERE (bankaccnt_id=_c.ccbank_bankaccnt_id);
+
+  IF (NOT FOUND) THEN
+    RAISE EXCEPTION 'Cannot find the default Bank Account for this Credit Card [xtuple: postCCcredit, -1, %]',
+                    pCCpay;
+  END IF;
+
+  IF (_c.ccpay_type != 'R') THEN
+    RAISE EXCEPTION 'This Credit Card transaction is not a credit/refund [xtuple: postCCcredit, -4, %]',
+                    pCCpay;
+  END IF;
+
+  _sequence := fetchGLSequence();
+
+  IF (_c.ccpay_r_ref IS NOT NULL) THEN
+    _ccOrderDesc := (_c.ccard_type || '-' || _c.ccpay_r_ref);
+  ELSE
+    _ccOrderDesc := (_c.ccard_type || '-' || _c.ccpay_order_number::TEXT ||
+                    '-' || COALESCE(_c.ccpay_order_number_seq::TEXT, ''));
+  END IF;
+
+  _glseriesres := insertIntoGLSeries(_sequence, 'A/R', 'CC', _ccOrderDesc,
+                                     _dglaccnt,
+                                     ROUND(currToBase(_c.ccpay_curr_id,
+                                                      _c.ccpay_amount,
+                                                      _c.ccpay_transaction_datetime::DATE), 2) * -1,
+                                     CURRENT_DATE, _notes);
+  IF (_glseriesres < 0) THEN
+    RAISE EXCEPTION 'Could not write debit side of Credit Card credit to the G/L [xtuple: insertIntoGLSeries, %]',
+                    _glseriesres;
+  END IF;
+
+  _glseriesres := insertIntoGLSeries(_sequence, 'A/R', 'CC', _ccOrderDesc,
+                                     _cglaccnt,
+                                     ROUND(currToBase(_c.ccpay_curr_id,
+                                                      _c.ccpay_amount,
+                                                      _c.ccpay_transaction_datetime::DATE),2),
+                                     CURRENT_DATE, _notes);
+  IF (_glseriesres < 0) THEN
+    RAISE EXCEPTION 'Could not write credit side of Credit Card credit to the G/L [xtuple: insertIntoGLSeries, %]',
+                    _glseriesres;
+  END IF;
+
+  _glseriesres := postGLSeries(_sequence, fetchJournalNumber('C/R') );
+  IF (_glseriesres < 0) THEN
+    RAISE EXCEPTION 'Could not post Credit Card credit to the G/L [xtuple: postglseries, %]',
+                    _glseriesres;
+  END IF;
+
+  IF (preftype = 'aropen') THEN
+    SELECT * INTO _r
+    FROM aropen
+    WHERE (aropen_id=prefid);
+
+  ELSE
+    SELECT aropen.* INTO _r
+    FROM ccpay n
+      JOIN ccpay o  ON (o.ccpay_id=n.ccpay_ccpay_id)
+      JOIN payaropen ON (payaropen_ccpay_id=o.ccpay_id)
+      JOIN aropen ON (payaropen_aropen_id=aropen_id)
+    WHERE (n.ccpay_id=pCCpay);
+  END IF;
+
+  IF (FOUND) THEN
+    SELECT createardebitmemo(
+            NULL, 
+            _r.aropen_cust_id, NULL, fetchARMemoNumber(),
+            _r.aropen_ordernumber, current_date, _c.ccpay_amount,
+            _notes,
+            -1, -1, -1, CURRENT_DATE, -1, NULL, 0, 
+            _r.aropen_curr_id) INTO _dmaropenid;
+
+    IF (_r.aropen_open) THEN
+      PERFORM applyARCreditMemoToBalance(_r.aropen_id, _dmaropenid);
+      PERFORM postARCreditMemoApplication(_r.aropen_id);
+    END IF;
+    
+  END IF;
+
+  IF (preftype = 'cohead') THEN
+    INSERT INTO payco (
+      payco_ccpay_id, payco_cohead_id, payco_amount, payco_curr_id 
+    ) VALUES (
+      pCCpay, prefid, 0 - _c.ccpay_amount, _c.ccpay_curr_id
+    );
+  END IF;
+
+  RETURN 0;
+
+END;
+$$
+LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/postccvoid.sql b/foundation-database/public/functions/postccvoid.sql
new file mode 100644 (file)
index 0000000..d678c65
--- /dev/null
@@ -0,0 +1,23 @@
+CREATE OR REPLACE FUNCTION postCCVoid(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pccpayid ALIAS FOR $1;
+
+BEGIN
+  -- for now this is very simple: mark the ccpay record voided.
+  -- in the future this might be expanded to back out changes to other tables
+  -- but for now the VOID request is sent to the credit card processing company
+  -- before those other tables are modified.
+
+  UPDATE ccpay SET ccpay_status = ''V'' WHERE (ccpay_id=pccpayid);
+
+  IF (NOT FOUND) THEN
+    RETURN -1;
+  END IF;
+
+  RETURN 0;
+
+END;
+'
+  LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/postcheck.sql b/foundation-database/public/functions/postcheck.sql
new file mode 100644 (file)
index 0000000..5c1b091
--- /dev/null
@@ -0,0 +1,278 @@
+CREATE OR REPLACE FUNCTION postCheck(INTEGER, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pcheckid             ALIAS FOR $1;
+  _journalNumber       INTEGER := $2;
+  _amount_base         NUMERIC := 0;
+  _credit_glaccnt      INTEGER;
+  _exchGain            NUMERIC := 0;
+  _exchGainTmp         NUMERIC := 0;
+  _gltransNote         TEXT;
+  _p                   RECORD;
+  _r                   RECORD;
+  _t                   RECORD;
+  _sequence            INTEGER;
+  _test                 INTEGER;
+  _cm                   BOOLEAN;
+  _amount_check         NUMERIC := 0;
+
+BEGIN
+
+  _cm := FALSE;
+
+  SELECT fetchGLSequence() INTO _sequence;
+  IF (_journalNumber IS NULL) THEN
+    _journalNumber := fetchJournalNumber('AP-CK');
+  END IF;
+
+  SELECT checkhead.*,
+         checkhead_amount / checkhead_curr_rate AS checkhead_amount_base,
+         bankaccnt_accnt_id AS bankaccntid INTO _p
+  FROM checkhead
+   JOIN bankaccnt ON (checkhead_bankaccnt_id=bankaccnt_id)
+  WHERE (checkhead_id=pcheckid);
+
+  IF (FOUND) THEN
+    IF (_p.checkhead_recip_type = 'V') THEN
+      SELECT
+        vend_number AS checkrecip_number,
+        vend_name AS checkrecip_name,
+        findAPAccount(vend_id) AS checkrecip_accnt_id,
+        'A/P'::text AS checkrecip_gltrans_source
+        INTO _t
+      FROM vendinfo
+      WHERE (vend_id=_p.checkhead_recip_id);
+    ELSIF (_p.checkhead_recip_type = 'C') THEN
+      SELECT
+        cust_number AS checkrecip_number,
+        cust_name AS checkrecip_name,
+        findARAccount(cust_id) AS checkrecip_accnt_id,
+        'A/R'::text AS checkrecip_gltrans_source
+        INTO _t
+      FROM custinfo
+      WHERE (cust_id=_p.checkhead_recip_id); 
+    ELSIF (_p.checkhead_recip_type = 'T') THEN
+      SELECT
+        taxauth_code AS checkrecip_number,
+        taxauth_name AS checkrecip_name,
+        taxauth_accnt_id AS checkrecip_accnt_id,
+        'G/L'::text AS checkrecip_gltrans_source
+        INTO _t
+      FROM taxauth
+      WHERE (taxauth_id=_p.checkhead_recip_id);
+    ELSE
+      RETURN -11;
+    END IF;
+  ELSE
+    RETURN -11;
+  END IF;
+
+  IF (_p.checkhead_posted) THEN
+    RETURN -10;
+  END IF;
+
+  IF (_p.checkhead_recip_type = 'C') THEN
+    SELECT checkitem_id FROM checkitem INTO _test
+    WHERE (checkitem_checkhead_id=pcheckid)
+    LIMIT 1;
+    IF (FOUND) THEN
+      _cm := TRUE;
+    END IF;
+  END IF;
+
+  _gltransNote := _t.checkrecip_number || '-' || _t.checkrecip_name;
+
+  IF (_p.checkhead_misc AND NOT _cm) THEN
+    IF (COALESCE(_p.checkhead_expcat_id, -1) < 0) THEN
+      IF (_p.checkhead_recip_type = 'V') THEN
+       PERFORM createAPCreditMemo( _p.checkhead_recip_id, _journalNumber,
+                                   CAST(fetchAPMemoNumber() AS text), '',
+                                   _p.checkhead_checkdate, _p.checkhead_amount,
+                                   _gltransNote || ' ' || _p.checkhead_notes,
+                                   -1, _p.checkhead_checkdate,
+                                   -1, _p.checkhead_curr_id );
+       _credit_glaccnt := findAPPrepaidAccount(_p.checkhead_recip_id);
+
+      ELSIF (_p.checkhead_recip_type = 'C') THEN
+       PERFORM createARDebitMemo(NULL, _p.checkhead_recip_id, NULL,
+                                    fetchARMemoNumber(), '',
+                                    _p.checkhead_checkdate, _p.checkhead_amount,
+                                    _gltransNote || ' ' || _p.checkhead_notes,
+                                     -1, -1, -1, _p.checkhead_checkdate, -1, NULL, 0,
+                                    _p.checkhead_curr_id );
+        _credit_glaccnt := findPrepaidAccount(_p.checkhead_recip_id);
+      ELSIF (_p.checkhead_recip_type = 'T') THEN
+       -- TODO: should we create a credit memo for the tax authority? how?
+       _credit_glaccnt := _t.checkrecip_accnt_id;
+
+      END IF; -- recip type
+
+    ELSE
+      IF (_cm) THEN
+        _credit_glaccnt := findARAccount(_p.checkhead_recip_id);
+      ELSE
+        SELECT expcat_exp_accnt_id INTO _credit_glaccnt
+        FROM expcat
+        WHERE (expcat_id=_p.checkhead_expcat_id);
+        IF (NOT FOUND) THEN
+          RETURN -12;
+        END IF;
+      END IF;
+    END IF;
+
+    IF (COALESCE(_credit_glaccnt, -1) < 0) THEN
+      RETURN -13;
+    END IF;
+
+    PERFORM insertIntoGLSeries( _sequence, _t.checkrecip_gltrans_source, 'CK',
+                               CAST(_p.checkhead_number AS TEXT),
+                               _credit_glaccnt,
+                               round(_p.checkhead_amount_base, 2) * -1,
+                               _p.checkhead_checkdate, _gltransNote, pcheckid );
+
+    _amount_base := _p.checkhead_amount_base;
+
+  ELSE
+    FOR _r IN SELECT checkitem_amount, checkitem_discount,
+                     CASE WHEN (checkitem_apopen_id IS NOT NULL) THEN
+                       checkitem_amount / apopen_curr_rate
+                     ELSE
+                       currToBase(checkitem_curr_id,
+                                  checkitem_amount,
+                                  COALESCE(checkitem_docdate, _p.checkhead_checkdate)) 
+                     END AS checkitem_amount_base,
+                     currTocurr(checkitem_curr_id, _p.checkhead_curr_id,
+                                  checkitem_amount,
+                                  _p.checkhead_checkdate) AS amount_check,
+                     apopen_id, apopen_doctype, apopen_docnumber,
+                     aropen_id, aropen_doctype, aropen_docnumber,
+                     checkitem_curr_id, checkitem_curr_rate, apopen_curr_rate,
+                     COALESCE(checkitem_docdate, _p.checkhead_checkdate) AS docdate
+              FROM (checkitem LEFT OUTER JOIN
+                   apopen ON (checkitem_apopen_id=apopen_id)) LEFT OUTER JOIN
+                   aropen ON (checkitem_aropen_id=aropen_id)
+              WHERE (checkitem_checkhead_id=pcheckid) LOOP
+
+      _exchGainTmp := 0;
+      IF (_r.apopen_id IS NOT NULL) THEN
+       --  take the discount if specified before we do anything else
+        IF(_r.checkitem_discount > 0.0) THEN
+          PERFORM createAPDiscount(_r.apopen_id, _r.checkitem_discount);
+        END IF;
+
+        UPDATE apopen
+
+        SET apopen_paid = round(apopen_paid + _r.checkitem_amount, 2),
+            apopen_open = round(apopen_amount, 2) >
+                         round(apopen_paid + _r.checkitem_amount, 2),
+            apopen_closedate = CASE WHEN (round(apopen_amount, 2) <=
+                                         round(apopen_paid + _r.checkitem_amount, 2)) THEN _p.checkhead_checkdate END
+        WHERE (apopen_id=_r.apopen_id);
+
+       --  Post the application
+        INSERT INTO apapply
+        ( apapply_vend_id, apapply_postdate, apapply_username,
+          apapply_source_apopen_id, apapply_source_doctype, apapply_source_docnumber,
+          apapply_target_apopen_id, apapply_target_doctype, apapply_target_docnumber,
+          apapply_journalnumber, apapply_amount, apapply_curr_id, apapply_checkhead_id )
+        VALUES
+        ( _p.checkhead_recip_id, _p.checkhead_checkdate, getEffectiveXtUser(),
+          -1, 'K', _p.checkhead_number,
+          _r.apopen_id, _r.apopen_doctype, _r.apopen_docnumber,
+          _journalNumber, _r.checkitem_amount, _r.checkitem_curr_id, _p.checkhead_id );
+      END IF; -- if check item's apopen_id is not null
+
+      IF (_r.aropen_id IS NOT NULL) THEN
+
+        UPDATE aropen
+        SET aropen_paid = round(aropen_paid + _r.checkitem_amount, 2),
+            aropen_open = round(aropen_amount, 2) >
+                         round(aropen_paid + _r.checkitem_amount, 2),
+            aropen_closedate = CASE WHEN (round(aropen_amount, 2) <=
+                                         round(aropen_paid + _r.checkitem_amount, 2)) THEN _p.checkhead_checkdate END
+        WHERE (aropen_id=_r.aropen_id);
+
+       --  Post the application
+        INSERT INTO arapply
+        ( arapply_cust_id, arapply_postdate, arapply_distdate, arapply_username,
+          arapply_source_aropen_id, arapply_source_doctype, arapply_source_docnumber,
+          arapply_target_aropen_id, arapply_target_doctype, arapply_target_docnumber,
+          arapply_journalnumber, arapply_applied, arapply_curr_id )
+        VALUES
+        ( _p.checkhead_recip_id, _p.checkhead_checkdate, _p.checkhead_checkdate, getEffectiveXtUser(),
+          _r.aropen_id,_r.aropen_doctype, _r.aropen_docnumber,
+          -1, 'K',_p.checkhead_number ,
+          _journalNumber, _r.checkitem_amount, _r.checkitem_curr_id );
+
+      END IF; -- if check item's aropen_id is not null
+
+      IF (_r.apopen_id IS NOT NULL) THEN
+        SELECT apCurrGain(_r.apopen_id,_r.checkitem_curr_id, _r.checkitem_amount,
+                        _p.checkhead_checkdate)
+              INTO _exchGainTmp;
+      ELSIF (_r.aropen_id IS NOT NULL) THEN
+        SELECT arCurrGain(_r.aropen_id,_r.checkitem_curr_id, _r.checkitem_amount,
+                        _p.checkhead_checkdate)
+              INTO _exchGainTmp;
+      END IF;
+      _exchGain := _exchGain + _exchGainTmp;
+
+      PERFORM insertIntoGLSeries( _sequence, _t.checkrecip_gltrans_source,
+                                 'CK', CAST(_p.checkhead_number AS TEXT),
+                                  _t.checkrecip_accnt_id,
+                                  round(_r.checkitem_amount_base, 2) * -1,
+                                  _p.checkhead_checkdate, _gltransNote, pcheckid );
+      IF (_exchGainTmp <> 0) THEN
+       PERFORM insertIntoGLSeries( _sequence, _t.checkrecip_gltrans_source,
+                                   'CK', CAST(_p.checkhead_number AS TEXT),
+                                   getGainLossAccntId(_t.checkrecip_accnt_id), round(_exchGainTmp,2),
+                                   _p.checkhead_checkdate, _gltransNote, pcheckid );
+      END IF;
+
+      _amount_check := (_amount_check + _r.amount_check);
+      _amount_base := (_amount_base + _r.checkitem_amount_base);
+
+    END LOOP;
+
+    IF( (_amount_check - _p.checkhead_amount) <> 0.0 ) THEN 
+      _exchGainTmp := currToBase(_p.checkhead_curr_id,
+                                 _amount_check - _p.checkhead_amount,
+                                 _p.checkhead_checkdate);
+      _exchGain := _exchGain + _exchGainTmp;
+    END IF;
+    --  ensure that the check balances, attribute rounding errors to gain/loss
+    IF round(_amount_base, 2) - round(_exchGain, 2) <> round(_p.checkhead_amount_base, 2) THEN
+      IF round(_amount_base - _exchGain, 2) = round(_p.checkhead_amount_base, 2) THEN
+       PERFORM insertIntoGLSeries( _sequence, _t.checkrecip_gltrans_source,
+                                   'CK',
+                                   CAST(_p.checkhead_number AS TEXT),
+                                    getGainLossAccntId(_p.bankaccntid),
+                                   round(_amount_base, 2) -
+                                     round(_exchGain, 2) -
+                                     round(_p.checkhead_amount_base, 2),
+                                   _p.checkhead_checkdate, _gltransNote, pcheckid );
+      ELSE
+       RAISE EXCEPTION 'checkhead_id % does not balance (% - % <> %)', pcheckid,
+             _amount_base, _exchGain, _p.checkhead_amount_base;
+      END IF;
+    END IF;
+  END IF;
+
+  PERFORM insertIntoGLSeries( _sequence, _t.checkrecip_gltrans_source, 'CK',
+                             CAST(_p.checkhead_number AS TEXT),
+                              _p.bankaccntid,
+                             round(_p.checkhead_amount_base, 2),
+                              _p.checkhead_checkdate, _gltransNote, pcheckid );
+
+  PERFORM postGLSeries(_sequence, _journalNumber);
+
+  UPDATE checkhead
+  SET checkhead_posted=TRUE,
+      checkhead_journalnumber=_journalNumber
+  WHERE (checkhead_id=pcheckid);
+
+  RETURN _journalNumber;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/postchecks.sql b/foundation-database/public/functions/postchecks.sql
new file mode 100644 (file)
index 0000000..1b0f7cc
--- /dev/null
@@ -0,0 +1,22 @@
+CREATE OR REPLACE FUNCTION postChecks(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pBankaccntid ALIAS FOR $1;
+  _journalNumber INTEGER;
+
+BEGIN
+
+  SELECT fetchJournalNumber(''AP-CK'') INTO _journalNumber;
+
+  PERFORM postCheck(checkhead_id, _journalNumber)
+  FROM checkhead
+  WHERE ( (NOT checkhead_void)
+    AND   (NOT checkhead_posted)
+    AND   (checkhead_printed)
+    AND   (checkhead_bankaccnt_id=pBankaccntid) );
+
+  RETURN _journalNumber;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/postcomment.sql b/foundation-database/public/functions/postcomment.sql
new file mode 100644 (file)
index 0000000..8e439ef
--- /dev/null
@@ -0,0 +1,62 @@
+
+CREATE OR REPLACE FUNCTION postComment(pCmnttypename TEXT,
+                                       pSource TEXT,
+                                       pSourceid INTEGER,
+                                       pText TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _cmnttypeid INTEGER;
+
+BEGIN
+  SELECT cmnttype_id INTO _cmnttypeid
+  FROM cmnttype
+  WHERE (cmnttype_name=pCmnttypename);
+  IF (NOT FOUND) THEN
+    RAISE EXCEPTION 'Comment type % not found.', pCmnttypename;
+  END IF;
+
+  RETURN postComment(_cmnttypeid, pSource, pSourceid, pText, NULL);
+END
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION postComment(pCmnttypeid INTEGER,
+                                       pSource TEXT,
+                                       pSourceid INTEGER,
+                                       pText TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+
+BEGIN
+  RETURN postComment(pCmnttypeid, pSource, pSourceid, pText, NULL);
+END
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION postComment(pCmnttypeid INTEGER,
+                                       pSource TEXT,
+                                       pSourceid INTEGER,
+                                       pText TEXT,
+                                       pPublic BOOLEAN) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _commentid INTEGER;
+  _public BOOLEAN;
+
+BEGIN
+  _public := COALESCE(pPublic, fetchmetricbool('CommentPublicDefault'));
+
+  INSERT INTO comment
+  ( comment_cmnttype_id, comment_source, comment_source_id,
+    comment_date, comment_user, comment_text, comment_public )
+  VALUES
+  ( pCmnttypeid, pSource, pSourceid,
+    CURRENT_TIMESTAMP, getEffectiveXtUser(), pText, _public )
+  RETURNING comment_id INTO _commentid;
+
+  RETURN _commentid;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/postcost.sql b/foundation-database/public/functions/postcost.sql
new file mode 100644 (file)
index 0000000..119f460
--- /dev/null
@@ -0,0 +1,28 @@
+CREATE OR REPLACE FUNCTION postCost(INTEGER) RETURNS BOOLEAN AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemcostid ALIAS FOR $1;
+  _p RECORD;
+
+BEGIN
+
+  SELECT round(currToBase(itemcost_curr_id, itemcost_actcost, CURRENT_DATE),6) AS newcost,
+         itemcost_curr_id, CURRENT_DATE AS effective,
+         item_number,
+         itemcost_stdcost AS oldcost INTO _p
+  FROM itemcost, item
+  WHERE ((itemcost_item_id=item_id)
+    AND  (itemcost_id=pItemcostid));
+
+  IF (_p.newcost IS NULL) THEN
+      RAISE EXCEPTION ''There is no valid Exchange Rate for this currency. (%, %)'',
+                  _p.itemcost_curr_id, _p.effective;
+      RETURN FALSE;
+  END IF;
+
+  RETURN updateStdCost(pItemcostid, _p.newcost, _p.oldcost, ''Post Cost'',
+               (''Post Actual Cost to Standard for item '' || _p.item_number));
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/postcountslip.sql b/foundation-database/public/functions/postcountslip.sql
new file mode 100644 (file)
index 0000000..b01d8ed
--- /dev/null
@@ -0,0 +1,61 @@
+CREATE OR REPLACE FUNCTION postCountSlip(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCntslipid ALIAS FOR $1;
+  _p RECORD;
+  _comments TEXT;
+  _temp TEXT;
+
+BEGIN
+
+  SELECT itemsite_loccntrl, itemsite_controlmethod,
+         cntslip_posted, cntslip_lotserial, cntslip_comments,
+         cntslip_number, cntslip_qty INTO _p
+  FROM cntslip, invcnt, itemsite
+  WHERE ( (cntslip_cnttag_id=invcnt_id)
+   AND (invcnt_itemsite_id=itemsite_id)
+   AND (cntslip_id=pCntslipid) );
+
+  IF (NOT _p.cntslip_posted) THEN
+    SELECT ( E'\nCount Slip #' || _p.cntslip_number ||
+             ' counted ' || formatQty(_p.cntslip_qty) ) INTO _comments;
+
+--  Add the Location name if the itemsite is MLC
+    IF (_p.itemsite_loccntrl) THEN
+      SELECT ( ', Location:' || location_name ) INTO _temp
+      FROM location, cntslip
+      WHERE ( (cntslip_location_id=location_id)
+       AND (cntslip_id=pCntslipid) );
+
+      _comments := (_comments || _temp);
+    END IF;
+
+--  Add the Lot/Serial if the itemsite is Lot or Serial controlled
+    IF (_p.itemsite_controlmethod = 'L') THEN
+      _comments := (_comments || ( ', Lot #:' || _p.cntslip_lotserial));
+    ELSIF (_p.itemsite_controlmethod = 'S') THEN
+      _comments := (_comments || ( ', Serial #:' || _p.cntslip_lotserial));
+    END IF;
+
+    _comments := (_comments || ' ' || _p.cntslip_comments);
+
+    UPDATE cntslip
+    SET cntslip_posted=TRUE
+    WHERE (cntslip_id=pCntslipid);
+
+    UPDATE invcnt
+    SET invcnt_qoh_after = ( COALESCE(invcnt_qoh_after, 0) + cntslip_qty),
+        invcnt_comments = (invcnt_comments || _comments)
+    FROM cntslip
+    WHERE ( (cntslip_cnttag_id=invcnt_id)
+     AND (cntslip_id=pCntslipid) );
+
+    RETURN 1;
+
+  ELSE
+    RETURN -1;
+  END IF;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/postcounttag.sql b/foundation-database/public/functions/postcounttag.sql
new file mode 100644 (file)
index 0000000..04406bb
--- /dev/null
@@ -0,0 +1,281 @@
+SELECT dropIfExists('FUNCTION', 'postCountTag(integer, boolean, text)', 'public');
+
+CREATE OR REPLACE FUNCTION postCountTag(INTEGER, BOOLEAN) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pInvcntid ALIAS FOR $1;
+  pThaw ALIAS FOR $2;
+  _avgCostingMethod TEXT;
+  _invhistid INTEGER;
+  _postDate TIMESTAMP;
+  _runningQty NUMERIC;
+  _errorCode INTEGER;
+  _itemlocSeries INTEGER := 0;
+  _hasDetail BOOLEAN;
+  _p RECORD;
+  _itemloc RECORD;
+  _cntslip RECORD;
+  _lsid INTEGER;
+
+BEGIN
+
+  SELECT COALESCE(fetchMetricText('CountAvgCostMethod'), 'STD') INTO _avgCostingMethod;
+
+  SELECT invcnt_id, invcnt_tagnumber, invcnt_qoh_after,
+         invcnt_location_id,
+         item_number,
+         itemsite_id, itemsite_freeze,
+         itemsite_qtyonhand,
+         itemsite_loccntrl, itemsite_location_id,
+         CASE WHEN (itemsite_costmethod = 'N') THEN 0
+              WHEN ( (itemsite_costmethod = 'A') AND
+                     (itemsite_qtyonhand = 0) AND
+                     (_avgCostingMethod = 'ACT') ) THEN actcost(itemsite_item_id)
+              WHEN ( (itemsite_costmethod = 'A') AND
+                     (_avgCostingMethod IN ('ACT', 'AVG')) ) THEN avgcost(itemsite_id)
+              ELSE stdcost(itemsite_item_id)
+         END AS cost, itemsite_costmethod,
+         itemsite_controlmethod,
+         itemsite_value INTO _p
+  FROM invcnt, itemsite, item
+  WHERE ( (invcnt_itemsite_id=itemsite_id)
+   AND (itemsite_item_id=item_id)
+   AND (invcnt_qoh_after IS NOT NULL)
+   AND (NOT invcnt_posted)
+   AND (invcnt_id=pInvcntid) );
+  IF (FOUND) THEN
+-- If the invcnt_location_id is not null then
+-- call a separate function so as not to affect
+-- the existing functionality.
+    IF (_p.invcnt_location_id IS NOT NULL) THEN
+      RETURN postCountTagLocation(pInvcntid, pThaw);
+    END IF;
+
+    SELECT NEXTVAL('invhist_invhist_id_seq') INTO _invhistid;
+
+    IF (_p.itemsite_freeze) THEN
+      SELECT invcnt_tagdate INTO _postDate
+      FROM invcnt
+      WHERE (invcnt_id=pInvcntid) ;
+    ELSE
+      _postDate = CURRENT_TIMESTAMP;
+    END IF;
+
+    _hasDetail = FALSE;
+
+--  Post the detail indicated by cntslips
+    IF ( (_p.itemsite_loccntrl) OR
+         (_p.itemsite_controlmethod IN ('L', 'S')) ) THEN
+
+      SELECT NEXTVAL('itemloc_series_seq') INTO _itemlocSeries;
+
+--  Adjust any existing detail to 0
+      FOR _itemloc IN SELECT itemloc_id, itemloc_location_id,
+                             itemloc_ls_id, itemloc_qty
+                      FROM itemloc
+                      WHERE (itemloc_itemsite_id=_p.itemsite_id) LOOP
+
+        _hasDetail = TRUE;
+
+--  Create the itemlocdist flushing records
+        INSERT INTO itemlocdist
+        ( itemlocdist_series, itemlocdist_source_type, itemlocdist_source_id,
+          itemlocdist_expiration,
+          itemlocdist_itemsite_id, itemlocdist_invhist_id, itemlocdist_flush )
+        VALUES
+        ( _itemlocSeries, 'I', _itemloc.itemloc_id,
+          endOfTime(),
+          _p.itemsite_id, _invhistid, TRUE );
+
+      END LOOP;
+
+--  Clear the running detail Qty
+      _runningQty := 0;
+
+--  Adjust the detail to the cntslip indicated value
+      FOR _cntslip IN SELECT cntslip_location_id, cntslip_lotserial,
+                             cntslip_lotserial_expiration,
+                             cntslip_lotserial_warrpurc,
+                             SUM(cntslip_qty) AS qty,
+                             itemsite_item_id
+                      FROM cntslip, invcnt, itemsite
+                      WHERE ((cntslip_cnttag_id=pInvcntid)
+                      AND (cntslip_cnttag_id=invcnt_id)
+                      AND (invcnt_itemsite_id=itemsite_id))
+                      GROUP BY cntslip_location_id, cntslip_lotserial, 
+                      cntslip_lotserial_expiration, cntslip_lotserial_warrpurc,itemsite_item_id LOOP
+
+--  Handle the LotSerial
+        IF (LENGTH(_cntslip.cntslip_lotserial)>0) THEN
+          SELECT ls_id INTO _lsid
+          FROM ls
+          WHERE ((ls_item_id=_cntslip.itemsite_item_id)
+          AND (UPPER(ls_number)=UPPER(_cntslip.cntslip_lotserial)));
+
+          IF (NOT FOUND) THEN
+            _lsid := NEXTVAL('ls_ls_id_seq');
+            INSERT INTO ls
+            VALUES (_lsid,_cntslip.itemsite_item_id,UPPER(_cntslip.cntslip_lotserial));
+          END IF;
+        END IF;
+
+--  Track the running Qty
+        _runningQty := (_runningQty + _cntslip.qty);
+        _hasDetail = TRUE;
+
+--  Create the itemlocdist populating record
+        INSERT INTO itemlocdist
+        ( itemlocdist_series, itemlocdist_source_type, itemlocdist_source_id,
+          itemlocdist_itemsite_id,
+          itemlocdist_ls_id, itemlocdist_expiration, itemlocdist_warranty,
+          itemlocdist_qty, itemlocdist_invhist_id )
+        VALUES
+        ( _itemlocSeries, 'L', _cntslip.cntslip_location_id,
+          _p.itemsite_id,
+          _lsid, COALESCE(_cntslip.cntslip_lotserial_expiration, endOfTime()),
+          _cntslip.cntslip_lotserial_warrpurc,_cntslip.qty, _invhistid );
+
+      END LOOP;
+
+      IF (_runningQty > _p.invcnt_qoh_after) THEN
+--  The total Count Slip Qty is greater than the Count Tag Qty,
+--  Don't post the Count.
+        _errorCode = -1;
+
+      ELSIF ( (_runningQty < _p.invcnt_qoh_after) AND
+              (_p.itemsite_controlmethod IN ('L', 'S')) ) THEN
+--  The total Count Slip Qty is less than the Count Tag Qty,
+--  and the Item Site is Lot/Serial controlled.
+--  Don't post the Count.
+        _errorCode = -2;
+
+      ELSIF (_runningQty < _p.invcnt_qoh_after) THEN
+        IF ( (NOT _p.itemsite_loccntrl) OR
+             (_p.itemsite_location_id = -1) ) THEN
+--  The total Count Slip Qty is less than the Count Tag Qty,
+--  and there isn't a default location to post into.
+--  Don't post the Count.
+          _errorCode = -3;
+
+        ELSIF ( SELECT (metric_value='f')
+                FROM metric
+                WHERE (metric_name='PostCountTagToDefault') ) THEN
+--  The total Count Slip Qty is less than the Count Tag Qty,
+--  and we don't post Count Tags to default Locations
+--  Don't post the Count.
+          _errorCode = -4;
+
+        ELSE
+--  Distribute the remaining qty into the default location.
+          INSERT INTO itemlocdist
+          ( itemlocdist_series, itemlocdist_source_type, itemlocdist_source_id,
+            itemlocdist_itemsite_id,
+            itemlocdist_expiration,
+            itemlocdist_qty, itemlocdist_invhist_id )
+          SELECT _itemlocSeries, 'L', _p.itemsite_location_id,
+                 _p.itemsite_id,
+                 endOfTime(),
+                 (_p.invcnt_qoh_after - _runningQty), _invhistid;
+
+          _hasDetail = TRUE;
+          _errorCode = 0;
+        END IF;
+      ELSE
+--  The Count Slip Qty. must equal the Count Tag Qty.
+        _errorCode = 0;
+      END IF;
+
+--  If we shouldn't post the count then delete the itemlocdist records,
+--  and return with the error.
+      IF (_errorCode <> 0) THEN
+        DELETE FROM itemlocdist
+        WHERE (itemlocdist_series=_itemlocSeries);
+  
+        RETURN _errorCode;
+      END IF;
+
+    END IF;
+
+--  Mod. the Count Tag.
+    UPDATE invcnt
+    SET invcnt_qoh_before=_p.itemsite_qtyonhand,
+        invcnt_postdate=_postDate,
+        invcnt_posted=TRUE,
+        invcnt_invhist_id=_invhistid,
+        invcnt_post_username=getEffectiveXtUser()
+    WHERE (invcnt_id=pInvcntid);
+
+--  Create the CC transaction
+    INSERT INTO invhist
+     ( invhist_id, invhist_itemsite_id,
+       invhist_transdate, invhist_transtype, invhist_invqty,
+       invhist_qoh_before, invhist_qoh_after,
+       invhist_docnumber, invhist_comments,
+       invhist_invuom, invhist_unitcost, invhist_hasdetail,
+       invhist_costmethod, invhist_value_before, invhist_value_after,
+       invhist_series )
+    SELECT _invhistid, itemsite_id,
+           _postDate, 'CC', (invcnt_qoh_after - invcnt_qoh_before),
+           invcnt_qoh_before, invcnt_qoh_after,
+           invcnt_tagnumber, invcnt_comments,
+           uom_name, _p.cost, _hasDetail,
+           _p.itemsite_costmethod, _p.itemsite_value, 
+           _p.itemsite_value + (_p.cost * (invcnt_qoh_after - invcnt_qoh_before)),
+           _itemlocSeries
+    FROM itemsite, invcnt, item, uom
+    WHERE ( (invcnt_itemsite_id=itemsite_id)
+     AND (itemsite_item_id=item_id)
+     AND (item_inv_uom_id=uom_id)
+     AND (itemsite_controlmethod <> 'N')
+     AND (invcnt_id=pInvcntid) );
+
+    IF ( SELECT metric_value
+        FROM metric
+        WHERE ((metric_name = 'EnableAsOfQOH')
+        AND (metric_value = 't'))) THEN
+      IF (NOT postIntoInvBalance(_invhistid)) THEN
+        RAISE EXCEPTION 'Post into Inventory Balance for invhist_id=% was unsuccessful',_invhistid;
+      END IF;
+    END IF;
+
+--  Update the QOH
+--  Avoid negative value when average cost item
+    UPDATE itemsite
+    SET itemsite_qtyonhand=_p.invcnt_qoh_after,
+        itemsite_nnqoh = 0,
+        itemsite_value = CASE WHEN ((itemsite_costmethod='A') AND (_p.itemsite_value + (_p.cost * (_p.invcnt_qoh_after - itemsite_qtyonhand))) < 0.0) THEN 0.0
+                              ELSE (_p.itemsite_value + (_p.cost * (_p.invcnt_qoh_after - itemsite_qtyonhand)))
+                         END,
+        itemsite_datelastcount=_postDate
+    WHERE (itemsite_id=_p.itemsite_id);
+--  Post the detail, if any
+    IF (_hasDetail) THEN
+      PERFORM distributeItemlocSeries(_itemlocSeries);
+    END IF;
+
+--  Thaw the itemsite if it's frozen
+    IF (pThaw) THEN
+      PERFORM thawItemSite(invcnt_itemsite_id) 
+      FROM invcnt
+      WHERE (invcnt_id=pInvcntid);
+    END IF;
+
+--  Distribute to G/L
+    PERFORM insertGLTransaction( 'I/M', 'CT', _p.invcnt_tagnumber, ('Post Count Tag #' || _p.invcnt_tagnumber || ' for Item ' || _p.item_number),
+                                 costcat_adjustment_accnt_id, costcat_asset_accnt_id, _invhistid,
+                                 ( (_p.invcnt_qoh_after - _p.itemsite_qtyonhand) * _p.cost), _postDate::DATE )
+    FROM invcnt, itemsite, costcat
+    WHERE ( (invcnt_itemsite_id=itemsite_id)
+     AND (itemsite_costcat_id=costcat_id)
+     AND (invcnt_id=pInvcntid) );
+
+    RETURN 0;
+
+  ELSE
+    RETURN -9;
+  END IF;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/postcounttaglocation.sql b/foundation-database/public/functions/postcounttaglocation.sql
new file mode 100644 (file)
index 0000000..c6fac13
--- /dev/null
@@ -0,0 +1,275 @@
+CREATE OR REPLACE FUNCTION postCountTagLocation(INTEGER, BOOLEAN) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pInvcntid ALIAS FOR $1;
+  pThaw ALIAS FOR $2;
+  _avgCostingMethod TEXT;
+  _invhistid INTEGER;
+  _postDate TIMESTAMP;
+  _runningQty NUMERIC;
+  _errorCode INTEGER;
+  _itemlocSeries INTEGER := 0;
+  _hasDetail BOOLEAN;
+  _p RECORD;
+  _itemloc RECORD;
+  _cntslip RECORD;
+  _origLocQty NUMERIC;
+  _netable BOOLEAN;
+  _lsid INTEGER;
+BEGIN
+
+  SELECT COALESCE(fetchMetricText('CountAvgCostMethod'), 'STD') INTO _avgCostingMethod;
+
+  SELECT invcnt_id, invcnt_tagnumber, invcnt_qoh_after,
+         invcnt_location_id, invcnt_tagdate,
+         item_number,
+         itemsite_id, itemsite_freeze,
+         itemsite_qtyonhand,
+         itemsite_loccntrl, COALESCE(invcnt_location_id, -1) AS itemsite_location_id,
+         CASE WHEN (itemsite_costmethod = 'N') THEN 0
+              WHEN ( (itemsite_costmethod = 'A') AND
+                     (itemsite_qtyonhand = 0) AND
+                     (_avgCostingMethod = 'ACT') ) THEN actcost(itemsite_item_id)
+              WHEN ( (itemsite_costmethod = 'A') AND
+                     (_avgCostingMethod IN ('ACT', 'AVG')) ) THEN avgcost(itemsite_id)
+              ELSE stdcost(itemsite_item_id)
+         END AS cost, itemsite_costmethod,
+         itemsite_controlmethod, itemsite_value INTO _p
+  FROM invcnt, itemsite, item
+  WHERE ( (invcnt_itemsite_id=itemsite_id)
+   AND (itemsite_item_id=item_id)
+   AND (invcnt_qoh_after IS NOT NULL)
+   AND (NOT invcnt_posted)
+   AND (invcnt_id=pInvcntid) );
+  IF (NOT FOUND) THEN
+    RETURN -9;
+  END IF;
+
+  SELECT COALESCE(SUM(itemloc_qty),0.0), location_netable INTO _origLocQty,_netable
+    FROM itemloc,location
+   WHERE ((itemloc_itemsite_id=_p.itemsite_id)
+     AND  (location_id=itemloc_location_id)
+     AND  (itemloc_location_id=_p.invcnt_location_id))
+   GROUP BY location_netable;
+  IF (NOT FOUND) THEN
+    _origLocQty := 0.0;
+    _netable := TRUE;
+  END IF;
+
+  SELECT NEXTVAL('invhist_invhist_id_seq') INTO _invhistid;
+
+  IF (_p.itemsite_freeze) THEN
+    _postDate := _p.invcnt_tagdate;
+  ELSE
+    _postDate := CURRENT_TIMESTAMP;
+  END IF;
+
+  _hasDetail = FALSE;
+
+--  Post the detail indicated by cntslips
+  IF ( (_p.itemsite_loccntrl) OR
+       (_p.itemsite_controlmethod IN ('L', 'S')) ) THEN
+
+    SELECT NEXTVAL('itemloc_series_seq') INTO _itemlocSeries;
+
+--  Adjust any existing detail to 0
+    FOR _itemloc IN SELECT itemloc_id, itemloc_location_id,
+                           itemloc_ls_id, itemloc_qty
+                    FROM itemloc
+                    WHERE ((itemloc_itemsite_id=_p.itemsite_id)
+                      AND  (itemloc_location_id=_p.invcnt_location_id)) LOOP
+
+      _hasDetail = TRUE;
+
+--  Create the itemlocdist flushing records
+      INSERT INTO itemlocdist
+      ( itemlocdist_series, itemlocdist_source_type, itemlocdist_source_id,
+        itemlocdist_expiration,
+        itemlocdist_itemsite_id, itemlocdist_invhist_id, itemlocdist_flush )
+      VALUES
+      ( _itemlocSeries, 'I', _itemloc.itemloc_id,
+        endOfTime(),
+        _p.itemsite_id, _invhistid, TRUE );
+
+    END LOOP;
+
+--  Clear the running detail Qty
+    _runningQty := 0;
+
+--  Adjust the detail to the cntslip indicated value
+    FOR _cntslip IN SELECT cntslip_location_id, cntslip_lotserial,
+                           cntslip_lotserial_expiration,
+                           cntslip_lotserial_warrpurc,
+                           SUM(cntslip_qty) AS qty,
+                           itemsite_item_id
+                    FROM cntslip,invcnt,itemsite
+                    WHERE ((cntslip_cnttag_id=pInvcntid)
+                    AND (cntslip_cnttag_id=invcnt_id)
+                    AND (invcnt_itemsite_id=itemsite_id))
+                    GROUP BY cntslip_location_id, cntslip_lotserial, cntslip_lotserial_expiration,
+                    cntslip_lotserial_warrpurc, itemsite_item_id LOOP
+
+--  Handle the LotSerial
+      IF (LENGTH(_cntslip.cntslip_lotserial)>0) THEN
+        SELECT ls_id INTO _lsid
+        FROM ls
+        WHERE ((ls_item_id=_cntslip.itemsite_item_id)
+        AND (UPPER(ls_number)=UPPER(_cntslip.cntslip_lotserial)));
+
+        IF (NOT FOUND) THEN
+          _lsid := NEXTVAL('ls_ls_id_seq');
+          INSERT INTO ls
+          VALUES (_lsid,_cntslip.itemsite_item_id,UPPER(_cntslip.cntslip_lotserial));
+        END IF;
+      END IF;
+       
+--  Track the running Qty
+      _runningQty := (_runningQty + _cntslip.qty);
+      _hasDetail = TRUE;
+
+--  Create the itemlocdist populating record
+      INSERT INTO itemlocdist
+      ( itemlocdist_series, itemlocdist_source_type, itemlocdist_source_id,
+        itemlocdist_itemsite_id,
+        itemlocdist_ls_id, itemlocdist_expiration, itemlocdist_warranty,
+        itemlocdist_qty, itemlocdist_invhist_id )
+      VALUES
+      ( _itemlocSeries, 'L', _cntslip.cntslip_location_id,
+        _p.itemsite_id,
+        _lsid, COALESCE(_cntslip.cntslip_lotserial_expiration, endOfTime()),
+        _cntslip.cntslip_lotserial_warrpurc,
+        _cntslip.qty, _invhistid );
+
+    END LOOP;
+
+    IF (_runningQty > _p.invcnt_qoh_after) THEN
+--  The total Count Slip Qty is greater than the Count Tag Qty,
+--  Don't post the Count.
+      _errorCode = -1;
+
+    ELSIF ( (_runningQty < _p.invcnt_qoh_after) AND
+            (_p.itemsite_controlmethod IN ('L', 'S')) ) THEN
+--  The total Count Slip Qty is less than the Count Tag Qty,
+--  and the Item Site is Lot/Serial controlled.
+--  Don't post the Count.
+      _errorCode = -2;
+
+    ELSIF (_runningQty < _p.invcnt_qoh_after) THEN
+      IF ( (NOT _p.itemsite_loccntrl) OR
+           (_p.itemsite_location_id = -1) ) THEN
+--  The total Count Slip Qty is less than the Count Tag Qty,
+--  and there isn't a default location to post into.
+--  Don't post the Count.
+        _errorCode = -3;
+
+      ELSIF ( SELECT (metric_value='f')
+              FROM metric
+              WHERE (metric_name='PostCountTagToDefault') ) THEN
+--  The total Count Slip Qty is less than the Count Tag Qty,
+--  and we don't post Count Tags to default Locations
+--  Don't post the Count.
+        _errorCode = -4;
+
+      ELSE
+--  Distribute the remaining qty into the default location.
+        INSERT INTO itemlocdist
+        ( itemlocdist_series, itemlocdist_source_type, itemlocdist_source_id,
+          itemlocdist_itemsite_id,
+          itemlocdist_ls_id, itemlocdist_expiration,
+          itemlocdist_qty, itemlocdist_invhist_id )
+        SELECT _itemlocSeries, 'L', _p.itemsite_location_id,
+               _p.itemsite_id,
+               _lsid, endOfTime(),
+               (_p.invcnt_qoh_after - _runningQty), _invhistid;
+
+        _hasDetail = TRUE;
+        _errorCode = 0;
+      END IF;
+    ELSE
+--  The Count Slip Qty. must equal the Count Tag Qty.
+      _errorCode = 0;
+    END IF;
+
+--  If we shouldn't post the count then delete the itemlocdist records,
+--  and return with the error.
+    IF (_errorCode <> 0) THEN
+      DELETE FROM itemlocdist
+      WHERE (itemlocdist_series=_itemlocSeries);
+  
+      RETURN _errorCode;
+    END IF;
+
+  END IF;
+
+--  Mod. the Count Tag.
+  UPDATE invcnt
+  SET invcnt_qoh_before=_origLocQty,
+      invcnt_postdate=_postDate,
+      invcnt_posted=TRUE,
+      invcnt_invhist_id=_invhistid,
+      invcnt_post_username=getEffectiveXtUser()
+  WHERE (invcnt_id=pInvcntid);
+
+--  Create the CC transaction
+  INSERT INTO invhist
+   ( invhist_id, invhist_itemsite_id,
+     invhist_transdate, invhist_transtype, invhist_invqty,
+     invhist_qoh_before, invhist_qoh_after,
+     invhist_docnumber, invhist_comments,
+     invhist_invuom, invhist_unitcost, invhist_hasdetail,
+     invhist_costmethod, invhist_value_before, invhist_value_after,
+     invhist_series )
+  SELECT _invhistid, itemsite_id,
+         _postDate, 'CC', (invcnt_qoh_after - invcnt_qoh_before),
+         invcnt_qoh_before, invcnt_qoh_after,
+         invcnt_tagnumber, invcnt_comments,
+         uom_name, _p.cost, _hasDetail,
+         _p.itemsite_costmethod, _p.itemsite_value,
+         _p.itemsite_value + (_p.cost * (invcnt_qoh_after - invcnt_qoh_before)),
+         _itemlocSeries
+  FROM itemsite, invcnt, item, uom
+  WHERE ( (invcnt_itemsite_id=itemsite_id)
+   AND (itemsite_item_id=item_id)
+   AND (item_inv_uom_id=uom_id)
+   AND (itemsite_controlmethod <> 'N')
+   AND (invcnt_id=pInvcntid) );
+
+--  Update the QOH
+  IF (_netable) THEN
+    UPDATE itemsite
+    SET itemsite_qtyonhand= itemsite_qtyonhand + (_p.invcnt_qoh_after - _origLocQty),
+        itemsite_datelastcount=_postDate
+    WHERE (itemsite_id=_p.itemsite_id);
+  ELSE
+    UPDATE itemsite
+    SET itemsite_nnqoh =  itemsite_nnqoh - _origLocQty,
+       itemsite_qtyonhand = itemsite_qtyonhand + _p.invcnt_qoh_after,
+        itemsite_datelastcount=_postDate
+    WHERE (itemsite_id=_p.itemsite_id);
+  END IF;
+--  Post the detail, if any
+  IF (_hasDetail) THEN
+    PERFORM distributeItemlocSeries(_itemlocSeries);
+  END IF;
+
+--  Thaw the itemsite if it's frozen
+  IF (pThaw) THEN
+    PERFORM thawItemSite(invcnt_itemsite_id) 
+    FROM invcnt
+    WHERE (invcnt_id=pInvcntid);
+  END IF;
+
+--  Distribute to G/L
+  PERFORM insertGLTransaction( 'I/M', 'CT', _p.invcnt_tagnumber, ('Post Count Tag #' || _p.invcnt_tagnumber || ' for Item ' || _p.item_number),
+                               costcat_adjustment_accnt_id, costcat_asset_accnt_id, _invhistid,
+                               ( (_p.invcnt_qoh_after - _origLocQty) * _p.cost), CURRENT_DATE )
+  FROM invcnt, itemsite, costcat
+  WHERE ( (invcnt_itemsite_id=itemsite_id)
+   AND (itemsite_costcat_id=costcat_id)
+   AND (invcnt_id=pInvcntid) );
+
+  RETURN 0;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/postcounttags.sql b/foundation-database/public/functions/postcounttags.sql
new file mode 100644 (file)
index 0000000..3a7a22e
--- /dev/null
@@ -0,0 +1,28 @@
+CREATE OR REPLACE FUNCTION postCountTags(INTEGER, BOOLEAN) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWarehousid ALIAS FOR $1;
+  pThaw ALIAS FOR $2;
+  _invcnt RECORD;
+  _result INTEGER := 0;
+  _return INTEGER := 0;
+
+BEGIN
+
+  FOR _invcnt IN SELECT invcnt_id
+                 FROM invcnt, itemsite
+                 WHERE ( (invcnt_itemsite_id=itemsite_id)
+                  AND ( (pWarehousid=-1) OR (itemsite_warehous_id=pWarehousid) )
+                  AND (invcnt_qoh_after IS NOT NULL)
+                  AND (NOT invcnt_posted) ) LOOP
+    SELECT postCountTag(_invcnt.invcnt_id, pThaw) INTO _result;
+    IF (_result < _return) THEN
+      _return := _result;
+    END IF;
+  END LOOP;
+
+  RETURN _return;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/postcreditmemo.sql b/foundation-database/public/functions/postcreditmemo.sql
new file mode 100644 (file)
index 0000000..c38c314
--- /dev/null
@@ -0,0 +1,533 @@
+
+CREATE OR REPLACE FUNCTION postCreditMemo(INTEGER, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCmheadid ALIAS FOR $1;
+  pItemlocSeries ALIAS FOR $2;
+  _return INTEGER;
+
+BEGIN
+
+  SELECT postCreditMemo(pCmheadid, fetchJournalNumber('AR-CM'), pItemlocSeries) INTO _return;
+
+  RETURN _return;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION postCreditMemo(INTEGER, INTEGER, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCmheadid ALIAS FOR $1;
+  pJournalNumber ALIAS FOR $2;
+  pItemlocSeries ALIAS FOR $3;
+  _r RECORD;
+  _p RECORD;
+  _aropenid INTEGER;
+  _cohistid INTEGER;
+  _sequence INTEGER;
+  _itemlocSeries INTEGER;
+  _invhistid INTEGER;
+  _test INTEGER;
+  _totalAmount NUMERIC   := 0;
+  _commissionDue NUMERIC := 0;
+  _toApply NUMERIC;
+  _toClose BOOLEAN;
+  _glDate      DATE;
+  _taxBaseValue        NUMERIC := 0;
+
+BEGIN
+
+--  Cache some parameters
+  SELECT cmhead.*,
+         findARAccount(cmhead_cust_id) AS ar_accnt_id,
+         ( SELECT COALESCE(SUM(taxhist_tax), 0)
+           FROM cmheadtax
+           WHERE ( (taxhist_parent_id = cmhead_id)
+             AND   (taxhist_taxtype_id = getAdjustmentTaxtypeId()) ) ) AS adjtax
+         INTO _p
+  FROM cmhead
+  WHERE (cmhead_id=pCmheadid);
+
+  IF (_p.cmhead_posted) THEN
+    RETURN -10;
+  END IF;
+
+  IF (_p.cmhead_hold) THEN
+    RETURN -11;
+  END IF;
+
+  _glDate := COALESCE(_p.cmhead_gldistdate, _p.cmhead_docdate);
+
+  _itemlocSeries = pItemlocSeries;
+
+  SELECT fetchGLSequence() INTO _sequence;
+
+--  Start by handling taxes
+  FOR _r IN SELECT tax_sales_accnt_id, 
+              round(sum(taxdetail_tax),2) AS tax,
+              currToBase(_p.cmhead_curr_id, round(sum(taxdetail_tax),2), _p.cmhead_docdate) AS taxbasevalue
+            FROM tax 
+             JOIN calculateTaxDetailSummary('CM', pCmheadid, 'T') ON (taxdetail_tax_id=tax_id)
+           GROUP BY tax_id, tax_sales_accnt_id LOOP
+
+    PERFORM insertIntoGLSeries( _sequence, 'A/R', 'CM', _p.cmhead_number,
+                                _r.tax_sales_accnt_id, 
+                                _r.taxbasevalue,
+                                _glDate, _p.cmhead_billtoname );
+
+    _totalAmount := _totalAmount + _r.tax * -1;
+  END LOOP;
+
+-- Update item tax records with posting data
+  UPDATE cmitemtax SET 
+    taxhist_docdate=_p.cmhead_docdate,
+    taxhist_distdate=_glDate,
+    taxhist_curr_id=_p.cmhead_curr_id,
+    taxhist_curr_rate=curr_rate,
+    taxhist_journalnumber=pJournalNumber
+  FROM cmhead
+   JOIN cmitem ON (cmhead_id=cmitem_cmhead_id),
+   curr_rate
+  WHERE ((cmhead_id=pCmheadId)
+    AND (taxhist_parent_id=cmitem_id)
+    AND (_p.cmhead_curr_id=curr_id)
+    AND (_p.cmhead_docdate BETWEEN curr_effective 
+                           AND curr_expires) );
+
+-- Update Header taxes (Freight and Adjustments) with posting data
+  UPDATE cmheadtax SET 
+    taxhist_docdate=_p.cmhead_docdate,
+    taxhist_distdate=_glDate,
+    taxhist_curr_id=_p.cmhead_curr_id,
+    taxhist_curr_rate=curr_rate,
+    taxhist_journalnumber=pJournalNumber
+  FROM curr_rate
+  WHERE ((taxhist_parent_id=pCmheadId)
+    AND (_p.cmhead_curr_id=curr_id)
+    AND (_p.cmhead_docdate BETWEEN curr_effective 
+                           AND curr_expires) );
+
+-- Process line items
+-- Always use std cost
+  FOR _r IN SELECT *, stdCost(item_id) AS std_cost
+            FROM creditmemoitem
+            WHERE ( (cmitem_cmhead_id=pCmheadid)
+              AND   (cmitem_qtycredit <> 0 ) ) LOOP
+
+--  Calcuate the Commission to be debited
+    _commissionDue := (_commissionDue + (_r.extprice * _p.cmhead_commission));
+
+    IF (_r.extprice <> 0) THEN
+--  Debit the Sales Account for the current cmitem
+      SELECT insertIntoGLSeries( _sequence, 'A/R', 'CM', _p.cmhead_number,
+                                 CASE WHEN _p.cmhead_rahead_id IS NULL THEN
+                                   getPrjAccntId(_p.cmhead_prj_id, salesaccnt_credit_accnt_id)
+                                 ELSE
+                                   getPrjAccntId(_p.cmhead_prj_id, salesaccnt_returns_accnt_id)
+                                 END,
+                               round(currToBase(_p.cmhead_curr_id,
+                                                _r.extprice * -1,
+                                                _p.cmhead_docdate), 2),
+                                 _glDate, _p.cmhead_billtoname) INTO _test
+      FROM salesaccnt
+      WHERE (salesaccnt_id=findSalesAccnt(_r.cmitem_itemsite_id, 'IS', _p.cmhead_cust_id,
+                                          _p.cmhead_saletype_id, _p.cmhead_shipzone_id));
+      IF (NOT FOUND) THEN
+        PERFORM deleteGLSeries(_sequence);
+        RETURN -12;
+      END IF;
+    END IF;
+
+--  Record Sales History for this C/M Item
+    SELECT nextval('cohist_cohist_id_seq') INTO _cohistid;
+    INSERT INTO cohist
+    ( cohist_id, cohist_cust_id, cohist_itemsite_id, cohist_shipto_id,
+      cohist_shipdate, cohist_shipvia,
+      cohist_ordernumber, cohist_ponumber, cohist_orderdate,
+      cohist_doctype, cohist_invcnumber, cohist_invcdate,
+      cohist_qtyshipped, cohist_unitprice, cohist_unitcost,
+      cohist_salesrep_id, cohist_commission, cohist_commissionpaid,
+      cohist_billtoname, cohist_billtoaddress1,
+      cohist_billtoaddress2, cohist_billtoaddress3,
+      cohist_billtocity, cohist_billtostate, cohist_billtozip,
+      cohist_shiptoname, cohist_shiptoaddress1,
+      cohist_shiptoaddress2, cohist_shiptoaddress3,
+      cohist_shiptocity, cohist_shiptostate, cohist_shiptozip,
+      cohist_curr_id, cohist_taxtype_id, cohist_taxzone_id,
+      cohist_shipzone_id, cohist_saletype_id )
+    VALUES
+    ( _cohistid, _p.cmhead_cust_id, _r.cmitem_itemsite_id, _p.cmhead_shipto_id,
+      _p.cmhead_docdate, '',
+      _p.cmhead_number, _p.cmhead_custponumber, _p.cmhead_docdate,
+      'C', _p.cmhead_invcnumber, _p.cmhead_docdate,
+      (_r.qty * -1), _r.unitprice, _r.std_cost,
+      _p.cmhead_salesrep_id, (_p.cmhead_commission * _r.extprice * -1), FALSE,
+      _p.cmhead_billtoname, _p.cmhead_billtoaddress1,
+      _p.cmhead_billtoaddress2, _p.cmhead_billtoaddress3,
+      _p.cmhead_billtocity, _p.cmhead_billtostate, _p.cmhead_billtozip,
+      _p.cmhead_shipto_name, _p.cmhead_shipto_address1,
+      _p.cmhead_shipto_address2, _p.cmhead_shipto_address3,
+      _p.cmhead_shipto_city, _p.cmhead_shipto_state, _p.cmhead_shipto_zipcode,
+      _p.cmhead_curr_id, _r.cmitem_taxtype_id, _p.cmhead_taxzone_id,
+      _p.cmhead_shipzone_id, _p.cmhead_saletype_id );
+    INSERT INTO cohisttax
+    ( taxhist_parent_id, taxhist_taxtype_id, taxhist_tax_id,
+      taxhist_basis, taxhist_basis_tax_id, taxhist_sequence,
+      taxhist_percent, taxhist_amount, taxhist_tax,
+      taxhist_docdate, taxhist_distdate, taxhist_curr_id, taxhist_curr_rate,
+      taxhist_journalnumber )
+    SELECT _cohistid, taxhist_taxtype_id, taxhist_tax_id,
+           taxhist_basis, taxhist_basis_tax_id, taxhist_sequence,
+           taxhist_percent, taxhist_amount, taxhist_tax,
+           taxhist_docdate, taxhist_distdate, taxhist_curr_id, taxhist_curr_rate,
+           taxhist_journalnumber 
+    FROM cmitemtax
+    WHERE (taxhist_parent_id=_r.cmitem_id);
+
+    _totalAmount := _totalAmount + round(_r.extprice, 2);
+
+  END LOOP;
+
+--  Credit the Misc. Account for Miscellaneous Charges
+  IF (_p.cmhead_misc <> 0) THEN
+    SELECT insertIntoGLSeries( _sequence, 'A/R', 'CM', _p.cmhead_number,
+                               getPrjAccntId(_p.cmhead_prj_id, accnt_id), round(currToBase(_p.cmhead_curr_id,
+                                                          _p.cmhead_misc * -1,
+                                                          _p.cmhead_docdate), 2),
+                               _glDate, _p.cmhead_billtoname) INTO _test
+    FROM accnt
+    WHERE (accnt_id=_p.cmhead_misc_accnt_id);
+
+--  If the Misc. Charges Account was not found then punt
+    IF (NOT FOUND) THEN
+      PERFORM deleteGLSeries(_sequence);
+      RETURN -14;
+    END IF;
+
+--  Record the Sales History for any Misc. Charge
+    INSERT INTO cohist
+    ( cohist_cust_id, cohist_itemsite_id, cohist_shipto_id,
+      cohist_misc_type, cohist_misc_descrip, cohist_misc_id,
+      cohist_shipdate, cohist_shipvia,
+      cohist_ordernumber, cohist_ponumber, cohist_orderdate,
+      cohist_doctype, cohist_invcnumber, cohist_invcdate,
+      cohist_qtyshipped, cohist_unitprice, cohist_unitcost,
+      cohist_salesrep_id, cohist_commission, cohist_commissionpaid,
+      cohist_billtoname, cohist_billtoaddress1,
+      cohist_billtoaddress2, cohist_billtoaddress3,
+      cohist_billtocity, cohist_billtostate, cohist_billtozip,
+      cohist_shiptoname, cohist_shiptoaddress1,
+      cohist_shiptoaddress2, cohist_shiptoaddress3,
+      cohist_shiptocity, cohist_shiptostate, cohist_shiptozip,
+      cohist_curr_id,
+      cohist_shipzone_id, cohist_saletype_id )
+    VALUES
+    ( _p.cmhead_cust_id, -1, _p.cmhead_shipto_id,
+      'M', _p.cmhead_misc_descrip, _p.cmhead_misc_accnt_id,
+      _p.cmhead_docdate, '',
+      _p.cmhead_number, _p.cmhead_custponumber, _p.cmhead_docdate,
+      'C', _p.cmhead_invcnumber, _p.cmhead_docdate,
+      1, (_p.cmhead_misc * -1), (_p.cmhead_misc * -1),
+      _p.cmhead_salesrep_id, 0, FALSE,
+      _p.cmhead_billtoname, _p.cmhead_billtoaddress1,
+      _p.cmhead_billtoaddress2, _p.cmhead_billtoaddress3,
+      _p.cmhead_billtocity, _p.cmhead_billtostate, _p.cmhead_billtozip,
+      _p.cmhead_shipto_name, _p.cmhead_shipto_address1,
+      _p.cmhead_shipto_address2, _p.cmhead_shipto_address3,
+      _p.cmhead_shipto_city, _p.cmhead_shipto_state, _p.cmhead_shipto_zipcode,
+      _p.cmhead_curr_id,
+      _p.cmhead_shipzone_id, _p.cmhead_saletype_id );
+
+--  Cache the Misc. Amount distributed
+    _totalAmount := _totalAmount + _p.cmhead_misc;
+  END IF;
+
+  -- Credit Tax Adjustments
+  IF (_p.adjtax <> 0) THEN
+  --  Record the Sales History for Tax Adjustment
+    SELECT nextval('cohist_cohist_id_seq') INTO _cohistid;
+    INSERT INTO cohist
+    ( cohist_id, cohist_cust_id, cohist_itemsite_id, cohist_shipto_id,
+      cohist_misc_type, cohist_misc_descrip,
+      cohist_shipdate, cohist_shipvia,
+      cohist_ordernumber, cohist_ponumber, cohist_orderdate,
+      cohist_doctype, cohist_invcnumber, cohist_invcdate,
+      cohist_qtyshipped, cohist_unitprice, cohist_unitcost,
+      cohist_salesrep_id, cohist_commission, cohist_commissionpaid,
+      cohist_billtoname, cohist_billtoaddress1,
+      cohist_billtoaddress2, cohist_billtoaddress3,
+      cohist_billtocity, cohist_billtostate, cohist_billtozip,
+      cohist_shiptoname, cohist_shiptoaddress1,
+      cohist_shiptoaddress2, cohist_shiptoaddress3,
+      cohist_shiptocity, cohist_shiptostate, cohist_shiptozip,
+      cohist_curr_id, cohist_taxtype_id, cohist_taxzone_id,
+      cohist_shipzone_id, cohist_saletype_id )
+    VALUES
+    ( _cohistid, _p.cmhead_cust_id, -1, _p.cmhead_shipto_id,
+      'T', 'Misc Tax Adjustment',
+      _p.cmhead_docdate, '',
+      _p.cmhead_number, _p.cmhead_custponumber, _p.cmhead_docdate,
+      'C', _p.cmhead_invcnumber, _p.cmhead_docdate,
+      0, 0, 0,
+      _p.cmhead_salesrep_id, 0, FALSE,
+      _p.cmhead_billtoname, _p.cmhead_billtoaddress1,
+      _p.cmhead_billtoaddress2, _p.cmhead_billtoaddress3,
+      _p.cmhead_billtocity, _p.cmhead_billtostate, _p.cmhead_billtozip,
+      _p.cmhead_shipto_name, _p.cmhead_shipto_address1,
+      _p.cmhead_shipto_address2, _p.cmhead_shipto_address3,
+      _p.cmhead_shipto_city, _p.cmhead_shipto_state, _p.cmhead_shipto_zipcode,
+      _p.cmhead_curr_id, getAdjustmentTaxtypeId(), _p.cmhead_taxzone_id,
+      _p.cmhead_shipzone_id, _p.cmhead_saletype_id );
+    INSERT INTO cohisttax
+    ( taxhist_parent_id, taxhist_taxtype_id, taxhist_tax_id,
+      taxhist_basis, taxhist_basis_tax_id, taxhist_sequence,
+      taxhist_percent, taxhist_amount, taxhist_tax,
+      taxhist_docdate, taxhist_distdate, taxhist_curr_id, taxhist_curr_rate,
+      taxhist_journalnumber  )
+    SELECT _cohistid, taxhist_taxtype_id, taxhist_tax_id,
+           (taxhist_basis * -1), taxhist_basis_tax_id, taxhist_sequence,
+           taxhist_percent, taxhist_amount, taxhist_tax,
+           taxhist_docdate, taxhist_distdate, taxhist_curr_id, taxhist_curr_rate,
+           taxhist_journalnumber 
+    FROM cmheadtax
+    WHERE ( (taxhist_parent_id=_p.cmhead_id)
+      AND   (taxhist_taxtype_id=getAdjustmentTaxtypeId()) );
+
+  END IF;
+
+--  Debit the Freight Account
+  IF (_p.cmhead_freight <> 0) THEN
+    SELECT insertIntoGLSeries( _sequence, 'A/R', 'CM', _p.cmhead_number,
+                               getPrjAccntId(_p.cmhead_prj_id, accnt_id),
+                               round(currToBase(_p.cmhead_curr_id,
+                                                _p.cmhead_freight * -1,
+                                                _p.cmhead_docdate), 2),
+                               _glDate, _p.cmhead_billtoname) INTO _test
+    FROM accnt
+    WHERE (accnt_id=findFreightAccount(_p.cmhead_cust_id));
+
+--  If the Freight Charges Account was not found then punt
+    IF (NOT FOUND) THEN
+      PERFORM deleteGLSeries(_sequence);
+      RETURN -16;
+    END IF;
+
+--  Cache the Amount Distributed to Freight
+    _totalAmount := _totalAmount + _p.cmhead_freight;
+
+--  Record the Sales History for any Freight
+    SELECT nextval('cohist_cohist_id_seq') INTO _cohistid;
+    INSERT INTO cohist
+    ( cohist_id, cohist_cust_id, cohist_itemsite_id, cohist_shipto_id,
+      cohist_misc_type, cohist_misc_descrip,
+      cohist_shipdate, cohist_shipvia,
+      cohist_ordernumber, cohist_ponumber, cohist_orderdate,
+      cohist_doctype, cohist_invcnumber, cohist_invcdate,
+      cohist_qtyshipped, cohist_unitprice, cohist_unitcost,
+      cohist_salesrep_id, cohist_commission, cohist_commissionpaid,
+      cohist_billtoname, cohist_billtoaddress1,
+      cohist_billtoaddress2, cohist_billtoaddress3,
+      cohist_billtocity, cohist_billtostate, cohist_billtozip,
+      cohist_shiptoname, cohist_shiptoaddress1,
+      cohist_shiptoaddress2, cohist_shiptoaddress3,
+      cohist_shiptocity, cohist_shiptostate, cohist_shiptozip,
+      cohist_curr_id, cohist_taxtype_id, cohist_taxzone_id,
+      cohist_shipzone_id, cohist_saletype_id )
+    VALUES
+    ( _cohistid, _p.cmhead_cust_id, -1, _p.cmhead_shipto_id,
+      'F', 'Freight Charge',
+      _p.cmhead_docdate, '',
+      _p.cmhead_number, _p.cmhead_custponumber, _p.cmhead_docdate,
+      'C', _p.cmhead_invcnumber, _p.cmhead_docdate,
+      1, (_p.cmhead_freight * -1), (_p.cmhead_freight * -1),
+      _p.cmhead_salesrep_id, 0, FALSE,
+      _p.cmhead_billtoname, _p.cmhead_billtoaddress1,
+      _p.cmhead_billtoaddress2, _p.cmhead_billtoaddress3,
+      _p.cmhead_billtocity, _p.cmhead_billtostate, _p.cmhead_billtozip,
+      _p.cmhead_shipto_name, _p.cmhead_shipto_address1,
+      _p.cmhead_shipto_address2, _p.cmhead_shipto_address3,
+      _p.cmhead_shipto_city, _p.cmhead_shipto_state, _p.cmhead_shipto_zipcode,
+      _p.cmhead_curr_id, getFreightTaxtypeId(), _p.cmhead_taxzone_id,
+      _p.cmhead_shipzone_id, _p.cmhead_saletype_id );
+    INSERT INTO cohisttax
+    ( taxhist_parent_id, taxhist_taxtype_id, taxhist_tax_id,
+      taxhist_basis, taxhist_basis_tax_id, taxhist_sequence,
+      taxhist_percent, taxhist_amount, taxhist_tax,
+      taxhist_docdate, taxhist_distdate, taxhist_curr_id, taxhist_curr_rate,
+      taxhist_journalnumber  )
+    SELECT _cohistid, taxhist_taxtype_id, taxhist_tax_id,
+           (taxhist_basis * -1), taxhist_basis_tax_id, taxhist_sequence,
+           taxhist_percent, taxhist_amount, taxhist_tax,
+           taxhist_docdate, taxhist_distdate, taxhist_curr_id, taxhist_curr_rate,
+           taxhist_journalnumber 
+    FROM cmheadtax
+    WHERE ( (taxhist_parent_id=_p.cmhead_id)
+      AND   (taxhist_taxtype_id=getFreightTaxtypeId()) );
+
+  END IF;
+
+  _totalAmount := _totalAmount;
+
+--  Credit the A/R for the total Amount
+  IF (_totalAmount <> 0) THEN
+    IF (_p.ar_accnt_id != -1) THEN
+      PERFORM insertIntoGLSeries( _sequence, 'A/R', 'CM', _p.cmhead_number,
+                                  _p.ar_accnt_id,
+                                  round(currToBase(_p.cmhead_curr_id,
+                                                   _totalAmount,
+                                                   _p.cmhead_docdate), 2),
+                                  _glDate, _p.cmhead_billtoname);
+    ELSE
+      PERFORM deleteGLSeries(_sequence);
+      RETURN -18;
+    END IF;
+  END IF;
+
+--  Commit the GLSeries;
+  PERFORM postGLSeries(_sequence, pJournalNumber);
+
+--  Create the Invoice aropen item
+  SELECT NEXTVAL('aropen_aropen_id_seq') INTO _aropenid;
+  INSERT INTO aropen
+  ( aropen_id, aropen_username, aropen_journalnumber,
+    aropen_open, aropen_posted,
+    aropen_cust_id, aropen_ponumber,
+    aropen_docnumber,
+    aropen_applyto, aropen_doctype,
+    aropen_docdate, aropen_duedate, aropen_distdate, aropen_terms_id,
+    aropen_amount, aropen_paid,
+    aropen_salesrep_id, aropen_commission_due, aropen_commission_paid,
+    aropen_ordernumber, aropen_notes,
+    aropen_rsncode_id, aropen_curr_id )
+  SELECT _aropenid, getEffectiveXtUser(), pJournalNumber,
+         TRUE, FALSE,
+         cmhead_cust_id, cmhead_custponumber,
+         cmhead_number,
+         CASE WHEN (cmhead_invcnumber='-1') THEN 'OPEN'
+              ELSE (cmhead_invcnumber::TEXT)
+         END,
+         'C',
+         cmhead_docdate, cmhead_docdate, _glDate, -1,
+         _totalAmount, 0,
+         cmhead_salesrep_id, (_commissionDue * -1), FALSE,
+         cmhead_number::TEXT, cmhead_comments,
+         cmhead_rsncode_id, cmhead_curr_id
+  FROM cmhead
+  WHERE (cmhead_id=pCmheadid);
+
+-- Handle the Inventory and G/L Transactions for any returned Inventory where cmitem_updateinv is true
+  FOR _r IN SELECT cmitem_itemsite_id AS itemsite_id, cmitem_id,
+                   (cmitem_qtyreturned * cmitem_qty_invuomratio) AS qty,
+                   cmhead_number, cmhead_cust_id AS cust_id, item_number,
+                   cmhead_saletype_id AS saletype_id, cmhead_shipzone_id AS shipzone_id,
+                   stdCost(item_id) AS std_cost, cmhead_prj_id,
+                   itemsite_costmethod
+            FROM cmhead, cmitem, itemsite, item
+            WHERE ( (cmitem_cmhead_id=cmhead_id)
+             AND (cmitem_itemsite_id=itemsite_id)
+             AND (itemsite_item_id=item_id)
+             AND (cmitem_qtyreturned <> 0)
+             AND (cmitem_updateinv)
+             AND (cmhead_id=pCmheadid) ) LOOP
+
+--  Return credited stock to inventory
+    IF (_itemlocSeries = 0) THEN
+      _itemlocSeries := NEXTVAL('itemloc_series_seq');
+    END IF;
+    IF (_r.itemsite_costmethod != 'J') THEN
+      SELECT postInvTrans(itemsite_id, 'RS', _r.qty,
+                         'S/O', 'CM', _r.cmhead_number, '',
+                         ('Credit Return ' || _r.item_number),
+                         costcat_asset_accnt_id,
+                         getPrjAccntId(_r.cmhead_prj_id, resolveCOSAccount(itemsite_id, _r.cust_id, _r.saletype_id, _r.shipzone_id)), 
+                         _itemlocSeries, _glDate, _r.std_cost) INTO _invhistid
+        FROM itemsite, costcat
+       WHERE ((itemsite_costcat_id=costcat_id)
+          AND (itemsite_id=_r.itemsite_id));
+    ELSE
+      RAISE DEBUG 'postCreditMemo(%, %, %) tried to postInvTrans a %-costed item',
+                  pCmheadid, pJournalNumber, pItemlocSeries,
+                  _r.itemsite_costmethod;
+    END IF;
+
+  END LOOP;
+
+--  Update coitem to reflect the returned qty where cmitem_updateinv is true
+  FOR _r IN SELECT cmitem_qtyreturned, cmitem_itemsite_id, cohead_id
+            FROM cmitem, cmhead, invchead, cohead
+            WHERE ( (cmitem_cmhead_id=cmhead_id)
+             AND (cmhead_invcnumber=invchead_invcnumber)
+             AND (invchead_ordernumber=cohead_number)
+             AND (cmitem_qtyreturned <> 0)
+             AND (cmitem_updateinv)
+             AND (cmhead_id=pCmheadid) ) LOOP
+    UPDATE coitem
+    SET coitem_qtyreturned = (coitem_qtyreturned + _r.cmitem_qtyreturned)
+    WHERE coitem_id IN ( SELECT coitem_id
+                         FROM coitem
+                         WHERE ( (coitem_cohead_id=_r.cohead_id)
+                          AND (coitem_itemsite_id = _r.cmitem_itemsite_id) )
+                         LIMIT 1 );
+  END LOOP;
+
+--  Mark the cmhead as posted
+  UPDATE cmhead
+  SET cmhead_posted=TRUE, cmhead_gldistdate=_glDate
+  WHERE (cmhead_id=pCmheadid);
+
+--  Find the apply-to document and make the application
+  SELECT cmhead_number, cmhead_curr_id, cmhead_docdate,
+         aropen_id, aropen_cust_id, aropen_docnumber,
+         currToCurr(aropen_curr_id, cmhead_curr_id, aropen_amount - aropen_paid,
+                    cmhead_docdate) AS balance INTO _p
+  FROM aropen, cmhead
+  WHERE ( (aropen_doctype='I')
+   AND (aropen_docnumber=cmhead_invcnumber)
+   AND (cmhead_id=pCmheadid) );
+  IF (FOUND) THEN
+
+    IF round(_totalAmount, 2) <= round(_p.balance, 2) THEN
+      _toApply = _totalAmount;
+    ELSE
+      _toApply = _p.balance;
+    END IF;
+
+    UPDATE aropen
+    SET aropen_paid = round(aropen_paid + currToCurr(_p.cmhead_curr_id,
+                                                     aropen_curr_id, _toApply,
+                                                     _p.cmhead_docdate), 2)
+    WHERE (aropen_id=_p.aropen_id);
+
+--  Alter the new A/R Open Item to reflect the application
+    UPDATE aropen
+    SET aropen_paid = round(currToCurr(_p.cmhead_curr_id, aropen_curr_id,
+                                       _toApply, _p.cmhead_docdate), 2)
+    WHERE (aropen_id=_aropenid);
+
+--  Record the application
+    INSERT INTO arapply
+    ( arapply_cust_id,
+      arapply_source_aropen_id, arapply_source_doctype, arapply_source_docnumber,
+      arapply_target_aropen_id, arapply_target_doctype, arapply_target_docnumber,
+      arapply_fundstype, arapply_refnumber,
+      arapply_applied, arapply_closed,
+      arapply_postdate, arapply_distdate, arapply_journalnumber, arapply_curr_id )
+    VALUES
+    ( _p.aropen_cust_id,
+      _aropenid, 'C', _p.cmhead_number,
+      _p.aropen_id, 'I', _p.aropen_docnumber,
+      '', '',
+      round(_toApply, 2), _toClose,
+      CURRENT_DATE, _p.cmhead_docdate, 0, _p.cmhead_curr_id );
+
+  END IF;
+    
+  RETURN _itemlocSeries;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/postcreditmemos.sql b/foundation-database/public/functions/postcreditmemos.sql
new file mode 100644 (file)
index 0000000..44bbb7f
--- /dev/null
@@ -0,0 +1,62 @@
+
+CREATE OR REPLACE FUNCTION postCreditMemos(BOOLEAN) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pPostUnprinted ALIAS FOR $1;
+  _cmhead RECORD;
+  _result INTEGER;
+  _return INTEGER        := 0;
+  _itemlocSeries INTEGER := 0;
+
+BEGIN
+
+  _itemlocSeries := 0;
+
+  FOR _cmhead IN SELECT cmhead_id
+                 FROM cmhead
+                 WHERE ( (NOT cmhead_posted)
+                   AND   (NOT cmhead_hold)
+                   AND   (checkCreditMemoSitePrivs(cmhead_id))
+                   AND   ((pPostUnprinted) OR (cmhead_printed)) ) LOOP
+
+    SELECT postCreditMemo(_cmhead.cmhead_id, _itemlocSeries) INTO _result;
+    IF (_result < _return) THEN
+      _return := _result;
+    END IF;
+
+  END LOOP;
+
+  RETURN _return;
+
+END;
+' LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION postCreditMemos(BOOLEAN, INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pPostUnprinted ALIAS FOR $1;
+  pJournalNumber ALIAS FOR $2;
+  _r RECORD;
+  _itemlocSeries INTEGER := 0;
+
+BEGIN
+
+  _itemlocSeries := 0;
+
+  FOR _r IN SELECT cmhead_id
+            FROM cmhead
+            WHERE ( (NOT cmhead_posted)
+              AND   (NOT cmhead_hold)
+              AND   (checkCreditMemoSitePrivs(cmhead_id))
+              AND   ((pPostUnprinted) OR (cmhead_printed)) ) LOOP
+
+    SELECT postCreditMemo(_r.cmhead_id, pJournalNumber, _itemlocSeries) INTO _itemlocSeries;
+
+  END LOOP;
+
+  RETURN _itemlocSeries;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/postglseries.sql b/foundation-database/public/functions/postglseries.sql
new file mode 100644 (file)
index 0000000..dfe9144
--- /dev/null
@@ -0,0 +1,178 @@
+
+CREATE OR REPLACE FUNCTION postGLSeries(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSequence ALIAS FOR $1;
+  _journalNumber INTEGER;
+  _returnValue INTEGER;
+
+BEGIN
+
+  SELECT postGLSeries(pSequence, fetchJournalNumber('G/L')) INTO _returnValue;
+  RETURN _returnValue;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION postGLSeries(INTEGER, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSequence ALIAS FOR $1;
+  pJournalNumber ALIAS FOR $2;
+  _returnValue INTEGER;
+
+BEGIN
+
+  SELECT postGLSeries(pSequence, pJournalNumber, true) INTO _returnValue;
+  RETURN _returnValue;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION postGLSeries(INTEGER, INTEGER, BOOLEAN) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSequence            ALIAS FOR $1;
+  pJournalNumber       ALIAS FOR $2;
+  pPostZero            ALIAS FOR $3;
+  _glseries RECORD;
+  _transCount INTEGER := 0;
+  _delta NUMERIC;
+  _discrepDate DATE;
+  _discrepAccntid INTEGER;
+  _rows INTEGER;
+BEGIN
+
+/*  Make sure we don't create an imbalance across companies.
+    The 'IgnoreCompanyBalance' metric is a back door mechanism to
+    allow legacy users to create transactions accross companies if
+    they have been using the company segment for something else
+    and they MUST continue to be able to do so.  It can only be 
+    implemented by direct sql update to the metric table and should 
+    otherwise be discouraged.
+*/ 
+  IF (COALESCE(fetchMetricValue('GLCompanySize'),0) > 0 
+    AND fetchMetricBool('IgnoreCompany') = false)  THEN
+
+    SELECT count(accnt_company) INTO _rows
+    FROM (
+      SELECT DISTINCT accnt_company
+      FROM accnt 
+        JOIN glseries ON (glseries_accnt_id=accnt_id)
+      WHERE (glseries_sequence=pSequence)) _data;
+    
+    IF (_rows > 1) THEN
+      RAISE EXCEPTION 'G/L Series can not be posted because multiple companies are referenced in the same series.';
+    END IF;
+  END IF;
+  
+--  Make sure that we balance
+  SELECT SUM(glseries_amount), MAX(glseries_distdate) INTO _delta, _discrepDate
+    FROM glseries
+   WHERE (glseries_sequence=pSequence);
+  IF ( _delta <> 0 ) THEN
+    IF (COALESCE(fetchMetricValue('GLCompanySize'),0) = 0) THEN
+      SELECT accnt_id INTO _discrepAccntid
+        FROM accnt, metric
+       WHERE ((metric_name='GLSeriesDiscrepancyAccount')
+         AND  (accnt_id=CAST(metric_value AS INTEGER)));
+    ELSE
+       SELECT company_dscrp_accnt_id INTO _discrepAccntid
+        FROM company
+          JOIN accnt ON (accnt_company=company_number) 
+          JOIN glseries ON (glseries_accnt_id=accnt_id)
+       WHERE (glseries_sequence=pSequence)
+       LIMIT 1;
+    END IF;
+
+    IF (NOT FOUND) THEN
+      RETURN -5;
+    END IF;
+    
+    INSERT INTO glseries
+           ( glseries_sequence, glseries_source, glseries_doctype, glseries_docnumber,
+             glseries_accnt_id, glseries_amount, glseries_distdate, glseries_notes )
+    SELECT glseries_sequence, glseries_source, glseries_doctype, glseries_docnumber,
+             _discrepAccntid, (_delta * -1), _discrepDate, 'G/L Series Discrepancy'
+      FROM glseries
+     WHERE (glseries_sequence=pSequence)
+     LIMIT 1;
+  END IF;
+
+--  March through the glseries members, posting them one at a time
+  FOR _glseries IN SELECT glseries_source, glseries_doctype, glseries_docnumber,
+                          glseries_accnt_id, glseries_distdate, glseries_notes,
+                          glseries_misc_id,
+                          SUM(glseries_amount) as amount
+                     FROM glseries
+                    WHERE ((glseries_amount<>0.0)
+                      AND  (glseries_sequence=pSequence))
+                    GROUP BY glseries_source, glseries_doctype, glseries_docnumber,
+                             glseries_accnt_id, glseries_distdate, glseries_notes,
+                             glseries_misc_id LOOP
+
+-- refuse to accept postings into closed periods
+    IF (SELECT BOOL_AND(COALESCE(period_closed, FALSE))
+        FROM accnt LEFT OUTER JOIN
+             period ON (_glseries.glseries_distdate BETWEEN period_start AND period_end)
+        WHERE (accnt_id = _glseries.glseries_accnt_id)) THEN
+      RAISE EXCEPTION 'Cannot post to closed period (%).', _glseries.glseries_distdate;
+      RETURN -4;        -- remove raise exception when all callers check return code
+    END IF;
+
+-- refuse to accept postings into frozen periods without proper priv
+    IF (SELECT NOT BOOL_AND(checkPrivilege('PostFrozenPeriod')) AND
+               BOOL_AND(COALESCE(period_freeze, FALSE))
+        FROM accnt LEFT OUTER JOIN
+             period ON (_glseries.glseries_distdate BETWEEN period_start AND period_end)
+        WHERE (accnt_id = _glseries.glseries_accnt_id)) THEN
+      RAISE EXCEPTION 'Cannot post to frozen period (%).', _glseries.glseries_distdate;
+      RETURN -4;        -- remove raise exception when all callers check return code
+    END IF;
+
+-- refuse to accept postings into nonexistent periods
+    IF NOT EXISTS(SELECT period_id
+                  FROM period
+                  WHERE (_glseries.glseries_distdate BETWEEN period_start AND period_end)) THEN
+      RAISE EXCEPTION 'Cannot post to nonexistent period (%).', pDistDate;
+    END IF;
+
+    IF (_glseries.amount != 0 OR pPostZero) THEN
+      IF (fetchMetricBool('UseJournals')) THEN
+       INSERT INTO sltrans
+        ( sltrans_posted, sltrans_created, sltrans_date, sltrans_misc_id,
+          sltrans_sequence, sltrans_accnt_id, sltrans_source, sltrans_notes,
+          sltrans_doctype, sltrans_docnumber, sltrans_amount, sltrans_journalnumber )
+        VALUES
+        ( FALSE, CURRENT_TIMESTAMP, _glseries.glseries_distdate, _glseries.glseries_misc_id,
+          pSequence, _glseries.glseries_accnt_id, _glseries.glseries_source, _glseries.glseries_notes,
+          _glseries.glseries_doctype, _glseries.glseries_docnumber, _glseries.amount, pJournalNumber );      
+      ELSE
+       INSERT INTO gltrans
+        ( gltrans_posted, gltrans_exported, gltrans_created, gltrans_date, gltrans_misc_id,
+          gltrans_sequence, gltrans_accnt_id, gltrans_source, gltrans_notes,
+          gltrans_doctype, gltrans_docnumber, gltrans_amount, gltrans_journalnumber )
+        VALUES
+        ( FALSE, FALSE, CURRENT_TIMESTAMP, _glseries.glseries_distdate, _glseries.glseries_misc_id,
+          pSequence, _glseries.glseries_accnt_id, _glseries.glseries_source, _glseries.glseries_notes,
+          _glseries.glseries_doctype, _glseries.glseries_docnumber, _glseries.amount, pJournalNumber );
+      END IF;
+      
+      _transCount := _transCount + 1;
+    END IF;
+  END LOOP;
+
+--  Delete all of the posted glseries members
+  DELETE FROM glseries
+  WHERE (glseries_sequence=pSequence);
+
+  PERFORM postIntoTrialBalance(pSequence);
+
+  RETURN _transCount;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/postglseriesnosumm.sql b/foundation-database/public/functions/postglseriesnosumm.sql
new file mode 100644 (file)
index 0000000..b7ed2d8
--- /dev/null
@@ -0,0 +1,129 @@
+
+CREATE OR REPLACE FUNCTION postGLSeriesNoSumm(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSequence ALIAS FOR $1;
+  _journalNumber INTEGER;
+  _returnValue INTEGER;
+
+BEGIN
+
+  SELECT postGLSeriesNoSumm(pSequence, fetchJournalNumber('G/L')) INTO _returnValue;
+  RETURN _returnValue;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION postGLSeriesNoSumm(INTEGER, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSequence ALIAS FOR $1;
+  pJournalNumber ALIAS FOR $2;
+  _glseries RECORD;
+  _transCount INTEGER := 0;
+  _rows INTEGER;
+
+BEGIN
+
+/*  Make sure we don't create an imbalance across companies.
+    The 'IgnoreCompanyBalance' metric is a back door mechanism to
+    allow legacy users to create transactions accross companies if
+    they have been using the company segment for something else
+    and they MUST continue to be able to do so.  It can only be 
+    implemented by direct sql update to the metric table and should 
+    otherwise be discouraged.
+*/  
+  IF (COALESCE(fetchMetricValue('GLCompanySize'),0) > 0 
+    AND fetchMetricBool('IgnoreCompany') = false)  THEN
+
+    SELECT count(accnt_company) INTO _rows
+    FROM (
+      SELECT DISTINCT accnt_company
+      FROM accnt 
+        JOIN glseries ON (glseries_accnt_id=accnt_id)
+      WHERE (glseries_sequence=pSequence)) _data;
+    
+    IF (_rows > 1) THEN
+      RAISE EXCEPTION 'G/L Series can not be posted because multiple companies are referenced in the same series.';
+    END IF;
+  END IF;
+  
+--  Make sure that we balance
+  IF ( ( SELECT SUM(glseries_amount)
+         FROM glseries
+         WHERE (glseries_sequence=pSequence) ) <> 0 ) THEN
+    RETURN -1;
+  END IF;
+
+--  March through the glseries members, posting them one at a time
+  FOR _glseries IN SELECT glseries_source, glseries_doctype, glseries_docnumber,
+                          glseries_accnt_id, glseries_distdate, glseries_notes,
+                          glseries_misc_id, glseries_amount as amount
+                     FROM glseries
+                    WHERE ((glseries_amount<>0.0)
+                      AND  (glseries_sequence=pSequence)) LOOP
+
+-- refuse to accept postings into closed periods
+    IF (SELECT BOOL_AND(COALESCE(period_closed, FALSE))
+        FROM accnt LEFT OUTER JOIN
+             period ON (_glseries.glseries_distdate BETWEEN period_start AND period_end)
+        WHERE (accnt_id = _glseries.glseries_accnt_id)) THEN
+      RAISE EXCEPTION 'Cannot post to closed period (%).', _glseries.glseries_distdate;
+      RETURN -4;        -- remove raise exception when all callers check return code
+    END IF;
+
+-- refuse to accept postings into frozen periods without proper priv
+    IF (SELECT NOT BOOL_AND(checkPrivilege('PostFrozenPeriod')) AND
+               BOOL_AND(COALESCE(period_freeze, FALSE))
+        FROM accnt LEFT OUTER JOIN
+             period ON (_glseries.glseries_distdate BETWEEN period_start AND period_end)
+        WHERE (accnt_id = _glseries.glseries_accnt_id)) THEN
+      RAISE EXCEPTION 'Cannot post to frozen period (%).', _glseries.glseries_distdate;
+      RETURN -4;        -- remove raise exception when all callers check return code
+    END IF;
+
+-- refuse to accept postings into nonexistent periods
+    IF NOT EXISTS(SELECT period_id
+                  FROM period
+                  WHERE (_glseries.glseries_distdate BETWEEN period_start AND period_end)) THEN
+      RAISE EXCEPTION 'Cannot post to nonexistent period (%).', pDistDate;
+    END IF;
+
+    IF (fetchMetricBool('UseJournals')) THEN
+      INSERT INTO sltrans
+      ( sltrans_posted, sltrans_created, sltrans_date, sltrans_misc_id,
+        sltrans_sequence, sltrans_accnt_id, sltrans_source, sltrans_notes,
+        sltrans_doctype, sltrans_docnumber, sltrans_amount, sltrans_journalnumber )
+      VALUES
+      ( FALSE, CURRENT_TIMESTAMP, _glseries.glseries_distdate, _glseries.glseries_misc_id,
+        pSequence, _glseries.glseries_accnt_id, _glseries.glseries_source, _glseries.glseries_notes,
+        _glseries.glseries_doctype, _glseries.glseries_docnumber, _glseries.amount, pJournalNumber );
+    ELSE
+      INSERT INTO gltrans
+      ( gltrans_posted, gltrans_exported, gltrans_created, gltrans_date, gltrans_misc_id,
+        gltrans_sequence, gltrans_accnt_id, gltrans_source, gltrans_notes,
+        gltrans_doctype, gltrans_docnumber, gltrans_amount, gltrans_journalnumber )
+      VALUES
+      ( FALSE, FALSE, CURRENT_TIMESTAMP, _glseries.glseries_distdate, _glseries.glseries_misc_id,
+        pSequence, _glseries.glseries_accnt_id, _glseries.glseries_source, _glseries.glseries_notes,
+        _glseries.glseries_doctype, _glseries.glseries_docnumber, _glseries.amount, pJournalNumber );
+    END IF;
+
+    _transCount := _transCount + 1;
+
+  END LOOP;
+
+--  Delete all of the posted glseries members
+  DELETE FROM glseries
+  WHERE (glseries_sequence=pSequence);
+
+  PERFORM postIntoTrialBalance(pSequence);
+
+  RETURN _transCount;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/postintoinvbalance.sql b/foundation-database/public/functions/postintoinvbalance.sql
new file mode 100644 (file)
index 0000000..60a399c
--- /dev/null
@@ -0,0 +1,158 @@
+
+CREATE OR REPLACE FUNCTION postIntoInvBalance(INTEGER) RETURNS BOOLEAN AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pInvhistId ALIAS FOR $1;
+  _invbalid INTEGER;
+  _r RECORD;
+  _count INTEGER;
+  _qty NUMERIC;
+
+BEGIN
+
+--  Grab the invhist record to post
+--  Special fix for transit sites when transtype=TS and invqty<0
+--  Set the sense to 1 to correct invhist populated incorrectly.
+  SELECT invhist.*,
+         CASE WHEN (invhist_transtype='TS' AND invhist_invqty < 0.0 AND warehous_transit) THEN 1
+              ELSE invhistSense(invhist_id)
+         END AS sense,
+         period_id INTO _r
+  FROM invhist
+    JOIN itemsite ON (itemsite_id=invhist_itemsite_id)
+    JOIN whsinfo ON (warehous_id=itemsite_warehous_id)
+    LEFT OUTER JOIN period ON (invhist_transdate::date BETWEEN period_start AND period_end)
+  WHERE ( invhist_id=pInvhistId );
+
+  GET DIAGNOSTICS _count = ROW_COUNT;
+
+--  If we can post into a Inv Balance, do so
+  IF ( _count > 0 ) THEN
+
+--  Validate
+    IF (_r.period_id IS NULL) THEN
+      RAISE EXCEPTION 'No accounting period exists for invhist_id %, transaction date %.  Transaction can not be posted.', _r.invhist_id, formatDate(_r.invhist_transdate);
+    END IF;
+
+--  If cycle count, then we need to reference balance which needs to be accurate
+--    IF (_r.invhist_transtype = 'CC') THEN
+--      PERFORM forwardupdateitemsite(_r.invhist_itemsite_id);
+--    END IF;
+
+--  Try to find an existing invbal
+    SELECT 
+      invbal_id, 
+--      CASE WHEN (_r.invhist_transtype != 'CC') THEN _r.invhist_invqty ELSE _r.invhist_invqty - invbal_qoh_ending END 
+      _r.invhist_invqty
+      INTO _invbalid, _qty
+    FROM invbal
+    WHERE ( (invbal_period_id=_r.period_id)
+      AND (invbal_itemsite_id=_r.invhist_itemsite_id) );
+
+    GET DIAGNOSTICS _count = ROW_COUNT;
+    IF (_count > 0) THEN
+
+--  We found a invbal, update it with the Inventory Transaction
+--  Note - two stage update to avoid any funny value caching logic
+    IF (_r.sense * _qty > 0) THEN
+      UPDATE invbal SET 
+        invbal_qty_in = (invbal_qty_in + abs(_qty)),
+        invbal_value_in = (invbal_value_in + abs(_qty) * _r.invhist_unitcost)
+      WHERE (invbal_id=_invbalid);
+    ELSIF (_r.sense * _qty < 0) THEN
+      UPDATE invbal SET 
+        invbal_qty_out = (invbal_qty_out + abs(_qty)),
+        invbal_value_out = (invbal_value_out + abs(_qty) *  _r.invhist_unitcost)
+      WHERE (invbal_id=_invbalid);
+    END IF;
+
+    -- Non-netable transactions have their own balances
+    IF (_r.invhist_transtype = 'NN') THEN
+      UPDATE invbal SET 
+        invbal_nn_in = (invbal_nn_in + _qty * -1),
+        invbal_nnval_in = (invbal_nnval_in + _qty * -1 * _r.invhist_unitcost)
+      WHERE (invbal_id=_invbalid);
+    END IF;
+
+    UPDATE invbal SET 
+      invbal_qoh_ending = (invbal_qoh_beginning + invbal_qty_in - invbal_qty_out),
+      invbal_value_ending = (invbal_value_beginning + invbal_value_in - invbal_value_out),
+      invbal_nn_ending = (invbal_nn_beginning + invbal_nn_in - invbal_nn_out),
+      invbal_nnval_ending = (invbal_nnval_beginning + invbal_nnval_in - invbal_nnval_out),
+      invbal_dirty=true
+    WHERE (invbal_id=_invbalid);
+  ELSE
+
+--  No existing invbal, make one
+    SELECT NEXTVAL('invbal_invbal_id_seq') INTO _invbalid;
+      INSERT INTO invbal
+        ( invbal_id, invbal_itemsite_id, invbal_period_id,
+          invbal_qoh_beginning,
+          invbal_qoh_ending,
+          invbal_qty_in,
+          invbal_qty_out,
+          invbal_value_beginning,
+          invbal_value_ending,
+          invbal_value_in,
+          invbal_value_out,
+          invbal_nn_beginning,
+          invbal_nn_ending,
+          invbal_nn_in,
+          invbal_nn_out,
+          invbal_nnval_beginning,
+          invbal_nnval_ending,
+          invbal_nnval_in,
+          invbal_nnval_out,
+          invbal_dirty )
+      VALUES
+        ( _invbalid, _r.invhist_itemsite_id, _r.period_id,
+         -- Netable
+          0, 
+          _r.invhist_invqty * _r.sense,
+          CASE WHEN (_r.sense > 0) THEN _r.invhist_invqty
+               ELSE 0
+          END,
+          CASE WHEN (_r.sense < 0) THEN (_r.invhist_invqty)
+               ELSE 0
+          END,
+          0,
+          _r.invhist_invqty * _r.invhist_unitcost * _r.sense,
+          CASE WHEN (_r.sense > 0) THEN _r.invhist_invqty * _r.invhist_unitcost
+               ELSE 0
+          END,
+          CASE WHEN (_r.sense < 0) THEN (_r.invhist_invqty  * _r.invhist_unitcost)
+               ELSE 0
+          END,
+          -- Non netable
+          0, 
+          CASE WHEN (_r.invhist_transtype='NN') THEN _r.invhist_invqty * -1
+               ELSE 0
+          END,
+          CASE WHEN (_r.sense > 0 AND _r.invhist_transtype='NN') THEN _r.invhist_invqty * -1
+               ELSE 0
+          END,
+          CASE WHEN (_r.sense < 0 AND _r.invhist_transtype='NN') THEN _r.invhist_invqty * -1
+               ELSE 0
+          END,
+          0,
+          CASE WHEN (_r.invhist_transtype='NN') THEN _r.invhist_invqty * _r.invhist_unitcost * -1
+               ELSE 0
+          END,
+          CASE WHEN (_r.sense > 0 AND _r.invhist_transtype='NN') THEN _r.invhist_invqty * -1 * _r.invhist_unitcost
+               ELSE 0
+          END,
+          CASE WHEN (_r.sense < 0 AND _r.invhist_transtype='NN') THEN (_r.invhist_invqty  * -1 * _r.invhist_unitcost)
+               ELSE 0
+          END,
+          true );
+    END IF;
+  ELSE
+    RETURN FALSE;
+  END IF;
+
+  RETURN TRUE;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/postintotrialbalance.sql b/foundation-database/public/functions/postintotrialbalance.sql
new file mode 100644 (file)
index 0000000..8890fa7
--- /dev/null
@@ -0,0 +1,87 @@
+
+CREATE OR REPLACE FUNCTION postIntoTrialBalance(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSequence ALIAS FOR $1;
+  _trialbalid INTEGER;
+  _r RECORD;
+
+BEGIN
+
+--  March through all of the G/L Transactions for the passed sequence that are not posted
+  FOR _r IN SELECT gltrans_id, gltrans_date, gltrans_accnt_id, gltrans_amount,
+                   accnt_forwardupdate, period_id, period_closed, period_freeze
+            FROM accnt, gltrans LEFT OUTER JOIN period ON (gltrans_date BETWEEN period_start AND period_end)
+            WHERE ( (gltrans_accnt_id=accnt_id)
+             AND (NOT gltrans_posted)
+             AND (NOT gltrans_deleted)
+             AND (gltrans_sequence=pSequence) ) LOOP
+
+--  If we can post into a Trial Balance, do so
+    IF ( (NOT _r.period_closed) AND ( (NOT _r.period_freeze) OR (checkPrivilege('PostFrozenPeriod')) ) ) THEN
+
+--  Try to find an existing trialbal
+      SELECT trialbal_id INTO _trialbalid
+      FROM trialbal
+      WHERE ( (trialbal_period_id=_r.period_id)
+       AND (trialbal_accnt_id=_r.gltrans_accnt_id) );
+      IF (FOUND) THEN
+
+--  We found a trialbal, update it with the G/L Transaction
+--  Note - two stage update to avoid any funny value caching logic
+        IF (_r.gltrans_amount > 0) THEN
+          UPDATE trialbal
+          SET trialbal_credits = (trialbal_credits + _r.gltrans_amount)
+          WHERE (trialbal_id=_trialbalid);
+        ELSE
+          UPDATE trialbal
+          SET trialbal_debits = (trialbal_debits + (_r.gltrans_amount * -1))
+          WHERE (trialbal_id=_trialbalid);
+        END IF;
+
+        UPDATE trialbal
+        SET trialbal_ending = (trialbal_beginning - trialbal_debits + trialbal_credits),
+            trialbal_dirty=TRUE
+        WHERE (trialbal_id=_trialbalid);
+      ELSE
+
+--  No existing trialbal, make one
+        SELECT NEXTVAL('trialbal_trialbal_id_seq') INTO _trialbalid;
+        INSERT INTO trialbal
+        ( trialbal_id, trialbal_accnt_id, trialbal_period_id,
+          trialbal_beginning, trialbal_dirty,
+          trialbal_ending,
+          trialbal_credits,
+          trialbal_debits )
+        VALUES
+        ( _trialbalid, _r.gltrans_accnt_id, _r.period_id,
+          0, TRUE,
+          _r.gltrans_amount,
+          CASE WHEN (_r.gltrans_amount > 0) THEN _r.gltrans_amount
+               ELSE 0
+          END,
+          CASE WHEN (_r.gltrans_amount < 0) THEN (_r.gltrans_amount * -1)
+               ELSE 0
+          END );
+      END IF;
+
+--  Forward update if we should
+      IF (_r.accnt_forwardupdate AND fetchmetricbool('ManualForwardUpdate')) THEN
+        PERFORM forwardUpdateTrialBalance(_trialbalid);
+      END IF;
+
+--  Mark the G/L Transaction as posted
+      UPDATE gltrans
+      SET gltrans_posted=TRUE
+      WHERE (gltrans_id=_r.gltrans_id);
+
+    END IF;
+
+  END LOOP;
+
+  RETURN 1;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/postinvhist.sql b/foundation-database/public/functions/postinvhist.sql
new file mode 100644 (file)
index 0000000..929741d
--- /dev/null
@@ -0,0 +1,36 @@
+CREATE OR REPLACE FUNCTION postInvHist( INTEGER ) RETURNS BOOLEAN AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pInvhistId ALIAS FOR $1;
+  _r RECORD;
+BEGIN
+
+    IF ( SELECT metric_value
+        FROM metric
+        WHERE ((metric_name = 'EnableAsOfQOH')
+        AND (metric_value = 't'))) THEN
+      IF (NOT postIntoInvBalance(pInvhistId)) THEN
+        RAISE EXCEPTION 'Post into Inventory Balance for invhist_id=% was unsuccessful',pInvhistId;
+      END IF;
+    END IF;
+
+    --Update itemsite qoh and change posted flag
+    UPDATE itemsite SET 
+      itemsite_qtyonhand = (itemsite_qtyonhand + (invhist_invqty * invhistSense(invhist_id))),
+      itemsite_value = itemsite_value + invhist_value_after - invhist_value_before
+    FROM invhist
+    WHERE ( (itemsite_id=invhist_itemsite_id)
+    AND (invhist_id=pInvhistId)
+    AND (NOT invhist_posted) );
+
+    --Flag as posted
+    UPDATE invhist SET
+      invhist_posted=TRUE
+    WHERE ( (invhist_id=pInvhistId)
+    AND (invhist_posted=FALSE) );
+
+RETURN TRUE;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/postinvoice.sql b/foundation-database/public/functions/postinvoice.sql
new file mode 100644 (file)
index 0000000..d2140b9
--- /dev/null
@@ -0,0 +1,683 @@
+CREATE OR REPLACE FUNCTION postInvoice(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pInvcheadid ALIAS FOR $1;
+  _return INTEGER;
+
+BEGIN
+
+  SELECT postInvoice(pInvcheadid, fetchJournalNumber('AR-IN')) INTO _return;
+
+  RETURN _return;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION postInvoice(INTEGER, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pInvcheadid ALIAS FOR $1;
+  pJournalNumber ALIAS FOR $2;
+  _itemlocSeries INTEGER;
+  _return INTEGER;
+
+BEGIN
+
+  SELECT NEXTVAL('itemloc_series_seq') INTO _itemlocSeries;
+  SELECT postInvoice(pInvcheadid, pJournalNumber, _itemlocseries) INTO _return;
+
+  RETURN _return;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION postInvoice(INTEGER, INTEGER, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pInvcheadid ALIAS FOR $1;
+  pJournalNumber ALIAS FOR $2;
+  pItemlocSeries ALIAS FOR $3;
+  _aropenid INTEGER;
+  _cohistid INTEGER;
+  _itemlocSeries INTEGER := 0;
+  _invhistid INTEGER := 0;
+  _amount NUMERIC;
+  _roundedBase NUMERIC;
+  _sequence INTEGER;
+  _r RECORD;
+  _p RECORD;
+  _test INTEGER;
+  _totalAmount          NUMERIC := 0;
+  _totalRoundedBase     NUMERIC := 0;
+  _totalAmountBase      NUMERIC := 0;
+  _appliedAmount        NUMERIC := 0;
+  _commissionDue        NUMERIC := 0;
+  _tmpAccntId INTEGER;
+  _tmpCurrId  INTEGER;
+  _firstExchDate        DATE;
+  _glDate              DATE;
+  _exchGain             NUMERIC := 0;
+
+BEGIN
+
+  IF ( ( SELECT invchead_posted
+         FROM invchead
+         WHERE (invchead_id=pInvcheadid) ) ) THEN
+    RETURN -10;
+  END IF;
+
+--  Cache some parameters
+  SELECT invchead.*, fetchGLSequence() AS sequence,
+         findFreightAccount(invchead_cust_id) AS freightaccntid,
+         findARAccount(invchead_cust_id) AS araccntid,
+         ( SELECT COALESCE(SUM(taxhist_tax), 0)
+           FROM invcheadtax
+           WHERE ( (taxhist_parent_id = invchead_id)
+             AND   (taxhist_taxtype_id = getFreightTaxtypeId()) ) ) AS freighttax,
+         ( SELECT COALESCE(SUM(taxhist_tax), 0)
+           FROM invcheadtax
+           WHERE ( (taxhist_parent_id = invchead_id)
+             AND   (taxhist_taxtype_id = getAdjustmentTaxtypeId()) ) ) AS adjtax
+       INTO _p 
+  FROM invchead
+  WHERE (invchead_id=pInvcheadid);
+
+  _itemlocSeries = pItemlocSeries;
+
+  _glDate := COALESCE(_p.invchead_gldistdate, _p.invchead_invcdate);
+
+  IF (_p.invchead_salesrep_id < 0) THEN
+    RAISE NOTICE 'Patch negative invchead_salesrep_id until invchead_salesrep_id is a true fkey';
+    _p.invchead_salesrep_id := NULL;
+  END IF;
+
+-- the 1st MC iteration used the cohead_orderdate so we could get curr exch
+-- gain/loss between the sales and invoice dates, but see issue 3892.  leave
+-- this condition TRUE until we make this configurable or decide not to.
+  IF TRUE THEN
+      _firstExchDate := _p.invchead_invcdate;
+  ELSE
+-- can we save a select by using: _firstExchDate := _p.invchead_orderdate;
+      SELECT cohead_orderdate INTO _firstExchDate
+      FROM cohead JOIN invchead ON (cohead_number = invchead_ordernumber)
+      WHERE (invchead_id = pInvcheadid);
+  END IF;
+
+--  Start by handling taxes
+  FOR _r IN SELECT tax_sales_accnt_id, 
+              round(sum(taxdetail_tax),2) AS tax,
+              currToBase(_p.invchead_curr_id, round(sum(taxdetail_tax),2), _firstExchDate) AS taxbasevalue
+            FROM tax 
+             JOIN calculateTaxDetailSummary('I', pInvcheadid, 'T') ON (taxdetail_tax_id=tax_id)
+           GROUP BY tax_id, tax_sales_accnt_id LOOP
+
+    PERFORM insertIntoGLSeries( _p.sequence, 'A/R', 'IN', _p.invchead_invcnumber,
+                                _r.tax_sales_accnt_id, 
+                                _r.taxbasevalue,
+                                _glDate, _p.invchead_billto_name );
+
+    _totalAmount := _totalAmount + _r.tax;
+    _totalRoundedBase := _totalRoundedBase + _r.taxbasevalue;  
+  END LOOP;
+
+-- Update item tax records with posting data
+    UPDATE invcitemtax SET 
+      taxhist_docdate=_firstExchDate,
+      taxhist_distdate=_glDate,
+      taxhist_curr_id=_p.invchead_curr_id,
+      taxhist_curr_rate=curr_rate,
+      taxhist_journalnumber=pJournalNumber
+    FROM invchead
+     JOIN invcitem ON (invchead_id=invcitem_invchead_id), 
+     curr_rate
+    WHERE ((invchead_id=pInvcheadId)
+      AND (taxhist_parent_id=invcitem_id)
+      AND (_p.invchead_curr_id=curr_id)
+      AND ( _firstExchDate BETWEEN curr_effective 
+                           AND curr_expires) );
+
+-- Update Invchead taxes (Freight and Adjustments) with posting data
+    UPDATE invcheadtax SET 
+      taxhist_docdate=_firstExchDate,
+      taxhist_distdate=_glDate,
+      taxhist_curr_id=_p.invchead_curr_id,
+      taxhist_curr_rate=curr_rate,
+      taxhist_journalnumber=pJournalNumber
+    FROM curr_rate
+    WHERE ((taxhist_parent_id=pInvcheadid)
+      AND (_p.invchead_curr_id=curr_id)
+      AND ( _firstExchDate BETWEEN curr_effective 
+                           AND curr_expires) );
+
+--  March through the Non-Misc. Invcitems
+  FOR _r IN SELECT *
+            FROM invoiceitem
+            WHERE ( (invcitem_invchead_id = pInvcheadid)
+              AND   (invcitem_item_id <> -1) ) LOOP
+
+--  Cache the amount due for this line
+    _amount := _r.extprice;
+
+    IF (_amount > 0) THEN
+--  Credit the Sales Account for the invcitem item
+      IF (_r.invcitem_rev_accnt_id IS NOT NULL) THEN
+        SELECT getPrjAccntId(_p.invchead_prj_id, _r.invcitem_rev_accnt_id)
+       INTO _tmpAccntId;
+      ELSEIF (_r.itemsite_id IS NULL) THEN
+       SELECT getPrjAccntId(_p.invchead_prj_id, salesaccnt_sales_accnt_id) 
+       INTO _tmpAccntId
+       FROM salesaccnt
+       WHERE (salesaccnt_id=findSalesAccnt(_r.invcitem_item_id, 'I', _p.invchead_cust_id,
+                                            _p.invchead_saletype_id, _p.invchead_shipzone_id));
+      ELSE
+       SELECT getPrjAccntId(_p.invchead_prj_id, salesaccnt_sales_accnt_id) 
+       INTO _tmpAccntId
+       FROM salesaccnt
+       WHERE (salesaccnt_id=findSalesAccnt(_r.itemsite_id, 'IS', _p.invchead_cust_id,
+                                            _p.invchead_saletype_id, _p.invchead_shipzone_id));
+      END IF;
+
+--  If the Sales Account Assignment was not found then punt
+      IF (NOT FOUND) THEN
+        PERFORM deleteGLSeries(_p.sequence);
+        DELETE FROM cohist
+         WHERE ((cohist_sequence=_p.sequence)
+           AND  (cohist_invcnumber=_p.invchead_invcnumber));
+        RETURN -11;
+      END IF;
+
+      _roundedBase := round(currToBase(_p.invchead_curr_id, _amount, _firstExchDate), 2);
+      SELECT insertIntoGLSeries( _p.sequence, 'A/R', 'IN',
+                                 _p.invchead_invcnumber, _tmpAccntId,
+                                 _roundedBase, _glDate, _p.invchead_billto_name ) INTO _test;
+
+      _totalAmount := (_totalAmount + _amount);
+      _totalRoundedBase := _totalRoundedBase + _roundedBase;
+      _commissionDue := (_commissionDue + (_amount * _p.invchead_commission));
+    END IF;
+
+    _totalAmount := _totalAmount;
+    _totalRoundedBase := _totalRoundedBase;
+
+--  Record Sales History for this S/O Item
+    SELECT nextval('cohist_cohist_id_seq') INTO _cohistid;
+    INSERT INTO cohist
+    ( cohist_id, cohist_cust_id, cohist_itemsite_id, cohist_shipto_id,
+      cohist_shipdate, cohist_shipvia,
+      cohist_ordernumber, cohist_ponumber, cohist_orderdate,
+      cohist_doctype, cohist_invcnumber, cohist_invcdate,
+      cohist_qtyshipped, cohist_unitprice, cohist_unitcost,
+      cohist_salesrep_id, cohist_commission, cohist_commissionpaid,
+      cohist_billtoname, cohist_billtoaddress1,
+      cohist_billtoaddress2, cohist_billtoaddress3,
+      cohist_billtocity, cohist_billtostate, cohist_billtozip,
+      cohist_shiptoname, cohist_shiptoaddress1,
+      cohist_shiptoaddress2, cohist_shiptoaddress3,
+      cohist_shiptocity, cohist_shiptostate, cohist_shiptozip,
+      cohist_curr_id, cohist_sequence, cohist_taxtype_id, cohist_taxzone_id,
+      cohist_shipzone_id, cohist_saletype_id )
+    VALUES
+    ( _cohistid, _p.invchead_cust_id, _r.itemsite_id, _p.invchead_shipto_id,
+      _p.invchead_shipdate, _p.invchead_shipvia,
+      COALESCE(_p.invchead_ordernumber, _r.cohead_number), _p.invchead_ponumber, _p.invchead_orderdate,
+      'I', _p.invchead_invcnumber, _p.invchead_invcdate,
+      _r.qty, _r.unitprice, _r.unitcost,
+      _p.invchead_salesrep_id, (_p.invchead_commission * _r.extprice), FALSE,
+      _p.invchead_billto_name, _p.invchead_billto_address1,
+      _p.invchead_billto_address2, _p.invchead_billto_address3,
+      _p.invchead_billto_city, _p.invchead_billto_state, _p.invchead_billto_zipcode,
+      _p.invchead_shipto_name, _p.invchead_shipto_address1,
+      _p.invchead_shipto_address2, _p.invchead_shipto_address3,
+      _p.invchead_shipto_city, _p.invchead_shipto_state,
+      _p.invchead_shipto_zipcode, _p.invchead_curr_id,
+      _p.sequence, _r.invcitem_taxtype_id, _p.invchead_taxzone_id,
+      _p.invchead_shipzone_id, _p.invchead_saletype_id );
+    INSERT INTO cohisttax
+    ( taxhist_parent_id, taxhist_taxtype_id, taxhist_tax_id,
+      taxhist_basis, taxhist_basis_tax_id, taxhist_sequence,
+      taxhist_percent, taxhist_amount, taxhist_tax,
+      taxhist_docdate, taxhist_distdate, taxhist_curr_id, taxhist_curr_rate,
+      taxhist_journalnumber )
+    SELECT _cohistid, taxhist_taxtype_id, taxhist_tax_id,
+           taxhist_basis, taxhist_basis_tax_id, taxhist_sequence,
+           taxhist_percent, taxhist_amount, taxhist_tax,
+           taxhist_docdate, taxhist_distdate, taxhist_curr_id, taxhist_curr_rate,
+           taxhist_journalnumber
+    FROM invcitemtax
+    WHERE (taxhist_parent_id=_r.invcitem_id);
+
+  END LOOP;
+
+--  March through the Misc. Invcitems
+  FOR _r IN SELECT *
+            FROM invoiceitem JOIN salescat ON (salescat_id = invcitem_salescat_id)
+            WHERE ( (invcitem_item_id = -1)
+              AND   (invcitem_invchead_id=pInvcheadid) ) LOOP
+
+--  Cache the amount due for this line and the commission due for such
+    _amount := _r.extprice;
+
+    IF (_amount > 0) THEN
+--  Credit the Sales Account for the invcitem item
+      _roundedBase = round(currToBase(_p.invchead_curr_id, _amount,
+                                      _firstExchDate), 2);
+      SELECT insertIntoGLSeries( _p.sequence, 'A/R', 'IN', _p.invchead_invcnumber,
+                                 getPrjAccntId(_p.invchead_prj_id, COALESCE(_r.invcitem_rev_accnt_id, _r.salescat_sales_accnt_id)), 
+                                 _roundedBase,
+                                 _glDate, _p.invchead_billto_name ) INTO _test;
+      IF (_test < 0) THEN
+        PERFORM deleteGLSeries(_p.sequence);
+        DELETE FROM cohist
+         WHERE ((cohist_sequence=_p.sequence)
+           AND  (cohist_invcnumber=_p.invchead_invcnumber));
+        RETURN _test;
+      END IF;
+
+      _totalAmount := (_totalAmount + _amount);
+      _totalRoundedBase :=  _totalRoundedBase + _roundedBase;
+      _commissionDue := (_commissionDue + (_amount * _p.invchead_commission));
+    END IF;
+
+--  Record Sales History for this S/O Item
+    SELECT nextval('cohist_cohist_id_seq') INTO _cohistid;
+    INSERT INTO cohist
+    ( cohist_id, cohist_cust_id, cohist_itemsite_id, cohist_shipto_id,
+      cohist_misc_type, cohist_misc_descrip,
+      cohist_shipdate, cohist_shipvia,
+      cohist_ordernumber, cohist_ponumber, cohist_orderdate,
+      cohist_doctype, cohist_invcnumber, cohist_invcdate,
+      cohist_qtyshipped, cohist_unitprice, cohist_unitcost,
+      cohist_salesrep_id, cohist_commission, cohist_commissionpaid,
+      cohist_billtoname, cohist_billtoaddress1,
+      cohist_billtoaddress2, cohist_billtoaddress3,
+      cohist_billtocity, cohist_billtostate, cohist_billtozip,
+      cohist_shiptoname, cohist_shiptoaddress1,
+      cohist_shiptoaddress2, cohist_shiptoaddress3,
+      cohist_shiptocity, cohist_shiptostate, cohist_shiptozip,
+      cohist_curr_id, cohist_sequence, cohist_taxtype_id, cohist_taxzone_id,
+      cohist_shipzone_id, cohist_saletype_id )
+    VALUES
+    ( _cohistid, _p.invchead_cust_id, -1, _p.invchead_shipto_id,
+      'M', (_r.invcitem_number || '-' || _r.invcitem_descrip),
+      _p.invchead_shipdate, _p.invchead_shipvia,
+      COALESCE(_p.invchead_ordernumber, _r.cohead_number), _p.invchead_ponumber, _p.invchead_orderdate,
+      'I', _p.invchead_invcnumber, _p.invchead_invcdate,
+      _r.qty, _r.unitprice, 0,
+      _p.invchead_salesrep_id, (_p.invchead_commission * _r.extprice), FALSE,
+      _p.invchead_billto_name, _p.invchead_billto_address1,
+      _p.invchead_billto_address2, _p.invchead_billto_address3,
+      _p.invchead_billto_city, _p.invchead_billto_state, _p.invchead_billto_zipcode,
+      _p.invchead_shipto_name, _p.invchead_shipto_address1,
+      _p.invchead_shipto_address2, _p.invchead_shipto_address3,
+      _p.invchead_shipto_city, _p.invchead_shipto_state,
+      _p.invchead_shipto_zipcode, _p.invchead_curr_id,
+      _p.sequence, _r.invcitem_taxtype_id, _p.invchead_taxzone_id,
+      _p.invchead_shipzone_id, _p.invchead_saletype_id );
+    INSERT INTO cohisttax
+    ( taxhist_parent_id, taxhist_taxtype_id, taxhist_tax_id,
+      taxhist_basis, taxhist_basis_tax_id, taxhist_sequence,
+      taxhist_percent, taxhist_amount, taxhist_tax,
+      taxhist_docdate, taxhist_distdate, taxhist_curr_id, taxhist_curr_rate,
+      taxhist_journalnumber )
+    SELECT _cohistid, taxhist_taxtype_id, taxhist_tax_id,
+           taxhist_basis, taxhist_basis_tax_id, taxhist_sequence,
+           taxhist_percent, taxhist_amount, taxhist_tax,
+           taxhist_docdate, taxhist_distdate, taxhist_curr_id, taxhist_curr_rate,
+           taxhist_journalnumber
+    FROM invcitemtax
+    WHERE (taxhist_parent_id=_r.invcitem_id);
+
+  END LOOP;
+
+--  Credit the Freight Account for Freight Charges
+  IF (_p.invchead_freight <> 0) THEN
+    IF (_p.freightaccntid <> -1) THEN
+      _roundedBase = round(currToBase(_p.invchead_curr_id, _p.invchead_freight,
+                                      _firstExchDate), 2);
+      SELECT insertIntoGLSeries( _p.sequence, 'A/R', 'IN', _p.invchead_invcnumber,
+                                 getPrjAccntId(_p.invchead_prj_id,_p.freightaccntid), 
+                                 _roundedBase,
+                                 _glDate, _p.invchead_billto_name ) INTO _test;
+
+--  Cache the Freight Amount distributed
+        _totalAmount := (_totalAmount + _p.invchead_freight);
+        _totalRoundedBase := _totalRoundedBase + _roundedBase;
+    ELSE
+      _test := -14;
+    END IF;
+
+--  If the Freight Account was not found then punt
+    IF (_test < 0) THEN
+      PERFORM deleteGLSeries(_p.sequence);
+      DELETE FROM cohist
+       WHERE ((cohist_sequence=_p.sequence)
+         AND  (cohist_invcnumber=_p.invchead_invcnumber));
+      RETURN _test;
+    END IF;
+
+--  Record Sales History for the Freight
+    SELECT nextval('cohist_cohist_id_seq') INTO _cohistid;
+    INSERT INTO cohist
+    ( cohist_id, cohist_cust_id, cohist_itemsite_id, cohist_shipto_id,
+      cohist_misc_type, cohist_misc_descrip,
+      cohist_shipdate, cohist_shipvia,
+      cohist_ordernumber, cohist_ponumber, cohist_orderdate,
+      cohist_doctype, cohist_invcnumber, cohist_invcdate,
+      cohist_qtyshipped, cohist_unitprice, cohist_unitcost,
+      cohist_salesrep_id, cohist_commission, cohist_commissionpaid,
+      cohist_billtoname, cohist_billtoaddress1,
+      cohist_billtoaddress2, cohist_billtoaddress3,
+      cohist_billtocity, cohist_billtostate, cohist_billtozip,
+      cohist_shiptoname, cohist_shiptoaddress1,
+      cohist_shiptoaddress2, cohist_shiptoaddress3,
+      cohist_shiptocity, cohist_shiptostate, cohist_shiptozip,
+      cohist_curr_id, cohist_sequence, cohist_taxtype_id, cohist_taxzone_id,
+      cohist_shipzone_id, cohist_saletype_id )
+    VALUES
+    ( _cohistid, _p.invchead_cust_id, -1, _p.invchead_shipto_id,
+      'F', 'Freight',
+      _p.invchead_shipdate, _p.invchead_shipvia,
+      _p.invchead_ordernumber, _p.invchead_ponumber, _p.invchead_orderdate,
+      'I', _p.invchead_invcnumber, _p.invchead_invcdate,
+      1, _p.invchead_freight, _p.invchead_freight,
+      _p.invchead_salesrep_id, 0, FALSE,
+      _p.invchead_billto_name, _p.invchead_billto_address1,
+      _p.invchead_billto_address2, _p.invchead_billto_address3,
+      _p.invchead_billto_city, _p.invchead_billto_state, _p.invchead_billto_zipcode,
+      _p.invchead_shipto_name, _p.invchead_shipto_address1,
+      _p.invchead_shipto_address2, _p.invchead_shipto_address3,
+      _p.invchead_shipto_city, _p.invchead_shipto_state,
+      _p.invchead_shipto_zipcode, _p.invchead_curr_id,
+      _p.sequence, getFreightTaxtypeId(), _p.invchead_taxzone_id,
+      _p.invchead_shipzone_id, _p.invchead_saletype_id );
+    INSERT INTO cohisttax
+    ( taxhist_parent_id, taxhist_taxtype_id, taxhist_tax_id,
+      taxhist_basis, taxhist_basis_tax_id, taxhist_sequence,
+      taxhist_percent, taxhist_amount, taxhist_tax,
+      taxhist_docdate, taxhist_distdate, taxhist_curr_id, taxhist_curr_rate,
+      taxhist_journalnumber )
+    SELECT _cohistid, taxhist_taxtype_id, taxhist_tax_id,
+           taxhist_basis, taxhist_basis_tax_id, taxhist_sequence,
+           taxhist_percent, taxhist_amount, taxhist_tax,
+           taxhist_docdate, taxhist_distdate, taxhist_curr_id, taxhist_curr_rate,
+           taxhist_journalnumber
+    FROM invcheadtax
+    WHERE ( (taxhist_parent_id=_p.invchead_id)
+      AND   (taxhist_taxtype_id=getFreightTaxtypeId()) );
+
+  END IF;
+
+--  Credit the Misc. Account for Miscellaneous Charges
+  IF (_p.invchead_misc_amount <> 0) THEN
+    _roundedBase := round(currToBase(_p.invchead_curr_id, _p.invchead_misc_amount,
+                                     _firstExchDate), 2);
+    SELECT insertIntoGLSeries( _p.sequence, 'A/R', 'IN', _p.invchead_invcnumber,
+                               getPrjAccntId(_p.invchead_prj_id, _p.invchead_misc_accnt_id), 
+                               _roundedBase,
+                               _glDate, _p.invchead_billto_name ) INTO _test;
+
+--  If the Misc. Charges Account was not found then punt
+    IF (_test < 0) THEN
+      PERFORM deleteGLSeries(_p.sequence);
+      DELETE FROM cohist
+       WHERE ((cohist_sequence=_p.sequence)
+         AND  (cohist_invcnumber=_p.invchead_invcnumber));
+      RETURN _test;
+    END IF;
+
+--  Cache the Misc. Amount distributed
+    _totalAmount := (_totalAmount + _p.invchead_misc_amount);
+    _totalRoundedBase := _totalRoundedBase + _roundedBase;
+
+--  Record Sales History for the Misc. Charge
+    INSERT INTO cohist
+    ( cohist_cust_id, cohist_itemsite_id, cohist_shipto_id,
+      cohist_misc_type, cohist_misc_descrip, cohist_misc_id,
+      cohist_shipdate, cohist_shipvia,
+      cohist_ordernumber, cohist_ponumber, cohist_orderdate,
+      cohist_doctype, cohist_invcnumber, cohist_invcdate,
+      cohist_qtyshipped, cohist_unitprice, cohist_unitcost,
+      cohist_salesrep_id, cohist_commission, cohist_commissionpaid,
+      cohist_billtoname, cohist_billtoaddress1,
+      cohist_billtoaddress2, cohist_billtoaddress3,
+      cohist_billtocity, cohist_billtostate, cohist_billtozip,
+      cohist_shiptoname, cohist_shiptoaddress1,
+      cohist_shiptoaddress2, cohist_shiptoaddress3,
+      cohist_shiptocity, cohist_shiptostate, cohist_shiptozip,
+      cohist_curr_id, cohist_sequence,
+      cohist_shipzone_id, cohist_saletype_id )
+    VALUES
+    ( _p.invchead_cust_id, -1, _p.invchead_shipto_id,
+      'M', _p.invchead_misc_descrip, _p.invchead_misc_accnt_id,
+      _p.invchead_shipdate, _p.invchead_shipvia,
+      _p.invchead_ordernumber, _p.invchead_ponumber, _p.invchead_orderdate,
+      'I', _p.invchead_invcnumber, _p.invchead_invcdate,
+      1, _p.invchead_misc_amount, _p.invchead_misc_amount,
+      _p.invchead_salesrep_id, 0, FALSE,
+      _p.invchead_billto_name, _p.invchead_billto_address1,
+      _p.invchead_billto_address2, _p.invchead_billto_address3,
+      _p.invchead_billto_city, _p.invchead_billto_state, _p.invchead_billto_zipcode,
+      _p.invchead_shipto_name, _p.invchead_shipto_address1,
+      _p.invchead_shipto_address2, _p.invchead_shipto_address3,
+      _p.invchead_shipto_city, _p.invchead_shipto_state,
+      _p.invchead_shipto_zipcode, _p.invchead_curr_id,
+      _p.sequence,
+      _p.invchead_shipzone_id, _p.invchead_saletype_id );
+
+  END IF;
+
+--  Record Sales History for the Tax Adjustment
+  IF (_p.adjtax <> 0) THEN
+    SELECT nextval('cohist_cohist_id_seq') INTO _cohistid;
+    INSERT INTO cohist
+    ( cohist_id, cohist_cust_id, cohist_itemsite_id, cohist_shipto_id,
+      cohist_misc_type, cohist_misc_descrip,
+      cohist_shipdate, cohist_shipvia,
+      cohist_ordernumber, cohist_ponumber, cohist_orderdate,
+      cohist_doctype, cohist_invcnumber, cohist_invcdate,
+      cohist_qtyshipped, cohist_unitprice, cohist_unitcost,
+      cohist_salesrep_id, cohist_commission, cohist_commissionpaid,
+      cohist_billtoname, cohist_billtoaddress1,
+      cohist_billtoaddress2, cohist_billtoaddress3,
+      cohist_billtocity, cohist_billtostate, cohist_billtozip,
+      cohist_shiptoname, cohist_shiptoaddress1,
+      cohist_shiptoaddress2, cohist_shiptoaddress3,
+      cohist_shiptocity, cohist_shiptostate, cohist_shiptozip,
+      cohist_curr_id, cohist_sequence, cohist_taxtype_id, cohist_taxzone_id,
+      cohist_shipzone_id, cohist_saletype_id )
+    VALUES
+    ( _cohistid, _p.invchead_cust_id, -1, _p.invchead_shipto_id,
+      'T', 'Misc Tax Adjustment',
+      _p.invchead_shipdate, _p.invchead_shipvia,
+      _p.invchead_ordernumber, _p.invchead_ponumber, _p.invchead_orderdate,
+      'I', _p.invchead_invcnumber, _p.invchead_invcdate,
+      1, 0.0, 0.0,
+      _p.invchead_salesrep_id, 0, FALSE,
+      _p.invchead_billto_name, _p.invchead_billto_address1,
+      _p.invchead_billto_address2, _p.invchead_billto_address3,
+      _p.invchead_billto_city, _p.invchead_billto_state, _p.invchead_billto_zipcode,
+      _p.invchead_shipto_name, _p.invchead_shipto_address1,
+      _p.invchead_shipto_address2, _p.invchead_shipto_address3,
+      _p.invchead_shipto_city, _p.invchead_shipto_state,
+      _p.invchead_shipto_zipcode, _p.invchead_curr_id,
+      _p.sequence, getAdjustmentTaxtypeId(), _p.invchead_taxzone_id,
+      _p.invchead_shipzone_id, _p.invchead_saletype_id );
+    INSERT INTO cohisttax
+    ( taxhist_parent_id, taxhist_taxtype_id, taxhist_tax_id,
+      taxhist_basis, taxhist_basis_tax_id, taxhist_sequence,
+      taxhist_percent, taxhist_amount, taxhist_tax,
+      taxhist_docdate, taxhist_distdate, taxhist_curr_id, taxhist_curr_rate,
+      taxhist_journalnumber )
+    SELECT _cohistid, taxhist_taxtype_id, taxhist_tax_id,
+           taxhist_basis, taxhist_basis_tax_id, taxhist_sequence,
+           taxhist_percent, taxhist_amount, taxhist_tax,
+           taxhist_docdate, taxhist_distdate, taxhist_curr_id, taxhist_curr_rate,
+           taxhist_journalnumber
+    FROM invcheadtax
+    WHERE ( (taxhist_parent_id=_p.invchead_id)
+      AND   (taxhist_taxtype_id=getAdjustmentTaxtypeId()) );
+
+  END IF;
+
+-- ToDo: handle rounding errors
+    _exchGain := currGain(_p.invchead_curr_id, _totalAmount,
+                          _firstExchDate, _glDate);
+    IF (_exchGain <> 0) THEN
+        SELECT insertIntoGLSeries( _p.sequence, 'A/R', 'IN',
+                                   _p.invchead_invcnumber, getGainLossAccntId(_p.araccntid),
+                                   round(_exchGain, 2) * -1,
+                                   _glDate, _p.invchead_billto_name ) INTO _test ;
+        IF (_test < 0) THEN
+          PERFORM deleteGLSeries(_p.sequence);
+          DELETE FROM cohist
+           WHERE ((cohist_sequence=_p.sequence)
+             AND  (cohist_invcnumber=_p.invchead_invcnumber));
+          RETURN _test;
+        END IF;
+    END IF;
+
+--  Debit A/R for the total Amount
+  IF (_totalRoundedBase <> 0) THEN
+    IF (_p.araccntid != -1) THEN
+      PERFORM insertIntoGLSeries( _p.sequence, 'A/R', 'IN', _p.invchead_invcnumber,
+                                  _p.araccntid, round(_totalRoundedBase * -1, 2),
+                                  _glDate, _p.invchead_billto_name );
+    ELSE
+      PERFORM deleteGLSeries(_p.sequence);
+      DELETE FROM cohist
+       WHERE ((cohist_sequence=_p.sequence)
+         AND  (cohist_invcnumber=_p.invchead_invcnumber));
+      RETURN -17;
+    END IF;
+  END IF;
+
+--  Commit the GLSeries;
+  SELECT postGLSeries(_p.sequence, pJournalNumber) INTO _test;
+  IF (_test < 0) THEN
+    PERFORM deleteGLSeries(_p.sequence);
+    DELETE FROM cohist
+     WHERE ((cohist_sequence=_p.sequence)
+       AND  (cohist_invcnumber=_p.invchead_invcnumber));
+    RETURN _test;
+  END IF;
+
+--  Create the Invoice aropen item
+  SELECT nextval('aropen_aropen_id_seq') INTO _aropenid;
+  INSERT INTO aropen
+  ( aropen_id, aropen_username, aropen_journalnumber,
+    aropen_open, aropen_posted,
+    aropen_cust_id, aropen_ponumber,
+    aropen_docnumber, aropen_applyto, aropen_doctype,
+    aropen_docdate, aropen_duedate, aropen_distdate, aropen_terms_id,
+    aropen_amount, aropen_paid,
+    aropen_salesrep_id, aropen_commission_due, aropen_commission_paid,
+    aropen_ordernumber, aropen_notes, aropen_cobmisc_id,
+    aropen_curr_id )
+  VALUES
+  ( _aropenid, getEffectiveXtUser(), pJournalNumber,
+    TRUE, FALSE,
+    _p.invchead_cust_id, _p.invchead_ponumber,
+    _p.invchead_invcnumber, _p.invchead_invcnumber, 'I',
+    _p.invchead_invcdate, determineDueDate(_p.invchead_terms_id, _p.invchead_invcdate), _glDate, _p.invchead_terms_id,
+    round(_totalAmount, 2), 0, 
+    _p.invchead_salesrep_id, _commissionDue, FALSE,
+    _p.invchead_ordernumber::text, _p.invchead_notes, pInvcheadid,
+    _p.invchead_curr_id );
+
+-- Handle the Inventory and G/L Transactions for any billed Inventory where invcitem_updateinv is true
+  FOR _r IN SELECT itemsite_id AS itemsite_id, invcitem_id,
+                   (invcitem_billed * invcitem_qty_invuomratio) AS qty,
+                   invchead_invcnumber, invchead_cust_id AS cust_id, item_number,
+                   invchead_saletype_id AS saletype_id, invchead_shipzone_id AS shipzone_id,
+                   invchead_prj_id, itemsite_costmethod
+            FROM invchead JOIN invcitem ON ( (invcitem_invchead_id=invchead_id) AND
+                                             (invcitem_billed <> 0) AND
+                                             (invcitem_updateinv) )
+                          JOIN itemsite ON ( (itemsite_item_id=invcitem_item_id) AND
+                                             (itemsite_warehous_id=invcitem_warehous_id) )
+                          JOIN item ON (item_id=invcitem_item_id)
+            WHERE (invchead_id=pInvcheadid) LOOP
+
+--  Issue billed stock from inventory
+    IF (_itemlocSeries = 0) THEN
+      _itemlocSeries := NEXTVAL('itemloc_series_seq');
+    END IF;
+    IF (_r.itemsite_costmethod != 'J') THEN
+      SELECT postInvTrans(itemsite_id, 'SH', _r.qty,
+                         'S/O', 'IN', _r.invchead_invcnumber, '',
+                         ('Invoice Billed ' || _r.item_number),
+                         getPrjAccntId(_r.invchead_prj_id, resolveCOSAccount(itemsite_id, _r.cust_id, _r.saletype_id, _r.shipzone_id)),
+                         costcat_asset_accnt_id, _itemlocSeries, _glDate) INTO _invhistid
+      FROM itemsite, costcat
+      WHERE ( (itemsite_costcat_id=costcat_id)
+       AND (itemsite_id=_r.itemsite_id) );
+    ELSE
+      RAISE DEBUG 'postInvoice(%, %, %) tried to postInvTrans a %-costed item',
+                  pInvcheadid, pJournalNumber, pItemlocSeries,
+                  _r.itemsite_costmethod;
+    END IF;
+
+  END LOOP;
+
+--  Mark the invoice as posted
+  UPDATE invchead
+  SET invchead_posted=TRUE, invchead_gldistdate=_glDate
+  WHERE (invchead_id=pInvcheadid);
+  IF (_totalAmount > 0) THEN
+    -- get a list of allocated CMs
+    FOR _r IN SELECT aropen_id,
+                    CASE WHEN((aropen_amount - aropen_paid) >=
+                                aropenalloc_amount / (1 / aropen_curr_rate / 
+                                currRate(aropenalloc_curr_id,_firstExchDate))) THEN
+                             aropenalloc_amount / (1 / aropen_curr_rate / 
+                                currRate(aropenalloc_curr_id,_firstExchDate))
+                         ELSE (aropen_amount - aropen_paid)
+                    END AS balance,
+                    aropen_curr_id, aropen_curr_rate,
+                    aropenalloc_doctype, aropenalloc_doc_id
+                FROM aropenalloc, aropen
+               WHERE ( (aropenalloc_aropen_id=aropen_id)
+                 AND   ((aropenalloc_doctype='S' AND aropenalloc_doc_id=(SELECT cohead_id
+                                                                           FROM cohead
+                                                                          WHERE cohead_number=_p.invchead_ordernumber)) OR
+                        (aropenalloc_doctype='I' AND aropenalloc_doc_id=_p.invchead_id)) ) LOOP
+
+      _appliedAmount := _r.balance;
+      IF (_totalAmount < _appliedAmount / (1 / currRate(_r.aropen_curr_id,_firstExchDate) /
+                        _r.aropen_curr_rate)) THEN
+        _appliedAmount := _totalAmount;
+       _tmpCurrId := _p.invchead_curr_id;
+      ELSE
+       _tmpCurrId := _r.aropen_curr_id;
+      END IF;
+
+      -- ignore if no appliable balance
+      IF (_appliedAmount > 0) THEN
+        -- create an arcreditapply record linking the source c/m and the target invoice
+        -- for an amount that is equal to the balance on the invoice or the balance on
+        -- c/m whichever is greater.
+        INSERT INTO arcreditapply
+              (arcreditapply_source_aropen_id, arcreditapply_target_aropen_id,
+              arcreditapply_amount, arcreditapply_curr_id, arcreditapply_reftype, arcreditapply_ref_id)
+        VALUES(_r.aropen_id, _aropenid, _appliedAmount, _tmpCurrId, 'S',  _r.aropenalloc_doc_id);
+
+        -- call postARCreditMemoApplication(aropen_id of C/M)
+        SELECT postARCreditMemoApplication(_r.aropen_id) into _test;
+
+        -- if no error decrement the balance and contiue on
+        IF (_test >= 0) THEN
+          _totalAmount := _totalAmount - currToCurr(_tmpCurrId, _p.invchead_curr_id,
+                                                   _appliedAmount, _firstExchDate);
+        END IF;
+      END IF;
+    END LOOP;
+  END IF;
+
+  RETURN _itemlocSeries;
+
+END;
+$$ LANGUAGE plpgsql;
diff --git a/foundation-database/public/functions/postinvoices.sql b/foundation-database/public/functions/postinvoices.sql
new file mode 100644 (file)
index 0000000..0740618
--- /dev/null
@@ -0,0 +1,81 @@
+CREATE OR REPLACE FUNCTION postInvoices(BOOLEAN) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pPostUnprinted ALIAS FOR $1;
+BEGIN
+  RETURN postInvoices(pPostUnprinted, FALSE);
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION postInvoices(BOOLEAN, BOOLEAN) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pPostUnprinted ALIAS FOR $1;
+  pInclZeros     ALIAS FOR $2;
+BEGIN
+  RETURN postInvoices(pPostUnprinted, pInclZeros, fetchJournalNumber('AR-IN'));
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION postInvoices(BOOLEAN, BOOLEAN, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pPostUnprinted ALIAS FOR $1;
+  pInclZeros     ALIAS FOR $2;
+  pJournalNumber ALIAS FOR $3;
+  _invcheadid    INTEGER;
+  _itemlocSeries INTEGER;
+  _counter INTEGER;
+  _r RECORD;
+
+BEGIN
+
+  _itemlocSeries := 0;
+
+  IF (pInclZeros) THEN
+
+    FOR _invcheadid IN
+      SELECT invchead_id
+      FROM invchead
+      WHERE ( (NOT invchead_posted)
+       AND (checkInvoiceSitePrivs(invchead_id))
+       AND (pPostUnprinted OR invchead_printed) ) LOOP
+
+      SELECT postInvoice(_invcheadid, pJournalNumber, _itemlocSeries) INTO _itemlocSeries;
+      IF (_itemlocSeries < 0) THEN
+        RETURN _itemlocSeries;
+      END IF;
+    END LOOP;
+
+  ELSE
+
+    FOR _invcheadid IN
+      SELECT invchead_id
+      FROM invchead LEFT OUTER JOIN invcitem ON (invchead_id=invcitem_invchead_id)
+                    LEFT OUTER JOIN item ON (invcitem_item_id=item_id)  
+      WHERE((NOT invchead_posted)
+        AND (checkInvoiceSitePrivs(invchead_id))
+        AND (pPostUnprinted OR invchead_printed))
+      GROUP BY invchead_id, invchead_freight, invchead_misc_amount
+      HAVING (COALESCE(SUM(round((invcitem_billed * invcitem_qty_invuomratio) * (invcitem_price /  
+              CASE WHEN (item_id IS NULL) THEN 1 
+              ELSE invcitem_price_invuomratio END), 2)),0)
+             + invchead_freight + invchead_misc_amount) > 0 LOOP
+
+      SELECT postInvoice(_invcheadid, pJournalNumber, _itemlocSeries) INTO _itemlocSeries;
+      IF (_itemlocSeries < 0) THEN
+        RETURN _itemlocSeries;
+      END IF;
+    END LOOP;
+
+  END IF;
+
+  RETURN _itemlocSeries;
+
+END;
+$$ LANGUAGE plpgsql;
diff --git a/foundation-database/public/functions/postinvtrans.sql b/foundation-database/public/functions/postinvtrans.sql
new file mode 100644 (file)
index 0000000..975b78a
--- /dev/null
@@ -0,0 +1,228 @@
+CREATE OR REPLACE FUNCTION postInvTrans(pItemsiteId    INTEGER,
+                                        pTransType     TEXT,
+                                        pQty           NUMERIC,
+                                        pModule        TEXT,
+                                        pOrderType     TEXT,
+                                        pOrderNumber   TEXT,
+                                        pDocNumber     TEXT,
+                                        pComments      TEXT,
+                                        pDebitid       INTEGER,
+                                        pCreditid      INTEGER,
+                                        pItemlocSeries INTEGER,
+                                        pTimestamp     TIMESTAMP WITH TIME ZONE
+                                                       DEFAULT CURRENT_TIMESTAMP,
+                                        pCostOvrld     NUMERIC DEFAULT NULL,
+                                        pInvhistid     INTEGER DEFAULT NULL)
+  RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+-- pInvhistid is the original transaction to be returned, reversed, etc.
+DECLARE
+  _creditid         INTEGER;
+  _debitid          INTEGER;
+  _glreturn         INTEGER;
+  _invhistid        INTEGER;
+  _itemlocdistid     INTEGER;
+  _r                RECORD;
+  _sense            INTEGER;  -- direction in which to adjust inventory QOH
+  _t                RECORD;
+  _timestamp         TIMESTAMP WITH TIME ZONE;
+  _xferwhsid        INTEGER;
+
+BEGIN
+
+  --  Cache item and itemsite info  
+  SELECT CASE WHEN(itemsite_costmethod IN ('A','J')) THEN COALESCE(abs(pCostOvrld / pQty), avgcost(itemsite_id))
+              ELSE stdCost(itemsite_item_id)
+         END AS cost,
+         itemsite_costmethod,
+         itemsite_qtyonhand,
+        itemsite_warehous_id,
+         ( (item_type = 'R') OR (itemsite_controlmethod = 'N') ) AS nocontrol,
+         (itemsite_controlmethod IN ('L', 'S')) AS lotserial,
+         (itemsite_loccntrl) AS loccntrl,
+         itemsite_freeze AS frozen INTO _r
+  FROM itemsite JOIN item ON (item_id=itemsite_item_id)
+  WHERE (itemsite_id=pItemsiteid);
+
+  --Post the Inventory Transactions
+  IF (_r.nocontrol) THEN
+    RETURN -1; -- non-fatal error so dont throw an exception?
+  END IF;
+
+  IF (COALESCE(pItemlocSeries,0) = 0) THEN
+    RAISE EXCEPTION 'Transaction series must be provided';
+  END IF;
+
+  SELECT NEXTVAL('invhist_invhist_id_seq') INTO _invhistid;
+
+  IF ((pTimestamp IS NULL) OR (CAST(pTimestamp AS date)=CURRENT_DATE)) THEN
+    _timestamp := CURRENT_TIMESTAMP;
+  ELSE
+    _timestamp := pTimestamp;
+  END IF;
+
+  IF (pTransType = 'TS' OR pTransType = 'TR') THEN
+    SELECT * INTO _t FROM tohead WHERE (tohead_number=pDocNumber);
+    IF (pTransType = 'TS') THEN
+      _xferwhsid := CASE
+          WHEN (_t.tohead_src_warehous_id=_r.itemsite_warehous_id) THEN _t.tohead_trns_warehous_id
+          WHEN (_t.tohead_trns_warehous_id=_r.itemsite_warehous_id AND pComments ~* 'recall') THEN _t.tohead_src_warehous_id
+          WHEN (_t.tohead_trns_warehous_id=_r.itemsite_warehous_id) THEN _t.tohead_dest_warehous_id
+          WHEN (_t.tohead_dest_warehous_id=_r.itemsite_warehous_id) THEN _t.tohead_trns_warehous_id
+          ELSE NULL
+          END;
+    ELSIF (pTransType = 'TR') THEN
+      _xferwhsid := CASE
+          WHEN (_t.tohead_src_warehous_id=_r.itemsite_warehous_id) THEN _t.tohead_trns_warehous_id
+          WHEN (_t.tohead_trns_warehous_id=_r.itemsite_warehous_id AND pComments ~* 'recall') THEN _t.tohead_dest_warehous_id
+          WHEN (_t.tohead_trns_warehous_id=_r.itemsite_warehous_id) THEN _t.tohead_src_warehous_id
+          WHEN (_t.tohead_dest_warehous_id=_r.itemsite_warehous_id) THEN _t.tohead_trns_warehous_id
+          ELSE NULL
+          END;
+    END IF;
+  END IF;
+
+
+  -- increase inventory: AD RM RT RP RR RS RX RB TR
+  -- decrease inventory: IM IB IT SH SI EX RI
+  -- TS and TR are special: shipShipment and recallShipment should not change
+  -- QOH at the Transfer Order src whs (as this was done by issueToShipping)
+  -- but postReceipt should change QOH at the transit whs
+  IF (pTransType='TS') THEN
+    _sense := CASE WHEN (SELECT tohead_trns_warehous_id=_r.itemsite_warehous_id
+                         FROM tohead
+                         WHERE (tohead_number=pDocNumber)) THEN -1
+                         ELSE 0
+                         END;
+  ELSIF (pTransType='TR') THEN
+    _sense := CASE WHEN (SELECT tohead_src_warehous_id=_r.itemsite_warehous_id
+                         FROM tohead
+                         WHERE (tohead_number=pDocNumber)) THEN 0
+                         ELSE 1
+                         END;
+  ELSIF (pTransType IN ('IM', 'IB', 'IT', 'SH', 'SI', 'EX', 'RI')) THEN
+    _sense := -1;
+
+  ELSE
+    _sense := 1;
+  END IF;
+
+  IF((_r.itemsite_costmethod='A') AND (_r.itemsite_qtyonhand + round(_sense * pQty, 6)) < 0) THEN
+    -- Can not let average costed itemsites go negative
+    RAISE EXCEPTION 'This transaction will cause an Average Costed item to go negative which is not allowed [xtuple: postinvtrans, -2]';
+  END IF;
+
+  INSERT INTO invhist
+  ( invhist_id, invhist_itemsite_id, invhist_transtype, invhist_transdate,
+      invhist_invqty, invhist_qoh_before,
+      invhist_qoh_after,
+      invhist_costmethod, invhist_value_before, invhist_value_after,
+      invhist_ordtype, invhist_ordnumber, invhist_docnumber, invhist_comments,
+      invhist_invuom, invhist_unitcost, invhist_xfer_warehous_id, invhist_posted,
+      invhist_series )
+  SELECT
+    _invhistid, itemsite_id, pTransType, _timestamp,
+    pQty, itemsite_qtyonhand,
+    (itemsite_qtyonhand + (_sense * pQty)),
+    itemsite_costmethod, itemsite_value, itemsite_value + (_r.cost * _sense * pQty),
+    pOrderType, pOrderNumber, pDocNumber, pComments,
+    uom_name, _r.cost, _xferwhsid, FALSE, pItemlocSeries
+  FROM itemsite, item, uom
+  WHERE ( (itemsite_item_id=item_id)
+   AND (item_inv_uom_id=uom_id)
+   AND (itemsite_id=pItemsiteid) );
+
+  IF (pCreditid IN (SELECT accnt_id FROM accnt)) THEN
+    _creditid = pCreditid;
+  ELSE
+    SELECT warehous_default_accnt_id INTO _creditid
+    FROM itemsite, whsinfo
+    WHERE ( (itemsite_warehous_id=warehous_id)
+      AND  (itemsite_id=pItemsiteid) );
+  END IF;
+
+  IF (pDebitid IN (SELECT accnt_id FROM accnt)) THEN
+    _debitid = pDebitid;
+  ELSE
+    SELECT warehous_default_accnt_id INTO _debitid
+    FROM itemsite, whsinfo
+    WHERE ( (itemsite_warehous_id=warehous_id)
+      AND  (itemsite_id=pItemsiteid) );
+  END IF;
+
+  --  Post the G/L Transaction
+  IF (_creditid <> _debitid) THEN
+    SELECT insertGLTransaction(pModule, pOrderType, pOrderNumber, pComments,
+                               _creditid, _debitid, _invhistid,
+                               (_r.cost * pQty), _timestamp::DATE, FALSE) INTO _glreturn;
+  END IF;
+
+  --  Distribute this if this itemsite is controlled
+  IF ( _r.lotserial OR _r.loccntrl ) THEN
+
+    _itemlocdistid := nextval('itemlocdist_itemlocdist_id_seq');
+    INSERT INTO itemlocdist
+    ( itemlocdist_id,
+      itemlocdist_itemsite_id,
+      itemlocdist_source_type,
+      itemlocdist_reqlotserial,
+      itemlocdist_distlotserial,
+      itemlocdist_expiration,
+      itemlocdist_qty,
+      itemlocdist_series,
+      itemlocdist_invhist_id,
+      itemlocdist_order_type,
+      itemlocdist_order_id )
+    SELECT _itemlocdistid,
+           pItemsiteid,
+           'O',
+           (((pQty * _sense) > 0)  AND _r.lotserial),
+           ((pQty * _sense) < 0),
+           endOfTime(),
+           (_sense * pQty),
+           pItemlocSeries,
+           _invhistid,
+           pOrderType, 
+           CASE WHEN pOrderType='SO' THEN getSalesLineItemId(pOrderNumber)
+                ELSE NULL
+           END;
+
+    -- populate distributions if invhist_id parameter passed to undo
+    IF (pInvhistid IS NOT NULL) THEN
+      INSERT INTO itemlocdist
+        ( itemlocdist_itemlocdist_id, itemlocdist_source_type, itemlocdist_source_id,
+          itemlocdist_itemsite_id, itemlocdist_ls_id, itemlocdist_expiration,
+          itemlocdist_qty, itemlocdist_series, itemlocdist_invhist_id ) 
+      SELECT _itemlocdistid, 'L', COALESCE(invdetail_location_id, -1),
+             invhist_itemsite_id, invdetail_ls_id,  COALESCE(invdetail_expiration, endoftime()),
+             (invdetail_qty * -1.0), pItemlocSeries, _invhistid
+      FROM invhist JOIN invdetail ON (invdetail_invhist_id=invhist_id)
+      WHERE (invhist_id=pInvhistid);
+
+      IF ( _r.lotserial)  THEN          
+        INSERT INTO lsdetail 
+          ( lsdetail_itemsite_id, lsdetail_ls_id, lsdetail_created,
+            lsdetail_source_type, lsdetail_source_id, lsdetail_source_number ) 
+        SELECT invhist_itemsite_id, invdetail_ls_id, CURRENT_TIMESTAMP,
+               'I', _itemlocdistid, ''
+        FROM invhist JOIN invdetail ON (invdetail_invhist_id=invhist_id)
+        WHERE (invhist_id=pInvhistid);
+      END IF;
+
+      PERFORM distributeitemlocseries(pItemlocSeries);
+      
+    END IF;
+
+  END IF;   -- end of distributions
+
+  -- These records will be used for posting G/L transactions to trial balance after records committed.
+  -- If we try to do it now concurrency locking prevents any transactions while
+  -- user enters item distribution information.  Cant have that.
+  INSERT INTO itemlocpost ( itemlocpost_glseq, itemlocpost_itemlocseries)
+  VALUES ( _glreturn, pItemlocSeries );
+
+  RETURN _invhistid;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/postitemlocseries.sql b/foundation-database/public/functions/postitemlocseries.sql
new file mode 100644 (file)
index 0000000..c717a99
--- /dev/null
@@ -0,0 +1,32 @@
+CREATE OR REPLACE FUNCTION postItemlocSeries(INTEGER) RETURNS BOOLEAN AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemlocseries ALIAS FOR $1;
+  _result INTEGER;
+
+BEGIN
+
+  PERFORM postIntoTrialBalance(itemlocpost_glseq)
+  FROM (
+    SELECT DISTINCT itemlocpost_glseq, gltrans_accnt_id
+    FROM itemlocpost
+      JOIN gltrans ON (itemlocpost_glseq=gltrans_sequence)
+    WHERE (itemlocpost_itemlocseries=pItemlocseries)
+    ORDER BY gltrans_accnt_id
+  ) AS data;
+  
+  PERFORM postInvHist(invhist_id)
+  FROM invhist
+    JOIN itemsite ON (invhist_itemsite_id=itemsite_id)
+  WHERE ( (invhist_series=pItemlocseries)
+  AND ( NOT invhist_posted) 
+  AND ( NOT itemsite_freeze) );
+
+  DELETE FROM itemlocpost WHERE (itemlocpost_itemlocseries=pItemlocseries);
+
+  RETURN TRUE;
+  
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/postjournals.sql b/foundation-database/public/functions/postjournals.sql
new file mode 100644 (file)
index 0000000..a188cdd
--- /dev/null
@@ -0,0 +1,166 @@
+CREATE OR REPLACE FUNCTION postJournals(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSequence    ALIAS FOR $1;
+  _transCount INTEGER := 0;
+  _journalnumber INTEGER := fetchJournalNumber('J/P');
+  _sequence INTEGER := fetchGLSequence();
+  _sltrans RECORD;
+BEGIN
+
+--  Make sure that we balance
+  IF (SELECT SUM(sltrans_amount) != 0
+      FROM sltrans
+      WHERE ((NOT sltrans_posted )
+       AND (sltrans_sequence=pSequence))) THEN
+     RAISE EXCEPTION 'Can not post journals. Transactions do not balance in selected date range.';
+  END IF;
+
+--  March through the sltrans members, posting them one at a time
+  FOR _sltrans IN SELECT sltrans_source, sltrans_accnt_id,
+                          SUM(sltrans_amount) as amount
+                     FROM sltrans
+                    WHERE ((sltrans_amount<>0.0)
+                      AND  (NOT sltrans_posted)
+                      AND  (sltrans_sequence=pSequence))
+                    GROUP BY sltrans_source, sltrans_accnt_id LOOP
+
+-- refuse to accept postings into closed periods
+    IF (SELECT BOOL_AND(COALESCE(period_closed, FALSE))
+        FROM accnt LEFT OUTER JOIN
+             period ON (CURRENT_DATE BETWEEN period_start AND period_end)
+        WHERE (accnt_id = _sltrans.sltrans_accnt_id)) THEN
+      RAISE EXCEPTION 'Cannot post to closed period (%).', _sltrans.sltrans_distdate;
+      RETURN -4;        -- remove raise exception when all callers check return code
+    END IF;
+
+-- refuse to accept postings into frozen periods without proper priv
+    IF (SELECT NOT BOOL_AND(checkPrivilege('PostFrozenPeriod')) AND
+               BOOL_AND(COALESCE(period_freeze, FALSE))
+        FROM accnt LEFT OUTER JOIN
+             period ON (CURRENT_DATE BETWEEN period_start AND period_end)
+        WHERE (accnt_id = _sltrans.sltrans_accnt_id)) THEN
+      RAISE EXCEPTION 'Cannot post to frozen period (%).', _sltrans.sltrans_distdate;
+      RETURN -4;        -- remove raise exception when all callers check return code
+    END IF;
+
+    IF (_sltrans.amount != 0) THEN
+       INSERT INTO gltrans
+        ( gltrans_posted, gltrans_exported, gltrans_created, gltrans_date,
+          gltrans_sequence, gltrans_accnt_id, gltrans_source, gltrans_notes,
+          gltrans_doctype, gltrans_docnumber, gltrans_amount, gltrans_journalnumber, gltrans_rec )
+        VALUES
+        ( FALSE, FALSE, CURRENT_TIMESTAMP, CURRENT_DATE,
+          _sequence, _sltrans.sltrans_accnt_id, _sltrans.sltrans_source, 'Journal Posting',
+          'JP', _journalnumber, _sltrans.amount, _journalnumber, TRUE );
+      
+      _transCount := _transCount + 1;
+    END IF;
+  END LOOP;
+
+--  Update all of the posted sltrans members
+  UPDATE sltrans SET
+    sltrans_posted=true,
+    sltrans_gltrans_journalnumber=_journalnumber
+  WHERE ((NOT sltrans_posted)
+    AND (sltrans_sequence=pSequence));
+
+  PERFORM postIntoTrialBalance(_sequence);
+
+  RETURN _journalnumber;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION postJournals(TEXT[], DATE, DATE, DATE) RETURNS SETOF INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSources             ALIAS FOR $1;
+  pStartDate           ALIAS FOR $2;
+  pEndDate             ALIAS FOR $3;
+  pDistDate            ALIAS FOR $4;
+  _i INTEGER;
+BEGIN
+  FOR _i IN 1..ARRAY_UPPER(pSources,1)
+  LOOP
+    RETURN NEXT postJournals(pSources[_i], pStartDate, pEndDate, pDistDate);
+  END LOOP;
+  RETURN;
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION postJournals(TEXT, DATE, DATE, DATE) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSource              ALIAS FOR $1;
+  pStartDate           ALIAS FOR $2;
+  pEndDate             ALIAS FOR $3;
+  pDistDate            ALIAS FOR $4;
+  _transCount INTEGER := 0;
+  _journalnumber INTEGER := fetchJournalNumber('J/P');
+  _sequence INTEGER := fetchGLSequence();
+  _sltrans RECORD;
+BEGIN
+
+--  Make sure that we balance
+  IF (SELECT SUM(sltrans_amount) != 0
+      FROM sltrans
+      WHERE ((NOT sltrans_posted )
+       AND (sltrans_source=pSource)
+       AND (sltrans_date BETWEEN pStartDate AND pEndDate))) THEN
+     RAISE EXCEPTION 'Can not post journals. Transactions do not balance in selected date range.';
+  END IF;
+
+--  March through the sltrans members, posting them one at a time
+  FOR _sltrans IN SELECT sltrans_source, sltrans_accnt_id,
+                          SUM(sltrans_amount) as amount
+                     FROM sltrans
+                    WHERE ((sltrans_amount<>0.0)
+                      AND  (NOT sltrans_posted)
+                      AND  (sltrans_source=pSource)
+                      AND  (sltrans_date BETWEEN pStartDate AND pEndDate))
+                    GROUP BY sltrans_source, sltrans_accnt_id LOOP
+
+-- refuse to accept postings into frozen periods if any of the accounts disallow it
+    IF (SELECT NOT BOOL_AND(checkPrivilege('PostFrozenPeriod')) AND
+               BOOL_AND(COALESCE(period_freeze, FALSE))
+        FROM accnt LEFT OUTER JOIN
+             period ON (pDistDate BETWEEN period_start AND period_end)
+        WHERE (accnt_id = _sltrans.sltrans_accnt_id)) THEN
+      RAISE EXCEPTION 'Cannot post to frozen period (%).', _sltrans.sltrans_distdate;
+      RETURN -4;        -- remove raise exception when all callers check return code
+    END IF;
+
+    IF (_sltrans.amount != 0) THEN
+       INSERT INTO gltrans
+        ( gltrans_posted, gltrans_exported, gltrans_created, gltrans_date,
+          gltrans_sequence, gltrans_accnt_id, gltrans_source, gltrans_notes,
+          gltrans_doctype, gltrans_docnumber, gltrans_amount, gltrans_journalnumber )
+        VALUES
+        ( FALSE, FALSE, CURRENT_TIMESTAMP, pDistDate,
+          _sequence, _sltrans.sltrans_accnt_id, _sltrans.sltrans_source, 'Journal Posting',
+          'JP', _journalnumber, _sltrans.amount, _journalnumber );
+      
+      _transCount := _transCount + 1;
+    END IF;
+  END LOOP;
+
+--  Update all of the posted sltrans members
+  UPDATE sltrans SET
+    sltrans_posted=true,
+    sltrans_gltrans_journalnumber=_journalnumber
+  WHERE ((NOT sltrans_posted)
+    AND (sltrans_source=pSource)
+    AND (sltrans_date BETWEEN pStartDate AND pEndDate));
+
+  PERFORM postIntoTrialBalance(_sequence);
+
+  RETURN _journalnumber;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/postmessage.sql b/foundation-database/public/functions/postmessage.sql
new file mode 100644 (file)
index 0000000..bab6c20
--- /dev/null
@@ -0,0 +1,90 @@
+
+CREATE OR REPLACE FUNCTION postMessage(TIMESTAMP, TIMESTAMP, TEXT, TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pScheduled ALIAS FOR $1;
+  pExpires ALIAS FOR $2;
+  pUsername ALIAS FOR $3;
+  pText ALIAS FOR $4;
+  _msgid INTEGER;
+
+BEGIN
+
+  SELECT NEXTVAL('msg_msg_id_seq') INTO _msgid;
+  INSERT INTO msg
+  (msg_id, msg_posted, msg_scheduled, msg_expires, msg_username, msg_text)
+  VALUES
+  (_msgid, CURRENT_TIMESTAMP, pScheduled, pExpires, getEffectiveXtUser(), pText);
+
+  INSERT INTO msguser
+  ( msguser_msg_id, msguser_username )
+  VALUES
+  ( _msgid, pUsername );
+
+  NOTIFY "messagePosted";
+
+  RETURN _msgid;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION postMessage(TIMESTAMP, TIMESTAMP, TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pScheduled ALIAS FOR $1;
+  pExpires ALIAS FOR $2;
+  pText ALIAS FOR $3;
+  _msgid INTEGER;
+
+BEGIN
+
+  SELECT NEXTVAL('msg_msg_id_seq') INTO _msgid;
+  INSERT INTO msg
+  (msg_id, msg_posted, msg_scheduled, msg_expires, msg_username, msg_text)
+  VALUES
+  (_msgid, CURRENT_TIMESTAMP, pScheduled, pExpires, getEffectiveXtUser(), pText);
+
+  INSERT INTO msguser
+  ( msguser_msg_id, msguser_username )
+  SELECT _msgid, usr_username
+  FROM usr
+  WHERE (usr_username <> getEffectiveXtUser());
+
+  NOTIFY "messagePosted";
+
+  RETURN _msgid;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION postMessage(TEXT, TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pUsername ALIAS FOR $1;
+  pText ALIAS FOR $2;
+  _msgid INTEGER;
+
+BEGIN
+
+  SELECT NEXTVAL('msg_msg_id_seq') INTO _msgid;
+  INSERT INTO msg
+  (msg_id, msg_posted, msg_scheduled, msg_expires, msg_username, msg_text)
+  VALUES
+  (_msgid, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, endOfTime(), getEffectiveXtUser(), pText);
+
+  INSERT INTO msguser
+  ( msguser_msg_id, msguser_username )
+  VALUES
+  ( _msgid, pUsername );
+
+  NOTIFY "messagePosted";
+
+  RETURN _msgid;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/postmisccount.sql b/foundation-database/public/functions/postmisccount.sql
new file mode 100644 (file)
index 0000000..e397fae
--- /dev/null
@@ -0,0 +1,57 @@
+CREATE OR REPLACE FUNCTION postMiscCount(pItemsiteid INTEGER,
+                                         pQty NUMERIC,
+                                         pComments TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  pQty ALIAS FOR $2;
+  pComments ALIAS FOR $3;
+  _invcntid INTEGER;
+  _result INTEGER;
+
+BEGIN
+
+--  Make sure the passed itemsite points to a real item
+  IF ( ( SELECT (item_type IN ('R', 'F') OR itemsite_costmethod = 'J')
+         FROM itemsite, item
+         WHERE ( (itemsite_item_id=item_id)
+          AND (itemsite_id=pItemsiteid) ) ) ) THEN
+    RETURN 0;
+  END IF;
+
+  SELECT invcnt_id INTO _invcntid
+  FROM invcnt
+  WHERE ( (NOT invcnt_posted)
+   AND (invcnt_itemsite_id=pItemsiteid) );
+
+  IF (_invcntid IS NULL) THEN
+    _invcntid := NEXTVAL('invcnt_invcnt_id_seq');
+
+    INSERT INTO invcnt
+     ( invcnt_id, invcnt_itemsite_id, invcnt_tagdate,
+       invcnt_qoh_before, invcnt_qoh_after,
+       invcnt_tag_username, invcnt_cntdate, invcnt_cnt_username,
+       invcnt_postdate, invcnt_post_username, invcnt_posted,
+       invcnt_priority, invcnt_comments )
+    SELECT _invcntid, pItemsiteid, now(),
+           itemsite_qtyonhand, pQty,
+           getEffectiveXtUser(), now(), getEffectiveXtUser(),
+           now(), getEffectiveXtUser(), FALSE,
+           FALSE, pComments
+    FROM itemsite
+    WHERE (itemsite_id=pItemsiteid);
+
+    SELECT postCountTag(_invcntid, FALSE) INTO _result;
+    IF (_result < 0) THEN
+      DELETE FROM invcnt
+      WHERE (invcnt_id=_invcntid);
+    END IF;
+
+    RETURN _result;
+  ELSE
+    RETURN -2;
+  END IF;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/postpogltransactions.sql b/foundation-database/public/functions/postpogltransactions.sql
new file mode 100644 (file)
index 0000000..4888445
--- /dev/null
@@ -0,0 +1,15 @@
+CREATE OR REPLACE FUNCTION postPoGLTransactions() RETURNS BOOLEAN AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+
+  UPDATE gltrans
+  SET gltrans_exported=TRUE
+  WHERE ( (NOT gltrans_exported)
+   AND (gltrans_source=''A/P'')
+   AND (gltrans_doctype IN (''VO'')) );
+
+  RETURN TRUE;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/postporeceipt.sql b/foundation-database/public/functions/postporeceipt.sql
new file mode 100644 (file)
index 0000000..ada1659
--- /dev/null
@@ -0,0 +1,7 @@
+CREATE OR REPLACE FUNCTION postPoReceipt(INTEGER, INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN postReceipt($1, $2);
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/postporeceipts.sql b/foundation-database/public/functions/postporeceipts.sql
new file mode 100644 (file)
index 0000000..45e9ab8
--- /dev/null
@@ -0,0 +1,7 @@
+CREATE OR REPLACE FUNCTION postPoReceipts(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN postReceipts(''PO'', $1, NULL);
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/postporeturncreditmemo.sql b/foundation-database/public/functions/postporeturncreditmemo.sql
new file mode 100644 (file)
index 0000000..c61b6d9
--- /dev/null
@@ -0,0 +1,193 @@
+
+CREATE OR REPLACE FUNCTION postPoReturnCreditMemo(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pPorejectId ALIAS FOR $1;
+
+BEGIN
+  RETURN postPoReturnCreditMemo(pPorejectId, NULL);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION postPoReturnCreditMemo(INTEGER, NUMERIC) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pPorejectId ALIAS FOR $1;
+  pAmount ALIAS FOR $2;
+  _p RECORD;
+  _a RECORD;
+  _itemsiteId INTEGER;
+  _docNumber TEXT;
+  _sequence INTEGER;
+  _journalNumber INTEGER;
+  _apopenid INTEGER;
+  _exchGainItem NUMERIC;
+  _itemAmount_base NUMERIC;
+  _itemAmount NUMERIC;
+  _glseriesTotal NUMERIC;
+  _tmpTotal NUMERIC;
+  _test INTEGER;
+  _exchDate DATE;
+  _tax RECORD;
+  _taxAmount NUMERIC := 0;
+  _taxAmount_base NUMERIC;
+  _apaccntid INTEGER;
+
+BEGIN
+--Set things up
+  SELECT NEXTVAL('apopen_apopen_id_seq') INTO _apopenid;
+  SELECT fetchGLSequence() INTO _sequence;
+  SELECT fetchJournalNumber('AP-MISC') INTO _journalNumber;
+  SELECT fetchapmemonumber() INTO _docNumber;
+  _glseriesTotal := 0;
+
+--Get poreject data
+  SELECT pohead_vend_id, pohead_number, pohead_curr_id, pohead_orderdate, pohead_taxzone_id,
+         poitem_id, poitem_itemsite_id,poitem_expcat_id, poitem_taxtype_id,
+        itemsite_costcat_id, poreject_qty, poreject_date,
+        ('Return of Item ' || COALESCE(item_number,poitem_vend_item_number)
+           || ', qty. ' || formatqty(poreject_qty)) AS notes,
+        poreject_value AS value,
+        currToBase(pohead_curr_id,(poitem_unitprice * poreject_qty),CURRENT_DATE) AS itemAmount_base,
+        (poitem_unitprice * poreject_qty) AS itemAmount
+        INTO _p
+  FROM pohead, poreject, poitem
+        LEFT OUTER JOIN itemsite ON (poitem_itemsite_id=itemsite_id)
+        LEFT OUTER JOIN item ON (itemsite_item_id=item_id)
+  WHERE ((poreject_poitem_id=poitem_id)
+  AND (pohead_id=poitem_pohead_id)
+  AND (poreject_id=pPorejectId));
+
+  _itemAmount := _p.itemAmount;
+  _itemAmount_base := _p.itemAmount_base;
+  IF (pAmount IS NOT NULL) THEN
+    _itemAmount := pAmount;
+    _itemAmount_base := currToBase(_p.pohead_curr_id, pAmount, CURRENT_DATE);
+  END IF;
+  
+
+--  Grab the G/L Accounts
+  IF (COALESCE(_p.poitem_itemsite_id, -1) = -1) THEN
+    SELECT pp.accnt_id AS pp_accnt_id,
+           lb.accnt_id AS lb_accnt_id INTO _a
+    FROM expcat, accnt AS pp, accnt AS lb
+    WHERE ( (expcat_purchprice_accnt_id=pp.accnt_id)
+     AND (expcat_liability_accnt_id=lb.accnt_id)
+     AND (expcat_id=_p.poitem_expcat_id) );
+    IF (NOT FOUND) THEN
+      RAISE EXCEPTION 'Cannot Post Credit Memo due to unassigned G/L Accounts.';
+    END IF;
+  ELSE
+    SELECT pp.accnt_id AS pp_accnt_id,
+           lb.accnt_id AS lb_accnt_id INTO _a
+    FROM costcat, accnt AS pp, accnt AS lb
+    WHERE ( (costcat_purchprice_accnt_id=pp.accnt_id)
+     AND (costcat_liability_accnt_id=lb.accnt_id)
+     AND (costcat_id=_p.itemsite_costcat_id) );
+    IF (NOT FOUND) THEN
+      RAISE EXCEPTION 'Cannot Post Credit Memo due to unassigned G/L Accounts.';
+    END IF;
+  END IF;
+
+--  AP Open Item record
+    INSERT INTO apopen
+    ( apopen_id, apopen_username, apopen_journalnumber,
+      apopen_vend_id, apopen_docnumber, apopen_doctype, apopen_ponumber,
+      apopen_docdate, apopen_duedate, apopen_distdate, apopen_terms_id,
+      apopen_amount, apopen_paid, apopen_open, apopen_notes, apopen_accnt_id, apopen_curr_id,
+      apopen_closedate )
+    VALUES
+    ( _apopenid, getEffectiveXtUser(), _journalNumber,
+      _p.pohead_vend_id, _docNumber, 'C', _p.pohead_number,
+      CURRENT_DATE, CURRENT_DATE, CURRENT_DATE, -1,
+      round(_itemAmount, 2), 0, (round(_itemAmount, 2) <> 0), _p.notes, -1, _p.pohead_curr_id,
+      CASE WHEN (round(_itemAmount, 2) = 0) THEN _p.poreject_date END );
+
+-- Taxes
+    FOR _tax IN
+      SELECT taxdetail_tax_id, sum(taxdetail_tax) AS taxdetail_tax,
+        currToBase(_p.pohead_curr_id, round(sum(taxdetail_tax),2), current_date) AS taxbasevalue
+      FROM calculateTaxDetail(_p.pohead_taxzone_id, _p.poitem_taxtype_id,
+                              current_date, _p.pohead_curr_id, 
+                              _itemAmount) 
+      GROUP BY taxdetail_tax_id
+    LOOP
+      INSERT INTO apopentax (taxhist_basis,taxhist_percent,taxhist_amount,taxhist_docdate, taxhist_tax_id, taxhist_tax, 
+                             taxhist_taxtype_id, taxhist_parent_id, taxhist_journalnumber ) 
+      VALUES (0, 0, 0, current_date, _tax.taxdetail_tax_id, _tax.taxdetail_tax, getadjustmenttaxtypeid(), 
+              _apopenid, _journalNumber);
+
+      _taxAmount := _taxAmount + _tax.taxdetail_tax;
+    END LOOP;
+
+    _taxAmount_base := addTaxToGLSeries(_sequence,
+                      'A/P', 'CM', _docNumber,
+                      _p.pohead_curr_id, current_date, current_date,
+                      'apopentax', _apopenid,
+                      _p.notes);
+
+    UPDATE apopen SET apopen_amount = round(_itemAmount + _taxAmount,2)
+    WHERE (apopen_id = _apopenid);
+
+--  Distribute from the clearing account
+    PERFORM insertIntoGLSeries( _sequence, 'A/P', 'CM', _docNumber,
+                _a.lb_accnt_id,
+                round(_p.value, 2),
+                current_date, _p.notes );
+    _glseriesTotal := _glseriesTotal + round(_p.value, 2);
+
+--  Distribute the remaining variance to the Purchase Price Variance account
+    IF (round(_itemAmount_base, 2) <> round(_p.value, 2)) THEN
+      _tmpTotal := round(_itemAmount_base, 2) - round(_p.value, 2);
+      PERFORM insertIntoGLSeries( _sequence, 'A/P', 'CM', _docNumber,
+                                  _a.pp_accnt_id,
+                                  _tmpTotal,
+                                  current_date, _p.notes );
+        _glseriesTotal := _glseriesTotal + _tmpTotal;
+    END IF;
+
+--  Post the reject item for this P/O Item as Invoiced
+    UPDATE poreject
+    SET poreject_invoiced=TRUE
+    WHERE poreject_id=pPorejectId;
+
+--  Update the qty vouchered field
+    UPDATE poitem
+       SET poitem_qty_vouchered = (poitem_qty_vouchered - _p.poreject_qty)
+     WHERE (poitem_id=_p.poitem_id);
+
+--  Post to A/P
+  SELECT findAPAccount(_p.pohead_vend_id) INTO _apaccntid;
+  IF (NOT FOUND) THEN
+    RAISE EXCEPTION 'Cannot Post Credit Memo due to an unassigned A/P Account.';
+  END IF;
+
+  SELECT insertIntoGLSeries( _sequence, 'A/P', 'CM', _docNumber,
+                             _apaccntid, round(_itemAmount_base + _taxAmount_base, 2) *-1,
+                             current_date, _p.notes ) INTO _test;
+  IF (NOT FOUND) THEN
+    RAISE EXCEPTION 'Cannot Post Credit Memo.';
+  END IF;
+
+-- Clean up loose ends
+
+  _glseriesTotal := _glseriesTotal + round(_itemAmount_base, 2)*-1;
+
+  IF (round(_glseriesTotal, 2) != 0) THEN
+        PERFORM insertIntoGLSeries(_sequence, 'A/P', 'CM',
+            'Currency Exchange Rounding - ' || _docNumber,
+            getGainLossAccntId(_apaccntid), round(_glseriesTotal, 2) * -1,
+           current_date, _p.notes);
+  END IF;
+
+--  Post it all
+  PERFORM postGLSeries(_sequence, _journalNumber);
+
+  RETURN _journalNumber;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/postporeturns.sql b/foundation-database/public/functions/postporeturns.sql
new file mode 100644 (file)
index 0000000..ef855a4
--- /dev/null
@@ -0,0 +1,164 @@
+CREATE OR REPLACE FUNCTION postPoReturns(INTEGER, BOOLEAN) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pPoheadid ALIAS FOR $1;
+  pCreateMemo ALIAS FOR $2;
+  _itemlocSeries INTEGER;
+  _p RECORD;
+  _returnval   INTEGER;
+  _tmp        INTEGER;
+  _pricevar   NUMERIC := 0.00;
+  _invhistid           INTEGER;
+  _journalNumber INTEGER := fetchJournalNumber('GL-MISC');
+
+BEGIN
+
+  _itemlocSeries := 0;
+
+  FOR _p IN SELECT pohead_number, pohead_curr_id, poreject_id, poitem_prj_id,
+                  poreject_poitem_id, poitem_id, poitem_expcat_id, poitem_linenumber,
+                  currToBase(COALESCE(recv_purchcost_curr_id, pohead_curr_id),
+                              COALESCE(recv_purchcost, poitem_unitprice),
+                             pohead_orderdate) AS poitem_unitprice_base,
+                   COALESCE(itemsite_id, -1) AS itemsiteid, poitem_invvenduomratio,
+                   SUM(poreject_qty) AS totalqty,
+                   itemsite_item_id, itemsite_costmethod, itemsite_controlmethod, recv_date
+            FROM pohead JOIN poitem ON (poitem_pohead_id=pohead_id)
+                        JOIN poreject ON (poreject_poitem_id=poitem_id AND NOT poreject_posted) 
+                        LEFT OUTER JOIN itemsite ON (poitem_itemsite_id=itemsite_id)
+                        LEFT OUTER JOIN recv ON (recv_id=poreject_recv_id)
+            WHERE (pohead_id=pPoheadid)
+            GROUP BY poreject_id, pohead_number, poreject_poitem_id, poitem_id, poitem_prj_id,
+                    poitem_expcat_id, poitem_linenumber, poitem_unitprice, pohead_curr_id,
+                    pohead_orderdate, itemsite_id, poitem_invvenduomratio,
+                     itemsite_item_id, itemsite_costmethod, itemsite_controlmethod, recv_date,
+                     recv_purchcost_curr_id, recv_purchcost LOOP
+
+    IF (_p.itemsiteid = -1) THEN
+        SELECT insertGLTransaction( 'S/R', 'PO', _p.pohead_number, 'Return Non-Inventory to P/O',
+                                     expcat_liability_accnt_id, 
+                                     getPrjAccntId(_p.poitem_prj_id, expcat_exp_accnt_id), -1,
+                                     round(_p.poitem_unitprice_base * _p.totalqty * -1, 2),
+                                    CURRENT_DATE ) INTO _returnval
+        FROM expcat
+        WHERE (expcat_id=_p.poitem_expcat_id);
+
+        UPDATE poreject
+        SET poreject_posted=TRUE, poreject_value= round(_p.poitem_unitprice_base * _p.totalqty, 2)
+        WHERE (poreject_id=_p.poreject_id);
+
+    ELSEIF (_p.itemsite_controlmethod='N') THEN
+      SELECT insertGLTransaction('S/R', 'PO', _p.pohead_number, 'Return Non-Controlled Inventory from PO',
+                                 costcat_liability_accnt_id,
+                                 getPrjAccntId(_p.poitem_prj_id, costcat_exp_accnt_id), -1,
+                                 round((_p.poitem_unitprice_base * _p.totalqty * -1), 2),
+                                 CURRENT_DATE ) INTO _returnval
+      FROM itemsite, costcat
+      WHERE((itemsite_costcat_id=costcat_id)
+        AND (itemsite_id=_p.itemsiteid));
+      IF (_returnval = -3) THEN -- zero value transaction
+        _returnval := 0;
+      END IF;
+      UPDATE poreject
+      SET poreject_posted=TRUE, poreject_value= round(_p.poitem_unitprice_base * _p.totalqty, 2)
+      WHERE (poreject_id=_p.poreject_id);
+    ELSE
+      IF (_itemlocSeries = 0) THEN
+        SELECT NEXTVAL('itemloc_series_seq') INTO _itemlocSeries;
+      END IF;
+
+      SELECT postInvTrans( itemsite_id, 'RP', (_p.totalqty * _p.poitem_invvenduomratio * -1),
+                           'S/R', 'PO', (_p.pohead_number || '-' || _p.poitem_linenumber::TEXT), '', 'Return Inventory to P/O',
+                           costcat_asset_accnt_id, costcat_liability_accnt_id, _itemlocSeries, CURRENT_TIMESTAMP) INTO _returnval
+      FROM itemsite, costcat
+      WHERE ( (itemsite_costcat_id=costcat_id)
+       AND (itemsite_id=_p.itemsiteid) );
+
+      UPDATE poreject
+      SET poreject_posted=TRUE, poreject_value=(invhist_unitcost *_p.totalqty * _p.poitem_invvenduomratio)
+      FROM invhist
+      WHERE ((poreject_id=_p.poreject_id)
+      AND (invhist_id=_returnval));
+
+    END IF;
+
+    IF (_returnval < 0) THEN
+      RETURN _returnval;
+    END IF;
+
+
+    UPDATE poitem
+    SET poitem_qty_returned=(poitem_qty_returned + _p.totalqty),
+       poitem_status='O'
+    WHERE (poitem_id=_p.poitem_id);
+
+      IF (fetchMetricBool('RecordPPVonReceipt')) THEN -- If the 'Purchase Price Variance on Receipt' option is true
+         _invhistid := _returnval;
+         -- Find the difference in the purchase price value expected from the P/O and the value of the transaction
+         SELECT ((_p.poitem_unitprice_base * poitem_qty_returned) - (invhist_value_before - invhist_value_after)) INTO _pricevar
+         FROM invhist, poitem
+         WHERE ((invhist_id = _invhistid)
+           AND  (poitem_id=_p.poitem_id));
+
+         -- If difference exists then
+         IF (_pricevar <> 0.00) THEN
+           -- Record an additional GL Transaction for the purchase price variance
+           SELECT insertGLTransaction( _journalNumber,
+                'S/R', 'PO', _p.pohead_number,
+                                       'Purchase price variance adjusted for P/O ' || _p.pohead_number || ' for item ' || _p.poitem_linenumber::TEXT,
+                                       costcat_liability_accnt_id,
+                                       getPrjAccntId(_p.poitem_prj_id, costcat_purchprice_accnt_id), -1,
+                                       _pricevar,
+                                       CURRENT_DATE, false ) INTO _tmp
+           FROM itemsite, costcat, poitem
+           WHERE ((itemsite_costcat_id=costcat_id)
+              AND (itemsite_id=poitem_itemsite_id) );
+           IF (NOT FOUND) THEN
+             RAISE EXCEPTION 'Could not insert G/L transaction: no cost category found for itemsite_id %',
+             _p.itemsiteid;
+           ELSIF (_tmp < 0 AND _tmp != -3) THEN -- error but not 0-value transaction
+             RETURN _tmp;
+           ELSE
+             -- Posting to trial balance is deferred to prevent locking
+             INSERT INTO itemlocpost ( itemlocpost_glseq, itemlocpost_itemlocseries)
+             VALUES ( _tmp, _itemlocSeries );
+           END IF;
+         END IF;
+       END IF;
+
+    IF (pCreateMemo) THEN
+       SELECT postPoReturnCreditMemo(_p.poreject_id) INTO _returnval;
+    END IF;
+
+    IF (_returnval < 0) THEN
+      RETURN _returnval;
+    END IF;
+
+  END LOOP;
+
+  RETURN _itemlocSeries;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION postPoReturns(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pPoheadid ALIAS FOR $1;
+  _itemlocSeries INTEGER;
+  _p RECORD;
+  _returnval   INTEGER;
+
+BEGIN
+
+  _itemlocSeries := 0;
+
+  SELECT postPoReturns(pPoheadid,false) INTO _itemlocseries;
+
+  RETURN _itemlocSeries;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/postproduction.sql b/foundation-database/public/functions/postproduction.sql
new file mode 100644 (file)
index 0000000..c4bec75
--- /dev/null
@@ -0,0 +1,215 @@
+CREATE OR REPLACE FUNCTION postProduction(INTEGER, NUMERIC, BOOLEAN, INTEGER, TIMESTAMP WITH TIME ZONE) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWoid          ALIAS FOR $1;
+  pQty           ALIAS FOR $2;
+  pBackflush     ALIAS FOR $3;
+  pItemlocSeries ALIAS FOR $4;
+  pGlDistTS      ALIAS FOR $5;
+  _test          INTEGER;
+  _invhistid     INTEGER;
+  _itemlocSeries INTEGER;
+  _parentQty     NUMERIC;
+  _r             RECORD;
+  _sense         TEXT;
+  _wipPost       NUMERIC;
+  _woNumber      TEXT;
+  _ucost         NUMERIC;
+
+BEGIN
+
+  IF (pQty = 0) THEN
+    RETURN 0;
+  ELSIF (pQty > 0) THEN
+    _sense = 'from';
+  ELSE
+    _sense = 'to';
+  END IF;
+
+  IF ( ( SELECT wo_status
+         FROM wo
+         WHERE (wo_id=pWoid) ) NOT IN  ('R','E','I') ) THEN
+    RETURN -1;
+  END IF;
+
+--  Make sure that all Component Item Sites exist
+  SELECT bomitem_id INTO _test
+  FROM wo, bomitem, itemsite
+  WHERE ( (wo_itemsite_id=itemsite_id)
+   AND (itemsite_item_id=bomitem_parent_item_id)
+   AND (woEffectiveDate(wo_startdate) BETWEEN bomitem_effective AND (bomitem_expires - 1))
+   AND (wo_id=pWoid)
+   AND (bomitem_rev_id=wo_bom_rev_id)
+   AND (bomitem_item_id NOT IN
+        ( SELECT component.itemsite_item_id
+          FROM itemsite AS component, itemsite AS parent
+          WHERE ( (wo_itemsite_id=parent.itemsite_id)
+           AND (parent.itemsite_item_id=bomitem_parent_item_id)
+           AND (bomitem_item_id=component.itemsite_item_id)
+           AND (woEffectiveDate(wo_startdate) BETWEEN bomitem_effective AND (bomitem_expires - 1))
+           AND (bomitem_rev_id=wo_bom_rev_id)
+           AND (component.itemsite_active)
+           AND (component.itemsite_warehous_id=parent.itemsite_warehous_id) ) ) ) )
+  LIMIT 1;
+  IF (FOUND AND pBackflush) THEN
+    RETURN -2;
+  END IF;
+
+  SELECT formatWoNumber(pWoid) INTO _woNumber;
+
+  SELECT roundQty(item_fractional, pQty) INTO _parentQty
+  FROM wo, itemsite, item
+  WHERE ((wo_itemsite_id=itemsite_id)
+   AND (itemsite_item_id=item_id)
+   AND (wo_id=pWoid));
+
+--  Create the material receipt transaction
+  IF (pItemlocSeries = 0) THEN
+    SELECT NEXTVAL('itemloc_series_seq') INTO _itemlocSeries;
+  ELSE
+    _itemlocSeries = pItemlocSeries;
+  END IF;
+
+  IF (pBackflush) THEN
+    FOR _r IN SELECT womatl_id, womatl_qtyiss + 
+                    (CASE 
+                      WHEN (womatl_qtywipscrap >  ((womatl_qtyfxd + (_parentQty + wo_qtyrcv) * womatl_qtyper) * womatl_scrap)) THEN
+                         (womatl_qtyfxd + (_parentQty + wo_qtyrcv) * womatl_qtyper) * womatl_scrap
+                      ELSE 
+                        womatl_qtywipscrap 
+                     END) AS consumed,
+                    (womatl_qtyfxd + ((_parentQty + wo_qtyrcv) * womatl_qtyper)) * (1 + womatl_scrap) AS expected
+             FROM womatl, wo, itemsite, item
+             WHERE ((womatl_issuemethod IN ('L', 'M'))
+               AND  (womatl_wo_id=pWoid)
+               AND  (womatl_wo_id=wo_id)
+               AND  (womatl_itemsite_id=itemsite_id)
+               AND  (itemsite_item_id=item_id)) LOOP
+      -- Don't issue more than should have already been consumed at this point
+      IF (pQty > 0) THEN
+        IF (noNeg(_r.expected - _r.consumed) > 0) THEN
+          SELECT issueWoMaterial(_r.womatl_id, noNeg(_r.expected - _r.consumed), _itemlocSeries, pGlDistTS) INTO _itemlocSeries;
+        END IF;
+      ELSE
+        -- Used by postMiscProduction of disassembly
+        SELECT returnWoMaterial(_r.womatl_id, (_r.expected * -1.0), _itemlocSeries, CURRENT_TIMESTAMP, true) INTO _itemlocSeries;
+      END IF;
+
+      UPDATE womatl
+      SET womatl_issuemethod='L'
+      WHERE ( (womatl_issuemethod='M')
+       AND (womatl_id=_r.womatl_id) );
+
+    END LOOP;
+  END IF;
+
+  SELECT CASE WHEN (pQty < 0 AND itemsite_costmethod='S') THEN stdcost(itemsite_item_id) * pQty
+              WHEN (pQty < 0) THEN avgcost(itemsite_id) * pQty
+              WHEN (wo_cosmethod = 'D') THEN wo_wipvalue
+              ELSE  round((wo_wipvalue - (wo_postedvalue / wo_qtyord * (wo_qtyord -
+                    CASE WHEN (wo_qtyord < wo_qtyrcv + pQty) THEN wo_qtyord
+                         ELSE wo_qtyrcv + pQty
+                    END ))),2)
+         END INTO _wipPost
+  FROM wo
+    JOIN itemsite ON (wo_itemsite_id=itemsite_id)
+  WHERE (wo_id=pWoid);
+
+  SELECT postInvTrans( itemsite_id, 'RM', _parentQty,
+                       'W/O', 'WO', _woNumber, '', ('Receive Inventory ' || item_number || ' ' || _sense || ' Manufacturing'),
+                       costcat_asset_accnt_id, getPrjAccntId(wo_prj_id, costcat_wip_accnt_id), _itemlocSeries, pGlDistTS,
+                       -- the following is only actually used when the item is average or job costed
+                       _wipPost ) INTO _invhistid
+  FROM wo, itemsite, item, costcat
+  WHERE ( (wo_itemsite_id=itemsite_id)
+   AND (itemsite_item_id=item_id)
+   AND (itemsite_costcat_id=costcat_id)
+   AND (wo_id=pWoid) );
+
+  IF (pQty < 0 ) THEN
+    _wipPost := _wipPost * -1;
+  END IF;
+  
+--  Increase this W/O's received qty decrease its WIP value
+  UPDATE wo
+  SET wo_qtyrcv = (wo_qtyrcv + _parentQty),
+      wo_wipvalue = (wo_wipvalue - (CASE WHEN (itemsite_costmethod IN ('A','J'))
+                                               THEN _wipPost
+                                         WHEN (itemsite_costmethod='S')
+                                               THEN (stdcost(itemsite_item_id) * _parentQty)
+                                         ELSE 0.0  END))
+  FROM itemsite, item
+  WHERE ((wo_itemsite_id=itemsite_id)
+   AND (itemsite_item_id=item_id)
+   AND (wo_id=pWoid));
+
+--  ROB Increase this W/O's WIP value for custom costing
+  SELECT SUM(itemcost_stdcost * _parentQty) INTO _ucost 
+  FROM wo JOIN itemsite ON (itemsite_id=wo_itemsite_id)
+          JOIN itemcost ON (itemcost_item_id=itemsite_item_id)
+          JOIN costelem ON ((costelem_id=itemcost_costelem_id) AND
+                            (costelem_exp_accnt_id IS NOT NULL) AND
+                            (NOT costelem_sys))
+  WHERE (wo_id=pWoid);
+
+  UPDATE wo
+  SET wo_wipvalue = (wo_wipvalue + coalesce(_ucost,0))
+  WHERE (wo_id=pWoid);
+
+--  ROB Distribute to G/L - create Cost Variance, debit WIP
+  PERFORM insertGLTransaction( 'W/O', 'WO', _woNumber,
+                               ('Post Other Cost ' || item_number || ' ' || _sense || ' Manufacturing'),
+                               getPrjAccntId(wo_prj_id, costelem_exp_accnt_id), 
+                               getPrjAccntId(wo_prj_id,costcat_wip_accnt_id), _invhistid,
+                              (itemcost_stdcost * _parentQty),
+                                pGlDistTS::DATE )
+FROM wo, costelem, itemcost, costcat, itemsite, item
+WHERE 
+  ((wo_id=pWoid) AND
+  (wo_itemsite_id=itemsite_id) AND
+  (itemsite_item_id=item_id) AND
+  (costelem_id = itemcost_costelem_id) AND
+  (itemcost_item_id = itemsite_item_id) AND
+  (itemsite_costcat_id = costcat_id) AND
+  (costelem_exp_accnt_id) IS NOT NULL  AND 
+  (costelem_sys = false));
+--End
+
+
+--  Make sure the W/O is at issue status
+  UPDATE wo
+  SET wo_status='I'
+  WHERE (wo_id=pWoid);
+
+  RETURN _itemlocSeries;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION postProduction(INTEGER, NUMERIC, BOOLEAN, BOOLEAN) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RAISE NOTICE 'postProduction(INTEGER, NUMERIC, BOOLEAN, BOOLEAN) is deprecated. please use postProduction(INTEGER, NUMERIC, BOOLEAN, INTEGER, TIMESTAMP WITH TIME ZONE) instead';
+  RETURN postProduction($1, $2, $3, 0, now());
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION postProduction(INTEGER, NUMERIC, BOOLEAN, BOOLEAN, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RAISE NOTICE 'postProduction(INTEGER, NUMERIC, BOOLEAN, BOOLEAN, INTEGER) is deprecated. please use postProduction(INTEGER, NUMERIC, BOOLEAN, INTEGER, TIMESTAMP WITH TIME ZONE) instead';
+  RETURN postProduction($1, $2, $3, $5, now());
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION postProduction(INTEGER, NUMERIC, BOOLEAN, BOOLEAN, INTEGER, TEXT, TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RAISE NOTICE 'postProduction(INTEGER, NUMERIC, BOOLEAN, BOOLEAN, INTEGER, TEXT, TEXT) is deprecated. please use postProduction(INTEGER, NUMERIC, BOOLEAN, INTEGER, TIMESTAMP WITH TIME ZONE) instead';
+  RETURN postProduction($1, $2, $3, $5, now());
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/postreceipt.sql b/foundation-database/public/functions/postreceipt.sql
new file mode 100644 (file)
index 0000000..31d8a50
--- /dev/null
@@ -0,0 +1,593 @@
+-- Function: postreceipt(integer, integer)
+
+-- DROP FUNCTION postreceipt(integer, integer);
+
+CREATE OR REPLACE FUNCTION postreceipt(integer, integer)
+  RETURNS integer AS
+$BODY$
+-- Copyright (c) 1999-2011 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  precvid              ALIAS FOR $1;
+  _itemlocSeries       INTEGER := COALESCE($2, 0);
+  _freightAccnt                INTEGER;
+  _glDate              TIMESTAMP WITH TIME ZONE;
+  _o                   RECORD;
+  _ordertypeabbr       TEXT;
+  _r                   RECORD;
+  _ra                  RECORD;
+  _recvinvqty          NUMERIC := 0.00;
+  _recvvalue           NUMERIC := 0.00;
+  _pricevar            NUMERIC := 0.00;
+  _tmp                 INTEGER;
+  _toitemitemid                INTEGER;
+  _coheadid            INTEGER;
+  _coitemid            INTEGER;
+  _linenumber          INTEGER;
+  _invhistid           INTEGER;
+  _shipheadid          INTEGER;
+  _ship                BOOLEAN;
+  _i                   RECORD;
+
+BEGIN
+  SELECT recv_id, recv_order_type, recv_orderitem_id, recv_qty,
+        round(currToBase(recv_freight_curr_id, recv_freight, recv_date::DATE),
+              2) AS recv_freight_base,
+        recv_freight, recv_freight_curr_id, recv_date, recv_gldistdate,
+        itemsite_id, itemsite_item_id, item_inv_uom_id, itemsite_costmethod,
+         itemsite_controlmethod, vend_name, item_number, item_fractional
+        INTO _r
+  FROM recv LEFT OUTER JOIN itemsite ON (recv_itemsite_id=itemsite_id)
+            LEFT OUTER JOIN item ON (itemsite_item_id=item_id)
+            LEFT OUTER JOIN vendinfo ON (recv_vend_id=vend_id)
+  WHERE ((recv_id=precvid)
+    AND  (NOT recv_posted));
+
+  IF (NOT FOUND) THEN
+    IF (_itemlocSeries = 0) THEN
+      RETURN -10;
+    END IF;
+    RETURN _itemlocSeries;
+
+  ELSEIF (_r.recv_qty <= 0) THEN
+    RETURN -11;
+
+  ELSIF (_r.recv_order_type ='PO') THEN
+    _ordertypeabbr := ('P/O for ' || _r.vend_name || ' for item ' || _r.item_number);
+
+    SELECT pohead_number AS orderhead_number, poitem_id AS orderitem_id,
+           poitem_linenumber AS orderitem_linenumber,
+          currToBase(pohead_curr_id,
+                      COALESCE(recv_purchcost, poitem_unitprice),
+                     recv_date::DATE) AS item_unitprice_base,
+          poitem_invvenduomratio AS invvenduomratio,
+          pohead_orderdate AS orderdate, pohead_dropship,
+          poitem_prj_id AS prj_id INTO _o
+    FROM recv, pohead, poitem
+    WHERE ((recv_orderitem_id=poitem_id)
+      AND  (poitem_pohead_id=pohead_id)
+      AND  (NOT recv_posted)
+      AND  (recv_id=precvid));
+  ELSIF (_r.recv_order_type ='RA') THEN
+    _ordertypeabbr := 'R/A for item ' || _r.item_number;
+    
+    SELECT rahead_id AS orderhead_id, rahead_number AS orderhead_number, raitem_id AS orderitem_id,
+           raitem_linenumber AS orderitem_linenumber,
+          currToBase(rahead_curr_id, raitem_unitprice,
+                   recv_date::DATE) AS item_unitprice_base,
+          raitem_qty_invuomratio AS invvenduomratio,
+          rahead_authdate AS orderdate,
+          raitem_unitcost AS unitcost,
+          rahead_prj_id AS prj_id INTO _o
+    FROM recv, rahead, raitem
+    WHERE ((recv_orderitem_id=raitem_id)
+      AND  (raitem_rahead_id=rahead_id)
+      AND  (NOT recv_posted)
+      AND  (recv_id=precvid));
+  ELSIF (_r.recv_order_type ='TO') THEN
+     _ordertypeabbr := 'T/O for item ' || _r.item_number;
+
+    SELECT tohead_number AS orderhead_number, toitem_id AS orderitem_id,
+           toitem_linenumber AS orderitem_linenumber,
+          toitem_stdcost AS item_unitprice_base,
+          1.0 AS invvenduomratio,
+          tohead_orderdate AS orderdate,
+          NULL AS prj_id INTO _o
+    FROM recv, tohead, toitem
+    WHERE ((recv_orderitem_id=toitem_id)
+      AND  (toitem_tohead_id=tohead_id)
+      AND  (NOT recv_posted)
+      AND  (recv_id=precvid));
+  ELSE
+    RETURN -13;        -- don't know how to handle this order type
+  END IF;
+
+  IF (NOT FOUND) THEN
+    IF (_itemlocSeries = 0) THEN
+      RETURN -10;
+    END IF;
+    RETURN _itemlocSeries;
+  END IF;
+
+  IF (_itemlocSeries = 0) THEN
+    _itemlocSeries := NEXTVAL('itemloc_series_seq');
+  ELSEIF (_itemlocSeries < 0) THEN
+    RETURN _itemlocSeries;
+  END IF;
+
+  _glDate := COALESCE(_r.recv_gldistdate, _r.recv_date);
+  _recvinvqty := roundQty(_r.item_fractional, (_r.recv_qty * _o.invvenduomratio));
+
+  IF ( (_r.recv_order_type = 'PO') AND
+        (_r.itemsite_id = -1 OR _r.itemsite_id IS NULL OR _r.itemsite_controlmethod = 'N') ) THEN
+
+    IF (_r.itemsite_id IS NOT NULL) THEN
+      SELECT insertGLTransaction( fetchJournalNumber('GL-MISC'), 
+                                 'S/R', _r.recv_order_type, _o.orderhead_number,
+                                 'Receive Non-Controlled Inventory from ' || _ordertypeabbr,
+                                  costcat_liability_accnt_id,
+                                  getPrjAccntId(_o.prj_id, costcat_exp_accnt_id), -1,
+                                  round((_o.item_unitprice_base * _r.recv_qty),2),
+                                  _glDate::DATE, false ) INTO _tmp
+      FROM poitem, itemsite, costcat
+      WHERE((poitem_itemsite_id=itemsite_id)
+        AND (itemsite_costcat_id=costcat_id)
+        AND (poitem_id=_o.orderitem_id));
+    ELSE
+      SELECT insertGLTransaction(fetchJournalNumber('GL-MISC'),
+                                 'S/R', _r.recv_order_type, _o.orderhead_number,
+                                 'Receive Non-Inventory from ' || 'P/O for ' || _r.vend_name || ' for ' || expcat_code,
+                                  expcat_liability_accnt_id,
+                                  getPrjAccntId(_o.prj_id, expcat_exp_accnt_id), -1,
+                                  round((_o.item_unitprice_base * _r.recv_qty),2),
+                                  _glDate::DATE, false ) INTO _tmp
+      FROM poitem, expcat
+      WHERE((poitem_expcat_id=expcat_id)
+        AND (poitem_id=_o.orderitem_id));
+    END IF;
+      
+
+    IF (_tmp < 0 AND _tmp != -3) THEN -- error but not 0-value transaction
+      RETURN _tmp;
+    ELSE
+      -- Posting to trial balance is deferred to prevent locking
+      INSERT INTO itemlocpost ( itemlocpost_glseq, itemlocpost_itemlocseries)
+      VALUES ( _tmp, _itemlocSeries );
+      
+    END IF;
+
+    SELECT insertGLTransaction( fetchJournalNumber('GL-MISC'),
+                               'S/R', _r.recv_order_type, _o.orderhead_number,
+                               'Receive Non-Inventory Freight from ' || _ordertypeabbr,
+                                expcat_liability_accnt_id,
+                                getPrjAccntId(_o.prj_id, expcat_freight_accnt_id), -1,
+                                _r.recv_freight_base,
+                                _glDate::DATE, false ),
+          expcat_freight_accnt_id INTO _tmp, _freightAccnt
+    FROM poitem, expcat
+    WHERE((poitem_expcat_id=expcat_id)
+      AND (poitem_id=_o.orderitem_id));
+
+    IF (_tmp < 0 AND _tmp != -3) THEN -- error but not 0-value transaction
+      RETURN _tmp;
+    ELSE
+      -- Posting to trial balance is deferred to prevent locking
+      INSERT INTO itemlocpost ( itemlocpost_glseq, itemlocpost_itemlocseries)
+      VALUES ( _tmp, _itemlocSeries );
+    END IF;
+
+    _recvvalue := ROUND((_o.item_unitprice_base * _r.recv_qty),2);
+
+    UPDATE poitem
+    SET poitem_qty_received = (poitem_qty_received + _r.recv_qty),
+       poitem_freight_received = (poitem_freight_received + _r.recv_freight_base)
+    WHERE (poitem_id=_o.orderitem_id);
+
+  ELSEIF ( (_r.recv_order_type = 'RA') AND
+           (_r.itemsite_id = -1 OR _r.itemsite_id IS NULL OR _r.itemsite_controlmethod = 'N') ) THEN
+    RAISE NOTICE 'itemsite controlmethod is %, cannot post receipt.', _r.itemsite_controlmethod;
+    RETURN -14;        -- otherwise how do we get the accounts?
+
+  ELSEIF ( (_r.recv_order_type = 'TO') AND
+           (_r.itemsite_id = -1 OR _r.itemsite_id IS NULL) ) THEN
+    RAISE NOTICE 'itemsite missing';
+    RETURN -14;        -- otherwise how do we get the accounts?
+
+  ELSE -- not ELSIF: some code is shared between diff order types
+    IF (_r.recv_order_type = 'PO') THEN
+      SELECT postInvTrans( itemsite_id, 'RP'::TEXT,
+                          _recvinvqty,
+                          'S/R'::TEXT,
+                          _r.recv_order_type::TEXT, _o.orderhead_number::TEXT || '-' || _o.orderitem_linenumber::TEXT,
+                          ''::TEXT,
+                          'Receive Inventory from ' || _ordertypeabbr,
+                          costcat_asset_accnt_id, costcat_liability_accnt_id,
+                          _itemlocSeries,
+                          _glDate,
+                           round((_o.item_unitprice_base * _r.recv_qty),2) -- always passing this in since it is ignored if it is not average costed item
+                          ) INTO _tmp
+      FROM itemsite, costcat
+      WHERE ( (itemsite_costcat_id=costcat_id)
+       AND (itemsite_id=_r.itemsite_id) );
+      IF (NOT FOUND) THEN
+       RAISE EXCEPTION 'Could not post inventory transaction: no cost category found for itemsite_id %',
+         _r.itemsite_id;
+      ELSIF (_tmp < -1) THEN -- less than -1 because -1 means it is a none controlled item
+       IF(_tmp = -3) THEN
+         RETURN -12; -- The GL trans value was 0 which means we likely do not have a std cost
+       END IF;
+       RETURN _tmp;
+      END IF;
+
+      -- If the 'Purchase Price Variance on Receipt' option is true
+      IF (fetchMetricBool('RecordPPVonReceipt')) THEN
+        _invhistid := _tmp;
+        -- Find the difference in the purchase price value expected from the P/O and the value of the transaction
+        SELECT ((_o.item_unitprice_base * _r.recv_qty) - (invhist_value_after - invhist_value_before)) INTO _pricevar
+        FROM invhist
+        WHERE (invhist_id = _invhistid);
+
+        -- If difference exists then
+        IF (_pricevar <> 0.00) THEN
+          -- Record an additional GL Transaction for the purchase price variance
+          SELECT insertGLTransaction( fetchJournalNumber('GL-MISC'),
+                                      'S/R', _r.recv_order_type, _o.orderhead_number,
+                                      'Purchase price variance adjusted for P/O ' || _o.orderhead_number || ' for item ' || _r.item_number,
+                                      costcat_liability_accnt_id,
+                                      getPrjAccntId(_o.prj_id, costcat_purchprice_accnt_id), -1,
+                                      _pricevar,
+                                      _glDate::DATE, false ) INTO _tmp
+          FROM itemsite, costcat
+          WHERE ((itemsite_costcat_id=costcat_id)
+             AND (itemsite_id=_r.itemsite_id) );
+          IF (NOT FOUND) THEN
+            RAISE EXCEPTION 'Could not insert G/L transaction: no cost category found for itemsite_id %',
+            _r.itemsite_id;
+          ELSIF (_tmp < 0 AND _tmp != -3) THEN -- error but not 0-value transaction
+            RETURN _tmp;
+          ELSE
+            -- Posting to trial balance is deferred to prevent locking
+            INSERT INTO itemlocpost ( itemlocpost_glseq, itemlocpost_itemlocseries)
+            VALUES ( _tmp, _itemlocSeries );
+          END IF;
+        END IF;
+      END IF;
+
+      SELECT insertGLTransaction(fetchJournalNumber('GL-MISC'),
+                                 'S/R', _r.recv_order_type, _o.orderhead_number,
+                                 'Receive Inventory Freight from ' || _o.orderhead_number || ' for item ' || _r.item_number,
+                                  costcat_liability_accnt_id,
+                                  getPrjAccntId(_o.prj_id, costcat_freight_accnt_id), -1,
+                                  _r.recv_freight_base,
+                                  _glDate::DATE, false ),
+            costcat_freight_accnt_id INTO _tmp, _freightAccnt
+      FROM itemsite, costcat
+      WHERE ( (itemsite_costcat_id=costcat_id)
+       AND (itemsite_id=_r.itemsite_id) );
+      IF (NOT FOUND) THEN
+       RAISE EXCEPTION 'Could not insert G/L transaction: no cost category found for itemsite_id %',
+         _r.itemsite_id;
+      ELSIF (_tmp < 0 AND _tmp != -3) THEN -- error but not 0-value transaction
+       RETURN _tmp;
+      ELSE
+          -- Posting to trial balance is deferred to prevent locking
+          INSERT INTO itemlocpost ( itemlocpost_glseq, itemlocpost_itemlocseries)
+          VALUES ( _tmp, _itemlocSeries );
+      END IF;
+
+      UPDATE poitem
+      SET poitem_qty_received = (poitem_qty_received + _r.recv_qty),
+         poitem_freight_received = (poitem_freight_received + _r.recv_freight_base)
+      WHERE (poitem_id=_o.orderitem_id);
+
+    ELSIF (_r.recv_order_type = 'RA') THEN
+      SELECT rahead.*, raitem.* INTO _ra
+           FROM rahead, raitem
+        WHERE ((rahead_id=raitem_rahead_id)
+        AND  (raitem_id=_r.recv_orderitem_id));
+      SELECT postInvTrans(_r.itemsite_id, 'RR',
+                         _recvinvqty,
+                         'S/R',
+                         _r.recv_order_type, _ra.rahead_number::TEXT || '-' || _ra.raitem_linenumber::TEXT,
+                         '',
+                         'Receive Inventory from ' || _ordertypeabbr,
+                         costcat_asset_accnt_id,
+                          CASE WHEN(COALESCE(_ra.raitem_cos_accnt_id, -1) != -1) THEN 
+                                getPrjAccntId(_o.prj_id, _ra.raitem_cos_accnt_id)
+                               WHEN (_ra.raitem_warranty) THEN 
+                                getPrjAccntId(_o.prj_id, resolveCOWAccount(_r.itemsite_id, _ra.rahead_cust_id, _ra.rahead_saletype_id, _ra.rahead_shipzone_id))
+                              ELSE getPrjAccntId(_o.prj_id, resolveCORAccount(_r.itemsite_id, _ra.rahead_cust_id, _ra.rahead_saletype_id, _ra.rahead_shipzone_id))
+                         END,
+                         _itemlocSeries, _glDate, COALESCE(_o.unitcost,stdcost(itemsite_item_id)) * _recvinvqty) INTO _tmp
+      FROM itemsite, costcat
+      WHERE ( (itemsite_costcat_id=costcat_id)
+       AND (itemsite_id=_r.itemsite_id) );
+
+      IF (NOT FOUND) THEN
+           RAISE EXCEPTION 'Could not post inventory transaction: no cost category found for itemsite_id %', _r.itemsite_id;
+      ELSIF (_tmp < -1) THEN -- less than -1 because -1 means it is a none controlled item
+           IF(_tmp = -3) THEN
+           RAISE NOTICE 'The GL trans value was 0 which means we likely do not have a std cost';
+           RETURN -12; -- The GL trans value was 0 which means we likely do not have a std cost
+           END IF;
+      RETURN _tmp;
+      END IF;
+
+      INSERT INTO rahist (rahist_itemsite_id, rahist_date,
+                         rahist_descrip,
+                         rahist_qty, rahist_uom_id,
+                         rahist_source, rahist_source_id, rahist_rahead_id
+         ) VALUES (_r.itemsite_id, _glDate,
+                     'Receive Inventory from ' || _ordertypeabbr,
+                     _recvinvqty, _r.item_inv_uom_id,
+                     'RR', _r.recv_id, _ra.rahead_id
+                 );
+
+      SELECT insertGLTransaction(fetchJournalNumber('GL-MISC'),
+                                 'S/R', _r.recv_order_type, _o.orderhead_number,
+                                 'Receive Inventory Freight from ' || _o.orderhead_number || ' for item ' || _r.item_number,
+                                  costcat_liability_accnt_id,
+                                  getPrjAccntId(_o.prj_id, costcat_freight_accnt_id), -1,
+                                  _r.recv_freight_base,
+                                  _glDate::DATE, false ),
+            costcat_freight_accnt_id INTO _tmp, _freightAccnt
+      FROM itemsite, costcat
+      WHERE ( (itemsite_costcat_id=costcat_id)
+       AND (itemsite_id=_r.itemsite_id) );
+      IF (_tmp < 0 AND _tmp != -3) THEN -- error but not 0-value transaction
+           RETURN _tmp;
+      ELSE
+        -- Posting to trial balance is deferred to prevent locking
+        INSERT INTO itemlocpost ( itemlocpost_glseq, itemlocpost_itemlocseries)
+        VALUES ( _tmp, _itemlocSeries );
+      END IF;
+
+      INSERT INTO rahist (rahist_date, rahist_descrip,
+                         rahist_source, rahist_source_id,
+                         rahist_curr_id, rahist_amount,
+                         rahist_rahead_id
+         ) VALUES (_glDate, 'Receive Inventory Freight from ' || _ordertypeabbr,
+                 'RR', _r.recv_id, _r.recv_freight_curr_id, _r.recv_freight,
+                 _ra.rahead_id
+         );
+
+      UPDATE raitem
+      SET raitem_qtyreceived = (raitem_qtyreceived + _r.recv_qty)
+      WHERE (raitem_id=_o.orderitem_id);
+      
+-- Expire date doesn't mean anything once the RA is received 
+-- WARNING: INSERTING 'NULL' MIGHT CAUSE PROBLEMS!!
+      UPDATE rahead
+      SET rahead_expiredate = NULL
+      WHERE (rahead_id=_o.orderhead_id);
+
+--  Look for 'ship' lines
+    SELECT (count(*) > 0) INTO _ship
+    FROM raitem
+    WHERE ((raitem_disposition = 'S')
+     AND (raitem_new_coitem_id IS NULL)
+     AND (raitem_rahead_id=_ra.rahead_id));
+
+--  If receiving a qty on a shippable and upon receipt item, create coitem
+      IF ((_ra.rahead_timing='R') AND
+          (_ship OR (
+          (_ra.raitem_disposition IN ('P','V')) AND
+          (_ra.raitem_new_coitem_id IS NULL) AND
+          (_ra.raitem_qtyauthorized > 0)))) THEN
+
+          IF (_ra.rahead_new_cohead_id IS NOT NULL) THEN
+            _coheadid = _ra.rahead_new_cohead_id;
+          ELSE  
+--  No header, so create a Sales Order header first.
+            SELECT nextval('cohead_cohead_id_seq') INTO _coheadid;
+
+            INSERT INTO cohead (
+              cohead_id,cohead_number,cohead_cust_id,cohead_custponumber,
+              cohead_orderdate,cohead_salesrep_id,cohead_terms_id,
+              cohead_shipvia,cohead_shipto_id,cohead_shiptoname,
+              cohead_shiptoaddress1,cohead_shiptoaddress2,cohead_shiptoaddress3,
+              cohead_shiptocity,cohead_shiptostate,cohead_shiptozipcode,
+              cohead_shiptocountry,cohead_freight,cohead_shiptophone,
+              cohead_shipto_cntct_id, cohead_shipto_cntct_honorific,
+              cohead_shipto_cntct_first_name, cohead_shipto_cntct_middle,
+              cohead_shipto_cntct_last_name, cohead_shipto_cntct_suffix,
+              cohead_shipto_cntct_phone, cohead_shipto_cntct_title,
+              cohead_shipto_cntct_fax, cohead_shipto_cntct_email,
+              cohead_shipchrg_id, cohead_shipform_id,cohead_billtoname,
+              cohead_billtoaddress1,cohead_billtoaddress2,cohead_billtoaddress3,
+              cohead_billtocity,cohead_billtostate,cohead_billtozipcode,
+              cohead_billtocountry,cohead_misc_accnt_id,cohead_misc_descrip,
+              cohead_commission,cohead_holdtype,cohead_prj_id,cohead_shipcomplete,
+              cohead_curr_id,cohead_taxzone_id,cohead_saletype_id,cohead_shipzone_id)
+            SELECT _coheadid,fetchsonumber(),rahead_cust_id,rahead_custponumber,
+              current_date,rahead_salesrep_id,COALESCE(cohead_terms_id,cust_terms_id),
+              COALESCE(cohead_shipvia,cust_shipvia),rahead_shipto_id,rahead_shipto_name,
+              rahead_shipto_address1,rahead_shipto_address2,rahead_shipto_address3,
+              rahead_shipto_city,rahead_shipto_state,rahead_shipto_zipcode,
+              rahead_shipto_country,0,COALESCE(cohead_shiptophone,''),
+              cntct_id, cntct_honorific,
+              cntct_first_name, cntct_middle,
+              cntct_last_name, cntct_suffix,
+              cntct_phone, cntct_title,
+              cntct_fax, cntct_email,
+              COALESCE(cohead_shipchrg_id,cust_shipchrg_id),
+              COALESCE(cohead_shipform_id,cust_shipform_id),
+              rahead_billtoname,rahead_billtoaddress1,rahead_billtoaddress2,rahead_billtoaddress3,
+              rahead_billtocity,rahead_billtostate,rahead_billtozip,
+              rahead_billtocountry,NULL,'',rahead_commission, 'N', rahead_prj_id,
+              COALESCE(cohead_shipcomplete,
+                CASE WHEN cust_partialship THEN 
+                  false 
+                ELSE true
+                END),rahead_curr_id,rahead_taxzone_id,rahead_saletype_id,rahead_shipzone_id
+            FROM rahead
+              JOIN custinfo ON (rahead_cust_id=cust_id)
+              LEFT OUTER JOIN cohead ON (rahead_orig_cohead_id=cohead_id)
+              LEFT OUTER JOIN shiptoinfo ON (rahead_shipto_id=shipto_id)
+              LEFT OUTER JOIN cntct ON (shipto_cntct_id=cntct_id)
+            WHERE (rahead_id=_ra.rahead_id);
+
+            UPDATE rahead SET rahead_new_cohead_id=_coheadid WHERE rahead_id=_ra.rahead_id;
+            
+          END IF;
+                  
+-- Now enter the line item(s)
+        IF (_ra.raitem_disposition IN ('P','V')) AND
+           (_ra.raitem_new_coitem_id IS NULL) AND
+           (_ra.raitem_qtyauthorized > 0) THEN
+           
+          SELECT nextval('coitem_coitem_id_seq') INTO _coitemid;
+
+          SELECT COALESCE(MAX(coitem_linenumber),0)+1 INTO _linenumber
+          FROM coitem
+          WHERE (coitem_cohead_id=_coheadid);
+      
+          INSERT INTO coitem (
+            coitem_id,coitem_cohead_id,coitem_linenumber,coitem_itemsite_id,
+            coitem_status,coitem_scheddate,coitem_promdate, coitem_qtyord,
+            coitem_unitcost,coitem_price,coitem_custprice,coitem_qtyshipped,
+            coitem_order_id,coitem_memo,coitem_qtyreturned,
+            coitem_taxtype_id,coitem_qty_uom_id,coitem_qty_invuomratio,
+            coitem_price_uom_id,coitem_price_invuomratio,coitem_warranty,
+            coitem_cos_accnt_id,coitem_order_type, coitem_custpn)
+          SELECT _coitemid,_coheadid,_linenumber,_ra.raitem_coitem_itemsite_id,
+              'O',_ra.raitem_scheddate,_ra.raitem_scheddate,_ra.raitem_qtyauthorized,
+              stdcost(itemsite_item_id),COALESCE(_ra.raitem_saleprice,0),0,0,
+              -1,_ra.raitem_notes,0,
+              _ra.raitem_taxtype_id,_ra.raitem_qty_uom_id,_ra.raitem_qty_invuomratio,
+              _ra.raitem_price_uom_id,_ra.raitem_price_invuomratio,_ra.raitem_warranty,
+              _ra.raitem_cos_accnt_id,
+              CASE WHEN itemsite_createwo THEN 'W' ELSE NULL END, _ra.raitem_custpn
+          FROM itemsite
+          WHERE (itemsite_id=_ra.raitem_coitem_itemsite_id);
+
+          UPDATE raitem SET raitem_new_coitem_id=_coitemid WHERE (raitem_id=_ra.raitem_id);
+        END IF;
+        
+        -- Create items to ship that have no direct relation to receipts.
+        IF (_ship) THEN
+          FOR _i IN
+            SELECT raitem_id FROM raitem
+            WHERE ((raitem_rahead_id=_ra.rahead_id)
+              AND (raitem_disposition = 'S')
+              AND (raitem_new_coitem_id IS NULL))
+          LOOP
+
+            SELECT nextval('coitem_coitem_id_seq') INTO _coitemid;
+
+            SELECT COALESCE(MAX(coitem_linenumber),0)+1 INTO _linenumber
+              FROM coitem
+            WHERE (coitem_cohead_id=_coheadid);
+      
+            INSERT INTO coitem (
+              coitem_id,coitem_cohead_id,coitem_linenumber,coitem_itemsite_id,
+              coitem_status,coitem_scheddate,coitem_promdate, coitem_qtyord,
+              coitem_unitcost,coitem_price,coitem_custprice,coitem_qtyshipped,
+              coitem_order_id,coitem_memo,coitem_qtyreturned,
+              coitem_taxtype_id,coitem_qty_uom_id,coitem_qty_invuomratio,
+              coitem_price_uom_id,coitem_price_invuomratio,coitem_warranty,
+              coitem_cos_accnt_id,coitem_order_type,coitem_custpn)
+            SELECT _coitemid,_coheadid,_linenumber,raitem_coitem_itemsite_id,
+              'O',raitem_scheddate,raitem_scheddate,raitem_qtyauthorized,
+              stdcost(itemsite_item_id),COALESCE(raitem_saleprice,0),0,0,
+              -1,raitem_notes,0,
+              raitem_taxtype_id,raitem_qty_uom_id,raitem_qty_invuomratio,
+              raitem_price_uom_id,raitem_price_invuomratio,raitem_warranty,
+              raitem_cos_accnt_id,
+              CASE WHEN itemsite_createwo THEN 'W' ELSE NULL END,raitem_custpn
+            FROM raitem
+              JOIN itemsite ON (itemsite_id=raitem_itemsite_id)
+            WHERE (raitem_id=_i.raitem_id);
+                        
+            UPDATE raitem SET raitem_new_coitem_id=_coitemid WHERE (raitem_id=_i.raitem_id);
+
+          END LOOP;
+        END IF;
+      END IF;
+
+
+    ELSIF (_r.recv_order_type = 'TO' AND fetchMetricBool('MultiWhs')) THEN
+      SELECT interWarehouseTransfer(toitem_item_id, tohead_trns_warehous_id,
+            tohead_dest_warehous_id, _r.recv_qty, 
+            'TO', formatToNumber(toitem_id), 'Receive from Transit To Dest Warehouse', _itemlocSeries, _glDate ) INTO _tmp
+      FROM tohead, toitem
+      WHERE ((tohead_id=toitem_tohead_id)
+        AND  (toitem_id=_r.recv_orderitem_id));     
+
+      IF (_tmp < 0) THEN
+           RETURN _tmp;
+      END IF;
+
+      SELECT insertGLTransaction(fetchJournalNumber('GL-MISC'), 
+                                 'S/R', _r.recv_order_type, _o.orderhead_number,
+                                 'Receive Inventory Freight from ' || _o.orderhead_number || ' for item ' || _r.item_number,
+                                  costcat_toliability_accnt_id,
+                                  costcat_freight_accnt_id, -1,
+                                  _r.recv_freight_base,
+                                  _glDate::DATE, false ),
+            costcat_freight_accnt_id INTO _tmp, _freightAccnt
+      FROM itemsite, costcat
+      WHERE ( (itemsite_costcat_id=costcat_id)
+       AND (itemsite_id=_r.itemsite_id) );
+      IF (_tmp < 0 AND _tmp != -3) THEN -- error but not 0-value transaction
+           RETURN _tmp;
+      ELSE
+        -- Posting to trial balance is deferred to prevent locking
+        INSERT INTO itemlocpost ( itemlocpost_glseq, itemlocpost_itemlocseries)
+        VALUES ( _tmp, _itemlocSeries );
+      END IF;
+
+      UPDATE toitem
+      SET toitem_qty_received = (toitem_qty_received + _r.recv_qty),
+         toitem_freight_received = (toitem_freight_received +
+                                     currToCurr(_r.recv_freight_curr_id,
+                                                toitem_freight_curr_id,
+                                                _r.recv_freight, _glDate::DATE))
+      WHERE (toitem_id=_o.orderitem_id);
+
+    END IF;
+    IF(_r.itemsite_costmethod='A') THEN
+      _recvvalue := ROUND((_o.item_unitprice_base * _r.recv_qty),2);
+    ELSIF (fetchMetricBool('RecordPPVonReceipt')) THEN
+      _recvvalue := ROUND((_o.item_unitprice_base * _r.recv_qty), 2);
+    ELSE
+      _recvvalue := ROUND(stdcost(_r.itemsite_item_id) * _recvinvqty, 2);
+    END IF;
+  END IF;
+
+  UPDATE recv
+  SET recv_value=_recvvalue, recv_recvcost=_recvvalue / recv_qty, recv_posted=TRUE, recv_gldistdate=_glDate::DATE
+  WHERE (recv_id=precvid);
+  
+  IF (_r.recv_order_type = 'PO') THEN
+    -- If this is a drop-shipped PO, then Issue the item to Shipping and Ship the item
+    IF (_o.pohead_dropship = TRUE) THEN
+
+      -- Generate the PoItemDropShipped event
+      INSERT INTO evntlog
+      ( evntlog_evnttime, evntlog_username, evntlog_evnttype_id,
+        evntlog_ordtype, evntlog_ord_id, evntlog_warehous_id, 
+        evntlog_number )
+      SELECT
+        CURRENT_TIMESTAMP, evntnot_username, evnttype_id,
+        'P', _o.orderitem_id, evntnot_warehous_id,
+        (pohead_number || '-' || poitem_linenumber || ': ' || item_number)
+      FROM evntnot JOIN evnttype ON (evntnot_evnttype_id = evnttype_id)
+           JOIN itemsite ON (evntnot_warehous_id = itemsite_warehous_id) 
+           JOIN item ON (itemsite_item_id = item_id)
+           JOIN poitem ON (poitem_itemsite_id = itemsite_id)
+           JOIN pohead ON (poitem_pohead_id = pohead_id)
+      WHERE( (poitem_id = _o.orderitem_id)
+      AND (poitem_duedate <= (CURRENT_DATE + itemsite_eventfence))
+      AND (evnttype_name = 'PoItemDropShipped') );
+
+    END IF;
+  END IF;
+  RETURN _itemlocSeries;
+
+END;
+$BODY$
+  LANGUAGE plpgsql VOLATILE
+  COST 100;
+ALTER FUNCTION postreceipt(integer, integer)
+  OWNER TO admin;
diff --git a/foundation-database/public/functions/postreceipts.sql b/foundation-database/public/functions/postreceipts.sql
new file mode 100644 (file)
index 0000000..6f284ef
--- /dev/null
@@ -0,0 +1,44 @@
+CREATE OR REPLACE FUNCTION postReceipts(TEXT, INTEGER, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pordertype           ALIAS FOR $1;
+  porderid             ALIAS FOR $2;
+  _itemlocSeries       INTEGER := $3;
+  _qtyToRecv           NUMERIC;
+  _r                   RECORD;
+
+BEGIN
+
+  SELECT SUM(qtyToReceive(pordertype, recv_orderitem_id)) INTO _qtyToRecv
+  FROM recv, orderitem
+  WHERE ((recv_orderitem_id=orderitem_id)
+    AND  (recv_order_type=pordertype)
+    AND  (orderitem_orderhead_type=pordertype)
+    AND  (orderitem_orderhead_id=porderid));
+
+  IF (_qtyToRecv <= 0) THEN
+    RETURN -11;
+  END IF;
+
+  IF (_itemlocSeries IS NULL OR _itemlocSeries <= 0) THEN
+    _itemlocSeries := NEXTVAL('itemloc_series_seq');
+  END IF;
+
+  FOR _r IN SELECT postReceipt(recv_id, _itemlocSeries) AS postResult
+           FROM recv, orderitem
+           WHERE ((recv_orderitem_id=orderitem_id)
+             AND  (orderitem_orderhead_id=porderid)
+             AND  (orderitem_orderhead_type=pordertype)
+             AND  (NOT recv_posted)
+-- Check for multiple users receiving the same order
+              AND  (recv_trans_usr_name=getEffectiveXtUser())
+             AND  (recv_order_type=pordertype)) LOOP
+    IF (_r.postResult < 0 AND _r.postResult != -11) THEN
+      RETURN _r.postResult; -- fail on 1st error but ignore lines with qty == 0
+    END IF;
+  END LOOP;
+
+  RETURN _itemlocSeries;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/postsogltransactions.sql b/foundation-database/public/functions/postsogltransactions.sql
new file mode 100644 (file)
index 0000000..2db28c0
--- /dev/null
@@ -0,0 +1,15 @@
+CREATE OR REPLACE FUNCTION postSoGLTransactions() RETURNS BOOLEAN AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+
+  UPDATE gltrans
+  SET gltrans_exported=TRUE
+  WHERE ( (NOT gltrans_exported)
+   AND (gltrans_source=''A/R'')
+   AND (gltrans_doctype IN (''IN'', ''CM'')) );
+
+  RETURN TRUE;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/postsoitemproduction.sql b/foundation-database/public/functions/postsoitemproduction.sql
new file mode 100644 (file)
index 0000000..1ee42dc
--- /dev/null
@@ -0,0 +1,60 @@
+CREATE OR REPLACE FUNCTION postSoItemProduction(INTEGER, TIMESTAMP WITH TIME ZONE) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSoitemId      ALIAS FOR $1;
+  pGlDistTS      ALIAS FOR $2;
+  _qty NUMERIC;
+  
+BEGIN
+  -- Issuing all, so determine line balance
+  SELECT noNeg( coitem_qtyord - coitem_qtyshipped + coitem_qtyreturned - 
+              ( SELECT COALESCE(SUM(shipitem_qty), 0)
+                FROM shipitem, shiphead
+                WHERE ((shipitem_orderitem_id=coitem_id)
+                  AND  (shipitem_shiphead_id=shiphead_id)
+                  AND  (NOT shiphead_shipped) ) ) ) INTO _qty
+  FROM coitem
+  WHERE (coitem_id=pSoitemId);
+
+  RETURN postSoItemProduction($1, _qty, $2);
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION postSoItemProduction(INTEGER, NUMERIC, TIMESTAMP WITH TIME ZONE) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSoitemId ALIAS FOR $1;
+  pQty ALIAS FOR $2;
+  pGlDistTS ALIAS FOR $3;
+  _itemlocSeries INTEGER := 0;
+  
+BEGIN
+  --If this cost method is not Job then we are using the wrong function
+  IF (NOT EXISTS(SELECT itemsite_costmethod
+             FROM coitem,itemsite
+             WHERE ((coitem_id=pSoitemId)
+                AND (coitem_itemsite_id=itemsite_id)
+                AND (itemsite_costmethod = 'J')))) THEN
+    RAISE EXCEPTION 'The postSoLineBalanceProduction function may only be used with Job costed item sites';
+  END IF;
+
+  IF (pQty > 0) THEN
+    SELECT COALESCE(postProduction(wo_id, (pQty * coitem_qty_invuomratio), true, 0, pGlDistTS),-1) INTO _itemlocSeries
+    FROM wo, coitem
+    WHERE ((wo_ordid=pSoItemid)
+     AND (wo_ordtype='S')
+     AND (coitem_id=pSoItemid));
+    
+    UPDATE wo SET wo_status = 'C'
+    WHERE ((wo_ordid=pSoItemid)
+     AND (wo_ordtype='S')
+     AND (wo_qtyrcv >= wo_qtyord));
+  END IF;
+
+  RETURN _itemlocSeries;
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/poststandardjournal.sql b/foundation-database/public/functions/poststandardjournal.sql
new file mode 100644 (file)
index 0000000..752abe7
--- /dev/null
@@ -0,0 +1,78 @@
+
+CREATE OR REPLACE FUNCTION postStandardJournal(INTEGER, DATE) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pStdjrnlid ALIAS FOR $1;
+  pDate ALIAS FOR $2;
+  _returnValue INTEGER;
+
+BEGIN
+
+  SELECT postStandardJournal(pStdjrnlid, pDate, FALSE, fetchGLSequence()) INTO _returnValue;
+
+  RETURN _returnValue;
+
+END;
+' LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION postStandardJournal(INTEGER, DATE, BOOLEAN) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pStdjrnlid ALIAS FOR $1;
+  pDate ALIAS FOR $2;
+  pReverse ALIAS FOR $3;
+  _returnValue INTEGER;
+
+BEGIN
+
+  RETURN postStandardJournal(pStdjrnlid, pDate, pReverse, fetchGLSequence());
+
+END;
+' LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION postStandardJournal(INTEGER, DATE, INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pStdjrnlid ALIAS FOR $1;
+  pDate ALIAS FOR $2;
+  pGlSequence ALIAS FOR $3;
+
+BEGIN
+
+  RETURN postStandardJournal(pStdjrnlid, pDate, FALSE, pGLSequence);
+
+END;
+' LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION postStandardJournal(INTEGER, DATE, BOOLEAN, INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pStdjrnlid ALIAS FOR $1;
+  pDate ALIAS FOR $2;
+  pReverse ALIAS FOR $3;
+  pGlSequence ALIAS FOR $4;
+
+BEGIN
+
+  INSERT INTO glseries
+  ( glseries_sequence, glseries_source, glseries_doctype, glseries_docnumber,
+    glseries_notes, glseries_accnt_id, glseries_amount, glseries_distdate )
+  SELECT pGlSequence, ''G/L'', ''ST'', stdjrnl_name,
+         stdjrnlitem_notes, stdjrnlitem_accnt_id,
+         CASE WHEN (pReverse=TRUE) THEN (stdjrnlitem_amount * -1)
+              ELSE stdjrnlitem_amount
+         END,
+         pDate
+  FROM stdjrnlitem, stdjrnl
+  WHERE ( (stdjrnlitem_stdjrnl_id=stdjrnl_id)
+   AND (stdjrnl_id=pStdjrnlid) );
+
+  RETURN pGlSequence;
+
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/poststandardjournalgroup.sql b/foundation-database/public/functions/poststandardjournalgroup.sql
new file mode 100644 (file)
index 0000000..af54d2e
--- /dev/null
@@ -0,0 +1,48 @@
+
+CREATE OR REPLACE FUNCTION postStandardJournalGroup(INTEGER, DATE) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pStdjrnlgrpid ALIAS FOR $1;
+  pDate ALIAS FOR $2;
+BEGIN
+  RETURN postStandardJournalGroup(pStdjrnlgrpid, pDate, FALSE);
+END;
+' LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION postStandardJournalGroup(INTEGER, DATE, BOOLEAN) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pStdjrnlgrpid ALIAS FOR $1;
+  pDate ALIAS FOR $2;
+  pReverse ALIAS FOR $3;
+  _r RECORD;
+  _glSequence INTEGER := -1;
+
+BEGIN
+
+  FOR _r IN SELECT stdjrnlgrpitem_id, stdjrnlgrpitem_stdjrnl_id
+            FROM stdjrnlgrpitem
+            WHERE ( (stdjrnlgrpitem_stdjrnlgrp_id=pStdjrnlgrpid)
+             AND (CURRENT_DATE BETWEEN stdjrnlgrpitem_effective AND (stdjrnlgrpitem_expires - 1))
+             AND ( (stdjrnlgrpitem_toapply = -1)
+              OR (stdjrnlgrpitem_toapply > stdjrnlgrpitem_applied) ) ) LOOP
+
+    IF (_glSequence = -1) THEN
+      SELECT fetchGLSequence() INTO _glSequence;
+    END IF;
+
+    PERFORM postStandardJournal(_r.stdjrnlgrpitem_stdjrnl_id, pDate, pReverse, _glSequence);
+
+    UPDATE stdjrnlgrpitem
+    SET stdjrnlgrpitem_applied=(stdjrnlgrpitem_applied + 1)
+    WHERE (stdjrnlgrpitem_id=_r.stdjrnlgrpitem_id);
+
+  END LOOP;
+
+  RETURN _glSequence;
+
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/postvalueintoinvbalance.sql b/foundation-database/public/functions/postvalueintoinvbalance.sql
new file mode 100644 (file)
index 0000000..fed503c
--- /dev/null
@@ -0,0 +1,89 @@
+CREATE OR REPLACE FUNCTION postValueintoInvBalance(INTEGER, DATE, NUMERIC, NUMERIC, NUMERIC, NUMERIC) RETURNS BOOLEAN AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteId ALIAS FOR $1;
+  pDate ALIAS FOR $2;
+  pQoh ALIAS FOR $3;
+  pNn ALIAS FOR $4;
+  pOldCost ALIAS FOR $5;
+  pNewCost ALIAS FOR $6;
+  _invbalid INTEGER;
+  _r RECORD;
+  _count INTEGER;
+  _valChange NUMERIC;
+  _nnvalChange NUMERIC;
+
+BEGIN
+
+--  Grab the costhist record to post
+  SELECT period_id INTO _r
+  FROM period
+  WHERE (pDate BETWEEN period_start AND period_end);
+
+  GET DIAGNOSTICS _count = ROW_COUNT;
+  
+--  Find an inventory balance to post into
+  IF ( _count > 0 ) THEN
+--  Try to find an existing invbal
+    SELECT invbal_id INTO _invbalid
+    FROM invbal
+    WHERE ( (invbal_period_id=_r.period_id)
+      AND (invbal_itemsite_id=pItemsiteId) );
+
+    GET DIAGNOSTICS _count = ROW_COUNT;
+    IF (_count = 0) THEN
+      -- Wasn't there, so forward update
+      PERFORM forwardUpdateItemsite(pItemsiteId);
+
+      --  Try to find an existing invbal again
+      SELECT invbal_id INTO _invbalid
+      FROM invbal
+      WHERE ( (invbal_period_id=_r.period_id)
+        AND (invbal_itemsite_id=pItemsiteId) );
+
+      GET DIAGNOSTICS _count = ROW_COUNT;
+      IF (_count = 0) THEN
+        RAISE EXCEPTION 'An inventory balance record was not found for updating standard costs';
+      END IF;
+    END IF;
+
+    _valChange := round((pNewCost - pOldCost) * pQoh, 2);
+    _nnvalChange := round((pNewCost - pOldCost) * pNn, 2);
+    
+--  We found an invbal, update it with the change
+    IF (_valChange > 0) THEN
+      UPDATE invbal SET 
+        invbal_value_in = (invbal_value_in + _valChange)
+      WHERE (invbal_id=_invbalid);
+    ELSE
+      UPDATE invbal SET 
+        invbal_value_out = (invbal_value_out - _valChange)
+      WHERE (invbal_id=_invbalid);
+    END IF;
+
+    IF (_nnvalChange > 0) THEN
+      UPDATE invbal SET 
+        invbal_nnval_in = (invbal_nnval_in + _nnvalChange)
+      WHERE (invbal_id=_invbalid);
+    ELSE
+      UPDATE invbal SET 
+        invbal_nnval_out = (invbal_nnval_out - _nnvalChange)
+      WHERE (invbal_id=_invbalid);
+    END IF;
+
+    UPDATE invbal SET 
+      invbal_value_ending = (invbal_value_beginning + invbal_value_in - invbal_value_out),
+      invbal_nnval_ending = (invbal_nnval_beginning + invbal_nnval_in - invbal_nnval_out),
+      invbal_dirty=true
+    WHERE (invbal_id=_invbalid);  
+
+  ELSE
+    RAISE EXCEPTION 'No period exists for date %.', pDate;
+  END IF;
+
+  RETURN TRUE;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/postvoucher.sql b/foundation-database/public/functions/postvoucher.sql
new file mode 100644 (file)
index 0000000..bc8dd3d
--- /dev/null
@@ -0,0 +1,461 @@
+CREATE OR REPLACE FUNCTION postVoucher(INTEGER, BOOLEAN) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pVoheadid ALIAS FOR $1;
+  pPostCosts ALIAS FOR $2;
+
+BEGIN
+  RETURN postVoucher(pVoheadid, fetchJournalNumber('AP-VO'), pPostCosts);
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION postVoucher(INTEGER, INTEGER, BOOLEAN) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pVoheadid ALIAS FOR $1;
+  pJournalNumber ALIAS FOR $2;
+  pPostCosts ALIAS FOR $3;
+  _sequence INTEGER;
+  _totalAmount_base NUMERIC;
+  _totalAmount NUMERIC;
+  _itemAmount_base NUMERIC;
+  _itemAmount NUMERIC;
+  _totalDiscountableAmount NUMERIC;
+  _test INTEGER;
+  _a RECORD;
+  _d RECORD;
+  _g RECORD;
+  _p RECORD;
+  _r RECORD;
+  _costx RECORD;
+  _pPostCosts BOOLEAN;
+  _pExplain BOOLEAN;
+  _pLowLevel BOOLEAN;
+  _exchGainFreight NUMERIC;
+  _taxBaseValue NUMERIC;
+  _firstExchDateFreight        DATE;
+  _tmpTotal            NUMERIC;
+  _glDate              DATE;
+
+BEGIN
+
+  RAISE DEBUG 'postVoucher(%, %, %)', pVoheadid, pJournalNumber, pPostCosts;
+
+  _pPostCosts := TRUE;
+  _totalAmount_base := 0;
+  _totalAmount := 0;
+  _totalDiscountableAmount := 0;
+  SELECT fetchGLSequence() INTO _sequence;
+
+--  Cache Voucher Infomation
+  SELECT vohead.*,
+        vend_number || '-' || vend_name || ' ' || vohead_reference
+                                                         AS glnotes,
+        COALESCE(pohead_orderdate, vohead_docdate) AS pohead_orderdate,
+        COALESCE(pohead_curr_id, vohead_curr_id) AS pohead_curr_id INTO _p
+  FROM vendinfo, vohead LEFT OUTER JOIN pohead ON (vohead_pohead_id = pohead_id)
+  WHERE ( (vohead_id=pVoheadid)
+  AND (vend_id=vohead_vend_id) )
+  FOR UPDATE OF vohead;
+
+  IF (_p.vohead_posted) THEN
+    RAISE EXCEPTION 'Cannot post Voucher #% as it is already posted [xtuple: postVoucher, -10, %]',
+                       _p.vohead_number, _p.vohead_number;
+  END IF;
+
+  _glDate := COALESCE(_p.vohead_gldistdate, _p.vohead_distdate);
+
+--  If the vohead_distdate is NULL, assume that this is a NULL vohead and quietly delete it
+  IF (_p.vohead_distdate IS NULL) THEN
+    DELETE FROM vohead WHERE vohead_id = pVoheadid;
+    RETURN 0;
+  END IF;
+  IF (_p.vohead_amount <= 0) THEN
+    RAISE EXCEPTION 'Cannot Post Voucher #% for a negative or zero amount (%) [xtuple: postVoucher, -1, %, %]',
+                       _p.vohead_number, _p.vohead_amount,
+                       _p.vohead_number, _p.vohead_amount;
+  END IF;
+
+-- there is no currency gain/loss on items, see issue 3892,
+-- but there might be on freight, which is first encountered at p/o receipt
+  SELECT recv_date::DATE INTO _firstExchDateFreight
+      FROM recv
+      WHERE (recv_vohead_id = pVoheadid);
+
+  SELECT round(SUM(amount),4) INTO _tmpTotal
+  FROM (
+  SELECT SUM(vodist_amount) AS amount
+    FROM vodist
+   WHERE ( (vodist_vohead_id=pVoheadid)
+     AND   (vodist_tax_id=-1) )
+  UNION ALL
+  SELECT SUM(voitem_freight) AS amount
+    FROM voitem
+   WHERE (voitem_vohead_id=pVoheadid)
+  UNION ALL
+  SELECT SUM(tax*-1)
+  FROM 
+    (SELECT round(sum(taxdetail_tax),2) AS tax,
+              currToBase(_p.vohead_curr_id, round(sum(taxdetail_tax),2), _p.vohead_docdate) AS taxbasevalue
+     FROM tax 
+     JOIN calculateTaxDetailSummary('VO', pVoheadid, 'T') ON (taxdetail_tax_id=tax_id)
+     GROUP BY tax_id, tax_sales_accnt_id
+    ) AS taxdata
+  ) AS data;
+
+  IF (_tmpTotal IS NULL OR _tmpTotal <= 0) THEN
+    RAISE EXCEPTION 'Cannot Post Voucher #% with negative or zero distributions (%) [xtuple: postVoucher, -2, %, %]',
+                       _p.vohead_number, _tmpTotal,
+                       _p.vohead_number, _tmpTotal;
+  END IF;
+
+  IF (_tmpTotal > _p.vohead_amount) THEN
+    RAISE EXCEPTION 'Cannot Post Voucher #% with distributions greater than the voucher amount (% > %) [xtuple: postVoucher, -3, %, %, %]',
+                       _p.vohead_number, _tmpTotal, _p.vohead_amount,
+                       _p.vohead_number, _tmpTotal, _p.vohead_amount;
+  END IF;
+
+  IF (_tmpTotal < _p.vohead_amount) THEN
+    RAISE EXCEPTION 'Cannot Post Voucher #% with distributions less than the voucher amount (% < %) [xtuple: postVoucher, -4, %, %, %]',
+                       _p.vohead_number, _tmpTotal, _p.vohead_amount,
+                       _p.vohead_number, _tmpTotal, _p.vohead_amount;
+  END IF;
+
+  SELECT DISTINCT poitem_linenumber INTO _test
+    FROM vodist, voitem, poitem 
+   WHERE ( (vodist_poitem_id=poitem_id)
+     AND   (voitem_poitem_id=poitem_id)
+     AND   (voitem_vohead_id=vodist_vohead_id)
+     AND   ((poitem_qty_received - poitem_qty_vouchered) = 0)
+     AND   (vodist_vohead_id=pVoheadid) )
+   LIMIT 1;
+  IF (FOUND) THEN
+    RAISE EXCEPTION 'Cannot Post Voucher #% as one or more of the line items have already been fully vouchered. Check P/O Line #% [postVoucher, -6, %, %]',
+         _p.vohead_number, _test,
+         _p.vohead_number, _test;
+  END IF;
+
+--  Start by handling taxes
+  FOR _r IN SELECT tax_sales_accnt_id, 
+              round(sum(taxdetail_tax),2) AS tax,
+              currToBase(_p.vohead_curr_id, round(sum(taxdetail_tax),2), _p.vohead_docdate) AS taxbasevalue
+            FROM tax 
+             JOIN calculateTaxDetailSummary('VO', pVoheadid, 'T') ON (taxdetail_tax_id=tax_id)
+           GROUP BY tax_id, tax_sales_accnt_id LOOP
+
+    PERFORM insertIntoGLSeries( _sequence, 'A/P', 'VO', _p.vohead_number,
+                                _r.tax_sales_accnt_id, 
+                                _r.taxbasevalue,
+                                _glDate, _p.glnotes );
+
+    RAISE DEBUG 'postVoucher: _r.tax=%', _r.tax;
+
+    _totalAmount_base := (_totalAmount_base - _r.taxbasevalue);
+    _totalAmount := (_totalAmount - _r.tax);
+     
+  END LOOP;
+
+-- Update item tax records with posting data
+    UPDATE voitemtax SET 
+      taxhist_docdate=_p.vohead_docdate,
+      taxhist_distdate=_glDate,
+      taxhist_curr_id=_p.vohead_curr_id,
+      taxhist_curr_rate=curr_rate,
+      taxhist_journalnumber=pJournalNumber
+    FROM vohead
+     JOIN voitem ON (vohead_id=voitem_vohead_id), 
+     curr_rate
+    WHERE ((vohead_id=pVoheadId)
+      AND (taxhist_parent_id=voitem_id)
+      AND (_p.vohead_curr_id=curr_id)
+      AND (_p.vohead_docdate BETWEEN curr_effective 
+                           AND curr_expires) );
+
+-- Update Misc distributions with posting data
+    UPDATE voheadtax SET 
+      taxhist_docdate=_p.vohead_docdate,
+      taxhist_distdate=_glDate,
+      taxhist_curr_id=_p.vohead_curr_id,
+      taxhist_curr_rate=curr_rate,
+      taxhist_journalnumber=pJournalNumber
+    FROM curr_rate
+    WHERE ((taxhist_parent_id=pVoheadid)
+      AND (_p.vohead_curr_id=curr_id)
+      AND (_p.vohead_docdate BETWEEN curr_effective 
+                           AND curr_expires) );
+
+--  Loop through the vodist records for the passed vohead that
+--  are posted against a P/O Item
+  FOR _g IN SELECT DISTINCT poitem_id, voitem_id, voitem_qty, poitem_expcat_id,
+                            poitem_invvenduomratio, poitem_prj_id,
+                            COALESCE(itemsite_id, -1) AS itemsiteid,
+                            COALESCE(itemsite_costcat_id, -1) AS costcatid,
+                            COALESCE(itemsite_item_id, -1) AS itemsite_item_id,
+                            (SELECT SUM(value) 
+                             FROM (
+                                SELECT SUM(recv_value) AS value
+                                FROM recv
+                                WHERE (recv_voitem_id=voitem_id)
+                             UNION
+                                SELECT SUM(poreject_value)*-1 AS value
+                                FROM poreject
+                                WHERE (poreject_voitem_id=voitem_id)) as data)
+                           AS value_base,
+                          (poitem_freight_received - poitem_freight_vouchered) /
+                              (poitem_qty_received - poitem_qty_vouchered) * voitem_qty AS vouchered_freight,
+                            currToBase(_p.pohead_curr_id,
+                                      (poitem_freight_received - poitem_freight_vouchered) /
+                                      (poitem_qty_received - poitem_qty_vouchered) * voitem_qty,
+                                       _firstExchDateFreight ) AS vouchered_freight_base,
+                           voitem_freight,
+                           currToBase(_p.vohead_curr_id, voitem_freight,
+                                       _p.vohead_distdate) AS voitem_freight_base
+            FROM vodist, voitem,
+                 poitem LEFT OUTER JOIN itemsite ON (poitem_itemsite_id=itemsite_id)
+            WHERE ( (vodist_poitem_id=poitem_id)
+             AND (voitem_poitem_id=poitem_id)
+             AND (voitem_vohead_id=vodist_vohead_id)
+             AND (vodist_vohead_id=pVoheadid)) LOOP
+
+--  Grab the G/L Accounts
+    IF (_g.costcatid = -1) THEN
+      SELECT getPrjAccntId(_g.poitem_prj_id, pp.accnt_id) AS pp_accnt_id,
+             lb.accnt_id AS lb_accnt_id,
+             fr.accnt_id AS freight_accnt_id INTO _a
+      FROM expcat, accnt AS pp, accnt AS lb, accnt AS fr
+      WHERE ( (expcat_purchprice_accnt_id=pp.accnt_id)
+       AND (expcat_liability_accnt_id=lb.accnt_id)
+       AND (expcat_freight_accnt_id=fr.accnt_id)
+       AND (expcat_id=_g.poitem_expcat_id) );
+      IF (NOT FOUND) THEN
+        RAISE EXCEPTION 'Cannot Post Voucher #% due to unassigned G/L Accounts [xtuple: postVoucher, -7, %]',
+                        _p.vohead_number, _p.vohead_number;
+      END IF;
+    ELSE
+      SELECT getPrjAccntId(_g.poitem_prj_id, costcat_purchprice_accnt_id) AS pp_accnt_id,
+             getPrjAccntId(_g.poitem_prj_id, costcat_liability_accnt_id) AS lb_accnt_id,
+             getPrjAccntId(_g.poitem_prj_id, costcat_freight_accnt_id) AS freight_accnt_id
+      INTO _a
+      FROM costcat
+      WHERE (costcat_id=_g.costcatid);
+      IF (NOT FOUND) THEN
+        RAISE EXCEPTION 'Cannot Post Voucher #% due to unassigned G/L Accounts [xtuple: postVoucher, -8, %]',
+                        _p.vohead_number, _p.vohead_number;
+      END IF;
+    END IF;
+
+--  Clear the Item Amount accumulator
+    _itemAmount_base := 0;
+    _itemAmount := 0;
+
+--  Figure out the total posted value for this line item
+    FOR _d IN SELECT vodist_id, vodist_amount, vodist_discountable,
+                    _p.vohead_curr_id, vodist_costelem_id,
+                    currToBase(_p.vohead_curr_id, vodist_amount,
+                               _p.vohead_distdate) AS vodist_amount_base
+              FROM vodist
+              WHERE ( (vodist_vohead_id=pVoheadid)
+               AND (vodist_poitem_id=_g.poitem_id) ) LOOP
+
+       _pExplain := FALSE;
+       SELECT * INTO _costx
+         FROM itemcost
+        WHERE ( (itemcost_item_id = _g.itemsite_item_id)
+          AND   (itemcost_costelem_id = _d.vodist_costelem_id) );
+
+       IF (FOUND) THEN
+         _pExplain := _costx.itemcost_lowlevel;
+       END IF;
+
+--  Post the cost to the Actual if requested
+--      IF ( (pPostCosts) AND (_d.vodist_costelem_id <> -1) ) THEN
+      IF ( (_d.vodist_costelem_id <> -1) AND (_g.itemsite_item_id <> -1) ) THEN
+        PERFORM updateCost( _g.itemsite_item_id, _d.vodist_costelem_id,
+                            _pExplain, (_d.vodist_amount / (_g.voitem_qty * _g.poitem_invvenduomratio)),
+                           _p.vohead_curr_id );
+      END IF;
+
+--  Add the Distribution Amount to the Item Amount
+      RAISE DEBUG 'postVoucher: _d.vodist_amount=%', _d.vodist_amount;
+
+      _itemAmount_base := _itemAmount_base + ROUND(_d.vodist_amount_base, 2);
+      _itemAmount := _itemAmount + _d.vodist_amount;
+      IF (_d.vodist_discountable) THEN
+        _totalDiscountableAmount := (_totalDiscountableAmount + _d.vodist_amount);
+      END IF;
+
+    END LOOP;
+
+--  Distribute from the clearing account
+    PERFORM insertIntoGLSeries( _sequence, 'A/P', 'VO', text(_p.vohead_number),
+               _a.lb_accnt_id,
+               round(_g.value_base + _g.vouchered_freight_base, 2) * -1,
+               _glDate, _p.glnotes );
+
+
+--  Attribute the correct portion to currency gain/loss
+    _exchGainFreight := 0;
+    SELECT currGain(_p.pohead_curr_id, _g.vouchered_freight,
+                   _firstExchDateFreight, _p.vohead_distdate )
+                   INTO _exchGainFreight;
+    IF (round(_exchGainFreight, 2) <> 0) THEN
+       PERFORM insertIntoGLSeries(_sequence, 'A/P', 'VO',
+           text(_p.vohead_number),
+           getGainLossAccntId(_a.lb_accnt_id), round(_exchGainFreight, 2),
+          _glDate, _p.glnotes);
+    END IF;
+
+--  Distribute the remaining variance to the Purchase Price Variance account
+    IF (round(_itemAmount_base, 2) <> round(_g.value_base, 2)) THEN
+      _tmpTotal := round(_itemAmount_base, 2) - round(_g.value_base, 2);
+      PERFORM insertIntoGLSeries( _sequence, 'A/P', 'VO', text(_p.vohead_number),
+                                 _a.pp_accnt_id,
+                                 _tmpTotal * -1,
+                                 _glDate, _p.glnotes );
+    END IF;
+
+--  Distribute the remaining freight variance to the Purchase Price Variance account
+    IF (round(_g.voitem_freight_base + _exchGainFreight, 2) <> round(_g.vouchered_freight_base, 2)) THEN
+      _tmpTotal := round(_g.voitem_freight_base + _exchGainFreight, 2) - round(_g.vouchered_freight_base, 2);
+      PERFORM insertIntoGLSeries( _sequence, 'A/P', 'VO', text(_p.vohead_number),
+        _a.freight_accnt_id,
+             _tmpTotal * -1,
+             _glDate, _p.glnotes );
+    END IF;
+
+--  Add the distribution amount to the total amount to distribute
+    RAISE DEBUG 'postVoucher: _itemAmount=%', _itemAmount;
+
+    _totalAmount_base := (_totalAmount_base + _itemAmount_base + _g.voitem_freight_base);
+    _totalAmount := (_totalAmount + _itemAmount + _g.voitem_freight);
+
+--  Post all the Tagged Receivings for this P/O Item as Invoiced and
+--  record the purchase and receive costs
+--  Comment out because recv cost is set at receiving now.
+    UPDATE recv
+    SET recv_invoiced=TRUE,
+       recv_recvcost_curr_id=basecurrid(),
+        recv_recvcost=round(_g.value_base / _g.voitem_qty, 2)
+    FROM poitem
+    WHERE ((recv_orderitem_id=poitem_id)
+      AND  (recv_order_type='PO')
+      AND  (recv_orderitem_id=_g.poitem_id)
+      AND  (recv_vohead_id=pVoheadid) );
+
+--  Post all the Tagged Rejections for this P/O Item as Invoiced
+    UPDATE poreject
+    SET poreject_invoiced=TRUE
+    WHERE ( (poreject_poitem_id=_g.poitem_id)
+     AND (poreject_vohead_id=pVoheadid) );
+
+--  Update the qty and freight vouchered fields
+    UPDATE poitem
+       SET poitem_qty_vouchered = (poitem_qty_vouchered + _g.voitem_qty),
+           poitem_freight_vouchered = (poitem_freight_vouchered + _g.vouchered_freight)
+     WHERE (poitem_id=_g.poitem_id);
+
+  END LOOP;
+
+--  Loop through the vodist records for the passed vohead that
+--  are not posted against a P/O Item
+--  Skip the tax distributions
+  FOR _d IN SELECT vodist_id, vodist_discountable,
+                  currToBase(_p.vohead_curr_id, vodist_amount,
+                             _p.vohead_distdate) AS vodist_amount_base,
+                  vodist_amount,
+                  vodist_accnt_id, vodist_expcat_id
+            FROM vodist
+            WHERE ( (vodist_vohead_id=pVoheadid)
+             AND (vodist_poitem_id=-1)
+             AND (vodist_tax_id=-1) ) LOOP
+
+--  Distribute from the misc. account
+    IF (_d.vodist_accnt_id = -1) THEN
+      PERFORM insertIntoGLSeries( _sequence, 'A/P', 'VO', text(_p.vohead_number),
+                         expcat_exp_accnt_id,
+                         round(_d.vodist_amount_base, 2) * -1,
+                         _glDate, _p.glnotes )
+         FROM expcat
+        WHERE (expcat_id=_d.vodist_expcat_id);
+    ELSE
+      PERFORM insertIntoGLSeries( _sequence, 'A/P', 'VO', text(_p.vohead_number),
+                         _d.vodist_accnt_id,
+                         round(_d.vodist_amount_base, 2) * -1,
+                         _glDate, _p.glnotes );
+    END IF;
+
+--  Add the Distribution Amount to the Total Amount
+    RAISE DEBUG 'postVoucher: _d.vodist_amount=%', _d.vodist_amount;
+
+    _totalAmount_base := _totalAmount_base + ROUND(_d.vodist_amount_base, 2);
+    _totalAmount := _totalAmount + _d.vodist_amount;
+    IF (_d.vodist_discountable) THEN
+      _totalDiscountableAmount := (_totalDiscountableAmount + _d.vodist_amount);
+    END IF;
+
+  END LOOP;
+
+  SELECT insertIntoGLSeries( _sequence, 'A/P', 'VO', text(vohead_number),
+                             accnt_id, round(_totalAmount_base, 2),
+                            _glDate, _p.glnotes ) INTO _test
+  FROM vohead LEFT OUTER JOIN accnt ON (accnt_id=findAPAccount(vohead_vend_id))
+  WHERE ( (findAPAccount(vohead_vend_id)=0 OR accnt_id > 0) -- G/L interface might be disabled
+    AND (vohead_id=pVoheadid) );
+  IF (NOT FOUND) THEN
+    RAISE EXCEPTION 'Cannot Post Voucher #% due to an unassigned A/P Account [xtuple: postVoucher, -9, %]',
+                    _p.vohead_number, _p.vohead_number;
+  END IF;
+
+  PERFORM postGLSeries(_sequence, pJournalNumber);
+
+--  Create the A/P Open Item
+  RAISE DEBUG 'postVoucher: _totalAmount=%, _totalDiscountableAmount=%',
+                _totalAmount, _totalDiscountableAmount;
+
+  INSERT INTO apopen
+  ( apopen_journalnumber, apopen_docdate, apopen_duedate, apopen_distdate, apopen_open,
+    apopen_terms_id, apopen_vend_id, apopen_doctype,
+    apopen_docnumber, apopen_invcnumber, apopen_ponumber, apopen_reference,
+    apopen_amount, apopen_paid, apopen_notes, apopen_username, apopen_posted,
+    apopen_curr_id, apopen_discountable_amount )
+-- TODO: 
+  SELECT pJournalNumber, vohead_docdate, vohead_duedate, _glDate, TRUE,
+         vohead_terms_id, vohead_vend_id, 'V',
+         vohead_number, vohead_invcnumber, COALESCE(TEXT(pohead_number), 'Misc.'), vohead_reference,
+         round(_totalAmount, 2), 0, '', getEffectiveXtUser(), FALSE,
+         vohead_curr_id, round(_totalDiscountableAmount, 2)
+  FROM vohead LEFT OUTER JOIN pohead ON (vohead_pohead_id=pohead_id)
+  WHERE (vohead_id=pVoheadid);
+
+--  Close all of the P/O Items that should be closed by this Voucher
+  UPDATE poitem
+  SET poitem_status='C'
+  FROM voitem
+  WHERE ( (voitem_poitem_id=poitem_id)
+   AND (voitem_close)
+   AND (voitem_vohead_id=pVoheadid) );
+
+--  Check the P/O items and if they are all closed go ahead
+--  and close the P/O head.
+  IF ( (SELECT (count(*) < 1)
+          FROM vohead, poitem
+         WHERE ((vohead_pohead_id=poitem_pohead_id)
+           AND  (poitem_status<>'C')
+           AND  (vohead_id=pVoheadid) ) ) ) THEN
+    PERFORM closePo(vohead_pohead_id)
+       FROM vohead
+      WHERE (vohead_id=pVoheadid);
+  END IF;
+
+--  Set the vohead as posted
+  UPDATE vohead
+  SET vohead_posted=TRUE, vohead_gldistdate=_glDate
+  WHERE (vohead_id=pVoheadid);
+
+  RETURN pJournalNumber;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/postvouchers.sql b/foundation-database/public/functions/postvouchers.sql
new file mode 100644 (file)
index 0000000..641e7a2
--- /dev/null
@@ -0,0 +1,19 @@
+CREATE OR REPLACE FUNCTION postVouchers(BOOLEAN) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pPostCosts ALIAS FOR $1;
+  _journalNumber INTEGER;
+
+BEGIN
+
+  SELECT fetchJournalNumber('AP-VO') INTO _journalNumber;
+
+  PERFORM postVoucher(vohead_id, _journalNumber, pPostCosts)
+  FROM vohead
+  WHERE (NOT vohead_posted);
+
+  RETURN _journalNumber;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/primarykeyfields.sql b/foundation-database/public/functions/primarykeyfields.sql
new file mode 100644 (file)
index 0000000..f960e86
--- /dev/null
@@ -0,0 +1,29 @@
+CREATE OR REPLACE FUNCTION primaryKeyFields(TEXT, TEXT) RETURNS TEXT[] AS $$ 
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSchema       ALIAS FOR $1;
+  pRelation     ALIAS FOR $2;
+  _colname      TEXT;
+  _counter      INTEGER := 0;
+  _result       TEXT[];
+
+BEGIN
+  EXECUTE 'SELECT ARRAY(SELECT attname
+                         FROM pg_attribute
+                         JOIN pg_class idx ON (attrelid         = idx.oid)
+                         JOIN pg_namespace ON (idx.relnamespace = pg_namespace.oid)
+                         JOIN pg_index     ON (idx.oid          = indexrelid)
+                         JOIN pg_class tab ON (indrelid         = tab.oid)
+                        WHERE NOT attisdropped
+                          AND nspname = ''' || pSchema || ''' 
+                          AND indisprimary
+                          AND LOWER(tab.relname) = ''' || pRelation || '''
+                       ORDER BY attnum);'
+  INTO _result;
+
+  RETURN _result;
+END;
+$$ LANGUAGE 'plpgsql' STABLE;
+
+COMMENT ON FUNCTION primaryKeyFields(TEXT, TEXT) IS 'Return an array containing the names of the primary key fields of pSchema.pRelation. The first key field is in _result[1].';
diff --git a/foundation-database/public/functions/prj.sql b/foundation-database/public/functions/prj.sql
new file mode 100644 (file)
index 0000000..21839e1
--- /dev/null
@@ -0,0 +1,39 @@
+CREATE OR REPLACE FUNCTION prj() RETURNS SETOF prj AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _row prj%ROWTYPE;
+  _priv TEXT;
+  _grant BOOLEAN;
+
+BEGIN
+  -- This query will give us the most permissive privilege the user has been granted
+  SELECT privilege, granted INTO _priv, _grant
+  FROM privgranted 
+  WHERE privilege IN ('MaintainAllProjects','ViewAllProjects','MaintainPersonalProjects','ViewPersonalProjects')
+  ORDER BY granted DESC, sequence
+  LIMIT 1;
+
+  -- If have an 'All' privilege return all results
+  IF (_priv ~ 'All' AND _grant) THEN
+    FOR _row IN 
+      SELECT * FROM prj
+    LOOP
+      RETURN NEXT _row;
+    END LOOP;
+  -- Otherwise if have any other grant, must be personal privilege.
+  ELSIF (_grant) THEN
+    FOR _row IN 
+      SELECT * FROM prj 
+      WHERE getEffectiveXtUser() IN (prj_owner_username, prj_username)
+    LOOP
+      RETURN NEXT _row;
+    END LOOP;
+  END IF;
+
+  RETURN;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+COMMENT ON FUNCTION prj() IS 'A table function that returns Project results according to privilege settings.';
diff --git a/foundation-database/public/functions/prjtask.sql b/foundation-database/public/functions/prjtask.sql
new file mode 100644 (file)
index 0000000..eccd458
--- /dev/null
@@ -0,0 +1,40 @@
+CREATE OR REPLACE FUNCTION prjtask() RETURNS SETOF prjtask AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _row prjtask%ROWTYPE;
+  _priv TEXT;
+  _grant BOOLEAN;
+
+BEGIN
+  -- This query will give us the most permissive privilege the user has been granted
+  SELECT privilege, granted INTO _priv, _grant
+  FROM privgranted 
+  WHERE privilege IN ('MaintainAllProjects','ViewAllProjects','MaintainPersonalProjects','ViewPersonalProjects')
+  ORDER BY granted DESC, sequence
+  LIMIT 1;
+
+  -- If have an 'All' privilege return all results
+  IF (_priv ~ 'All' AND _grant) THEN
+    FOR _row IN 
+      SELECT * FROM prjtask
+    LOOP
+      RETURN NEXT _row;
+    END LOOP;
+  -- Otherwise if have any other grant, must be personal privilege.
+  ELSIF (_grant) THEN
+    FOR _row IN 
+      SELECT prjtask.* FROM prjtask
+      JOIN prj ON prj_id=prjtask_prj_id
+      WHERE getEffectiveXtUser() IN (prjtask_owner_username,prjtask_username,prj_username,prj_owner_username)
+    LOOP
+      RETURN NEXT _row;
+    END LOOP;
+  END IF;
+
+  RETURN;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+COMMENT ON FUNCTION prjtask() IS 'A table function that returns Project results according to privilege settings.';
diff --git a/foundation-database/public/functions/purgecreditmemos.sql b/foundation-database/public/functions/purgecreditmemos.sql
new file mode 100644 (file)
index 0000000..653f32a
--- /dev/null
@@ -0,0 +1,27 @@
+CREATE OR REPLACE FUNCTION purgeCreditMemos(DATE) RETURNS BOOLEAN AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCutoffDate ALIAS FOR $1;
+
+BEGIN
+
+  DELETE FROM cmitem
+  WHERE (cmitem_id IN ( SELECT cmitem_id
+                        FROM cmitem, cmhead
+                        WHERE ( (cmitem_cmhead_id=cmhead_id)
+                          AND   (cmhead_posted)
+                          AND   (cmhead_printed)
+                          AND   (cmhead_docdate<=pCutoffDate)
+                          AND   (checkCreditMemoSitePrivs(cmhead_id)) ) ) );
+
+  DELETE FROM cmhead
+  WHERE ( (cmhead_posted)
+    AND   (cmhead_printed)
+    AND   (cmhead_docdate<=pCutoffDate)
+    AND   (checkCreditMemoSitePrivs(cmhead_id)) );
+
+  RETURN TRUE;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/purgecrmacctmerge.sql b/foundation-database/public/functions/purgecrmacctmerge.sql
new file mode 100644 (file)
index 0000000..bf37b0a
--- /dev/null
@@ -0,0 +1,46 @@
+CREATE OR REPLACE FUNCTION purgecrmacctmerge(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pDestid       ALIAS FOR $1;
+  _result       INTEGER := 0;
+  _tmpcount     INTEGER := 0;
+BEGIN
+  IF EXISTS(SELECT 1
+              FROM crmacctsel
+             WHERE crmacctsel_dest_crmacct_id=pDestid) THEN
+    DELETE FROM crmacctsel WHERE crmacctsel_dest_crmacct_id = pDestid;
+    GET DIAGNOSTICS _result = ROW_COUNT;
+
+  ELSIF EXISTS(SELECT 1
+                 FROM mrgundo
+                WHERE mrgundo_base_schema='public'
+                  AND mrgundo_base_table='crmacct'
+                  AND mrgundo_base_id=pDestid) THEN
+
+    DELETE FROM crmacct
+     WHERE crmacct_id IN (
+              SELECT mrgundo_pkey_id
+                FROM mrgundo
+               WHERE mrgundo_schema   = 'public'
+                 AND mrgundo_table    = 'crmacct'
+                 and mrgundo_pkey_col = 'crmacct_id'
+                 AND mrgundo_col IS NULL
+                 AND mrgundo_base_schema = 'public'
+                 AND mrgundo_base_table  = 'crmacct'
+                 AND mrgundo_base_id     = pDestid)
+        AND crmacct_id != pDestid;
+    GET DIAGNOSTICS _result = ROW_COUNT;
+
+    DELETE FROM mrgundo
+     WHERE mrgundo_base_schema ='public'
+       AND mrgundo_base_table  ='crmacct'
+       AND mrgundo_base_id     = pDestid;
+    GET DIAGNOSTICS _tmpcount = ROW_COUNT;
+
+    _result := _result + _tmpcount;
+  END IF;
+
+  RETURN _result;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/purgeinvoicerecord.sql b/foundation-database/public/functions/purgeinvoicerecord.sql
new file mode 100644 (file)
index 0000000..36b69ea
--- /dev/null
@@ -0,0 +1,376 @@
+CREATE OR REPLACE FUNCTION purgeInvoiceRecord(DATE, INTEGER) RETURNS TEXT AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCutoffDate ALIAS FOR $1;
+  pInvcheadId ALIAS FOR $2;
+  _r RECORD;
+  _ra RECORD;
+  _raheadid INTEGER;
+  _result INTEGER;
+  _debug BOOLEAN := FALSE;
+
+BEGIN
+
+-- Purge records where the entire Invoice, Billing, Shipper, Sales Order
+-- chain of associated documents are closed and complete
+
+  FOR _r IN
+  SELECT invchead_id, cobmisc_id, shiphead_id, ordershipped.cohead_id AS ordship_id, orderinvoiced.cohead_id AS ordinv_id
+    FROM invchead LEFT OUTER JOIN invcitem ON (invcitem_invchead_id=invchead_id)
+                  LEFT OUTER JOIN cobmisc ON (cobmisc_invcnumber::TEXT=invchead_invcnumber)
+                  LEFT OUTER JOIN shipitem ON (shipitem_invcitem_id=invcitem_id)
+                  LEFT OUTER JOIN shiphead ON (shiphead_id=shipitem_shiphead_id)
+                  LEFT OUTER JOIN cohead ordershipped ON (ordershipped.cohead_id=shiphead_order_id)
+                  LEFT OUTER JOIN coitem ON (coitem_id=invcitem_coitem_id)
+                  LEFT OUTER JOIN cohead orderinvoiced ON (orderinvoiced.cohead_id=coitem_cohead_id)
+   WHERE ( (invchead_id = pInvcheadId)
+     AND   (invchead_posted)
+     AND   (checkInvoiceSitePrivs(invchead_id)) )
+  GROUP BY invchead_id, cobmisc_id, shiphead_id, ordship_id, ordinv_id LOOP
+
+-- Check Billing
+
+-- Billing header (cobmisc) must be posted
+    SELECT cobmisc_id INTO _result
+      FROM cobmisc
+     WHERE ( (cobmisc_id=_r.cobmisc_id) AND (NOT cobmisc_posted) );
+    IF (FOUND) THEN
+      RETURN 'Billing not closed';
+    END IF;
+
+-- Billing line items (cobill), associated Invoice line items, and
+-- associated Sales Order line items must be closed, posted, and after cutoff date
+    SELECT cobill_id INTO _result
+      FROM cobill JOIN invcitem ON (invcitem_id=cobill_invcitem_id)
+                  JOIN invchead ON ( (invchead_id=invcitem_invchead_id) AND
+                                     ((NOT invchead_posted) OR (invchead_invcdate > pCutoffDate)) )
+                  JOIN coitem ON ( (coitem_id=cobill_coitem_id) AND
+                                   (coitem_status NOT IN ('C', 'X')) )
+     WHERE (cobill_cobmisc_id=_r.cobmisc_id);
+    IF (FOUND) THEN
+      RETURN 'Invoice/Sales Order associated with Billing not closed';
+    END IF;
+
+-- Check Shipping
+
+-- Shipping header (shiphead) must be shipped
+    SELECT shiphead_id INTO _result
+      FROM shiphead
+     WHERE ( (shiphead_id=_r.shiphead_id) AND (NOT shiphead_shipped) );
+    IF (FOUND) THEN
+      RETURN 'Shipper not closed';
+    END IF;
+
+-- Shipping line items (shipitem) and associated Sales Order line items
+-- must be closed
+    SELECT shiphead_id INTO _result
+      FROM shiphead, cohead, coitem
+     WHERE ( (shiphead_id=_r.shiphead_id)
+       AND   ( (shiphead_order_type='SO') AND (shiphead_order_id=cohead_id) )
+       AND   (coitem_cohead_id=cohead_id)
+       AND   (coitem_status NOT IN ('C', 'X')) );
+    IF (FOUND) THEN
+      RETURN 'Sales Order associated with Shipper not closed';
+    END IF;
+
+-- Shipping line items (shipitem) and associated Invoices must be posted
+-- and after cutoff date
+    SELECT shiphead_id INTO _result
+      FROM shiphead JOIN shipitem ON (shipitem_shiphead_id=shiphead_id)
+                    JOIN invcitem ON (invcitem_id=shipitem_invcitem_id)
+                    JOIN invchead ON ( (invchead_id=invcitem_invchead_id) AND
+                                       ((NOT invchead_posted) OR (invchead_invcdate > pCutoffDate)) )
+     WHERE (shiphead_id=_r.shiphead_id);
+    IF (FOUND) THEN
+      RETURN 'Invoice associated with Shipper not closed';
+    END IF;
+
+-- Check Sales Order
+
+-- Sales Order line items (coitem) must be closed
+    SELECT cohead_id INTO _result
+      FROM cohead JOIN coitem ON ( (coitem_cohead_id=cohead_id) AND
+                                   (coitem_status NOT IN ('C', 'X')) )
+     WHERE (cohead_id=_r.ordship_id);
+    IF (FOUND) THEN
+      RETURN 'Shipped Sales Order not closed';
+    END IF;
+    SELECT cohead_id INTO _result
+      FROM cohead JOIN coitem ON ( (coitem_cohead_id=cohead_id) AND
+                                   (coitem_status NOT IN ('C', 'X')) )
+     WHERE (cohead_id=_r.ordinv_id);
+    IF (FOUND) THEN
+      RETURN 'Invoiced Sales Order not closed';
+    END IF;
+
+    IF (fetchMetricBool('MultiWhs')) THEN
+    -- Check Original Return Authorization and cross check to New Sales Order
+      SELECT rahead_id INTO _result
+        FROM rahead JOIN raitem ON ( (raitem_rahead_id=rahead_id) AND
+                                     (raitem_status NOT IN ('C', 'X')) )
+                    JOIN coitem ON ( (coitem_id=raitem_new_coitem_id) AND
+                                     (coitem_status NOT IN ('C', 'X')) )
+                    JOIN invcitem ON (invcitem_coitem_id=coitem_id)
+                    JOIN invchead ON ( (invchead_id=invcitem_invchead_id) AND
+                                       ((NOT invchead_posted) OR (invchead_invcdate > pCutoffDate)) )
+       WHERE (rahead_orig_cohead_id=_r.ordship_id);
+      IF (FOUND) THEN
+        RETURN 'Shipped Original Return Authorization not closed';
+      END IF;
+      SELECT rahead_id INTO _result
+        FROM rahead JOIN raitem ON ( (raitem_rahead_id=rahead_id) AND
+                                     (raitem_status NOT IN ('C', 'X')) )
+                    JOIN coitem ON ( (coitem_id=raitem_new_coitem_id) AND
+                                     (coitem_status NOT IN ('C', 'X')) )
+                    JOIN invcitem ON (invcitem_coitem_id=coitem_id)
+                    JOIN invchead ON ( (invchead_id=invcitem_invchead_id) AND
+                                       ((NOT invchead_posted) OR (invchead_invcdate > pCutoffDate)) )
+       WHERE (rahead_orig_cohead_id=_r.ordinv_id);
+      IF (FOUND) THEN
+        RETURN 'Invoiced Original Return Authorization not closed';
+      END IF;
+
+  -- Check New Return Authorization
+      SELECT rahead_id INTO _result
+        FROM rahead JOIN raitem ON ( (raitem_rahead_id=rahead_id) AND
+                                     (NOT raitem_status IN ('C', 'X')) )
+                    JOIN coitem ON ( (coitem_id=raitem_orig_coitem_id) AND
+                                     (NOT coitem_status IN ('C', 'X')) )
+                    JOIN invcitem ON (invcitem_coitem_id=coitem_id)
+                    JOIN invchead ON ( (invchead_id=invcitem_invchead_id) AND
+                                       ((NOT invchead_posted) OR (invchead_invcdate > pCutoffDate)) )
+       WHERE (rahead_new_cohead_id=_r.ordship_id);
+      IF (FOUND) THEN
+        RETURN 'Shipped New Return Authorization not closed';
+      END IF;
+      SELECT rahead_id INTO _result
+        FROM rahead JOIN raitem ON ( (raitem_rahead_id=rahead_id) AND
+                                     (NOT raitem_status IN ('C', 'X')) )
+                    JOIN coitem ON ( (coitem_id=raitem_orig_coitem_id) AND
+                                     (NOT coitem_status IN ('C', 'X')) )
+                    JOIN invcitem ON (invcitem_coitem_id=coitem_id)
+                    JOIN invchead ON ( (invchead_id=invcitem_invchead_id) AND
+                                       ((NOT invchead_posted) OR (invchead_invcdate > pCutoffDate)) )
+       WHERE (rahead_new_cohead_id=_r.ordinv_id);
+      IF (FOUND) THEN
+        RETURN 'Invoiced New Return Authorization not closed';
+      END IF;
+    END IF;
+
+-- Check Lot/Serial Registration
+
+    IF (fetchMetricBool('MultiWhs')) THEN
+  -- Registration associated with Sales Order must be expired
+      SELECT lsreg_id INTO _result
+        FROM lsreg
+       WHERE ( (lsreg_cohead_id=_r.ordship_id)
+         AND   (lsreg_expiredate > CURRENT_DATE) );
+      IF (FOUND) THEN
+        RETURN 'Shipped Sales Order Lot/Serial Registration not closed';
+      END IF;
+      SELECT lsreg_id INTO _result
+        FROM lsreg
+       WHERE ( (lsreg_cohead_id=_r.ordinv_id)
+         AND   (lsreg_expiredate > CURRENT_DATE) );
+      IF (FOUND) THEN
+        RETURN 'Invoiced Sales Order Lot/Serial Registration not closed';
+      END IF;
+
+  -- Registration associated with Shipping must be expired
+      SELECT lsreg_id INTO _result
+        FROM lsreg
+       WHERE ( (lsreg_cohead_id=_r.shiphead_id)
+         AND   (lsreg_expiredate > CURRENT_DATE) );
+      IF (FOUND) THEN
+        RETURN 'Shipper Lot/Serial Registration not closed';
+      END IF;
+    END IF;
+
+    IF (fetchMetricBool('MultiWhs')) THEN
+  -- Registration associated with Original Return Authorization must be expired
+      SELECT rahead_id INTO _result
+        FROM rahead JOIN raitem ON (raitem_rahead_id=rahead_id)
+                    JOIN raitemls ON (raitemls_raitem_id=raitem_id)
+                    JOIN lsreg ON ( (lsreg_ls_id=raitemls_ls_id) AND
+                                    (lsreg_expiredate > CURRENT_DATE) )
+       WHERE (rahead_orig_cohead_id=_r.ordship_id);
+      IF (FOUND) THEN
+        RETURN 'Shipped Original Return Authorization Lot/Serial Registration not closed';
+      END IF;
+      SELECT rahead_id INTO _result
+        FROM rahead JOIN raitem ON (raitem_rahead_id=rahead_id)
+                    JOIN raitemls ON (raitemls_raitem_id=raitem_id)
+                    JOIN lsreg ON ( (lsreg_ls_id=raitemls_ls_id) AND
+                                    (lsreg_expiredate > CURRENT_DATE) )
+       WHERE (rahead_orig_cohead_id=_r.ordinv_id);
+      IF (FOUND) THEN
+        RETURN 'Invoiced Original Return Authorization Lot/Serial Registration not closed';
+      END IF;
+
+  -- Registration associated with New Return Authorization must be expired
+      SELECT rahead_id INTO _result
+        FROM rahead JOIN raitem ON (raitem_rahead_id=rahead_id)
+                    JOIN raitemls ON (raitemls_raitem_id=raitem_id)
+                    JOIN lsreg ON ( (lsreg_ls_id=raitemls_ls_id) AND
+                                    (lsreg_expiredate > CURRENT_DATE) )
+       WHERE (rahead_new_cohead_id=_r.ordship_id);
+      IF (FOUND) THEN
+        RETURN 'Shipped New Return Authorization Lot/Serial Registration not closed';
+      END IF;
+      SELECT rahead_id INTO _result
+        FROM rahead JOIN raitem ON (raitem_rahead_id=rahead_id)
+                    JOIN raitemls ON (raitemls_raitem_id=raitem_id)
+                    JOIN lsreg ON ( (lsreg_ls_id=raitemls_ls_id) AND
+                                    (lsreg_expiredate > CURRENT_DATE) )
+       WHERE (rahead_new_cohead_id=_r.ordinv_id);
+      IF (FOUND) THEN
+        RETURN 'Invoiced New Return Authorization Lot/Serial Registration not closed';
+      END IF;
+    END IF;
+
+-- Cash Advances associated with Sales Order cannot exist
+    SELECT aropenalloc_doc_id INTO _result   
+      FROM aropenalloc
+     WHERE ((aropenalloc_doctype='S')
+       AND  (aropenalloc_doc_id=_r.ordship_id));
+    IF (FOUND) THEN
+      RETURN 'Shipped Cash Advance not closed';
+    END IF;
+    SELECT aropenalloc_doc_id INTO _result   
+      FROM aropenalloc
+     WHERE ((aropenalloc_doctype='S')
+       AND  (aropenalloc_doc_id=_r.ordinv_id));
+    IF (FOUND) THEN
+      RETURN 'Invoiced Cash Advance not closed';
+    END IF;
+
+  END LOOP;
+
+-- Everything is OK, delete the chain
+  FOR _r IN
+  SELECT invchead_id, cobmisc_id, shiphead_id, ordershipped.cohead_id AS ordship_id, orderinvoiced.cohead_id AS ordinv_id
+    FROM invchead LEFT OUTER JOIN invcitem ON (invcitem_invchead_id=invchead_id)
+                  LEFT OUTER JOIN cobmisc ON (cobmisc_invcnumber::TEXT=invchead_invcnumber)
+                  LEFT OUTER JOIN shipitem ON (shipitem_invcitem_id=invcitem_id)
+                  LEFT OUTER JOIN shiphead ON (shiphead_id=shipitem_shiphead_id)
+                  LEFT OUTER JOIN cohead ordershipped ON (ordershipped.cohead_id=shiphead_order_id)
+                  LEFT OUTER JOIN coitem ON (coitem_id=invcitem_coitem_id)
+                  LEFT OUTER JOIN cohead orderinvoiced ON (orderinvoiced.cohead_id=coitem_cohead_id)
+   WHERE ( (invchead_id = pInvcheadId)
+     AND   (invchead_posted)
+     AND   (checkInvoiceSitePrivs(invchead_id)) )
+  GROUP BY invchead_id, cobmisc_id, shiphead_id, ordship_id, ordinv_id LOOP
+
+    IF (fetchMetricBool('MultiWhs')) THEN
+      FOR _ra IN
+        SELECT rahead_id
+        FROM rahead
+        WHERE (rahead_orig_cohead_id=_r.ordship_id) LOOP
+        IF (_debug) THEN
+          RAISE NOTICE 'Deleting Original Return head id %', _ra.rahead_id;
+        END IF;
+        DELETE FROM raitemls WHERE (raitemls_raitem_id IN (SELECT raitem_id
+                                                           FROM raitem
+                                                           WHERE (raitem_rahead_id=_ra.rahead_id)));
+        DELETE FROM rahist WHERE (rahist_rahead_id=_ra.rahead_id);
+        DELETE FROM raitem WHERE (raitem_rahead_id=_ra.rahead_id);
+        DELETE FROM rahead WHERE (rahead_id=_ra.rahead_id);
+      END LOOP;
+      FOR _ra IN
+        SELECT rahead_id
+        FROM rahead
+        WHERE (rahead_orig_cohead_id=_r.ordinv_id) LOOP
+        IF (_debug) THEN
+          RAISE NOTICE 'Deleting Original Return head id %', _ra.rahead_id;
+        END IF;
+        DELETE FROM raitemls WHERE (raitemls_raitem_id IN (SELECT raitem_id
+                                                           FROM raitem
+                                                           WHERE (raitem_rahead_id=_ra.rahead_id)));
+        DELETE FROM rahist WHERE (rahist_rahead_id=_ra.rahead_id);
+        DELETE FROM raitem WHERE (raitem_rahead_id=_ra.rahead_id);
+        DELETE FROM rahead WHERE (rahead_id=_ra.rahead_id);
+      END LOOP;
+
+      FOR _ra IN
+        SELECT rahead_id
+          FROM rahead
+         WHERE (rahead_new_cohead_id=_r.ordship_id) LOOP
+        IF (_debug) THEN
+          RAISE NOTICE 'Deleting New Return head id %', _ra.rahead_id;
+        END IF;
+        DELETE FROM raitemls WHERE (raitemls_raitem_id IN (SELECT raitem_id
+                                                           FROM raitem
+                                                           WHERE (raitem_rahead_id=_ra.rahead_id)));
+        DELETE FROM rahist WHERE (rahist_rahead_id=_ra.rahead_id);
+        DELETE FROM raitem WHERE (raitem_rahead_id=_ra.rahead_id);
+        DELETE FROM rahead WHERE (rahead_id=_ra.rahead_id);
+      END LOOP;
+      FOR _ra IN
+        SELECT rahead_id
+          FROM rahead
+         WHERE (rahead_new_cohead_id=_r.ordinv_id) LOOP
+        IF (_debug) THEN
+          RAISE NOTICE 'Deleting New Return head id %', _ra.rahead_id;
+        END IF;
+        DELETE FROM raitemls WHERE (raitemls_raitem_id IN (SELECT raitem_id
+                                                           FROM raitem
+                                                           WHERE (raitem_rahead_id=_ra.rahead_id)));
+        DELETE FROM rahist WHERE (rahist_rahead_id=_ra.rahead_id);
+        DELETE FROM raitem WHERE (raitem_rahead_id=_ra.rahead_id);
+        DELETE FROM rahead WHERE (rahead_id=_ra.rahead_id);
+      END LOOP;
+    END IF;
+
+    IF (fetchMetricBool('MultiWhs')) THEN
+      IF (_debug) THEN
+        RAISE NOTICE 'Deleting Lot/Serial Registrations';
+      END IF;
+      DELETE FROM lsreg WHERE (lsreg_cohead_id=_r.ordship_id);
+      DELETE FROM lsreg WHERE (lsreg_cohead_id=_r.ordinv_id);
+      DELETE FROM lsreg WHERE (lsreg_shiphead_id=_r.shiphead_id);
+    END IF;
+
+    IF (_debug) THEN
+      RAISE NOTICE 'Deleting Shipped Sales Order head id %', _r.ordship_id;
+    END IF;
+    DELETE FROM payco WHERE (payco_cohead_id=_r.ordship_id);
+    -- Delete kit components first
+    DELETE FROM coitem WHERE (coitem_cohead_id=_r.ordship_id AND coitem_subnumber > 0);
+    DELETE FROM coitem WHERE (coitem_cohead_id=_r.ordship_id);
+    DELETE FROM cohead WHERE (cohead_id=_r.ordship_id);
+
+    IF (_debug) THEN
+      RAISE NOTICE 'Deleting Sales Order head id %', _r.ordinv_id;
+    END IF;
+    DELETE FROM payco WHERE (payco_cohead_id=_r.ordinv_id);
+    -- Delete kit components first
+    DELETE FROM coitem WHERE (coitem_cohead_id=_r.ordinv_id AND coitem_subnumber > 0);
+    DELETE FROM coitem WHERE (coitem_cohead_id=_r.ordinv_id);
+    DELETE FROM cohead WHERE (cohead_id=_r.ordinv_id);
+
+    IF (_debug) THEN
+      RAISE NOTICE 'Deleting Ship head id %', _r.shiphead_id;
+    END IF;
+    DELETE FROM shipitem WHERE (shipitem_shiphead_id=_r.shiphead_id);
+    DELETE FROM pack WHERE (pack_shiphead_id=_r.shiphead_id);
+    DELETE FROM shiphead WHERE (shiphead_id=_r.shiphead_id);
+
+    IF (_debug) THEN
+      RAISE NOTICE 'Deleting Billing head id %', _r.cobmisc_id;
+    END IF;
+    DELETE FROM cobill WHERE (cobill_cobmisc_id=_r.cobmisc_id);
+    DELETE FROM cobmisc WHERE (cobmisc_id=_r.cobmisc_id);
+  END LOOP;
+  
+-- Everything is OK, delete the Invoice
+  IF (_debug) THEN
+    RAISE NOTICE 'Deleting Invoice head id %', _r.invchead_id;
+  END IF;
+  DELETE FROM invcitem WHERE (invcitem_invchead_id=_r.invchead_id);
+  DELETE FROM invchead WHERE (invchead_id=_r.invchead_id);
+
+  RETURN 'Purged';
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/purgeinvoicerecords.sql b/foundation-database/public/functions/purgeinvoicerecords.sql
new file mode 100644 (file)
index 0000000..16498c4
--- /dev/null
@@ -0,0 +1,49 @@
+CREATE OR REPLACE FUNCTION purgeInvoiceRecords(DATE) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCutoffDate ALIAS FOR $1;
+
+BEGIN
+
+-- Remove the shipitem records
+  DELETE FROM shipitem
+  WHERE (shipitem_invcitem_id IN (SELECT invcitem_id 
+                                  FROM invcitem 
+                                  WHERE invcitem_invchead_id IN ( SELECT invchead_id
+                                     FROM invchead
+                                     WHERE ( (invchead_invcdate <= pCutoffDate)
+                                     AND   (checkInvoiceSitePrivs(invchead_id))
+                                     AND   (invchead_posted) ) ) ) );
+
+-- Remove the cobill and cobmisc records
+  DELETE FROM cobill
+  WHERE (cobill_cobmisc_id IN ( SELECT cobmisc_id
+                                FROM cobmisc, invchead
+                                WHERE ( (invchead_invcnumber=cobmisc_invcnumber::TEXT)
+                                  AND   (checkInvoiceSitePrivs(invchead_id))
+                                  AND   (cobmisc_invcdate <= pCutoffDate)
+                                  AND   (cobmisc_posted)) ) );
+
+  DELETE FROM cobmisc
+  WHERE ( (checkInvoiceSitePrivs(getInvcheadId(cobmisc_invcnumber::TEXT)))
+    AND   (cobmisc_invcdate <= pCutoffDate)
+    AND   (cobmisc_posted) );
+
+-- Remove the invchead and invcitem records
+  DELETE FROM invcitem
+  WHERE (invcitem_invchead_id IN ( SELECT invchead_id
+                                   FROM invchead
+                                   WHERE ( (invchead_invcdate <= pCutoffDate)
+                                     AND   (checkInvoiceSitePrivs(invchead_id))
+                                     AND   (invchead_posted) ) ) );
+
+  DELETE FROM invchead
+  WHERE ( (invchead_invcdate <= pCutoffDate)
+    AND   (checkInvoiceSitePrivs(invchead_id))
+    AND   (invchead_posted) );
+
+  RETURN 1;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/purgepostedcountslips.sql b/foundation-database/public/functions/purgepostedcountslips.sql
new file mode 100644 (file)
index 0000000..5d4b0f4
--- /dev/null
@@ -0,0 +1,34 @@
+CREATE OR REPLACE FUNCTION purgePostedCountSlips(DATE, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCutoffDate ALIAS FOR $1;
+  pWarehousid ALIAS FOR $2;
+
+BEGIN
+
+  IF (pWarehousid = -1) THEN
+    DELETE FROM cntslip
+    WHERE (cntslip_id IN ( SELECT cntslip_id
+                           FROM cntslip, invcnt
+                           WHERE ( (cntslip_cnttag_id=invcnt_id)
+                            AND (invcnt_posted)
+                            AND (cntslip_posted)
+                            AND (date(invcnt_postdate) <= pCutoffDate) ) ) );
+
+  ELSE
+    DELETE FROM cntslip
+    WHERE (cntslip_id IN ( SELECT cntslip_id
+                           FROM invcnt, itemsite
+                           WHERE ( (cntslip_cnttag_id=invcnt_id)
+                            AND (invcnt_posted)
+                            AND (cntslip_posted)
+                            AND (invcnt_itemsite_id=itemsite_id)
+                            AND (date(invcnt_postdate) <= pCutoffDate)
+                            AND (itemsite_warehous_id=pWarehousid) ) ) );
+  END IF;
+
+  RETURN 1;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/purgepostedcounttags.sql b/foundation-database/public/functions/purgepostedcounttags.sql
new file mode 100644 (file)
index 0000000..c0da23a
--- /dev/null
@@ -0,0 +1,42 @@
+CREATE OR REPLACE FUNCTION purgePostedCountTags(DATE, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCutoffDate ALIAS FOR $1;
+  pWarehousid ALIAS FOR $2;
+
+BEGIN
+
+  IF (pWarehousid = -1) THEN
+    DELETE FROM cntslip
+    WHERE (cntslip_cnttag_id IN ( SELECT invcnt_id
+                                  FROM invcnt
+                                  WHERE ( (invcnt_posted)
+                                   AND (date(invcnt_postdate) <= pCutoffDate) ) ) );
+
+    DELETE FROM invcnt
+    WHERE ((invcnt_posted)
+     AND (date(invcnt_postdate) <= pCutoffDate));
+
+  ELSE
+    DELETE FROM cntslip
+    WHERE (cntslip_cnttag_id IN ( SELECT invcnt_id
+                                  FROM invcnt, itemsite
+                                  WHERE ( (invcnt_posted)
+                                   AND (invcnt_itemsite_id=itemsite_id)
+                                   AND (date(invcnt_postdate) <= pCutoffDate)
+                                   AND (itemsite_warehous_id=pWarehousid) ) ) );
+
+    DELETE FROM invcnt
+    WHERE (invcnt_id IN ( SELECT invcnt_id 
+                          FROM invcnt, itemsite
+                          WHERE ( (invcnt_posted)
+                           AND (invcnt_itemsite_id=itemsite_id)
+                           AND (date(invcnt_postdate) <= pCutoffDate)
+                           AND (itemsite_warehous_id=pWarehousid) ) ) );
+  END IF;
+
+  RETURN 1;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/purgeshipments.sql b/foundation-database/public/functions/purgeshipments.sql
new file mode 100644 (file)
index 0000000..fab0524
--- /dev/null
@@ -0,0 +1,23 @@
+CREATE OR REPLACE FUNCTION purgeShipments(DATE) RETURNS INTEGER  AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pcutoff ALIAS FOR $1;
+  _r RECORD;
+
+BEGIN
+
+  -- Used for transfer orders shipments (which are never invoiced)
+  FOR _r IN SELECT shiphead_id
+             FROM shiphead
+            WHERE ( (shiphead_order_type='TO')
+               AND   (shiphead_shipped)
+               AND   (shiphead_shipdate <= pcutoff) ) LOOP
+    DELETE FROM shipitem WHERE (shipitem_shiphead_id=_r.shiphead_id);
+    DELETE FROM shiphead WHERE (shiphead_id=_r.shiphead_id);
+  END LOOP;
+
+  RETURN 0;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/qtyallocated.sql b/foundation-database/public/functions/qtyallocated.sql
new file mode 100644 (file)
index 0000000..655e023
--- /dev/null
@@ -0,0 +1,65 @@
+CREATE OR REPLACE FUNCTION qtyAllocated(INTEGER, INTEGER) RETURNS NUMERIC AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  pLookAheaddays ALIAS FOR $2;
+
+BEGIN
+
+  RETURN qtyAllocated(pItemsiteid, startOfTime(), (CURRENT_DATE + pLookAheadDays));
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION qtyAllocated(INTEGER, DATE) RETURNS NUMERIC AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  pDate ALIAS FOR $2;
+
+BEGIN
+
+  RETURN qtyAllocated(pItemsiteid, startOfTime(), pDate);
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION qtyAllocated(INTEGER, DATE, DATE) RETURNS NUMERIC STABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  pStartDate ALIAS FOR $2;
+  pEndDate ALIAS FOR $3;
+
+BEGIN
+  IF ( fetchMetricBool('MultiWhs')) THEN
+    IF ( SELECT item_sold
+         FROM itemsite, item
+         WHERE ((itemsite_item_id=item_id)
+         AND (itemsite_id=pItemsiteid)) ) THEN
+      RETURN (allocatedForTo(pItemsiteid, pStartDate, pEndDate) +
+             allocatedForWo(pItemsiteid, pStartDate, pEndDate) +
+             allocatedForSo(pItemsiteid, pStartDate, pEndDate));
+    ELSE
+      RETURN (allocatedForTo(pItemsiteid, pStartDate, pEndDate) +
+             allocatedForWo(pItemsiteid, pStartDate, pEndDate));
+    END IF;
+  ELSE
+    IF ( SELECT item_sold
+         FROM itemsite, item
+         WHERE ((itemsite_item_id=item_id)
+         AND (itemsite_id=pItemsiteid)) ) THEN
+      RETURN (allocatedForWo(pItemsiteid, pStartDate, pEndDate) +
+             allocatedForSo(pItemsiteid, pStartDate, pEndDate));
+    ELSE
+      RETURN (allocatedForWo(pItemsiteid, pStartDate, pEndDate));
+    END IF;
+  END IF;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/qtyatshipping.sql b/foundation-database/public/functions/qtyatshipping.sql
new file mode 100644 (file)
index 0000000..2206c52
--- /dev/null
@@ -0,0 +1,50 @@
+CREATE OR REPLACE FUNCTION qtyAtShipping(INTEGER) RETURNS NUMERIC AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN qtyAtShipping('SO', $1);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION qtyAtShipping(TEXT, INTEGER) RETURNS NUMERIC AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN qtyAtShipping($1, $2, 'U');
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION qtyAtShipping(TEXT, INTEGER, TEXT) RETURNS NUMERIC AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pordertype   ALIAS FOR $1;
+  plineitemid  ALIAS FOR $2;
+  pstatus       ALIAS FOR $3;
+  _qty NUMERIC  := 0.0;
+
+BEGIN
+
+-- pstatus U=unshipped
+--         S=shipped
+--         B=both unshipped and shipped
+
+  IF (pordertype NOT IN ('SO', 'TO')) THEN
+    RAISE EXCEPTION '% is not a valid order type', pordertype;
+  END IF;
+
+  IF (pstatus NOT IN ('U', 'S', 'B')) THEN
+    RAISE EXCEPTION '% is not a valid status', pstatus;
+  END IF;
+
+  SELECT COALESCE(SUM(shipitem_qty), 0.0) INTO _qty
+  FROM shipitem, shiphead
+  WHERE ((shipitem_shiphead_id=shiphead_id)
+    AND  (shiphead_order_type=pordertype)
+    AND  (shipitem_orderitem_id=plineitemid)
+    AND  (((shiphead_shipped) AND (pstatus IN ('S', 'B'))) OR ((NOT shiphead_shipped) AND (pstatus IN ('U', 'B'))))  );
+
+  RETURN _qty;
+
+END;
+$$ LANGUAGE 'plpgsql' STABLE;
diff --git a/foundation-database/public/functions/qtyavailable.sql b/foundation-database/public/functions/qtyavailable.sql
new file mode 100644 (file)
index 0000000..487599e
--- /dev/null
@@ -0,0 +1,35 @@
+CREATE OR REPLACE FUNCTION qtyAvailable(INTEGER, INTEGER) RETURNS NUMERIC AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  pLookAheadDays ALIAS FOR $2;
+
+BEGIN
+
+  RETURN ( ( SELECT itemsite_qtyonhand
+             FROM itemsite
+             WHERE (itemsite_id=pItemsiteid) ) +
+           (SELECT qtyOrdered(pItemsiteid, pLookAheadDays)) -
+           (SELECT qtyAllocated(pitemsiteid, pLookAheadDays)) );
+END;
+' LANGUAGE 'plpgsql' STABLE;
+
+
+CREATE OR REPLACE FUNCTION qtyAvailable(INTEGER, DATE) RETURNS NUMERIC AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  pDate ALIAS FOR $2;
+
+BEGIN
+
+  RETURN ( ( SELECT itemsite_qtyonhand
+             FROM itemsite
+             WHERE (itemsite_id=pItemsiteid) ) +
+           (SELECT qtyOrdered(pItemsiteid, (pDate - CURRENT_DATE))) -
+           (SELECT qtyAllocated(pItemsiteid, (pDate - CURRENT_DATE))) );
+END;
+' LANGUAGE 'plpgsql' STABLE;
+
diff --git a/foundation-database/public/functions/qtyinshipment.sql b/foundation-database/public/functions/qtyinshipment.sql
new file mode 100644 (file)
index 0000000..3f83717
--- /dev/null
@@ -0,0 +1,35 @@
+CREATE OR REPLACE FUNCTION qtyInShipment(TEXT, INTEGER, INTEGER) RETURNS NUMERIC AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pordertype   ALIAS FOR $1;
+  plineitemid  ALIAS FOR $2;
+  pshipheadid   ALIAS FOR $3;
+  _qty NUMERIC;
+
+BEGIN
+
+  IF (pordertype NOT IN (''SO'', ''TO'')) THEN
+    RAISE EXCEPTION ''% is not a valid order type'', pordertype;
+  END IF;
+
+  IF (pshipheadid IS NULL) THEN
+    RAISE EXCEPTION ''Cannot calculate quantity in a shipment with a NULL shipment'';
+  END IF;
+
+  SELECT SUM(COALESCE(shipitem_qty, 0.0)) INTO _qty
+  FROM shipitem, shiphead
+  WHERE ((shipitem_shiphead_id=shiphead_id)
+      AND (shiphead_order_type=pordertype)
+      AND (shipitem_orderitem_id=plineitemid)
+      AND (shiphead_id=pshipheadid));
+
+  IF (NOT FOUND) THEN
+    RAISE NOTICE ''Quantity of % item % is 0 because shipment % does not exist.'',
+                  pordertype, plineitemid, pshipheadid;
+  END IF;
+
+  RETURN _qty;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/qtylocation.sql b/foundation-database/public/functions/qtylocation.sql
new file mode 100644 (file)
index 0000000..d28ac04
--- /dev/null
@@ -0,0 +1,59 @@
+SELECT dropIfExists('FUNCTION','qtyLocation(INTEGER, INTEGER, DATE, DATE, INTEGER, TEXT, INTEGER)');
+
+CREATE OR REPLACE FUNCTION qtyLocation(INTEGER, INTEGER, DATE, DATE, INTEGER, TEXT, INTEGER, INTEGER) RETURNS NUMERIC AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pLocationId  ALIAS FOR $1;
+  pLsId        ALIAS FOR $2;
+  pExpiration  ALIAS FOR $3;
+  pWarranty    ALIAS FOR $4;
+  pItemsiteId  ALIAS FOR $5;
+  pOrderType   ALIAS FOR $6;
+  pOrderId     ALIAS FOR $7;
+  pItemlocdistId ALIAS FOR $8;
+  _qty         NUMERIC = 0.0;
+  _qtyDist     NUMERIC = 0.0;
+  _qtyReserved NUMERIC = 0.0;
+
+BEGIN
+-- Summarize itemloc qty for this location/itemsite
+  SELECT COALESCE(SUM(itemloc_qty), 0) INTO _qty
+    FROM itemloc
+   WHERE ( (itemloc_itemsite_id=pItemsiteId)
+     AND (itemloc_location_id=pLocationId)
+     AND (COALESCE(itemloc_ls_id, -1)=COALESCE(pLsId, itemloc_ls_id, -1))
+     AND (COALESCE(itemloc_expiration, endoftime())=COALESCE(pExpiration, itemloc_expiration, endoftime()))
+     AND (COALESCE(itemloc_warrpurc, endoftime())=COALESCE(pWarranty, itemloc_warrpurc, endoftime())) );
+
+-- Summarize qty distributed but not yet committed by previous distributions
+  SELECT COALESCE(SUM(loc.itemlocdist_qty), 0) INTO _qtyDist
+    FROM itemlocdist loc
+      JOIN itemlocdist ls ON ((ls.itemlocdist_source_type='O')
+                         AND (ls.itemlocdist_id=loc.itemlocdist_itemlocdist_id))
+   WHERE ( (ls.itemlocdist_itemsite_id=pItemsiteId)
+     AND (loc.itemlocdist_source_type='L')
+     AND (loc.itemlocdist_source_id=pLocationId)
+     AND (COALESCE(ls.itemlocdist_ls_id, -1)=COALESCE(pLsId, ls.itemlocdist_ls_id, -1))
+     AND (COALESCE(ls.itemlocdist_expiration, endoftime())=COALESCE(pExpiration, ls.itemlocdist_expiration, endoftime()))
+     AND (COALESCE(ls.itemlocdist_warranty, endoftime())=COALESCE(pWarranty, ls.itemlocdist_warranty, endoftime()))
+     AND (ls.itemlocdist_id != pItemlocdistId ) );
+
+-- Summarize itemlocrsrv qty for this location/itemsite
+-- that is reserved for a different order
+  IF (fetchMetricBool('EnableSOReservationsByLocation')) THEN
+    SELECT COALESCE(SUM(itemlocrsrv_qty), 0) INTO _qtyReserved
+      FROM itemloc JOIN itemlocrsrv ON ( (itemlocrsrv_itemloc_id=itemloc_id)
+                                    AND  ((itemlocrsrv_source <> COALESCE(pOrderType, '')) OR
+                                          (itemlocrsrv_source_id <> COALESCE(pOrderId, -1))) )
+     WHERE ( (itemloc_itemsite_id=pItemsiteId)
+       AND (itemloc_location_id=pLocationId)
+       AND (COALESCE(itemloc_ls_id, -1)=COALESCE(pLsId, itemloc_ls_id, -1))
+       AND (COALESCE(itemloc_expiration, endoftime())=COALESCE(pExpiration, itemloc_expiration, endoftime()))
+       AND (COALESCE(itemloc_warrpurc, endoftime())=COALESCE(pWarranty, itemloc_warrpurc, endoftime())) );
+  END IF;
+
+  RETURN (_qty + _qtyDist - _qtyReserved);
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/qtyordered.sql b/foundation-database/public/functions/qtyordered.sql
new file mode 100644 (file)
index 0000000..8b3d713
--- /dev/null
@@ -0,0 +1,63 @@
+CREATE OR REPLACE FUNCTION qtyOrdered(INTEGER, INTEGER) RETURNS NUMERIC AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  pLookahead ALIAS FOR $2;
+  _itemType TEXT;
+  _result NUMERIC;
+
+BEGIN
+
+  RETURN qtyOrdered(pItemsiteid, startOfTime(), (CURRENT_DATE + pLookahead));
+
+END;
+' LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION qtyOrdered(INTEGER, DATE) RETURNS NUMERIC AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  pDate ALIAS FOR $2;
+
+BEGIN
+
+  RETURN qtyOrdered(pItemsiteid, startOfTime(), pDate);
+
+END;
+' LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION qtyOrdered(INTEGER, DATE, DATE) RETURNS NUMERIC AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  pStartDate ALIAS FOR $2;
+  pEndDate ALIAS FOR $3;
+  _itemType TEXT;
+
+BEGIN
+
+  SELECT item_type INTO _itemType
+  FROM item, itemsite
+  WHERE ( (itemsite_item_id=item_id)
+   AND (itemsite_id=pItemsiteid) );
+
+  IF ( SELECT metric_value
+       FROM metric
+       WHERE ((metric_name = ''MultiWhs'')
+       AND (metric_value = ''t''))) THEN
+    RETURN orderedByPo(pItemsiteid, pStartDate, pEndDate) +
+           orderedByWo(pItemsiteid, pStartDate, pEndDate) +
+           orderedByTo(pItemsiteid, pStartDate, pEndDate);
+  ELSE
+    RETURN orderedByPo(pItemsiteid, pStartDate, pEndDate) +
+           orderedByWo(pItemsiteid, pStartDate, pEndDate);
+
+  END IF;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/qtypr.sql b/foundation-database/public/functions/qtypr.sql
new file mode 100644 (file)
index 0000000..faa5160
--- /dev/null
@@ -0,0 +1,51 @@
+
+CREATE OR REPLACE FUNCTION qtypr(integer, integer) RETURNS numeric AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  pLookahead ALIAS FOR $2; 
+
+BEGIN
+  RETURN qtypr(pItemsiteid, startOfTime(), (CURRENT_DATE + pLookahead));
+END;
+$$ LANGUAGE 'plpgsql' VOLATILE;
+
+CREATE OR REPLACE FUNCTION qtypr(integer, date) RETURNS numeric AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  pDate ALIAS FOR $2;
+
+BEGIN
+  RETURN qtypr(pItemsiteid, startOfTime(), pDate);
+END;
+$$ LANGUAGE 'plpgsql' VOLATILE;
+
+CREATE OR REPLACE FUNCTION qtypr(integer, date, date) RETURNS numeric AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  pStartDate ALIAS FOR $2;
+  pEndDate ALIAS FOR $3;
+  _prtotal NUMERIC;
+
+BEGIN
+
+SELECT SUM(pr_qtyreq) INTO _prtotal
+  FROM pr
+  WHERE ((pr_status = 'O')
+    AND (pr_itemsite_id=pItemsiteid)
+    AND (pr_duedate BETWEEN pStartDate AND pEndDate));
+
+ IF (_prtotal IS NULL) THEN
+     RETURN 0.0;
+ END IF;
+
+ RETURN _prtotal;
+
+END;
+$$ LANGUAGE 'plpgsql' VOLATILE;
+
diff --git a/foundation-database/public/functions/qtytoreceive.sql b/foundation-database/public/functions/qtytoreceive.sql
new file mode 100644 (file)
index 0000000..b536879
--- /dev/null
@@ -0,0 +1,27 @@
+CREATE OR REPLACE FUNCTION qtyToReceive(TEXT, INTEGER) RETURNS NUMERIC AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pordertype   ALIAS FOR $1;
+  porderitemid ALIAS FOR $2;
+  _qty         NUMERIC;
+
+BEGIN
+  IF (pordertype = ''TO'' AND NOT fetchMetricBool(''MultiWhs'')) THEN
+    RETURN 0;
+  END IF;
+
+  IF (pordertype = ''RA'' AND NOT fetchMetricBool(''EnableReturnAuth'')) THEN
+    RETURN 0;
+  END IF;
+
+  SELECT SUM(recv_qty) INTO _qty
+  FROM recv
+  WHERE ((recv_orderitem_id=porderitemid)
+    AND  (NOT recv_posted)
+    AND  (recv_order_type=pordertype));
+
+  RETURN COALESCE(_qty, 0.0);
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/recallshipment.sql b/foundation-database/public/functions/recallshipment.sql
new file mode 100644 (file)
index 0000000..6d49226
--- /dev/null
@@ -0,0 +1,262 @@
+CREATE OR REPLACE FUNCTION recallShipment(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN recallShipment($1, CURRENT_TIMESTAMP);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION recallShipment(INTEGER, TIMESTAMP WITH TIME ZONE) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pshipheadid          ALIAS FOR $1;
+  _timestamp           TIMESTAMP WITH TIME ZONE := $2;
+  _allInvoiced         BOOLEAN;
+  _invoicePosted       BOOLEAN;
+  _in                   RECORD;
+  _co                  RECORD;
+  _cobill              RECORD;
+  _h                   RECORD;
+  _result               INTEGER;
+  _invhistid           INTEGER;
+  _itemlocSeries       INTEGER;
+  _qty                 NUMERIC;
+  _qtyToBill           NUMERIC;
+  _shiphead            RECORD;
+  _to                  RECORD;
+  _ti                  RECORD;
+  _value                NUMERIC;
+
+BEGIN
+
+  IF (_timestamp IS NULL) THEN
+    _timestamp := CURRENT_TIMESTAMP;
+  END IF;
+
+  SELECT * INTO _shiphead
+  FROM shiphead
+  WHERE (shiphead_id=pshipheadid);
+  IF (NOT FOUND OR NOT _shiphead.shiphead_shipped) THEN
+    RETURN -1;
+  END IF;
+
+  IF (_shiphead.shiphead_order_type = 'SO') THEN
+    SELECT cohead_number AS head_number, cohead_cust_id AS cust_id, cohead_prj_id AS prj_id,
+           cohead_saletype_id AS saletype_id, cohead_shipzone_id AS shipzone_id INTO _h
+      FROM cohead
+     WHERE (cohead_id=_shiphead.shiphead_order_id);
+    IF (NOT FOUND) THEN
+      RETURN -1;
+    END IF;
+
+    SELECT COALESCE(BOOL_AND(shipitem_invoiced), FALSE) INTO _allInvoiced
+     FROM cobill, shipitem
+    WHERE ((cobill_coitem_id=shipitem_orderitem_id)
+      AND  (shipitem_shiphead_id=pshipheadid));
+
+    IF (_allInvoiced AND NOT checkPrivilege('RecallInvoicedShipment')) THEN
+      RETURN -2;
+    END IF;
+
+    -- Check for any associated posted Invoices
+    SELECT COALESCE(BOOL_AND(invchead_posted), FALSE) INTO _invoicePosted
+    FROM shipitem JOIN invcitem ON (invcitem_id=shipitem_invcitem_id)
+                  JOIN invchead ON (invchead_id=invcitem_invchead_id)
+    WHERE (shipitem_shiphead_id=pshipheadid);
+
+    IF (_invoicePosted) THEN
+      RETURN -4;
+    END IF;
+
+    -- Delete any associated unposted Invoices
+    FOR _in IN SELECT DISTINCT invchead_id
+                 FROM shipitem JOIN invcitem ON (invcitem_id=shipitem_invcitem_id)
+                               JOIN invchead ON ( (invchead_id=invcitem_invchead_id) AND
+                                                  (NOT invchead_posted) )
+                WHERE (shipitem_shiphead_id=pshipheadid) LOOP
+      SELECT deleteInvoice(_in.invchead_id) INTO _result;
+      IF (_result < 0) THEN
+        RETURN _result;
+      END IF;
+    END LOOP;
+
+    FOR _co IN SELECT coitem_id, coitem_itemsite_id, coitem_qty_invuomratio, coitem_warranty, coitem_cos_accnt_id,
+                   itemsite_controlmethod
+                 FROM coitem
+                  JOIN itemsite ON (coitem_itemsite_id=itemsite_id)
+                WHERE(coitem_id IN (SELECT shipitem_orderitem_id
+                                      FROM shipitem, shiphead
+                                     WHERE((shipitem_shiphead_id=shiphead_id)
+                                       AND (shiphead_shipped)
+                                       AND (shiphead_id=pshipheadid)))) FOR UPDATE LOOP
+
+      SELECT SUM(shipitem_qty),SUM(COALESCE(shipitem_value, 0)) INTO _qty, _value
+      FROM shipitem
+      WHERE ( (shipitem_orderitem_id=_co.coitem_id)
+       AND (shipitem_shiphead_id=pshipheadid) );
+
+      UPDATE coitem
+      SET coitem_qtyshipped = (coitem_qtyshipped - _qty)
+      WHERE (coitem_id=_co.coitem_id);
+
+      _qtyToBill := _qty;
+      FOR _cobill IN SELECT cobill_id, cobill_qty
+                        FROM cobill, shipitem
+                       WHERE ((cobill_coitem_id=shipitem_orderitem_id)
+                         AND  (shipitem_shiphead_id=pshipheadid)
+                         AND  (cobill_coitem_id=_co.coitem_id)) FOR UPDATE LOOP
+
+        IF (noNeg(_cobill.cobill_qty - _qtyToBill) = 0) THEN
+          DELETE FROM cobill WHERE (cobill_id=_cobill.cobill_id);
+        ELSE
+         UPDATE cobill
+         SET cobill_qty = noNeg(cobill_qty - _qtyToBill)
+         WHERE (cobill_id=_cobill.cobill_id);
+       END IF;
+
+       _qtyToBill = _qtyToBill - _cobill.cobill_qty;
+       EXIT WHEN (_qtyToBill <= 0.0);
+      END LOOP;
+
+  --  Check to see if all of the cobills have been deleted for this cobmisc
+      IF (EXISTS(SELECT cobmisc_id
+                 FROM cobmisc JOIN cobill ON (cobill_cobmisc_id=cobmisc_id)
+                 WHERE (cobmisc_cohead_id=_shiphead.shiphead_order_id AND NOT cobmisc_posted))) THEN
+  --  Lines exist, update the freight
+        UPDATE cobmisc SET cobmisc_freight = (cobmisc_freight - _shiphead.shiphead_freight)
+        WHERE (cobmisc_cohead_id=_shiphead.shiphead_order_id AND NOT cobmisc_posted);
+      ELSE
+  --  No lines exist, delete the cobmisc
+        DELETE FROM cobmisc
+        WHERE (cobmisc_cohead_id=_shiphead.shiphead_order_id AND NOT cobmisc_posted);
+      END IF;
+
+  --  Distribute to G/L, debit Shipping Asset, credit COS
+      IF (_co.itemsite_controlmethod != 'N') THEN
+        PERFORM insertGLTransaction( 'S/R', _shiphead.shiphead_order_type,
+                                  _h.head_number::TEXT, 'Recall Shipment',
+                                   CASE WHEN(COALESCE(_co.coitem_cos_accnt_id, -1) != -1)
+                                          THEN getPrjAccntId(_h.prj_id, _co.coitem_cos_accnt_id)
+                                        WHEN(_co.coitem_warranty = TRUE)
+                                          THEN getPrjAccntId(_h.prj_id, resolveCOWAccount(itemsite_id, _h.cust_id, _h.saletype_id, _h.shipzone_id))
+                                       ELSE getPrjAccntId(_h.prj_id, resolveCOSAccount(itemsite_id, _h.cust_id, _h.saletype_id, _h.shipzone_id))
+                                   END,
+                                   getPrjAccntId(_h.prj_id,costcat_shipasset_accnt_id), -1,
+                                  _value,
+                                  _timestamp::DATE )
+        FROM itemsite, costcat
+        WHERE ( (itemsite_costcat_id=costcat_id)
+         AND (itemsite_id=_co.coitem_itemsite_id) );
+       END IF;
+
+    END LOOP;
+
+-- Kit billing selection
+-- Set kit billing qty to zero since kits are shipped complete
+    FOR _cobill IN SELECT cobill_id, cobill_qty
+                   FROM shipitem JOIN coitem sub ON (sub.coitem_id=shipitem_orderitem_id)
+                                 JOIN coitem kit ON (kit.coitem_id <> sub.coitem_id AND
+                                                     kit.coitem_cohead_id = sub.coitem_cohead_id AND
+                                                     kit.coitem_linenumber = sub.coitem_linenumber AND
+                                                     kit.coitem_subnumber = 0)
+                                 JOIN cobill ON (cobill_coitem_id=kit.coitem_id)
+                   WHERE (shipitem_shiphead_id=pshipheadid)
+                     AND (sub.coitem_subnumber > 0)
+                   GROUP BY cobill_id, cobill_qty
+    LOOP
+      UPDATE cobill SET cobill_qty = 0.0
+      WHERE (cobill_id=_cobill.cobill_id);
+    END LOOP;
+
+  ELSEIF (_shiphead.shiphead_order_type = 'TO') THEN
+    SELECT * INTO _to
+      FROM tohead
+     WHERE (tohead_id=_shiphead.shiphead_order_id);
+    IF (NOT FOUND) THEN
+      RETURN -1;
+    END IF;
+    IF (_to.tohead_status = 'C') THEN
+      RETURN -6;
+    END IF;
+
+    FOR _ti IN SELECT toitem_id,
+                      sis.itemsite_id AS src_itemsite_id,
+                      tis.itemsite_id AS trns_itemsite_id,
+                      scc.costcat_shipasset_accnt_id AS src_shipasset_accnt_id,
+                      tcc.costcat_asset_accnt_id AS trns_asset_accnt_id,
+                      itemcost(tis.itemsite_id) AS trns_cost,
+                      SUM(shipitem_qty) AS recall_qty
+               FROM shipitem JOIN toitem ON (toitem_id=shipitem_orderitem_id)
+                             JOIN itemsite sis ON (sis.itemsite_item_id=toitem_item_id AND sis.itemsite_warehous_id=_to.tohead_src_warehous_id)
+                             JOIN itemsite tis ON (tis.itemsite_item_id=toitem_item_id AND tis.itemsite_warehous_id=_to.tohead_trns_warehous_id)
+                             JOIN costcat scc ON (scc.costcat_id=sis.itemsite_costcat_id)
+                             JOIN costcat tcc ON (tcc.costcat_id=tis.itemsite_costcat_id)
+               WHERE (shipitem_shiphead_id=pshipheadid)
+               GROUP BY toitem_id, sis.itemsite_id, tis.itemsite_id,
+                        scc.costcat_shipasset_accnt_id, tcc.costcat_asset_accnt_id
+    LOOP
+
+      _itemlocSeries := NEXTVAL('itemloc_series_seq');
+      
+      SELECT postInvTrans(_ti.src_itemsite_id, 'TS', (_ti.recall_qty * -1.0), 'I/M',
+                         _shiphead.shiphead_order_type, formatToNumber(_ti.toitem_id),
+                         _to.tohead_number,
+                         'Recall TO Shipment To Src Warehouse',
+                         _ti.trns_asset_accnt_id,
+                         _ti.src_shipasset_accnt_id,
+                         _itemlocSeries, _timestamp,
+                          (_ti.trns_cost * _ti.recall_qty * -1.0)) INTO _invhistid;
+
+      IF (_invhistid < 0) THEN
+       RETURN _invhistid;
+      END IF;
+
+      -- post the inventory history if lot/serial or location control
+      PERFORM postItemlocseries(_itemlocSeries);
+
+      -- record inventory history and qoh changes at transit warehouse but
+      -- there is only one g/l account to touch
+      SELECT postInvTrans(_ti.trns_itemsite_id, 'TR', (_ti.recall_qty * -1.0), 'I/M',
+                         _shiphead.shiphead_order_type, formatToNumber(_ti.toitem_id),
+                         _to.tohead_number,
+                         'Recall TO Shipment From Transit Warehouse',
+                         _ti.trns_asset_accnt_id,
+                         _ti.trns_asset_accnt_id,
+                         _itemlocSeries, _timestamp,
+                          (_ti.trns_cost * _ti.recall_qty * -1.0)) INTO _invhistid;
+
+      IF (_invhistid < 0) THEN
+       RETURN _invhistid;
+      END IF;
+
+      -- post the inventory history if lot/serial or location control
+      PERFORM postItemlocseries(_itemlocSeries);
+
+      UPDATE toitem
+      SET toitem_qty_shipped = (toitem_qty_shipped - _ti.recall_qty)
+      WHERE (toitem_id=_ti.toitem_id);
+
+      UPDATE shipitem SET shipitem_shipdate=NULL,
+                          shipitem_shipped=FALSE,
+                          shipitem_value=(shipitem_qty * _ti.trns_cost)
+      WHERE ((shipitem_orderitem_id=_ti.toitem_id)
+        AND  (shipitem_shiphead_id=pshipheadid));
+
+      DELETE FROM recv
+       WHERE ((recv_orderitem_id=_ti.toitem_id)
+         AND  (recv_order_type='TO')
+         AND  (NOT recv_posted));
+
+    END LOOP;
+
+  END IF;
+
+  UPDATE shiphead
+  SET shiphead_shipped=FALSE
+  WHERE (shiphead_id=pshipheadid);
+
+  RETURN _itemlocSeries;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/recallwo.sql b/foundation-database/public/functions/recallwo.sql
new file mode 100644 (file)
index 0000000..6ec3269
--- /dev/null
@@ -0,0 +1,25 @@
+CREATE OR REPLACE FUNCTION recallWo(INTEGER, BOOLEAN) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWoid ALIAS FOR $1;
+  recallChildren ALIAS FOR $2;
+  returnCode INTEGER;
+
+BEGIN
+
+  UPDATE wo
+  SET wo_status=''E''
+  WHERE ((wo_status=''R'')
+   AND (wo_id=pWoid));
+
+  IF (recallChildren) THEN
+    returnCode := (SELECT MAX(recallWo(wo_id, TRUE))
+                   FROM wo
+                   WHERE ((wo_ordtype=''W'')
+                    AND (wo_ordid=pWoid)));
+  END IF;
+
+  RETURN 0;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/receipts.sql b/foundation-database/public/functions/receipts.sql
new file mode 100644 (file)
index 0000000..e9593e1
--- /dev/null
@@ -0,0 +1,15 @@
+CREATE OR REPLACE FUNCTION receipts(TEXT) RETURNS BOOLEAN AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pTransType ALIAS FOR $1;
+
+BEGIN
+  IF (pTransType IN ('RM', 'RB', 'RT', 'RP', 'RR', 'RX', 'TR')) THEN
+    RETURN TRUE;
+  ELSE
+    RETURN FALSE;
+  END IF;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/releaseapmemonumber.sql b/foundation-database/public/functions/releaseapmemonumber.sql
new file mode 100644 (file)
index 0000000..2e35ed4
--- /dev/null
@@ -0,0 +1,5 @@
+CREATE OR REPLACE FUNCTION releaseAPMemoNumber(INTEGER) RETURNS BOOLEAN AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+  SELECT releaseNumber('APMemoNumber', $1::INTEGER) > 0;
+$$ LANGUAGE 'sql';
diff --git a/foundation-database/public/functions/releasearmemonumber.sql b/foundation-database/public/functions/releasearmemonumber.sql
new file mode 100644 (file)
index 0000000..81089ab
--- /dev/null
@@ -0,0 +1,7 @@
+
+CREATE OR REPLACE FUNCTION releaseARMemoNumber(INTEGER) RETURNS BOOLEAN AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+  select releaseNumber('ARMemoNumber', $1::INTEGER) > 0;
+$$ LANGUAGE 'sql';
+
diff --git a/foundation-database/public/functions/releasecashrcptnumber.sql b/foundation-database/public/functions/releasecashrcptnumber.sql
new file mode 100644 (file)
index 0000000..3724a2c
--- /dev/null
@@ -0,0 +1,7 @@
+
+CREATE OR REPLACE FUNCTION releaseCashRcptNumber(INTEGER) RETURNS BOOLEAN AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+  SELECT releaseNumber('CashRcptNumber', $1) > 0;
+$$ LANGUAGE 'sql';
+
diff --git a/foundation-database/public/functions/releasecmnumber.sql b/foundation-database/public/functions/releasecmnumber.sql
new file mode 100644 (file)
index 0000000..9fe486e
--- /dev/null
@@ -0,0 +1,12 @@
+CREATE OR REPLACE FUNCTION releaseCMNumber(INTEGER) RETURNS BOOLEAN AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+  SELECT releaseNumber('CmNumber', $1) > 0;
+$$ LANGUAGE sql;
+
+CREATE OR REPLACE FUNCTION releaseCMNumber(TEXT) RETURNS BOOLEAN AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+  SELECT releaseNumber('CmNumber', $1::INTEGER) > 0;
+$$ LANGUAGE sql;
+
diff --git a/foundation-database/public/functions/releasecrmaccountnumber.sql b/foundation-database/public/functions/releasecrmaccountnumber.sql
new file mode 100644 (file)
index 0000000..acafc55
--- /dev/null
@@ -0,0 +1,6 @@
+CREATE OR REPLACE FUNCTION releaseCRMAccountNumber(INTEGER) RETURNS BOOLEAN AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+  SELECT releaseNumber('CRMAccountNumber', $1::INTEGER) > 0;
+$$ LANGUAGE sql;
+
diff --git a/foundation-database/public/functions/releaseincidentnumber.sql b/foundation-database/public/functions/releaseincidentnumber.sql
new file mode 100644 (file)
index 0000000..62e202e
--- /dev/null
@@ -0,0 +1,6 @@
+CREATE OR REPLACE FUNCTION releaseIncidentNumber(INTEGER) RETURNS BOOLEAN AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+  SELECT releaseNumber('IncidentNumber', $1) = 1;
+$$ LANGUAGE sql;
+
diff --git a/foundation-database/public/functions/releaseinvcnumber.sql b/foundation-database/public/functions/releaseinvcnumber.sql
new file mode 100644 (file)
index 0000000..94b71b6
--- /dev/null
@@ -0,0 +1,11 @@
+CREATE OR REPLACE FUNCTION releaseInvcNumber(INTEGER) RETURNS BOOLEAN AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+  SELECT releaseNumber('InvcNumber', $1) > 0;
+$$ LANGUAGE sql;
+
+CREATE OR REPLACE FUNCTION releaseInvcNumber(TEXT) RETURNS BOOLEAN AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+  SELECT releaseNumber('InvcNumber', $1::INTEGER) > 0;
+$$ LANGUAGE sql;
diff --git a/foundation-database/public/functions/releasenumber.sql b/foundation-database/public/functions/releasenumber.sql
new file mode 100644 (file)
index 0000000..590f607
--- /dev/null
@@ -0,0 +1,17 @@
+CREATE OR REPLACE FUNCTION releaseNumber(TEXT, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  psequence    ALIAS FOR $1;
+  pnumber      ALIAS FOR $2;
+BEGIN
+  -- drop the number back into the pool if it was not committed
+  PERFORM clearNumberIssue(psequence, pnumber);
+  
+  UPDATE orderseq SET
+    orderseq_number = LEAST(pnumber, orderseq_number)
+  WHERE (orderseq_name=psequence);
+
+  RETURN 1;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/releaseponumber.sql b/foundation-database/public/functions/releaseponumber.sql
new file mode 100644 (file)
index 0000000..93b62a1
--- /dev/null
@@ -0,0 +1,12 @@
+CREATE OR REPLACE FUNCTION releasePoNumber(INTEGER) RETURNS BOOLEAN AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+  SELECT releaseNumber('PoNumber', $1) > 0;
+$$ LANGUAGE 'sql';
+
+
+CREATE OR REPLACE FUNCTION releasePoNumber(TEXT) RETURNS BOOLEAN AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+  SELECT releaseNumber('PoNumber', $1::INTEGER) > 0;
+$$ LANGUAGE 'sql';
diff --git a/foundation-database/public/functions/releaseprnumber.sql b/foundation-database/public/functions/releaseprnumber.sql
new file mode 100644 (file)
index 0000000..5702863
--- /dev/null
@@ -0,0 +1,5 @@
+CREATE OR REPLACE FUNCTION releasePrNumber(INTEGER) RETURNS BOOLEAN AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+  SELECT releaseNumber('PrNumber', $1) > 0;
+$$ LANGUAGE 'sql';
diff --git a/foundation-database/public/functions/releasepurchaseorder.sql b/foundation-database/public/functions/releasepurchaseorder.sql
new file mode 100644 (file)
index 0000000..4d409cb
--- /dev/null
@@ -0,0 +1,35 @@
+CREATE OR REPLACE FUNCTION releasePurchaseOrder(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pPoheadid ALIAS FOR $1;
+
+BEGIN
+
+  IF ( ( SELECT (COUNT(*) = 0)
+         FROM poitem
+         WHERE ( (poitem_pohead_id=pPoheadid)
+           AND   (poitem_status='U') ) ) ) THEN
+    RETURN -1;
+  END IF;
+
+  IF ( ( SELECT (pohead_status='U')
+         FROM pohead
+         WHERE (pohead_id=pPoheadid) ) ) THEN
+
+    --update status and store the date that the order was released on
+    UPDATE pohead
+    SET pohead_status='O', pohead_released = current_date
+    WHERE (pohead_id=pPoheadid);
+
+  END IF;
+
+  --update status and store the duedates at release
+  UPDATE poitem
+  SET poitem_status='O', poitem_rlsd_duedate = poitem_duedate
+  WHERE (poitem_pohead_id=pPoheadid);
+
+  RETURN 1;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/releasequnumber.sql b/foundation-database/public/functions/releasequnumber.sql
new file mode 100644 (file)
index 0000000..2049453
--- /dev/null
@@ -0,0 +1,11 @@
+CREATE OR REPLACE FUNCTION releaseQuNumber(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+  SELECT releaseNumber('QuNumber', $1);
+$$ LANGUAGE 'sql';
+
+CREATE OR REPLACE FUNCTION releaseQuNumber(TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+  SELECT releaseNumber('QuNumber', $1::INTEGER);
+$$ LANGUAGE 'sql';
diff --git a/foundation-database/public/functions/releaseshipmentnumber.sql b/foundation-database/public/functions/releaseshipmentnumber.sql
new file mode 100644 (file)
index 0000000..419fd6d
--- /dev/null
@@ -0,0 +1,36 @@
+CREATE OR REPLACE FUNCTION releaseShipmentNumber(INTEGER) RETURNS BOOLEAN AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pNumber ALIAS FOR $1;
+  _test INTEGER;
+
+BEGIN
+
+--  Check to see if a Shipment exists with the passed number
+  SELECT shiphead_id INTO _test
+  FROM shiphead
+  WHERE (shiphead_number=pNumber);
+
+  IF (FOUND) THEN
+    RETURN FALSE;
+  END IF;
+
+--  Check to see if ShipmentNumber orderseq has been incremented past the passed number
+  SELECT orderseq_number INTO _test
+  FROM orderseq
+  WHERE (orderseq_name=''ShipmentNumber'');
+
+  IF ((_test - 1) <> pNumber) THEN
+    RETURN FALSE;
+  END IF;
+
+--  Decrement the orderseq, releasing the passed number
+  UPDATE orderseq
+  SET orderseq_number = (orderseq_number - 1)
+  WHERE (orderseq_name=''ShipmentNumber'');
+
+  RETURN TRUE;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/releasesohead.sql b/foundation-database/public/functions/releasesohead.sql
new file mode 100644 (file)
index 0000000..e6f3039
--- /dev/null
@@ -0,0 +1,18 @@
+
+CREATE OR REPLACE FUNCTION releaseSohead(INTEGER) RETURNS BOOLEAN AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSoheadid ALIAS FOR $1;
+
+BEGIN
+
+  DELETE FROM soheadlock
+   WHERE ( (soheadlock_sohead_id=pSoheadid)
+     AND   (soheadlock_username=getEffectiveXtUser())
+     AND   (soheadlock_procpid=pg_backend_pid()) );
+
+  RETURN TRUE;
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/releasesonumber.sql b/foundation-database/public/functions/releasesonumber.sql
new file mode 100644 (file)
index 0000000..dc10690
--- /dev/null
@@ -0,0 +1,20 @@
+CREATE OR REPLACE FUNCTION releaseSoNumber(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+  SELECT releaseNumber('SoNumber', $1);
+$$ LANGUAGE 'sql';
+
+CREATE OR REPLACE FUNCTION releaseSoNumber(TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+       IF (COALESCE($1, '') = '' OR $1 ~ '[^0-9]')
+       THEN
+       --do nothing;
+       RETURN 1;
+       ELSE
+       RETURN releaseNumber('SoNumber', $1::INTEGER);
+       
+       END IF;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/releaseunusedbillingheader.sql b/foundation-database/public/functions/releaseunusedbillingheader.sql
new file mode 100644 (file)
index 0000000..d2760a1
--- /dev/null
@@ -0,0 +1,29 @@
+CREATE OR REPLACE FUNCTION releaseUnusedBillingHeader(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCobmiscid ALIAS FOR $1;
+  _p RECORD;
+
+BEGIN
+
+  IF ( ( SELECT cobmisc_posted
+         FROM cobmisc
+         WHERE (cobmisc_id=pCobmiscid) ) ) THEN
+    RETURN -1;
+  END IF;
+
+  SELECT cobill_id INTO _p
+    FROM cobill
+   WHERE (cobill_cobmisc_id=pCobmiscid)
+   LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -2;
+  END IF;
+
+  DELETE FROM cobmisc
+  WHERE (cobmisc_id=pCobmiscid);
+
+  RETURN 0;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/releasevonumber.sql b/foundation-database/public/functions/releasevonumber.sql
new file mode 100644 (file)
index 0000000..fabba2c
--- /dev/null
@@ -0,0 +1,5 @@
+CREATE OR REPLACE FUNCTION releaseVoNumber(INTEGER) RETURNS BOOLEAN AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+  SELECT releaseNumber('VcNumber', $1) > 0;
+$$ LANGUAGE 'sql';
diff --git a/foundation-database/public/functions/releasewo.sql b/foundation-database/public/functions/releasewo.sql
new file mode 100644 (file)
index 0000000..62cd8c7
--- /dev/null
@@ -0,0 +1,23 @@
+CREATE OR REPLACE FUNCTION releaseWo(INTEGER, BOOLEAN) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWoid ALIAS FOR $1;
+  releaseChildren ALIAS FOR $2;
+  returnCode INTEGER;
+BEGIN
+  UPDATE wo
+  SET wo_status=''R''
+  WHERE ((wo_status=''E'')
+   AND (wo_id=pWoid));
+
+  IF (releaseChildren) THEN
+    returnCode := (SELECT MAX(releaseWo(wo_id, TRUE))
+                   FROM wo
+                   WHERE ((wo_ordtype=''W'')
+                    AND (wo_ordid=pWoid)));
+  END IF;
+
+  RETURN 0;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/releasewonumber.sql b/foundation-database/public/functions/releasewonumber.sql
new file mode 100644 (file)
index 0000000..69139f8
--- /dev/null
@@ -0,0 +1,5 @@
+CREATE OR REPLACE FUNCTION releaseWoNumber(INTEGER) RETURNS BOOLEAN AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+  SELECT releaseNumber('WoNumber', $1) > 0;
+$$ LANGUAGE 'sql';
diff --git a/foundation-database/public/functions/relocateinventory.sql b/foundation-database/public/functions/relocateinventory.sql
new file mode 100644 (file)
index 0000000..81b736c
--- /dev/null
@@ -0,0 +1,191 @@
+CREATE OR REPLACE FUNCTION relocateInventory(INTEGER, INTEGER, INTEGER, NUMERIC, TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN relocateInventory($1, $2, $3, $4, $5, CURRENT_TIMESTAMP);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION relocateInventory(INTEGER, INTEGER, INTEGER, NUMERIC, TEXT, TIMESTAMP WITH TIME ZONE) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSourceItemlocid      ALIAS FOR $1;
+  pTargetLocationid     ALIAS FOR $2;
+  pItemsiteid           ALIAS FOR $3;
+  pQty                  ALIAS FOR $4;
+  pComments             ALIAS FOR $5;
+  _GlDistTS             TIMESTAMP WITH TIME ZONE := $6;
+  _targetItemlocid      INTEGER;
+  _invhistid            INTEGER;
+  _p RECORD;
+  _qty NUMERIC;
+  _itemlocSeries INTEGER := NEXTVAL('itemloc_series_seq');
+
+BEGIN
+
+    IF ((_GlDistTS IS NULL) OR (CAST(_GlDistTS AS date)=CURRENT_DATE)) THEN
+      _GlDistTS := CURRENT_TIMESTAMP;
+    END IF;
+
+--  Make sure the passed itemsite points to a real item
+  IF ( ( SELECT (item_type IN ('R', 'F') OR itemsite_costmethod = 'J')
+         FROM itemsite, item
+         WHERE ( (itemsite_item_id=item_id)
+          AND (itemsite_id=pItemsiteid) ) ) ) THEN
+    RETURN 0;
+  END IF;
+
+--  Cache some parameters
+  SELECT itemloc_ls_id,
+         itemloc_itemsite_id AS itemsiteid,
+         itemloc_expiration,
+         itemloc_warrpurc,
+         itemloc_qty,
+         sourceloc.location_netable AS sourcenet,
+         targetloc.location_netable AS targetnet INTO _p
+  FROM itemloc, location AS sourceloc, location AS targetloc
+  WHERE ( (itemloc_location_id=sourceloc.location_id)
+   AND (targetloc.location_id=pTargetLocationid)
+   AND (itemloc_id=pSourceItemlocid) );
+
+--  Check to make sure the qty being transfered exists
+  IF (_p.itemloc_qty < pQty) THEN
+    RETURN -1;
+  END IF;
+
+--  Create the RL transaction
+  SELECT NEXTVAL('invhist_invhist_id_seq') INTO _invhistid;
+  INSERT INTO invhist
+  ( invhist_id, invhist_itemsite_id,
+    invhist_transtype, invhist_invqty,
+    invhist_qoh_before, invhist_qoh_after,
+    invhist_comments,   invhist_transdate,
+    invhist_invuom, invhist_unitcost, invhist_costmethod,
+    invhist_value_before, invhist_value_after, invhist_series) 
+  SELECT _invhistid, itemsite_id,
+         'RL', 0,
+         itemsite_qtyonhand, itemsite_qtyonhand,
+         pComments, _GlDistTS,
+         uom_name,
+         CASE WHEN (itemsite_costmethod='A') THEN avgcost(itemsite_id)
+              ELSE stdCost(item_id)
+         END, itemsite_costmethod,
+         itemsite_value, itemsite_value, _itemlocSeries
+  FROM item, itemsite, uom
+  WHERE ((itemsite_item_id=item_id)
+   AND (item_inv_uom_id=uom_id)
+   AND (itemsite_controlmethod <> 'N')
+   AND (itemsite_id=pItemsiteid));
+
+--  Relocate the inventory from the source and record the transactions
+  INSERT INTO invdetail
+  ( invdetail_invhist_id, invdetail_location_id, invdetail_ls_id,
+    invdetail_qty, invdetail_qty_before, invdetail_qty_after,
+    invdetail_expiration, invdetail_warrpurc )
+  SELECT _invhistid, itemloc_location_id, itemloc_ls_id,
+         (pQty * -1), itemloc_qty, (itemloc_qty - pQty),
+         itemloc_expiration, itemloc_warrpurc
+  FROM itemloc
+  WHERE (itemloc_id=pSourceItemlocid);
+
+  UPDATE itemloc
+  SET itemloc_qty=(itemloc_qty - pQty)
+  FROM itemsite
+  WHERE ( (itemloc_itemsite_id=itemsite_id)
+   AND (NOT itemsite_freeze)
+   AND (itemloc_id=pSourceItemlocid) );
+
+--  Check to see if there is anything left at the source Itemloc and delete if not
+  DELETE FROM itemloc
+  WHERE ( (itemloc_qty=0)
+   AND (itemloc_id=pSourceItemlocid) );
+
+--  Check to see if any of the current Lot/Serial #/Expiration exists at the target location
+  SELECT itemloc_id INTO _targetItemlocid
+  FROM itemloc 
+  WHERE ( (COALESCE(itemloc_ls_id, -1)=COALESCE(_p.itemloc_ls_id,-1))
+   AND (COALESCE(itemloc_expiration,endOfTime())=COALESCE(_p.itemloc_expiration,endOfTime()))
+   AND (COALESCE(itemloc_warrpurc,endOfTime())=COALESCE(_p.itemloc_warrpurc,endOfTime()))
+   AND (itemloc_itemsite_id=pItemsiteid)
+   AND (itemloc_location_id=pTargetLocationid) );
+
+  IF (NOT FOUND) THEN
+    SELECT NEXTVAL('itemloc_itemloc_id_seq') INTO _targetItemlocid;
+    INSERT INTO itemloc
+    ( itemloc_id, itemloc_itemsite_id, itemloc_location_id,
+      itemloc_ls_id, itemloc_expiration, itemloc_warrpurc, itemloc_qty )
+    VALUES
+    ( _targetItemlocid, pItemsiteid, pTargetLocationid,
+      _p.itemloc_ls_id, _p.itemloc_expiration, _p.itemloc_warrpurc, 0 );
+  END IF;
+
+--  Relocate the inventory to the resultant target and record the transactions
+  INSERT INTO invdetail
+  ( invdetail_invhist_id, invdetail_location_id, invdetail_ls_id,
+    invdetail_qty, invdetail_qty_before, invdetail_qty_after,
+    invdetail_expiration, invdetail_warrpurc )
+  SELECT _invhistid, pTargetLocationid, _p.itemloc_ls_id,
+         pQty, itemloc_qty, (itemloc_qty + pQty), 
+         _p.itemloc_expiration, _p.itemloc_warrpurc
+  FROM itemloc
+  WHERE (itemloc_id=_targetItemlocid);
+
+  UPDATE itemloc
+  SET itemloc_qty=(itemloc_qty + pQty)
+  FROM itemsite
+  WHERE ( (itemloc_itemsite_id=itemsite_id)
+   AND (NOT itemsite_freeze)
+   AND (itemloc_id=_targetItemlocid) );
+
+  UPDATE invhist
+  SET invhist_hasdetail=TRUE
+  WHERE (invhist_id=_invhistid);
+
+--  Post in incomming or outgoing NN transaction if required
+  IF (_p.sourcenet <> _p.targetnet) THEN
+    IF (_p.targetnet) THEN
+      _qty = (pQty * -1);
+    ELSE
+      _qty = pQty;
+    END IF;
+
+    INSERT INTO invhist
+    ( invhist_itemsite_id,
+      invhist_transtype, invhist_invqty,
+      invhist_qoh_before, invhist_qoh_after,
+      invhist_docnumber, invhist_comments, invhist_transdate,
+      invhist_invuom, invhist_unitcost, invhist_costmethod,
+      invhist_value_before, invhist_value_after, invhist_series) 
+    SELECT itemsite_id,
+           'NN', (_qty * -1),
+           itemsite_qtyonhand, (itemsite_qtyonhand - _qty),
+           '', '', _GlDistTS,
+           uom_name,
+           CASE WHEN (itemsite_costmethod='A') THEN avgcost(itemsite_id)
+                ELSE stdCost(item_id)
+           END, itemsite_costmethod,
+           itemsite_value, itemsite_value, _itemlocSeries
+    FROM item, itemsite, uom
+    WHERE ( (itemsite_item_id=item_id)
+     ANd (item_inv_uom_id=uom_id)
+     AND (itemsite_controlmethod <> 'N')
+     AND (itemsite_id=_p.itemsiteid) );
+
+    UPDATE itemsite
+    SET itemsite_qtyonhand = (itemsite_qtyonhand - _qty),
+        itemsite_nnqoh = (itemsite_nnqoh + _qty)
+    WHERE (itemsite_id=_p.itemsiteid);
+  END IF;
+
+--  Check to see if there is anything left at the target Itemloc and delete if not
+--  Could be zero if relocate increased a negative quantity to zero
+  DELETE FROM itemloc
+  WHERE ( (itemloc_qty=0)
+   AND (itemloc_id=_targetItemlocid) );
+
+--  Return the invhist_id
+  RETURN _invhistid;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/reorderdate.sql b/foundation-database/public/functions/reorderdate.sql
new file mode 100644 (file)
index 0000000..268028f
--- /dev/null
@@ -0,0 +1,116 @@
+
+CREATE OR REPLACE FUNCTION reorderDate(INTEGER, INTEGER, BOOL) RETURNS DATE AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  pLookAhead ALIAS FOR $2;
+  pIncludePlanned ALIAS FOR $3;
+  _runningAvailability NUMERIC;
+  _reorderLevel NUMERIC;
+  _availability RECORD;
+
+BEGIN
+
+--  Make sure that we know how to handle the passed part
+  IF ( SELECT (NOT (item_type IN (''M'', ''P'')))
+       FROM item, itemsite
+       WHERE ( (itemsite_item_id=item_id)
+        AND (itemsite_id=pItemsiteid) ) ) THEN
+    RETURN NULL;
+  END IF;
+
+--  Load the initial QOH
+  SELECT itemsite_qtyonhand INTO _runningAvailability
+  FROM itemsite
+  WHERE (itemsite_id=pItemsiteid);
+
+--  Grab the Reorder Level, if any
+  IF ( ( SELECT itemsite_useparams
+         FROM itemsite
+         WHERE (itemsite_id=pItemsiteid) ) ) THEN
+    SELECT itemsite_reorderlevel INTO _reorderLevel
+    FROM itemsite
+    WHERE (itemsite_id=pItemsiteid);
+  ELSE
+    _reorderLevel := 0;
+  END IF;
+
+--  If we are already below the Reorder Level then we should order ASAP
+  IF (_runningAvailability <= _reorderLevel) THEN
+    RETURN CURRENT_DATE;
+  END IF;
+
+--  Grab all of the availability trigger points
+  FOR _availability IN SELECT 1 AS seq,
+                              wo_duedate AS orderdate,
+                              (noNeg(wo_qtyord - wo_qtyrcv)) AS balance
+                       FROM wo
+                       WHERE ((wo_status IN (''O'', ''E'', ''R'', ''I''))
+                        AND (wo_duedate <= (CURRENT_DATE + pLookAhead))
+                        AND (wo_itemsite_id=pItemsiteid))
+
+                      UNION SELECT 2 AS seq,
+                                   womatl_duedate AS orderdate,
+                                   (noNeg(itemuomtouom(itemsite_item_id, womatl_uom_id, NULL, womatl_qtyreq - womatl_qtyiss)) * -1) AS balance
+                      FROM womatl, wo, itemsite
+                      WHERE ((wo_status IN (''O'', ''E'', ''R'', ''I''))
+                       AND (womatl_wo_id=wo_id)
+                       AND (womatl_itemsite_id=itemsite_id)
+                       AND (womatl_duedate <= (CURRENT_DATE + pLookahead))
+                       AND (womatl_itemsite_id=pItemsiteid))
+
+                      UNION SELECT 1 AS seq,
+                                   poitem_duedate AS orderdate,
+                                   (noNeg(poitem_qty_ordered - poitem_qty_received) * poitem_invvenduomratio) AS balance
+                      FROM pohead, poitem
+                      WHERE ((poitem_pohead_id=pohead_id)
+                       AND (poitem_status = ''O'')
+                       AND (poitem_duedate <= (CURRENT_DATE + pLookAhead))
+                       AND (poitem_itemsite_id=pItemsiteid))
+
+                      UNION SELECT 2 AS seq,
+                                   coitem_scheddate AS orderdate,
+                                   (noNeg(coitem_qtyord - coitem_qtyshipped + coitem_qtyreturned) * -1) AS balance
+                      FROM coitem, cohead
+                      WHERE ((coitem_status = ''O'')
+                       AND (coitem_cohead_id=cohead_id)
+                       AND (coitem_scheddate <= (CURRENT_DATE + pLookAhead))
+                       AND (coitem_itemsite_id=pItemsiteid))
+
+                      UNION SELECT 2 AS seq,
+                                   planord_startdate AS orderdate,
+                                   (planreq_qty * -1) AS balance
+                      FROM planreq, planord
+                      WHERE ( (pIncludePlanned)
+                       AND (planreq_source=''P'')
+                       AND (planreq_source_id=planord_id)
+                       AND (planord_startdate <= (CURRENT_DATE + pLookAhead))
+                       AND (planord_itemsite_id=pItemsiteid) )
+
+                      UNION SELECT 1 AS seq,
+                                   planord_duedate AS orderdate,
+                                   planord_qty AS balance
+                      FROM planord
+                      WHERE ( (pIncludePlanned)
+                       AND (planord_duedate <= (CURRENT_DATE + pLookAhead))
+                       AND (planord_itemsite_id=pItemsiteid) )
+
+                      ORDER BY orderdate, seq LOOP
+
+--  Calculate the new projected availability
+    _runningAvailability := (_runningAvailability + _availability.balance);
+
+--  Check to see if the project availability drop below the reorder level
+    IF (_runningAvailability < _reorderLevel) THEN
+      RETURN _availability.orderdate;
+    END IF;
+
+  END LOOP;
+
+--  The reorder level was not reached within the look ahead period
+  RETURN NULL;
+
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/replaceallvoidedapchecks.sql b/foundation-database/public/functions/replaceallvoidedapchecks.sql
new file mode 100644 (file)
index 0000000..eb0d068
--- /dev/null
@@ -0,0 +1,8 @@
+CREATE OR REPLACE FUNCTION replaceAllVoidedAPChecks(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RAISE NOTICE ''replaceAllVoidedAPChecks() is deprecated - use replaceAllVoidedChecks() instead'';
+  RETURN replaceAllVoidedChecks($1);
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/replaceallvoidedchecks.sql b/foundation-database/public/functions/replaceallvoidedchecks.sql
new file mode 100644 (file)
index 0000000..f8d22d1
--- /dev/null
@@ -0,0 +1,21 @@
+CREATE OR REPLACE FUNCTION replaceAllVoidedChecks(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pBankaccntid ALIAS FOR $1;
+  _returnValue INTEGER := 0;
+
+BEGIN
+
+  SELECT MIN(replaceVoidedCheck(checkhead_id)) INTO _returnValue
+    FROM checkhead
+    WHERE ( (checkhead_void)
+     AND (NOT checkhead_posted)
+     AND (NOT checkhead_replaced)
+     AND (NOT checkhead_deleted)
+     AND (checkhead_bankaccnt_id=pBankaccntid) );
+
+  RETURN _returnValue;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/replacevoidedapcheck.sql b/foundation-database/public/functions/replacevoidedapcheck.sql
new file mode 100644 (file)
index 0000000..6e715ad
--- /dev/null
@@ -0,0 +1,8 @@
+CREATE OR REPLACE FUNCTION replaceVoidedAPCheck(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RAISE NOTICE ''replaceVoidedAPCheck() is deprecated - use replaceVoidedCheck()'';
+  RETURN replaceVoidedCheck($1);
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/replacevoidedcheck.sql b/foundation-database/public/functions/replacevoidedcheck.sql
new file mode 100644 (file)
index 0000000..e101d44
--- /dev/null
@@ -0,0 +1,66 @@
+CREATE OR REPLACE FUNCTION replaceVoidedCheck(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCheckid     ALIAS FOR $1;
+  _newCheckid  INTEGER;
+
+BEGIN
+  IF ( ( SELECT ( (NOT checkhead_void) OR checkhead_posted OR checkhead_replaced )
+         FROM checkhead
+         WHERE (checkhead_id=pCheckid) ) ) THEN
+    RETURN -1;
+  END IF;
+
+  -- has someone created a new check for one of the items while this was void?
+  IF EXISTS (SELECT dup.checkitem_id
+             FROM checkitem orig, checkitem dup, checkhead AS duphead
+             WHERE ((COALESCE(orig.checkitem_aropen_id,-1)=COALESCE(dup.checkitem_aropen_id,-1))
+                AND (COALESCE(orig.checkitem_apopen_id,-1)=COALESCE(dup.checkitem_apopen_id,-1))
+                AND (orig.checkitem_checkhead_id!=dup.checkitem_checkhead_id)
+                AND (dup.checkitem_checkhead_id=duphead.checkhead_id)
+                AND (NOT duphead.checkhead_void)
+                AND (orig.checkitem_checkhead_id=pCheckid))) THEN
+    RETURN -2;
+  END IF;
+
+  SELECT NEXTVAL('checkhead_checkhead_id_seq') INTO _newCheckid;
+
+  INSERT INTO checkhead
+  ( checkhead_id, checkhead_recip_id, checkhead_recip_type,
+    checkhead_bankaccnt_id, checkhead_checkdate,
+    checkhead_number, checkhead_amount,
+    checkhead_for, checkhead_journalnumber,
+    checkhead_notes,
+    checkhead_misc, checkhead_expcat_id, checkhead_curr_id )
+  SELECT _newCheckid, checkhead_recip_id, checkhead_recip_type,
+        checkhead_bankaccnt_id, checkhead_checkdate,
+        -1, -- fetchNextCheckNumber(checkhead_bankaccnt_id),
+         checkhead_amount,
+        checkhead_for, checkhead_journalnumber,
+         checkhead_notes || '
+Replaces voided check ' || checkhead_number,
+        checkhead_misc, checkhead_expcat_id, checkhead_curr_id
+  FROM checkhead
+  WHERE (checkhead_id=pCheckid);
+
+  INSERT INTO checkitem
+  ( checkitem_checkhead_id, checkitem_amount, checkitem_discount,
+    checkitem_ponumber, checkitem_vouchernumber, checkitem_invcnumber,
+    checkitem_apopen_id, checkitem_aropen_id,
+    checkitem_docdate, checkitem_curr_id, checkitem_curr_rate )
+  SELECT _newCheckid, checkitem_amount, checkitem_discount,
+         checkitem_ponumber, checkitem_vouchernumber, checkitem_invcnumber,
+        checkitem_apopen_id, checkitem_aropen_id,
+        checkitem_docdate, checkitem_curr_id, checkitem_curr_rate
+  FROM checkitem
+  WHERE (checkitem_checkhead_id=pCheckid);
+
+  UPDATE checkhead
+  SET checkhead_replaced=TRUE
+  WHERE (checkhead_id=pCheckid);
+
+  RETURN _newCheckid;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/reprioritizewo.sql b/foundation-database/public/functions/reprioritizewo.sql
new file mode 100644 (file)
index 0000000..e1c365b
--- /dev/null
@@ -0,0 +1,37 @@
+CREATE OR REPLACE FUNCTION reprioritizeWo(INTEGER, INTEGER, BOOLEAN) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWoid ALIAS FOR $1;
+  pPriority ALIAS FOR $2;
+  pChangeChildren ALIAS FOR $3;
+  _status CHAR(1);
+  _result INTEGER;
+
+BEGIN
+
+  SELECT wo_status INTO _status
+  FROM wo
+  WHERE (wo_id=pWoid);
+
+  IF (NOT (_status IN (''O'', ''E'',''R'',''I''))) THEN
+    return -1;
+  END IF;
+
+  UPDATE wo
+  SET wo_priority=pPriority
+  WHERE (wo_id=pWoid);
+
+  IF ( (_status IN (''E'',''R'',''I'')) AND (pChangeChildren) ) THEN
+    SELECT COALESCE(MIN(reprioritizeWo(wo_id, pPriority, TRUE)), 1) INTO _result
+    FROM wo
+    WHERE ( (wo_ordtype=''W'')
+     AND (wo_ordid=pWoid) );
+  ELSE
+    _result = 1;
+  END IF;
+
+  RETURN _result;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/resetdbobjperms.sql b/foundation-database/public/functions/resetdbobjperms.sql
new file mode 100644 (file)
index 0000000..d3aa9b9
--- /dev/null
@@ -0,0 +1,28 @@
+CREATE OR REPLACE FUNCTION resetDBObjPerms(TEXT) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pObjname      ALIAS FOR $1;
+BEGIN
+  EXECUTE ''ALTER TABLE ''   || pObjname || '' OWNER TO '' || getEffectiveXtUser() || '';'';
+  EXECUTE ''REVOKE ALL ON '' || pObjname || '' FROM PUBLIC;'';
+  EXECUTE ''GRANT  ALL ON '' || pObjname || '' TO GROUP xtrole;'';
+  RETURN 1;
+END;
+' LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION resetDBObjPerms() RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _count        INTEGER := 0;
+BEGIN
+  SELECT SUM(resetDBObjPerms(nspname || ''.'' || relname)) INTO _count
+  FROM pg_catalog.pg_class, pg_catalog.pg_namespace
+  WHERE (relkind IN (''r'', ''S'', ''v'')
+    AND  (relnamespace=pg_namespace.oid)
+    AND  (nspname IN (''public'', ''api'')));
+
+  RETURN _count;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/resetlowlevelcode.sql b/foundation-database/public/functions/resetlowlevelcode.sql
new file mode 100644 (file)
index 0000000..e27e2a0
--- /dev/null
@@ -0,0 +1,60 @@
+CREATE OR REPLACE FUNCTION resetLowLevelCode(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+
+    pItemId ALIAS FOR $1;
+    _result    INTEGER;
+    _counterNum        INTEGER := 1;
+    _feedBackNum INTEGER := 1;
+    _r                  RECORD;
+
+BEGIN
+    DELETE FROM costUpdate;
+
+    IF pItemId = -1 THEN       -- -1 is an invalid item_id => do them all
+       INSERT INTO costUpdate ( costUpdate_item_id, costUpdate_item_type )
+                       SELECT item_id, item_type
+                       FROM   item;
+
+        -- Recalculate the Item Lowlevel codes
+        WHILE _feedBackNum > 0 LOOP
+            SELECT updateLowlevel(_counterNum) INTO _feedBackNum;
+            _counterNum := _counterNum + 1;
+        END LOOP;
+
+    ELSE
+       INSERT INTO costUpdate ( costUpdate_item_id, costUpdate_item_type )
+                       SELECT item_id, item_type
+                       FROM   item
+                        WHERE (item_id=pItemId);
+      FOR _r IN SELECT item_id, bomdata_bomwork_level, item_type
+                FROM item,
+                     indentedBOM(pItemId, getActiveRevId('BOM',pItemId),0,0)
+                WHERE (bomdata_item_id=item_id)
+                ORDER BY bomdata_bomwork_level LOOP
+
+        -- this only works because of the ORDER BY in the loop SELECT
+        UPDATE costUpdate
+        SET costupdate_lowlevel_code = _r.bomdata_bomwork_level
+        WHERE (costupdate_item_id=_r.item_id);
+
+        IF (NOT FOUND) THEN
+          INSERT INTO costUpdate (
+            costUpdate_item_id, costUpdate_lowlevel_code, costUpdate_item_type
+          ) VALUES (
+            _r.item_id, _r.bomdata_bomwork_level, _r.item_type
+          );
+        END IF;
+      END LOOP;
+
+    END IF;
+
+    SELECT count(*) INTO _result
+    FROM costUpdate;
+
+    RETURN _result;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/resetqohbalance.sql b/foundation-database/public/functions/resetqohbalance.sql
new file mode 100644 (file)
index 0000000..22789ac
--- /dev/null
@@ -0,0 +1,47 @@
+CREATE OR REPLACE FUNCTION resetQOHBalance(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN resetQOHBalance($1, CURRENT_TIMESTAMP);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION resetQOHBalance(INTEGER, TIMESTAMP WITH TIME ZONE) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid   ALIAS FOR $1;
+  pGlDistTS     ALIAS FOR $2;
+  _invhistid    INTEGER;
+  _itemlocSeries INTEGER;
+
+BEGIN
+
+  IF ( ( SELECT ( (itemsite_controlmethod IN ('L', 'S')) OR
+                  (item_type = 'R') OR
+                  (itemsite_costmethod = 'J') OR
+                  (itemsite_loccntrl) OR
+                  (itemsite_qtyonhand > 0) )
+         FROM itemsite, item
+         WHERE ( (itemsite_item_id=item_id)
+          AND (itemsite_id=pItemsiteid) ) ) ) THEN
+    RETURN 0;
+  END IF;
+
+  _itemlocSeries := NEXTVAL('itemloc_series_seq');
+
+  SELECT postInvTrans( itemsite_id, 'AD', (itemsite_qtyonhand * -1),
+                       'I/M', '', '', 'RESET',
+                       'Reset QOH Balance to 0',
+                       costcat_asset_accnt_id, costcat_adjustment_accnt_id,
+                       _itemlocSeries, pGlDistTS ) INTO _invhistid
+  FROM itemsite, costcat
+  WHERE ( (itemsite_costcat_id=costcat_id)
+   AND (itemsite_id=pItemsiteid) );
+
+  PERFORM postItemLocSeries(_itemlocSeries);
+
+  RETURN _invhistid;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/resolvecosaccount.sql b/foundation-database/public/functions/resolvecosaccount.sql
new file mode 100644 (file)
index 0000000..55571a8
--- /dev/null
@@ -0,0 +1,35 @@
+CREATE OR REPLACE FUNCTION resolveCOSAccount(INTEGER, INTEGER) RETURNS INTEGER STABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+
+  RETURN resolveCOSAccount($1, $2, -1, -1);
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION resolveCOSAccount(pItemsiteid INTEGER,
+                                             pCustid INTEGER,
+                                             pSaletypeid INTEGER,
+                                             pShipzoneid INTEGER) RETURNS INTEGER STABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _salesaccntid INTEGER;
+  _accntid INTEGER;
+
+BEGIN
+
+  SELECT findSalesAccnt(pItemsiteid, 'IS', pCustid, pSaletypeid, pShipzoneid) INTO _salesaccntid;
+  IF (_salesaccntid = -1) THEN
+    SELECT getUnassignedAccntId() INTO _accntid;
+  ELSE
+    SELECT salesaccnt_cos_accnt_id INTO _accntid
+    FROM salesaccnt
+    WHERE (salesaccnt_id=_salesaccntid);
+  END IF;
+
+  RETURN _accntid;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/resolvecowaccount.sql b/foundation-database/public/functions/resolvecowaccount.sql
new file mode 100644 (file)
index 0000000..b0642bc
--- /dev/null
@@ -0,0 +1,35 @@
+CREATE OR REPLACE FUNCTION resolveCOWAccount(INTEGER, INTEGER) RETURNS INTEGER STABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+
+  RETURN resolveCOWAccount($1, $2, -1, -1);
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION resolveCOWAccount(pItemsiteid INTEGER,
+                                             pCustid INTEGER,
+                                             pSaletypeid INTEGER,
+                                             pShipzoneid INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _salesaccntid INTEGER;
+  _accntid INTEGER;
+
+BEGIN
+
+  SELECT findSalesAccnt(pItemsiteid, 'IS', pCustid, pSaletypeid, pShipzoneid) INTO _salesaccntid;
+  IF (_salesaccntid = -1) THEN
+    SELECT getUnassignedAccntId() INTO _accntid;
+  ELSE
+    SELECT salesaccnt_cow_accnt_id INTO _accntid
+    FROM salesaccnt
+    WHERE (salesaccnt_id=_salesaccntid);
+  END IF;
+
+  RETURN _accntid;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/resolvecreditaccount.sql b/foundation-database/public/functions/resolvecreditaccount.sql
new file mode 100644 (file)
index 0000000..308ab46
--- /dev/null
@@ -0,0 +1,35 @@
+CREATE OR REPLACE FUNCTION resolveCreditAccount(INTEGER, INTEGER) RETURNS INTEGER STABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+
+  RETURN resolveCreditAccount($1, $2, -1, -1);
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION resolveCreditAccount(pItemsiteid INTEGER,
+                                                pCustid INTEGER,
+                                                pSaletypeid INTEGER,
+                                                pShipzoneid INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _salesaccntid INTEGER;
+  _accntid INTEGER;
+
+BEGIN
+
+  SELECT findSalesAccnt(pItemsiteid, 'IS', pCustid, pSaletypeid, pShipzoneid) INTO _salesaccntid;
+  IF (_salesaccntid = -1) THEN
+    SELECT getUnassignedAccntId() INTO _accntid;
+  ELSE
+    SELECT salesaccnt_credit_accnt_id INTO _accntid
+    FROM salesaccnt
+    WHERE (salesaccnt_id=_salesaccntid);
+  END IF;
+
+  RETURN _accntid;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/resolvesalesaccount.sql b/foundation-database/public/functions/resolvesalesaccount.sql
new file mode 100644 (file)
index 0000000..621219f
--- /dev/null
@@ -0,0 +1,35 @@
+CREATE OR REPLACE FUNCTION resolveSalesAccount(INTEGER, INTEGER) RETURNS INTEGER STABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+
+  RETURN resolveSalesAccount($1, $2, -1, -1);
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION resolveSalesAccount(pItemsiteid INTEGER,
+                                               pCustid INTEGER,
+                                               pSaletypeid INTEGER,
+                                               pShipzoneid INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _salesaccntid INTEGER;
+  _accntid INTEGER;
+
+BEGIN
+
+  SELECT findSalesAccnt(pItemsiteid, 'IS', pCustid, pSaletypeid, pShipzoneid) INTO _salesaccntid;
+  IF (_salesaccntid = -1) THEN
+    SELECT getUnassignedAccntId() INTO _accntid;
+  ELSE
+    SELECT salesaccnt_sales_accnt_id INTO _accntid
+    FROM salesaccnt
+    WHERE (salesaccnt_id=_salesaccntid);
+  END IF;
+
+  RETURN _accntid;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/restoresaleshistory.sql b/foundation-database/public/functions/restoresaleshistory.sql
new file mode 100644 (file)
index 0000000..389e785
--- /dev/null
@@ -0,0 +1,136 @@
+CREATE OR REPLACE FUNCTION restoreSalesHistory(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pAsohistid ALIAS FOR $1;
+
+BEGIN
+
+  INSERT INTO cohist ( cohist_id,
+                       cohist_cust_id,
+                       cohist_itemsite_id,
+                       cohist_shipdate,
+                       cohist_invcdate,
+                       cohist_duedate,
+                       cohist_promisedate,
+                       cohist_ordernumber,
+                       cohist_invcnumber,
+                       cohist_qtyshipped,
+                       cohist_unitprice,
+                       cohist_unitcost,
+                       cohist_billtoname,
+                       cohist_billtoaddress1,
+                       cohist_billtoaddress2,
+                       cohist_billtoaddress3,
+                       cohist_billtocity,
+                       cohist_billtostate,
+                       cohist_billtozip,
+                       cohist_shiptoname,
+                       cohist_shiptoaddress1,
+                       cohist_shiptoaddress2,
+                       cohist_shiptoaddress3,
+                       cohist_shiptocity,
+                       cohist_shiptostate,
+                       cohist_shiptozip,
+                       cohist_shipto_id,
+                       cohist_shipvia,
+                       cohist_salesrep_id,
+                       cohist_misc_type,
+                       cohist_misc_descrip,
+                       cohist_misc_id,
+                       cohist_commission,
+                       cohist_commissionpaid,
+                       cohist_doctype,
+                       cohist_orderdate,
+                       cohist_imported,
+                       cohist_ponumber,
+                       cohist_curr_id,
+                       cohist_taxtype_id,
+                       cohist_taxzone_id )
+  SELECT asohist_id,
+         CASE asohist_cust_id WHEN -1 THEN NULL ELSE asohist_cust_id END,
+         asohist_itemsite_id,
+         asohist_shipdate,
+         asohist_invcdate,
+         asohist_duedate,
+         asohist_promisedate,
+         asohist_ordernumber,
+         asohist_invcnumber,
+         asohist_qtyshipped,
+         asohist_unitprice,
+         asohist_unitcost,
+         asohist_billtoname,
+         asohist_billtoaddress1,
+         asohist_billtoaddress2,
+         asohist_billtoaddress3,
+         asohist_billtocity,
+         asohist_billtostate,
+         asohist_billtozip,
+         asohist_shiptoname,
+         asohist_shiptoaddress1,
+         asohist_shiptoaddress2,
+         asohist_shiptoaddress3,
+         asohist_shiptocity,
+         asohist_shiptostate,
+         asohist_shiptozip,
+         asohist_shipto_id,
+         asohist_shipvia,
+         CASE asohist_salesrep_id WHEN -1 THEN NULL ELSE asohist_salesrep_id END,
+         asohist_misc_type,
+         asohist_misc_descrip,
+         asohist_misc_id,
+         asohist_commission,
+         asohist_commissionpaid,
+         asohist_doctype,
+         asohist_orderdate,
+         asohist_imported,
+         asohist_ponumber,
+        asohist_curr_id,
+         asohist_taxtype_id,
+         asohist_taxzone_id
+  FROM asohist
+  WHERE (asohist_id=pAsohistid);
+
+  INSERT INTO cohisttax ( taxhist_id,
+                          taxhist_parent_id,
+                          taxhist_taxtype_id,
+                          taxhist_tax_id,
+                          taxhist_basis,
+                          taxhist_basis_tax_id,
+                          taxhist_sequence,
+                          taxhist_percent,
+                          taxhist_amount,
+                          taxhist_tax,
+                          taxhist_docdate,
+                          taxhist_distdate,
+                          taxhist_curr_id,
+                          taxhist_curr_rate,
+                          taxhist_journalnumber )
+  SELECT taxhist_id,
+         taxhist_parent_id,
+         taxhist_taxtype_id,
+         taxhist_tax_id,
+         taxhist_basis,
+         taxhist_basis_tax_id,
+         taxhist_sequence,
+         taxhist_percent,
+         taxhist_amount,
+         taxhist_tax,
+         taxhist_docdate,
+         taxhist_distdate,
+         taxhist_curr_id,
+         taxhist_curr_rate,
+         taxhist_journalnumber
+  FROM asohisttax
+  WHERE (taxhist_parent_id=pAsohistid);
+
+  DELETE FROM asohisttax
+  WHERE (taxhist_parent_id=pAsohistid);
+
+  DELETE FROM asohist
+  WHERE (asohist_id=pAsohistid);
+
+  RETURN pAsohistid;
+
+END;
+$$ LANGUAGE plpgsql;
diff --git a/foundation-database/public/functions/returncompleteshipment.sql b/foundation-database/public/functions/returncompleteshipment.sql
new file mode 100644 (file)
index 0000000..06e4ded
--- /dev/null
@@ -0,0 +1,62 @@
+CREATE OR REPLACE FUNCTION returnCompleteShipment(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN returnCompleteShipment($1, 0, CURRENT_TIMESTAMP);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION returnCompleteShipment(INTEGER, INTEGER, TIMESTAMP WITH TIME ZONE) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pshipheadid          ALIAS FOR $1;
+  _itemlocSeries       INTEGER := $2;
+  _timestamp           TIMESTAMP WITH TIME ZONE := $3;
+  _r RECORD;
+  _result              RECORD;
+  _shiphead_number     TEXT := '';
+  _count               INTEGER := 0;
+  _countsum            INTEGER := 0;
+
+BEGIN
+  FOR _r IN SELECT shipitem_id
+            FROM shipitem, shiphead
+            WHERE ( (shipitem_shiphead_id=shiphead_id)
+             AND (NOT shiphead_shipped)
+             AND (shiphead_id=pshipheadid) ) LOOP
+    _itemlocSeries := returnShipmentTransaction(_r.shipitem_id, _itemlocSeries, _timestamp);
+  END LOOP;
+
+  FOR _result IN SELECT shiphead_number
+                   FROM shiphead
+                  WHERE ( (shiphead_id=pshipheadid) ) LOOP
+    _shiphead_number := _result.shiphead_number;
+  END LOOP;
+
+  SELECT COUNT(*) INTO _count
+    FROM shipdata
+   WHERE(shipdata_shiphead_number=_shiphead_number);
+
+  SELECT COUNT(*) INTO _countsum
+    FROM shipdatasum
+   WHERE(shipdatasum_shiphead_number=_shiphead_number);
+
+  IF (_count > 0) THEN
+    DELETE FROM shipdata
+     WHERE(shipdata_shiphead_number=_shiphead_number);
+  END IF;
+  IF (_countsum > 0) THEN
+    DELETE FROM shipdatasum
+     WHERE(shipdatasum_shiphead_number=_shiphead_number);
+  END IF;
+
+  DELETE FROM pack
+   WHERE(pack_shiphead_id=pshipheadid);
+  DELETE FROM shiphead
+  WHERE (shiphead_id=pshipheadid);
+
+  RETURN _itemlocSeries;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/returnitemshipments.sql b/foundation-database/public/functions/returnitemshipments.sql
new file mode 100644 (file)
index 0000000..ffbe541
--- /dev/null
@@ -0,0 +1,55 @@
+CREATE OR REPLACE FUNCTION returnItemShipments(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN returnItemShipments('SO', $1, 0, CURRENT_TIMESTAMP);
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION returnItemShipments(INTEGER, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN returnItemShipments('SO', $1, $2, CURRENT_TIMESTAMP);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION returnItemShipments(TEXT, INTEGER, INTEGER, TIMESTAMP WITH TIME ZONE) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pordertype           ALIAS FOR $1;
+  pitemid              ALIAS FOR $2;
+  _itemlocSeries       INTEGER                         := $3;
+  _timestamp           TIMESTAMP WITH TIME ZONE        := $4;
+  _invhistid INTEGER;
+  _r RECORD;
+
+BEGIN
+
+  IF (COALESCE(_itemlocSeries,0) = 0 ) THEN
+    _itemlocSeries := NEXTVAL('itemloc_series_seq');
+  END IF;
+
+  FOR _r IN 
+    SELECT shipitem_id
+    FROM shipitem
+      JOIN shiphead ON (shiphead_id=shipitem_shiphead_id)
+    WHERE ((NOT shiphead_shipped)
+      AND  (shiphead_order_type=pordertype)
+      AND  (shipitem_orderitem_id=pitemid))
+  LOOP
+
+    SELECT returnShipmentTransaction(_r.shipitem_id, _itemlocSeries, _timestamp) INTO _itemlocSeries;
+
+    IF (_itemlocSeries < 0) THEN
+      RETURN _itemlocSeries;
+    END IF;
+
+  END LOOP;
+
+  RETURN _itemlocSeries;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/returnshipmenttransaction.sql b/foundation-database/public/functions/returnshipmenttransaction.sql
new file mode 100644 (file)
index 0000000..5e57792
--- /dev/null
@@ -0,0 +1,223 @@
+CREATE OR REPLACE FUNCTION returnShipmentTransaction(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN returnShipmentTransaction($1, 0, CURRENT_TIMESTAMP);
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION returnShipmentTransaction(INTEGER, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN returnShipmentTransaction($1, $2, CURRENT_TIMESTAMP);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION returnShipmentTransaction(INTEGER, INTEGER, TIMESTAMP WITH TIME ZONE) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pShipitemId           ALIAS FOR $1;
+  pItemlocSeries        ALIAS FOR $2;
+  pTimestamp            ALIAS FOR $3;
+  _itemlocSeries        INTEGER;
+  _invhistid            INTEGER;
+  _itemlocrsrvid        INTEGER;
+  _coheadid             INTEGER;
+  _rows                 INTEGER;
+  _r                    RECORD;
+  _rsrv                 RECORD;
+
+BEGIN
+
+    IF (COALESCE(pItemlocSeries, 0) = 0 ) THEN
+      _itemlocSeries := NEXTVAL('itemloc_series_seq');
+    ELSE
+      _itemlocSeries := pItemlocSeries;
+    END IF;
+  
+    -- Find the shipment transaction record
+    SELECT shipitem.*,
+           shiphead_id, shiphead_number, shiphead_order_type, invhist_series,
+           itemsite_loccntrl, itemsite_costmethod, itemsite_controlmethod,
+           cohead_prj_id AS prj_id     
+      INTO _r
+    FROM shipitem
+      JOIN shiphead ON (shiphead_id=shipitem_shiphead_id)
+      JOIN invhist ON (invhist_id=shipitem_invhist_id)
+      JOIN itemsite ON (itemsite_id=invhist_itemsite_id)
+      LEFT OUTER JOIN cohead ON ((shiphead_order_type = 'SO') AND (shiphead_order_id = cohead_id))
+    WHERE ((NOT shiphead_shipped)
+      AND  (shipitem_id=pShipitemId));
+      
+    GET DIAGNOSTICS _rows = ROW_COUNT;
+    IF (_rows = 0 ) THEN
+      -- Was it a non-controlled sales order item?
+      SELECT shipitem.*,
+             shiphead_id, shiphead_number, shiphead_order_type,
+             itemsite_loccntrl, itemsite_costmethod, itemsite_controlmethod,
+             cohead_prj_id AS prj_id
+      INTO _r
+      FROM shipitem
+        JOIN shiphead ON (shiphead_id=shipitem_shiphead_id)
+        JOIN coitem ON (shipitem_orderitem_id=coitem_id)
+        JOIN cohead ON (cohead_id=coitem_cohead_id)
+        JOIN itemsite ON (itemsite_id=coitem_itemsite_id)
+      WHERE ((NOT shiphead_shipped)
+        AND  (shipitem_id=pShipitemId)
+        AND  (shiphead_order_type = 'SO'));
+    END IF;
+    
+    GET DIAGNOSTICS _rows = ROW_COUNT;
+    IF (_rows = 0 AND fetchmetricbool('MultiWhs') ) THEN
+      -- Was it a non-controlled transfer order item?
+      SELECT shipitem.*,
+             shiphead_id, shiphead_number, shiphead_order_type,
+             itemsite_loccntrl, itemsite_costmethod, itemsite_controlmethod,
+             NULL AS prj_id
+      INTO _r
+      FROM shipitem
+        JOIN shiphead ON (shiphead_id=shipitem_shiphead_id)
+        JOIN toitem ON (shipitem_orderitem_id=toitem_id)
+        JOIN itemsite ON (itemsite_id=coitem_itemsite_id)
+      WHERE ((NOT shiphead_shipped)
+        AND  (shipitem_id=pShipitemId)
+        AND  (shiphead_order_type = 'TO'));
+    END IF;
+    
+    IF (_rows > 0 ) THEN  
+      IF (_r.shiphead_order_type = 'SO') THEN
+        -- Handle inventory transaction
+        IF (_r.itemsite_controlmethod != 'N' OR _r.itemsite_costmethod = 'J') THEN
+          SELECT postInvTrans( itemsite_id, 'RS', (_r.shipitem_qty * coitem_qty_invuomratio),
+                               'S/R', _r.shiphead_order_type, formatSoNumber(_r.shipitem_orderitem_id),
+                               shiphead_number, 'Return from Shipping',
+                               costcat_asset_accnt_id, getPrjAccntId(_r.prj_id, costcat_shipasset_accnt_id),
+                               _itemlocSeries, pTimestamp, _r.shipitem_value, _r.shipitem_invhist_id ) INTO _invhistid
+          FROM coitem, itemsite, costcat, shiphead, shipitem
+          WHERE ((coitem_itemsite_id=itemsite_id)
+            AND  (itemsite_costcat_id=costcat_id)
+            AND  (coitem_id=_r.shipitem_orderitem_id)
+            AND  (shiphead_order_type=_r.shiphead_order_type)
+            AND  (shiphead_id=shipitem_shiphead_id)
+            AND  (shipitem_orderitem_id=_r.shipitem_orderitem_id));
+          -- We know the distribution so post this through so the any w/o activity knows about it
+          PERFORM postItemlocseries(_itemlocSeries);
+        END IF;
+        IF (_r.itemsite_costmethod = 'J') THEN   
+          -- Reopen the work order
+          UPDATE wo SET wo_status = 'I' WHERE ((wo_ordtype='S') AND (wo_ordid=_r.shipitem_orderitem_id));
+          
+          --  Job cost, so correct Production Posting referencing original receipt for reverse info.
+          PERFORM correctProduction(wo_id, invhist_invqty, false, _itemlocSeries, pTimestamp, invhist_id)
+          FROM wo, invhist
+          WHERE ((wo_ordtype = 'S')
+            AND  (wo_ordid = _r.shipitem_orderitem_id) 
+            AND  (invhist_series=_r.invhist_series)
+            AND  (invhist_transtype='RM'));
+             
+          --  Return eligble material
+          PERFORM returnWoMaterial(womatlpost_womatl_id, _itemlocSeries, pTimestamp, womatlpost_invhist_id)
+          FROM womatlpost, invhist m, invhist s 
+          WHERE ((womatlpost_invhist_id=m.invhist_id)
+            AND  (m.invhist_series=s.invhist_series)
+            AND  (m.invhist_transtype='IM')
+            AND  (s.invhist_id=_r.shipitem_invhist_id));
+
+        END IF; -- end Job Costing
+
+      ELSIF (_r.shiphead_order_type = 'TO') THEN
+        SELECT postInvTrans(itemsite_id, 'RS', _r.shipitem_qty,
+                            'S/R', _r.shiphead_order_type, formatToNumber(toitem_id),
+                            tohead_number, 'Return from Shipping',
+                            costcat_asset_accnt_id, costcat_shipasset_accnt_id,
+                            _itemlocSeries, pTimestamp, _r.shipitem_value, _r.shipitem_invhist_id ) INTO _invhistid
+        FROM toitem, tohead, itemsite, costcat
+        WHERE ((toitem_item_id=itemsite_item_id)
+          AND  (toitem_tohead_id=tohead_id)
+         AND  (tohead_src_warehous_id=itemsite_warehous_id)
+          AND  (itemsite_costcat_id=costcat_id)
+          AND  (toitem_id=_r.shipitem_orderitem_id));
+
+      ELSE
+        -- Don't know what kind of order this is
+        RETURN -11;
+      END IF;
+
+      UPDATE shiphead
+      SET shiphead_sfstatus='D'
+      WHERE ((shiphead_id=_r.shiphead_id)
+        AND  (shiphead_sfstatus='P'));
+
+       -- Handle reservation if applicable
+      IF (fetchmetricbool('EnableSOReservations')) THEN
+        UPDATE coitem
+          SET coitem_qtyreserved = (coitem_qtyreserved + shipitemrsrv_qty)
+        FROM shipitemrsrv
+        WHERE ((coitem_id=_r.shipitem_orderitem_id)
+          AND  (shipitemrsrv_shipitem_id=_r.shipitem_id));
+
+        -- Handle location reservations if applicable
+        FOR _rsrv IN
+          SELECT *
+          FROM shipitemlocrsrv
+          WHERE (shipitemlocrsrv_shipitem_id=_r.shipitem_id)
+        LOOP
+          -- See if a reservation record still exists
+          SELECT itemlocrsrv_id, itemlocrsrv_qty INTO _itemlocrsrvid
+          FROM itemlocrsrv JOIN itemloc ON (itemlocrsrv_itemloc_id=itemloc_id)
+          WHERE ((itemlocrsrv_source = 'SO')
+            AND  (itemlocrsrv_source_id = _r.shipitem_orderitem_id )
+            AND  (itemloc_itemsite_id=_rsrv.shipitemlocrsrv_itemsite_id)
+            AND  (itemloc_location_id=_rsrv.shipitemlocrsrv_location_id)
+            AND  (COALESCE(itemloc_ls_id, -1)=COALESCE(_rsrv.shipitemlocrsrv_ls_id, -1))
+            AND  (COALESCE(itemloc_expiration, endOfTime())=COALESCE(_rsrv.shipitemlocrsrv_expiration, endOfTime()))
+            AND  (COALESCE(itemloc_warrpurc, endoftime())=COALESCE(_rsrv.shipitemlocrsrv_warrpurc, endoftime())) );
+
+          GET DIAGNOSTICS _rows = ROW_COUNT;
+          IF (_rows > 0 ) THEN  
+            -- Update existing
+            UPDATE itemlocrsrv
+            SET itemlocrsrv_qty = (itemlocrsrv_qty + _rsrv.shipitemlocrsrv_qty)
+            WHERE (itemlocrsrv_id=_itemlocrsvrid);
+          ELSE
+            -- Recreate record
+            INSERT INTO itemlocrsrv
+            SELECT nextval('itemlocrsrv_itemlocrsrv_id_seq'), 'SO', _r.shipitem_orderitem_id,
+                   itemloc_id, _rsrv.shipitemlocrsrv_qty
+            FROM itemloc
+            WHERE ((itemloc_itemsite_id=_rsrv.shipitemlocrsrv_itemsite_id)
+              AND  (itemloc_location_id=_rsrv.shipitemlocrsrv_location_id)
+              AND  (COALESCE(itemloc_ls_id, -1)=COALESCE(_rsrv.shipitemlocrsrv_ls_id, -1))
+              AND  (COALESCE(itemloc_expiration, endOfTime())=COALESCE(_rsrv.shipitemlocrsrv_expiration, endOfTime()))
+              AND  (COALESCE(itemloc_warrpurc, endoftime())=COALESCE(_rsrv.shipitemlocrsrv_warrpurc, endoftime())) );
+          END IF;
+        END LOOP;
+      END IF;
+      
+      DELETE FROM shipitem WHERE (shipitem_id = _r.shipitem_id );
+
+      -- Clean up if this is the last shipitem on the shipment
+      IF (NOT EXISTS(SELECT shipitem_shiphead_id
+                     FROM shipitem
+                     WHERE (shipitem_shiphead_id=_r.shiphead_id))) THEN
+        DELETE FROM shipdata
+         WHERE(shipdata_shiphead_number=_r.shiphead_number);
+        DELETE FROM shipdatasum
+         WHERE(shipdatasum_shiphead_number=_r.shiphead_number);
+        DELETE FROM pack
+         WHERE(pack_shiphead_id=_r.shiphead_id);
+        DELETE FROM shiphead
+         WHERE (shiphead_id=_r.shiphead_id);
+      END IF;
+
+    END IF;
+
+    RETURN _itemlocSeries;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/returnwomaterial.sql b/foundation-database/public/functions/returnwomaterial.sql
new file mode 100644 (file)
index 0000000..dbbb5cf
--- /dev/null
@@ -0,0 +1,250 @@
+CREATE OR REPLACE FUNCTION returnWoMaterial(INTEGER, NUMERIC, TIMESTAMP WITH TIME ZONE) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWomatlid ALIAS FOR $1;
+  pQty ALIAS FOR $2;
+  pGlDistTS ALIAS FOR $3;
+  _itemlocSeries INTEGER;
+
+BEGIN
+
+  SELECT NEXTVAL('itemloc_series_seq') INTO _itemlocSeries;
+  RETURN returnWoMaterial(pWomatlid, pQty, _itemlocSeries, pGlDistTS);
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION returnWoMaterial(INTEGER, INTEGER, TIMESTAMP WITH TIME ZONE, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWomatlid ALIAS FOR $1;
+  pItemlocSeries ALIAS FOR $2;
+  pGlDistTS ALIAS FOR $3;
+  pInvhistId ALIAS FOR $4;
+  _woNumber TEXT;
+  _invhistid INTEGER;
+  _itemlocSeries INTEGER;
+  _invqty NUMERIC;
+  _womatlqty NUMERIC;
+  _cost NUMERIC := 0;
+  _rows INTEGER;
+
+BEGIN
+
+  _itemlocSeries := 0;
+
+  SELECT invhist_invqty, invhist_invqty * invhist_unitcost INTO _invqty, _cost
+  FROM invhist
+  WHERE (invhist_id=pInvhistId);
+
+  GET DIAGNOSTICS _rows = ROW_COUNT;
+  
+  IF (_rows = 0) THEN
+    RAISE EXCEPTION 'No transaction found for invhist_id %', pInvhistId;
+  END IF;
+  
+  SELECT itemuomtouom(itemsite_item_id, NULL, womatl_uom_id, _invqty)
+    INTO _womatlqty
+    FROM womatl, itemsite
+    WHERE((womatl_itemsite_id=itemsite_id)
+     AND (womatl_id=pWomatlid));
+
+  GET DIAGNOSTICS _rows = ROW_COUNT;
+  
+  IF (_rows = 0) THEN
+    _womatlqty := _invqty;
+  END IF;
+
+  IF ( SELECT (
+         CASE WHEN (womatl_qtyreq >= 0) THEN
+           womatl_qtyiss < _womatlqty
+         ELSE
+           womatl_qtyiss > _womatlqty
+         END )
+       FROM womatl
+       WHERE ( womatl_id=pWomatlid ) ) THEN
+    RETURN pItemlocSeries;
+  END IF;
+
+  SELECT formatWoNumber(womatl_wo_id) INTO _woNumber
+  FROM womatl
+  WHERE (womatl_id=pWomatlid);
+
+  IF (pItemlocSeries = 0) THEN
+    SELECT NEXTVAL('itemloc_series_seq') INTO _itemlocSeries;
+  ELSE
+    _itemlocSeries = pItemlocSeries;
+  END IF;
+
+  -- Post the transaction
+  SELECT postInvTrans( ci.itemsite_id, 'IM', (_invqty * -1), 
+                       'W/O', 'WO', _woNumber, '',
+                       ('Return ' || item_number || ' from Work Order'),
+                       getPrjAccntId(wo_prj_id, pc.costcat_wip_accnt_id), cc.costcat_asset_accnt_id, _itemlocSeries, pGlDistTS,
+                       -- Cost will be ignored by Standard Cost items sites
+                       _cost, pInvhistId) INTO _invhistid
+    FROM womatl, wo,
+         itemsite AS ci, costcat AS cc,
+         itemsite AS pi, costcat AS pc,
+         item
+   WHERE((womatl_itemsite_id=ci.itemsite_id)
+     AND (ci.itemsite_costcat_id=cc.costcat_id)
+     AND (womatl_wo_id=wo_id)
+     AND (wo_itemsite_id=pi.itemsite_id)
+     AND (pi.itemsite_costcat_id=pc.costcat_id)
+     AND (ci.itemsite_item_id=item_id)
+     AND (womatl_id=pWomatlid) );
+
+--  Create linkage to the transaction created
+  INSERT INTO womatlpost (womatlpost_womatl_id,womatlpost_invhist_id)
+              VALUES (pWomatlid,_invhistid);
+
+--  Decrease the parent W/O's WIP value by the value of the returned components
+  UPDATE wo
+  SET wo_wipvalue = (wo_wipvalue - (CASE WHEN(itemsite_costmethod IN ('A','J'))
+                                              THEN _cost
+                                         WHEN(itemsite_costmethod='S')
+                                              THEN stdcost(itemsite_item_id) * _invqty
+                                         ELSE 0.0 END )),
+      wo_postedvalue = (wo_postedvalue - (CASE WHEN(itemsite_costmethod IN ('A','J'))
+                                                    THEN _cost
+                                               WHEN(itemsite_costmethod='S')
+                                                    THEN stdcost(itemsite_item_id) * _invqty
+                                               ELSE 0.0 END ))
+  FROM womatl, itemsite
+  WHERE ( (wo_id=womatl_wo_id)
+   AND (womatl_itemsite_id=itemsite_id)
+   AND (womatl_id=pWomatlid) );
+
+  UPDATE womatl
+  SET womatl_qtyiss = (womatl_qtyiss - _womatlqty),
+      womatl_lastreturn = CURRENT_DATE
+  WHERE (womatl_id=pWomatlid);
+
+  RETURN _itemlocSeries;
+END;
+$$ LANGUAGE 'plpgsql';
+
+COMMENT ON FUNCTION returnwomaterial(integer, integer, timestamp with time zone, integer) IS 'Returns material by reversing a specific historical transaction';
+
+select dropIfExists('FUNCTION', 'returnWoMaterial(INTEGER, NUMERIC, INTEGER, TIMESTAMP WITH TIME ZONE)'); 
+CREATE OR REPLACE FUNCTION returnWoMaterial(INTEGER, NUMERIC, INTEGER, TIMESTAMP WITH TIME ZONE, BOOLEAN DEFAULT FALSE) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWomatlid ALIAS FOR $1;
+  pQty ALIAS FOR $2;
+  pItemlocSeries ALIAS FOR $3;
+  pGlDistTS ALIAS FOR $4;
+  pReqStdCost ALIAS FOR $5;
+  _woNumber TEXT;
+  _invhistid INTEGER;
+  _itemlocSeries INTEGER;
+  _qty NUMERIC;
+  _cost NUMERIC := 0;
+
+BEGIN
+
+  _itemlocSeries := 0;
+  
+  IF ( SELECT (
+         CASE WHEN (womatl_qtyreq >= 0) THEN
+           womatl_qtyiss < pQty
+         ELSE
+           womatl_qtyiss > pQty
+         END )
+       FROM womatl
+       WHERE ( womatl_id=pWomatlid ) ) THEN
+    RETURN pItemlocSeries;
+  END IF;
+
+  SELECT itemuomtouom(itemsite_item_id, womatl_uom_id, NULL, pQty)
+    INTO _qty
+    FROM womatl, itemsite
+   WHERE((womatl_itemsite_id=itemsite_id)
+     AND (womatl_id=pWomatlid));
+  IF (NOT FOUND) THEN
+    _qty := pQty;
+  END IF;
+
+  SELECT formatWoNumber(womatl_wo_id) INTO _woNumber
+  FROM womatl
+  WHERE (womatl_id=pWomatlid);
+
+  IF (pItemlocSeries = 0) THEN
+    SELECT NEXTVAL('itemloc_series_seq') INTO _itemlocSeries;
+  ELSE
+    _itemlocSeries = pItemlocSeries;
+  END IF;
+
+  -- Get the cost average
+  IF (pReqStdCost) THEN
+    SELECT stdcost(itemsite_item_id) * _qty INTO _cost
+    FROM womatl, itemsite
+    WHERE((womatl_itemsite_id=itemsite_id)
+      AND (womatl_id=pWomatlid));
+  ELSE
+    SELECT SUM(invhist_value_before - invhist_value_after) / SUM(invhist_qoh_before - invhist_qoh_after)  * _qty INTO _cost
+    FROM invhist, womatlpost, womatl 
+    WHERE((womatlpost_womatl_id=womatl_id) 
+     AND (womatlpost_invhist_id=invhist_id) 
+     AND (invhist_qoh_before > invhist_qoh_after)
+     AND (womatl_id=pWomatlId));
+  END IF;
+
+  _cost := COALESCE(_cost, 0); -- make sure it's not a null value
+
+  -- Post the transaction
+  SELECT postInvTrans( ci.itemsite_id, 'IM', (_qty * -1), 
+                       'W/O', 'WO', _woNumber, '',
+                       ('Return ' || item_number || ' from Work Order'),
+                       getPrjAccntId(wo_prj_id, pc.costcat_wip_accnt_id), cc.costcat_asset_accnt_id, _itemlocSeries, pGlDistTS,
+                       -- Cost will be ignored by Standard Cost items sites
+                       _cost) INTO _invhistid
+    FROM womatl, wo,
+         itemsite AS ci, costcat AS cc,
+         itemsite AS pi, costcat AS pc,
+         item
+   WHERE((womatl_itemsite_id=ci.itemsite_id)
+     AND (ci.itemsite_costcat_id=cc.costcat_id)
+     AND (womatl_wo_id=wo_id)
+     AND (wo_itemsite_id=pi.itemsite_id)
+     AND (pi.itemsite_costcat_id=pc.costcat_id)
+     AND (ci.itemsite_item_id=item_id)
+     AND (womatl_id=pWomatlid) );
+
+--  Create linkage to the transaction created
+  IF (_invhistid != -1) THEN
+    INSERT INTO womatlpost (womatlpost_womatl_id,womatlpost_invhist_id)
+                VALUES (pWomatlid,_invhistid);
+  END IF;
+
+--  Decrease the parent W/O's WIP value by the value of the returned components
+  UPDATE wo
+  SET wo_wipvalue = (wo_wipvalue - (CASE WHEN(itemsite_costmethod IN ('A','J'))
+                                              THEN _cost
+                                         WHEN(itemsite_costmethod='S')
+                                              THEN stdcost(itemsite_item_id) * _qty
+                                         ELSE 0.0 END )),
+      wo_postedvalue = (wo_postedvalue - (CASE WHEN(itemsite_costmethod IN ('A','J'))
+                                                    THEN _cost
+                                               WHEN(itemsite_costmethod='S')
+                                                    THEN stdcost(itemsite_item_id) * _qty
+                                               ELSE 0.0 END ))
+  FROM womatl, itemsite
+  WHERE ( (wo_id=womatl_wo_id)
+   AND (womatl_itemsite_id=itemsite_id)
+   AND (womatl_id=pWomatlid) );
+
+  UPDATE womatl
+  SET womatl_qtyiss = (womatl_qtyiss - pQty),
+      womatl_lastreturn = CURRENT_DATE
+  WHERE (womatl_id=pWomatlid);
+
+  RETURN _itemlocSeries;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/returnwomaterialbatch.sql b/foundation-database/public/functions/returnwomaterialbatch.sql
new file mode 100644 (file)
index 0000000..0d358d9
--- /dev/null
@@ -0,0 +1,50 @@
+CREATE OR REPLACE FUNCTION returnWoMaterialBatch(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWoid ALIAS FOR $1;
+  _itemlocSeries INTEGER;
+  _woid INTEGER;
+  _r RECORD;
+
+BEGIN
+
+  SELECT wo_id INTO _woid
+  FROM wo
+  WHERE ( (wo_status IN ('E','I'))
+   AND (wo_id=pWoid) );
+
+  IF (FOUND) THEN
+    SELECT NEXTVAL('itemloc_series_seq') INTO _itemlocSeries;
+
+    FOR _r IN SELECT womatl_id, 
+                CASE WHEN wo_qtyord >= 0 THEN
+                  womatl_qtyiss
+                ELSE
+                  ((womatl_qtyreq - womatl_qtyiss) * -1)
+                END AS qty
+              FROM wo, womatl, itemsite
+              WHERE ((wo_id=womatl_wo_id)
+              AND (womatl_itemsite_id=itemsite_id)
+              AND ( (wo_qtyord < 0) OR (womatl_issuemethod IN ('S','M')) )
+              AND (womatl_wo_id=pWoid)) LOOP
+
+      IF (_r.qty != 0) THEN
+        PERFORM returnWoMaterial(_r.womatl_id, _r.qty, _itemlocSeries, now());
+      END IF;
+
+    END LOOP;
+
+--  Reset the W/O Status to E
+    UPDATE wo
+    SET wo_status='E'
+    WHERE (wo_id=pWoid);
+
+    RETURN _itemlocSeries;
+
+  ELSE 
+    RETURN -1;
+  END IF;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/reversecashreceipt.sql b/foundation-database/public/functions/reversecashreceipt.sql
new file mode 100644 (file)
index 0000000..92dd13f
--- /dev/null
@@ -0,0 +1,281 @@
+CREATE OR REPLACE FUNCTION reverseCashReceipt(INTEGER, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCashrcptid ALIAS FOR $1;
+  pJournalNumber ALIAS FOR $2;
+  _p RECORD;
+  _r RECORD;
+  _postToAR NUMERIC;
+  _postToMisc NUMERIC;
+  _posted_base NUMERIC := 0;
+  _posted NUMERIC := 0;
+  _sequence INTEGER;
+  _aropenid INTEGER;
+  _arMemoNumber TEXT;
+  _arAccntid INTEGER;
+  _closed BOOLEAN;
+  _debitAccntid INTEGER;
+  _exchGain NUMERIC;
+  _comment      TEXT;
+
+BEGIN
+  _posted := 0;
+  _posted_base := 0;
+
+  SELECT fetchGLSequence() INTO _sequence;
+
+  SELECT accnt_id INTO _arAccntid
+  FROM cashrcpt, accnt, salescat
+  WHERE ((cashrcpt_salescat_id=salescat_id)
+    AND  (salescat_ar_accnt_id=accnt_id)
+    AND  (cashrcpt_id=pCashrcptid));
+  IF (NOT FOUND) THEN
+    SELECT accnt_id INTO _arAccntid
+    FROM cashrcpt, accnt
+    WHERE ( (findARAccount(cashrcpt_cust_id)=accnt_id)
+     AND (cashrcpt_id=pCashrcptid) );
+    IF (NOT FOUND) THEN
+      RETURN -5;
+    END IF;
+  END IF;
+
+  SELECT cashrcpt_cust_id, ('Reverse Cash Receipt posting for ' || cust_number||'-'||cust_name) AS custnote,
+         cashrcpt_fundstype, cashrcpt_number, cashrcpt_docnumber,
+         cashrcpt_distdate, cashrcpt_amount, cashrcpt_discount,
+         (cashrcpt_amount / cashrcpt_curr_rate) AS cashrcpt_amount_base,
+         (cashrcpt_discount / cashrcpt_curr_rate) AS cashrcpt_discount_base,
+         cashrcpt_notes,
+         cashrcpt_bankaccnt_id AS bankaccnt_id,
+         accnt_id AS prepaid_accnt_id,
+         cashrcpt_usecustdeposit,
+         cashrcpt_curr_id, cashrcpt_curr_rate INTO _p
+  FROM accnt, cashrcpt LEFT OUTER JOIN custinfo ON (cashrcpt_cust_id=cust_id)
+  WHERE ( (findPrepaidAccount(cashrcpt_cust_id)=accnt_id)
+   AND (cashrcpt_id=pCashrcptid) );
+  IF (NOT FOUND) THEN
+    RETURN -7;
+  END IF;
+
+  IF (_p.cashrcpt_fundstype IN ('A', 'D', 'M', 'V')) THEN
+    IF NOT EXISTS(SELECT ccpay_id
+                  FROM ccpay
+                  WHERE ((ccpay_order_number=CAST(pCashrcptid AS TEXT))
+                     AND (ccpay_status IN ('C', 'A')))) THEN
+      RETURN -8;
+    END IF;
+    _debitAccntid := findPrepaidAccount(_p.cashrcpt_cust_id);
+  ELSE
+    SELECT accnt_id INTO _debitAccntid
+    FROM cashrcpt, bankaccnt, accnt
+    WHERE ( (cashrcpt_bankaccnt_id=bankaccnt_id)
+     AND (bankaccnt_accnt_id=accnt_id)
+     AND (cashrcpt_id=pCashrcptid) );
+    IF (NOT FOUND) THEN
+      RETURN -6;
+    END IF;
+  END IF;
+
+--  Determine the amount to post to A/R Open Items
+  SELECT COALESCE(SUM(cashrcptitem_amount),0) INTO _postToAR
+  FROM cashrcptitem JOIN aropen ON (aropen_id=cashrcptitem_aropen_id)
+  WHERE ((cashrcptitem_cashrcpt_id=pCashrcptid)
+   AND (cashrcptitem_applied));
+  IF (NOT FOUND) THEN
+    _postToAR := 0;
+  END IF;
+
+--  Determine the amount to post to Misc. Distributions
+  SELECT COALESCE(SUM(cashrcptmisc_amount),0) INTO _postToMisc
+  FROM cashrcptmisc
+  WHERE (cashrcptmisc_cashrcpt_id=pCashrcptid);
+  IF (NOT FOUND) THEN
+    _postToMisc := 0;
+  END IF;
+
+--  Check to see if the C/R is over applied
+  IF ((_postToAR + _postToMisc) > _p.cashrcpt_amount) THEN
+    RETURN -1;
+  END IF;
+
+--  Check to see if the C/R is positive amount
+  IF (_p.cashrcpt_amount <= 0) THEN
+    RETURN -2;
+  END IF;
+
+--  Distribute A/R Applications
+  FOR _r IN SELECT aropen_id, aropen_doctype, aropen_docnumber, aropen_docdate,
+                   aropen_duedate, aropen_curr_id, aropen_curr_rate,
+                   round(aropen_amount - aropen_paid, 2) <=
+                      round(aropen_paid + 
+                      currToCurr(_p.cashrcpt_curr_id, aropen_curr_id,abs(cashrcptitem_amount + cashrcptitem_discount),_p.cashrcpt_distdate),2)
+                               AS closed,
+                   cashrcptitem_id, cashrcptitem_amount, cashrcptitem_discount,
+                   (cashrcptitem_amount / _p.cashrcpt_curr_rate) AS cashrcptitem_amount_base,
+                   (cashrcptitem_discount / _p.cashrcpt_curr_rate) AS cashrcptitem_discount_base,
+                   round(aropen_paid - 
+                      currToCurr(_p.cashrcpt_curr_id, aropen_curr_id,abs(cashrcptitem_amount),_p.cashrcpt_distdate),2) AS new_paid,
+                   round(currToCurr(_p.cashrcpt_curr_id, aropen_curr_id,abs(cashrcptitem_discount),_p.cashrcpt_distdate),2) AS new_discount
+            FROM cashrcptitem JOIN aropen ON (cashrcptitem_aropen_id=aropen_id)
+            WHERE ((cashrcptitem_cashrcpt_id=pCashrcptid)
+              AND (cashrcptitem_applied)) LOOP
+
+--  Handle discount 
+    IF (_r.cashrcptitem_discount_base > 0) THEN
+      PERFORM reverseCashReceiptDisc(_r.cashrcptitem_id, pJournalNumber);
+    END IF;
+     
+--  Update the aropen item to post the paid amount
+    UPDATE aropen
+    SET aropen_paid = _r.new_paid - _r.new_discount,
+        aropen_open = TRUE,
+        aropen_closedate = NULL
+    WHERE (aropen_id=_r.aropen_id);
+
+--  Cache the running amount posted
+    _posted_base := _posted_base + _r.cashrcptitem_amount_base;
+    _posted := _posted + _r.cashrcptitem_amount;
+
+--  Record the cashrcpt application
+    IF (_r.aropen_doctype IN ('I','D')) THEN
+      INSERT INTO arapply
+      ( arapply_cust_id,
+        arapply_source_aropen_id, arapply_source_doctype, arapply_source_docnumber,
+        arapply_target_aropen_id, arapply_target_doctype, arapply_target_docnumber,
+        arapply_fundstype, arapply_refnumber, arapply_reftype, arapply_ref_id,
+        arapply_applied, arapply_closed,
+        arapply_postdate, arapply_distdate, arapply_journalnumber, arapply_username,
+        arapply_curr_id
+       )
+      VALUES
+      ( _p.cashrcpt_cust_id,
+        -1, 'K', _p.cashrcpt_number,
+        _r.aropen_id, _r.aropen_doctype, _r.aropen_docnumber,
+        _p.cashrcpt_fundstype, _p.cashrcpt_docnumber, 'CRA', _r.cashrcptitem_id,
+        (round(_r.cashrcptitem_amount, 2) * -1.0), _r.closed,
+        CURRENT_DATE, _p.cashrcpt_distdate, pJournalNumber, getEffectiveXtUser(), _p.cashrcpt_curr_id );
+    ELSE
+      INSERT INTO arapply
+      ( arapply_cust_id,
+        arapply_source_aropen_id, arapply_source_doctype, arapply_source_docnumber,
+        arapply_target_aropen_id, arapply_target_doctype, arapply_target_docnumber,
+        arapply_fundstype, arapply_refnumber, arapply_reftype, arapply_ref_id,
+        arapply_applied, arapply_closed, arapply_postdate, arapply_distdate,
+        arapply_journalnumber, arapply_username, arapply_curr_id )
+      VALUES
+      ( _p.cashrcpt_cust_id,
+        _r.aropen_id, _r.aropen_doctype, _r.aropen_docnumber,
+        -1, 'R', _p.cashrcpt_number,
+        '', '', 'CRA', _r.cashrcptitem_id,
+        (round(abs(_r.cashrcptitem_amount), 2) * -1.0), _r.closed,
+        CURRENT_DATE, _p.cashrcpt_distdate, pJournalNumber, getEffectiveXtUser(), _p.cashrcpt_curr_id );
+    END IF;
+
+    _exchGain := arCurrGain(_r.aropen_id,_p.cashrcpt_curr_id, abs(_r.cashrcptitem_amount),
+                           _p.cashrcpt_distdate);
+
+    PERFORM insertIntoGLSeries( _sequence, 'A/R', 'CR',
+                        (_r.aropen_doctype || '-' || _r.aropen_docnumber),
+                        CASE WHEN _r.aropen_doctype != 'R' THEN _arAccntid
+                        ELSE findDeferredAccount(_p.cashrcpt_cust_id) END, 
+                        (round(_r.cashrcptitem_amount_base + _exchGain, 2) * -1.0),
+                        _p.cashrcpt_distdate, _p.custnote );
+
+    IF (_exchGain <> 0) THEN
+        PERFORM insertIntoGLSeries(_sequence, 'A/R', 'CR',
+               _r.aropen_doctype || '-' || _r.aropen_docnumber,
+               getGainLossAccntId(
+               CASE WHEN _r.aropen_doctype != 'R' THEN _arAccntid
+               ELSE findDeferredAccount(_p.cashrcpt_cust_id) END
+               ), round(_exchGain, 2),
+               _p.cashrcpt_distdate, _p.custnote);
+
+    END IF;
+
+  END LOOP;
+
+--  Distribute Misc. Applications
+  FOR _r IN SELECT cashrcptmisc_id, cashrcptmisc_accnt_id, cashrcptmisc_amount,
+                   (cashrcptmisc_amount / _p.cashrcpt_curr_rate) AS cashrcptmisc_amount_base,
+                   cashrcptmisc_notes
+            FROM cashrcptmisc
+            WHERE (cashrcptmisc_cashrcpt_id=pCashrcptid)  LOOP
+
+--  Cache the running amount posted
+    _posted_base := (_posted_base + _r.cashrcptmisc_amount_base);
+    _posted := (_posted + _r.cashrcptmisc_amount);
+
+--  Record the cashrcpt application
+    INSERT INTO arapply
+    ( arapply_cust_id,
+      arapply_source_aropen_id, arapply_source_doctype, arapply_source_docnumber,
+      arapply_target_aropen_id, arapply_target_doctype, arapply_target_docnumber,
+      arapply_fundstype, arapply_refnumber,
+      arapply_applied, arapply_closed,
+      arapply_postdate, arapply_distdate, arapply_journalnumber, arapply_username,
+      arapply_curr_id, arapply_reftype, arapply_ref_id )
+    VALUES
+    ( _p.cashrcpt_cust_id,
+      -1, 'K', '',
+      -1, 'Misc.', '',
+      _p.cashrcpt_fundstype, _p.cashrcpt_docnumber,
+      (round(_r.cashrcptmisc_amount, 2) * -1.0), TRUE,
+      CURRENT_DATE, _p.cashrcpt_distdate, pJournalNumber, getEffectiveXtUser(), 
+      _p.cashrcpt_curr_id, 'CRD', _r.cashrcptmisc_id );
+
+    PERFORM insertIntoGLSeries( _sequence, 'A/R', 'CR', _r.cashrcptmisc_notes,
+                                _r.cashrcptmisc_accnt_id,
+                                (round(_r.cashrcptmisc_amount_base, 2) * -1.0),
+                                _p.cashrcpt_distdate, _p.custnote );
+
+  END LOOP;
+
+--  Post any remaining Cash to an A/R Debit Memo
+--  this credit memo may absorb an occasional currency exchange rounding error
+  IF (round(_posted_base, 2) < round(_p.cashrcpt_amount_base, 2)) THEN
+    _comment := ('Unapplied from ' || _p.cashrcpt_fundstype || '-' || _p.cashrcpt_docnumber);
+    PERFORM insertIntoGLSeries( _sequence, 'A/R', 'CR',
+                                _comment,
+                                _p.prepaid_accnt_id,
+                                ((round(_p.cashrcpt_amount_base, 2) - round(_posted_base, 2)) * -1.0),
+                                _p.cashrcpt_distdate, _p.custnote );
+    SELECT fetchArMemoNumber() INTO _arMemoNumber;
+    -- Post A/R Debit Memo
+    SELECT createARDebitMemo(NULL, _p.cashrcpt_cust_id, pJournalNumber, _arMemoNumber, '',
+                              _p.cashrcpt_distdate, (_p.cashrcpt_amount - _posted),
+                              _comment, -1, -1, -1, _p.cashrcpt_distdate, -1, NULL, 0,
+                              _p.cashrcpt_curr_id) INTO _aropenid;
+    -- Create Cash Receipt Item to capture posting
+    INSERT INTO cashrcptitem
+      ( cashrcptitem_cashrcpt_id, cashrcptitem_aropen_id, cashrcptitem_amount )
+    VALUES
+      ( pCashrcptid, _aropenid, ((_p.cashrcpt_amount - _posted) * 1.0) );
+
+  ELSIF (round(_posted_base, 2) > round(_p.cashrcpt_amount_base, 2)) THEN
+    PERFORM insertIntoGLSeries(_sequence, 'A/R', 'CR',
+                   'Currency Exchange Rounding - ' || _p.cashrcpt_docnumber,
+                   getGainLossAccntId(_debitAccntid),
+                   ((round(_posted_base, 2) - round((_p.cashrcpt_amount_base + _p.cashrcpt_discount_base), 2)) * 1.0),
+                   _p.cashrcpt_distdate, _p.custnote);
+  END IF;
+
+--  Debit Cash
+  PERFORM insertIntoGLSeries( _sequence, 'A/R', 'CR',
+                    (_p.cashrcpt_fundstype || '-' || _p.cashrcpt_docnumber),
+                     _debitAccntid, round(_p.cashrcpt_amount_base, 2),
+                     _p.cashrcpt_distdate,
+                     _p.custnote );
+
+  PERFORM postGLSeries(_sequence, pJournalNumber);
+
+--  Update and void the posted cashrcpt
+  UPDATE cashrcpt SET cashrcpt_posted=FALSE,
+                      cashrcpt_posteddate=NULL,
+                      cashrcpt_postedby=NULL,
+                      cashrcpt_void=TRUE
+  WHERE (cashrcpt_id=pCashrcptid);
+
+  RETURN 1;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/reversecashreceiptdisc.sql b/foundation-database/public/functions/reversecashreceiptdisc.sql
new file mode 100644 (file)
index 0000000..e70ba88
--- /dev/null
@@ -0,0 +1,108 @@
+CREATE OR REPLACE FUNCTION reverseCashReceiptDisc(INTEGER, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCashrcptItemId ALIAS FOR $1;
+  pJournalNumber ALIAS FOR $2;
+  _r RECORD;
+  _t RECORD;
+  _v RECORD;
+  _ardiscountid INTEGER;
+  _arMemoNumber TEXT;
+  _arAccntid INTEGER;
+  _discountAccntid INTEGER;
+  _comment      TEXT;
+  _discprcnt NUMERIC;
+  _check INTEGER;
+
+BEGIN
+
+    -- Fetch base records for processing
+    SELECT aropen_id, aropen_doctype, aropen_amount,
+           cashrcptitem_discount,
+           cashrcpt_cust_id, cashrcpt_distdate, cashrcpt_applydate,
+           cashrcpt_curr_id, cashrcpt_fundstype, cashrcpt_docnumber,
+           round(currToCurr(cashrcpt_curr_id, aropen_curr_id, cashrcptitem_discount, cashrcpt_distdate),2) AS aropen_discount
+      INTO _r
+    FROM cashrcptitem 
+      JOIN cashrcpt ON (cashrcptitem_cashrcpt_id=cashrcpt_id)
+      JOIN aropen ON ( (aropen_id=cashrcptitem_aropen_id) AND (aropen_doctype IN ('I', 'D')) )
+    WHERE (cashrcptitem_id=pCashrcptItemId);
+
+    -- Get discount account
+    _discountAccntid := findardiscountaccount(_r.cashrcpt_cust_id);
+  
+    IF (_r.cashrcptitem_discount > 0) THEN
+      --  Determine discount percentage
+      _discprcnt := _r.aropen_discount / _r.aropen_amount;
+
+      SELECT fetchArMemoNumber() INTO _arMemoNumber;
+      _comment := 'Discount Credit Reversal from ' || _r.cashrcpt_fundstype || '-' || _r.cashrcpt_docnumber;
+
+      -- Create misc debit memo record
+      _ardiscountid := nextval('aropen_aropen_id_seq');
+      INSERT INTO aropen (
+        aropen_id, aropen_docdate, aropen_duedate, aropen_doctype, 
+        aropen_docnumber, aropen_curr_id, aropen_posted, aropen_amount ) 
+      VALUES ( 
+        _ardiscountid, _r.cashrcpt_distdate, _r.cashrcpt_distdate, 'D', 
+        _arMemoNumber, _r.cashrcpt_curr_id, false,_r.cashrcptitem_discount);
+        
+      IF (fetchMetricBool('CreditTaxDiscount')) THEN
+        --  proportional tax credits calculated and implemented for the debit memo generated by the discount
+        IF (_r.aropen_doctype  = 'I') THEN
+          -- Tax for invoices
+          SELECT aropen_cobmisc_id AS invcheadid, 
+                 invchead_curr_id, 
+                 invchead_invcdate INTO _t
+          FROM aropen
+            LEFT OUTER JOIN invchead ON (aropen_cobmisc_id = invchead_id) 
+            LEFT OUTER JOIN invcitem ON (invchead_id = invcitem_invchead_id)
+          WHERE aropen_id = _r.aropen_id;
+
+          FOR _v IN SELECT tax_sales_accnt_id,
+                           tax_id, 
+                           round(sum(taxdetail_tax), 2) AS tax,
+                           currToBase(_t.invchead_curr_id, round(sum(taxdetail_tax), 2), _t.invchead_invcdate) AS taxbasevalue
+          FROM tax 
+            JOIN calculateTaxDetailSummary('I', _t.invcheadid, 'T') ON (taxdetail_tax_id=tax_id)
+            GROUP BY tax_id, tax_sales_accnt_id 
+          LOOP
+            INSERT INTO aropentax(
+              taxhist_parent_id, taxhist_taxtype_id, taxhist_tax_id,
+              taxhist_percent, taxhist_amount, taxhist_tax, 
+              taxhist_docdate, taxhist_basis)
+            VALUES (
+              _ardiscountid, getadjustmenttaxtypeid(), _v.tax_id, 
+              0.00, 0.00, (round((_v.tax * _discprcnt), 2)), 
+              _r.cashrcpt_distdate, 0.00);
+          END LOOP;
+
+        ELSIF (_r.aropen_doctype  = 'D') THEN
+          -- Tax for debit memos
+          INSERT INTO aropentax(
+            taxhist_parent_id, taxhist_taxtype_id, taxhist_tax_id,
+            taxhist_percent, taxhist_amount, taxhist_tax, 
+            taxhist_docdate, taxhist_basis)
+          SELECT
+            _ardiscountid, taxhist_taxtype_id, taxhist_tax_id, 
+            0.00, 0.00, (round((taxhist_tax * _discprcnt), 2)),  
+            _r.cashrcpt_distdate, 0.00
+          FROM aropentax
+          WHERE (taxhist_parent_id=_r.aropen_id);
+              
+        END IF;
+      END IF; -- End taxes
+
+      -- Create debit memo for discount
+      SELECT createARDebitMemo(_ardiscountid, _r.cashrcpt_cust_id, pJournalNumber, _arMemoNumber, '',
+                                _r.cashrcpt_distdate, _r.cashrcptitem_discount,
+                                _comment, -1, -1, _discountAccntid, _r.cashrcpt_distdate,
+                                -1, NULL, 0, _r.cashrcpt_curr_id) INTO _ardiscountid;
+
+    END IF; -- End handle Discount
+
+    RETURN 1;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/reverseglseries.sql b/foundation-database/public/functions/reverseglseries.sql
new file mode 100644 (file)
index 0000000..ab9b956
--- /dev/null
@@ -0,0 +1,59 @@
+
+CREATE OR REPLACE FUNCTION reverseGLSeries(INTEGER, DATE, TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSequence ALIAS FOR $1;
+  pDistDate ALIAS FOR $2;
+  pNotes ALIAS FOR $3;
+  _sequence INTEGER := fetchGLSequence();      
+  _journal INTEGER;
+
+BEGIN
+
+  IF (SELECT COUNT(gltrans_sequence) > 0 FROM gltrans WHERE gltrans_sequence = pSequence) THEN
+    SELECT fetchJournalNumber(jrnluse_use) INTO _journal
+    FROM gltrans
+      JOIN jrnluse ON (gltrans_journalnumber=jrnluse_number)
+    WHERE (gltrans_sequence=pSequence)
+    LIMIT 1;
+  
+    INSERT INTO gltrans (gltrans_created, gltrans_posted, gltrans_exported,
+                         gltrans_date, gltrans_sequence, gltrans_accnt_id,
+                         gltrans_source, gltrans_docnumber, gltrans_misc_id,
+                         gltrans_amount, gltrans_notes, gltrans_journalnumber,
+                         gltrans_doctype)
+                 SELECT  CURRENT_TIMESTAMP, FALSE, FALSE,
+                         pDistDate, _sequence, gltrans_accnt_id,
+                         gltrans_source, gltrans_docnumber, gltrans_misc_id,
+                         (gltrans_amount * -1), pNotes, _journal,
+                         gltrans_doctype
+                    FROM gltrans
+                   WHERE (gltrans_sequence=pSequence);
+
+    PERFORM postIntoTrialBalance(_sequence);
+  ELSE
+    SELECT fetchJournalNumber(jrnluse_use) INTO _journal
+    FROM sltrans
+      JOIN jrnluse ON (sltrans_journalnumber=jrnluse_number)
+    WHERE (sltrans_sequence=pSequence)
+    LIMIT 1;
+    
+    INSERT INTO sltrans (sltrans_created, sltrans_posted,
+                         sltrans_date, sltrans_sequence, sltrans_accnt_id,
+                         sltrans_source, sltrans_docnumber, sltrans_misc_id,
+                         sltrans_amount, sltrans_notes, sltrans_journalnumber,
+                         sltrans_doctype)
+                 SELECT  CURRENT_TIMESTAMP, FALSE,
+                         pDistDate, _sequence, sltrans_accnt_id,
+                         sltrans_source, sltrans_docnumber, sltrans_misc_id,
+                         (sltrans_amount * -1), pNotes, _journal,
+                         sltrans_doctype
+                    FROM sltrans
+                   WHERE (sltrans_sequence=pSequence);
+  END IF;
+
+  RETURN _journal;
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/revokeallmodulecmnttypesource.sql b/foundation-database/public/functions/revokeallmodulecmnttypesource.sql
new file mode 100644 (file)
index 0000000..ff5ff64
--- /dev/null
@@ -0,0 +1,21 @@
+CREATE OR REPLACE FUNCTION revokeAllModuleCmnttypeSource(INTEGER, TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCmnttypeid ALIAS FOR $1;
+  pModuleName ALIAS FOR $2;
+
+BEGIN
+
+  DELETE FROM cmnttypesource
+  WHERE (cmnttypesource_id IN ( SELECT cmnttypesource_id
+                                FROM cmnttypesource, source
+                                WHERE ( (cmnttypesource_source_id=source_id)
+                                  AND (cmnttypesource_cmnttype_id=pCmnttypeid)
+                                  AND (source_module=pModuleName) ) ) );
+
+  RETURN 1;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/revokeallmodulepriv.sql b/foundation-database/public/functions/revokeallmodulepriv.sql
new file mode 100644 (file)
index 0000000..1ba7881
--- /dev/null
@@ -0,0 +1,23 @@
+CREATE OR REPLACE FUNCTION revokeAllModulePriv(TEXT, TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pUsername ALIAS FOR $1;
+  pModuleName ALIAS FOR $2;
+
+BEGIN
+
+  DELETE FROM usrpriv
+  WHERE (usrpriv_id IN ( SELECT usrpriv_id
+                         FROM usrpriv, priv
+                         WHERE ( (usrpriv_priv_id=priv_id)
+                          AND (usrpriv_username=pUsername)
+                          AND (priv_module=pModuleName) ) ) );
+
+  NOTIFY "usrprivUpdated";
+
+  RETURN 1;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/revokeallmoduleprivgroup.sql b/foundation-database/public/functions/revokeallmoduleprivgroup.sql
new file mode 100644 (file)
index 0000000..89f3ebd
--- /dev/null
@@ -0,0 +1,21 @@
+CREATE OR REPLACE FUNCTION revokeAllModulePrivGroup(INTEGER, TEXT) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pGrpid ALIAS FOR $1;
+  pModuleName ALIAS FOR $2;
+
+BEGIN
+
+  DELETE FROM grppriv
+  WHERE (grppriv_id IN ( SELECT grppriv_id
+                         FROM grppriv, priv
+                         WHERE ( (grppriv_priv_id=priv_id)
+                          AND (grppriv_grp_id=pGrpid)
+                          AND (priv_module=pModuleName) ) ) );
+
+  RETURN 1;
+
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/revokecmnttypesource.sql b/foundation-database/public/functions/revokecmnttypesource.sql
new file mode 100644 (file)
index 0000000..bc8a01a
--- /dev/null
@@ -0,0 +1,19 @@
+
+CREATE OR REPLACE FUNCTION revokeCmnttypeSource(INTEGER, INTEGER) RETURNS BOOL AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCmnttypeid ALIAS FOR $1;
+  pSourceid ALIAS FOR $2;
+
+BEGIN
+
+  DELETE FROM cmnttypesource
+  WHERE ( (cmnttypesource_cmnttype_id=pCmnttypeid)
+    AND (cmnttypesource_source_id=pSourceid) );
+
+  RETURN TRUE;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/revokegroup.sql b/foundation-database/public/functions/revokegroup.sql
new file mode 100644 (file)
index 0000000..05860b3
--- /dev/null
@@ -0,0 +1,18 @@
+CREATE OR REPLACE FUNCTION revokeGroup(TEXT, INTEGER) RETURNS BOOL AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pUsername ALIAS FOR $1;
+  pGrpid ALIAS FOR $2;
+
+BEGIN
+
+  DELETE FROM usrgrp
+  WHERE ( (usrgrp_username=pUsername)
+   AND (usrgrp_grp_id=pGrpid) );
+
+  RETURN TRUE;
+
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/revokepriv.sql b/foundation-database/public/functions/revokepriv.sql
new file mode 100644 (file)
index 0000000..a77d9c6
--- /dev/null
@@ -0,0 +1,44 @@
+
+CREATE OR REPLACE FUNCTION revokePriv(TEXT, INTEGER) RETURNS BOOL AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pUsername ALIAS FOR $1;
+  pPrivid ALIAS FOR $2;
+
+BEGIN
+
+  DELETE FROM usrpriv
+  WHERE ( (usrpriv_username=pUsername)
+   AND (usrpriv_priv_id=pPrivid) );
+
+  NOTIFY "usrprivUpdated";
+
+  RETURN TRUE;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION revokePriv(TEXT, TEXT) RETURNS BOOL AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pUsername ALIAS FOR $1;
+  pPrivname ALIAS FOR $2;
+
+BEGIN
+
+  DELETE FROM usrpriv
+  WHERE ( (usrpriv_username=pUsername)
+   AND (usrpriv_priv_id IN (SELECT priv_id
+                              FROM priv
+                             WHERE priv_name=pPrivname) ));
+
+  NOTIFY "usrprivUpdated";
+
+  RETURN TRUE;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/revokeprivgroup.sql b/foundation-database/public/functions/revokeprivgroup.sql
new file mode 100644 (file)
index 0000000..a8058ca
--- /dev/null
@@ -0,0 +1,20 @@
+CREATE OR REPLACE FUNCTION revokePrivGroup(INTEGER, INTEGER) RETURNS BOOL AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pGrpid ALIAS FOR $1;
+  pPrivid ALIAS FOR $2;
+
+BEGIN
+
+  DELETE FROM grppriv
+  WHERE ( (grppriv_grp_id=pGrpid)
+   AND (grppriv_priv_id=pPrivid) );
+
+  NOTIFY "usrprivUpdated";
+
+  RETURN TRUE;
+
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/rollupactualcost.sql b/foundation-database/public/functions/rollupactualcost.sql
new file mode 100644 (file)
index 0000000..54fb0f2
--- /dev/null
@@ -0,0 +1,10 @@
+CREATE OR REPLACE FUNCTION rollUpActualCost(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+    pItemid ALIAS FOR $1;
+
+BEGIN
+    RETURN rollUpSorACost(pitemid, TRUE);
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/rollupsoracost.sql b/foundation-database/public/functions/rollupsoracost.sql
new file mode 100644 (file)
index 0000000..6705b5b
--- /dev/null
@@ -0,0 +1,39 @@
+CREATE OR REPLACE FUNCTION rollUpSorACost(INTEGER, BOOLEAN) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemid      ALIAS FOR $1;
+  pActual      ALIAS FOR $2;
+  _counter INTEGER;
+  _setid INTEGER;
+  _consumers RECORD;
+
+BEGIN
+
+  _counter := 0;
+
+  SELECT indentedWhereUsed(pItemid) INTO _setid;
+
+  FOR _consumers IN SELECT bomwork_item_id
+                    FROM bomwork
+                    WHERE (bomwork_set_id=_setid)
+                    ORDER BY bomwork_level LOOP
+    PERFORM updateSorACost( _consumers.bomwork_item_id, costelem_type, TRUE,
+                           lowerCost(_consumers.bomwork_item_id,
+                                     costelem_type, pActual),
+                           pActual )
+    FROM costelem
+    WHERE (costelem_sys);
+
+    PERFORM updateLowerUserCosts(_consumers.bomwork_item_id, pActual);
+
+    _counter := _counter + 1;
+
+  END LOOP;
+
+  PERFORM deleteBOMWorkset(_setid);
+
+  RETURN _counter;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/rollupstandardcost.sql b/foundation-database/public/functions/rollupstandardcost.sql
new file mode 100644 (file)
index 0000000..c1964a2
--- /dev/null
@@ -0,0 +1,10 @@
+CREATE OR REPLACE FUNCTION rollUpStandardCost(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+    pItemid ALIAS FOR $1;
+
+BEGIN
+    RETURN rollUpSorACost(pItemid, FALSE);
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/roundcost.sql b/foundation-database/public/functions/roundcost.sql
new file mode 100644 (file)
index 0000000..6f7bdb6
--- /dev/null
@@ -0,0 +1,21 @@
+
+CREATE OR REPLACE FUNCTION roundCost(pCost NUMERIC) RETURNS NUMERIC IMMUTABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _scale INTEGER;
+
+BEGIN
+  IF (pCost IS NULL) THEN
+    RETURN NULL;
+  END IF;
+
+  SELECT locale_cost_scale INTO _scale
+  FROM locale
+  WHERE (locale_id=getUsrLocaleId());
+
+  RETURN ROUND(pCost, _scale);
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/roundlocale.sql b/foundation-database/public/functions/roundlocale.sql
new file mode 100644 (file)
index 0000000..15a8cce
--- /dev/null
@@ -0,0 +1,32 @@
+
+CREATE OR REPLACE FUNCTION roundLocale(pFractional BOOLEAN,
+                                       pQty NUMERIC,
+                                       pLocale TEXT) RETURNS NUMERIC IMMUTABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _r RECORD;
+  _scale INTEGER;
+
+BEGIN
+  IF (pFractional) THEN
+    SELECT * INTO _r
+    FROM locale
+    WHERE (locale_id=getUsrLocaleId());
+
+    _scale := CASE pLocale WHEN 'qtyper' THEN _r.locale_qtyper_scale
+                           WHEN 'cost' THEN _r.locale_cost_scale
+                           ELSE _r.locale_qty_scale
+              END;
+
+    RETURN ROUND(pQty, _scale);
+  ELSE
+    IF (TRUNC(pQty) < pQty) THEN
+      RETURN (TRUNC(pQty) + 1);
+    ELSE
+      RETURN TRUNC(pQty);
+    END IF;
+  END IF;
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/roundqty.sql b/foundation-database/public/functions/roundqty.sql
new file mode 100644 (file)
index 0000000..bc1e318
--- /dev/null
@@ -0,0 +1,25 @@
+
+CREATE OR REPLACE FUNCTION roundQty(pFractional BOOLEAN,
+                                    pQty NUMERIC) RETURNS NUMERIC IMMUTABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _scale INTEGER;
+
+BEGIN
+  SELECT locale_qty_scale INTO _scale
+  FROM locale
+  WHERE (locale_id=getUsrLocaleId());
+
+  IF (pFractional) THEN
+    RETURN ROUND(pQty, _scale);
+  ELSE
+    IF (TRUNC(pQty) < ROUND(pQty, _scale)) THEN
+      RETURN (TRUNC(pQty) + 1);
+    ELSE
+      RETURN TRUNC(pQty);
+    END IF;
+  END IF;
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/roundsale.sql b/foundation-database/public/functions/roundsale.sql
new file mode 100644 (file)
index 0000000..b3a251d
--- /dev/null
@@ -0,0 +1,21 @@
+
+CREATE OR REPLACE FUNCTION roundSale(pSale NUMERIC) RETURNS NUMERIC IMMUTABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _scale INTEGER;
+
+BEGIN
+  IF (pSale IS NULL) THEN
+    RETURN NULL;
+  END IF;
+
+  SELECT locale_salesprice_scale INTO _scale
+  FROM locale
+  WHERE (locale_id=getUsrLocaleId());
+
+  RETURN ROUND(pSale, _scale);
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/roundup.sql b/foundation-database/public/functions/roundup.sql
new file mode 100644 (file)
index 0000000..7a39e93
--- /dev/null
@@ -0,0 +1,22 @@
+
+CREATE OR REPLACE FUNCTION roundUp(NUMERIC) RETURNS NUMERIC IMMUTABLE AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+
+  pValue ALIAS FOR $1;
+  _checkValue integer;
+
+BEGIN
+
+  _checkValue := pValue::integer;
+
+  IF (_checkValue::numeric < pValue) THEN
+    RETURN (_checkValue + 1)::numeric;
+  ELSE
+    RETURN _checkValue::numeric;
+  END IF;
+
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/saveaddr.sql b/foundation-database/public/functions/saveaddr.sql
new file mode 100644 (file)
index 0000000..bdd0f06
--- /dev/null
@@ -0,0 +1,177 @@
+CREATE OR REPLACE FUNCTION saveAddr(int4, text, text, text, text, text, text, text, text, boolean, text, text)
+  RETURNS integer AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pAddrId ALIAS FOR $1;
+  pNumber ALIAS FOR $2;
+  pAddr1 ALIAS FOR $3;
+  pAddr2 ALIAS FOR $4;
+  pAddr3 ALIAS FOR $5;
+  pCity ALIAS FOR $6;
+  pState ALIAS FOR $7;
+  pPostalCode ALIAS FOR $8;
+  pCountry ALIAS FOR $9;
+  pActive ALIAS FOR $10;
+  pNotes ALIAS FOR $11;
+  pFlag ALIAS FOR $12;
+  _addrId INTEGER;
+  _addrNumber INTEGER;
+  _flag TEXT;
+  _p RECORD;
+  _cnt INTEGER;
+  _notes TEXT;
+
+BEGIN
+  --Validate
+  IF ((pFlag IS NULL) OR (pFlag = '') OR (pFlag = 'CHECK') OR (pFlag = 'CHANGEONE') OR (pFlag = 'CHANGEALL')) THEN
+    IF (pFlag='') THEN
+      _flag := 'CHECK';
+    ELSE
+      _flag := COALESCE(pFlag,'CHECK');
+    END IF;
+  ELSE
+       RAISE EXCEPTION 'Invalid Flag (%). Valid flags are CHECK, CHANGEONE or CHANGEALL', pFlag;
+  END IF;
+
+  _notes := COALESCE(pNotes,'');
+  
+  --If there is nothing here, get out
+  IF ( (pNumber = '' OR pNumber IS NULL)
+    AND (pAddr1 = '' OR pAddr1 IS NULL)
+    AND (pAddr2 = '' OR pAddr2 IS NULL)
+    AND (pAddr3 = '' OR pAddr3 IS NULL)
+    AND (pCity = '' OR pCity IS NULL)
+    AND (pState = '' OR pState IS NULL)
+    AND (pPostalCode = '' OR pPostalCode IS NULL)
+    AND (pCountry = '' OR pCountry IS NULL) ) THEN
+    RETURN NULL;
+  
+  END IF;
+  
+  _addrId := COALESCE(pAddrId,-1);
+
+  --If we have an ID see if anything has changed, if not get out
+  IF (_addrId >= 0) THEN
+    SELECT * FROM addr INTO _p
+    WHERE ((pAddrId=addr_id)
+    AND (COALESCE(pNumber,addr_number)=addr_number)
+    AND (COALESCE(pAddr1, '')=COALESCE(addr_line1, ''))
+    AND (COALESCE(pAddr2, '')=COALESCE(addr_line2, ''))
+    AND (COALESCE(pAddr3, '')=COALESCE(addr_line3, ''))
+    AND (COALESCE(pCity, '')=COALESCE(addr_city, ''))
+    AND (COALESCE(pState, '')=COALESCE(addr_state, ''))
+    AND (COALESCE(pPostalCode, '')=COALESCE(addr_postalcode, ''))
+    AND (COALESCE(pCountry, '')=COALESCE(addr_country, ''))
+    AND (pActive=addr_active)
+    AND (_notes=COALESCE(addr_notes,'')));
+    IF (FOUND) THEN
+      RETURN _addrId;
+    END IF;
+  END IF;
+  --Check to see if duplicate address exists
+
+    SELECT addr_id, addr_notes INTO _p
+    FROM addr 
+    WHERE ((_addrId <> addr_id)
+    AND  (COALESCE(UPPER(addr_line1),'') = COALESCE(UPPER(pAddr1),''))
+    AND  (COALESCE(UPPER(addr_line2),'') = COALESCE(UPPER(pAddr2),''))
+    AND  (COALESCE(UPPER(addr_line3),'') = COALESCE(UPPER(pAddr3),''))
+    AND  (COALESCE(UPPER(addr_city),'') = COALESCE(UPPER(pCity),''))
+    AND  (COALESCE(UPPER(addr_state),'') = COALESCE(UPPER(pState),''))
+    AND  (COALESCE(UPPER(addr_postalcode),'') = COALESCE(UPPER(pPostalcode),''))
+    AND  (COALESCE(UPPER(addr_country),'') = COALESCE(UPPER(pCountry),'')));
+    IF (FOUND) THEN
+       --Note:  To prevent overwriting of existing notes, the application
+       --needs to load any existing notes for a matching address before altering them.
+       IF (_notes <> _p.addr_notes) THEN
+               UPDATE addr 
+               SET addr_notes=addr_notes || '
+' || _notes
+               WHERE addr_id=_p.addr_id;
+       END IF;
+        RETURN _p.addr_id;  --A matching address exits
+    END IF;
+  IF (_addrId < 0) THEN
+    _flag := 'CHANGEONE';
+  END IF;
+
+  IF (_flag = 'CHECK') THEN
+    IF addrUseCount(_addrId) > 1 THEN
+      RETURN -2;
+    ELSIF (SELECT COUNT(addr_id)=0 FROM addr WHERE (addr_id=_addrId)) THEN
+      _flag := 'CHANGEONE';
+    ELSE
+      _flag := 'CHANGEALL';
+    END IF;
+  END IF;
+
+  IF (_flag = 'CHANGEALL') THEN
+    _addrNumber := pNumber;
+    IF (_addrNumber IS NULL) THEN
+      SELECT addr_number INTO _addrNumber
+        FROM addr
+       WHERE(addr_id = _addrId);
+      IF (_addrNumber IS NULL) THEN
+        _addrNumber := fetchNextNumber('AddressNumber');
+      END IF;
+    END IF;
+   
+    UPDATE addr SET
+      addr_line1 = pAddr1, addr_line2 = pAddr2, addr_line3 = pAddr3,
+      addr_city = pCity, addr_state = pState,
+      addr_postalcode = pPostalcode, addr_country = pCountry,
+      addr_active = pActive, addr_notes = pNotes
+    WHERE addr_id = _addrId;
+    RETURN _addrId;
+
+  ELSE
+    SELECT NEXTVAL('addr_addr_id_seq') INTO _addrId;
+
+    IF (_flag = 'CHANGEONE') THEN
+      _addrNumber := fetchNextNumber('AddressNumber');
+    ELSE
+      _addrNumber := COALESCE(pNumber::text,fetchNextNumber('AddressNumber'));
+    END IF;
+
+    INSERT INTO addr ( addr_id, addr_number,
+    addr_line1, addr_line2, addr_line3, 
+    addr_city, addr_state, addr_postalcode, addr_country, 
+    addr_active, addr_notes  
+    ) VALUES ( _addrId, _addrNumber,
+    pAddr1, pAddr2, pAddr3, 
+    pCity, pState, pPostalcode, pCountry,
+    pActive, _notes);
+    RETURN _addrId;
+       
+  END IF;
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION saveAddr(int4, text, text, text, text, text, text, text, text, text)
+  RETURNS integer AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pAddrId ALIAS FOR $1;
+  pNumber ALIAS FOR $2;
+  pAddr1 ALIAS FOR $3;
+  pAddr2 ALIAS FOR $4;
+  pAddr3 ALIAS FOR $5;
+  pCity ALIAS FOR $6;
+  pState ALIAS FOR $7;
+  pPostalCode ALIAS FOR $8;
+  pCountry ALIAS FOR $9;
+  pFlag ALIAS FOR $10;
+  _returnVal INTEGER;
+
+BEGIN
+  SELECT saveAddr(pAddrId,pNumber, pAddr1,pAddr2,pAddr3,pCity,pState,pPostalCode,pCountry,true,'',pFlag) INTO _returnVal;
+  
+  RETURN _returnVal;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/savealarm.sql b/foundation-database/public/functions/savealarm.sql
new file mode 100644 (file)
index 0000000..7bf7de0
--- /dev/null
@@ -0,0 +1,159 @@
+SELECT dropIfExists('FUNCTION', 'saveAlarm(int,text,text,timestamp,int,text,text,text,int,text)', 'public');
+
+CREATE OR REPLACE FUNCTION saveAlarm(int,text,date,time,int,text,boolean,text,boolean,text,boolean,text,text,int,text) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pAlarmId ALIAS FOR $1;
+  pAlarmNumber ALIAS FOR $2;
+  pDate ALIAS FOR $3;
+  pTime ALIAS FOR $4;
+  pOffset ALIAS FOR $5;
+  pQualifier ALIAS FOR $6;
+  pEvent ALIAS FOR $7;
+  pEventRecipient ALIAS FOR $8;
+  pEmail ALIAS FOR $9;
+  pEmailRecipient ALIAS FOR $10;
+  pSysmsg ALIAS FOR $11;
+  pSysmsgRecipient ALIAS FOR $12;
+  pSource ALIAS FOR $13;
+  pSourceId ALIAS FOR $14;
+  pFlag ALIAS FOR $15;
+  _alarmId INTEGER;
+  _alarmNumber TEXT;
+  _alarmTime TIMESTAMP;
+  _alarmInterval INTERVAL;
+  _alarmTrigger TIMESTAMP;
+  _isNew BOOLEAN;
+  _flag TEXT;
+  _alarmCount INTEGER := 0;
+  _debug BOOLEAN := false;
+
+BEGIN
+  IF (_debug) THEN
+    RAISE NOTICE ''pAlarmId = %'', pAlarmId;
+    RAISE NOTICE ''pAlarmNumber = %'', pAlarmNumber;
+    RAISE NOTICE ''pDate = %'', pDate;
+    RAISE NOTICE ''pTime = %'', pTime;
+    RAISE NOTICE ''pOffset = %'', pOffset;
+    RAISE NOTICE ''pQualifier = %'', pQualifier;
+    RAISE NOTICE ''pEvent = %'', pEvent;
+    RAISE NOTICE ''pEventRecipient = %'', pEventRecipient;
+    RAISE NOTICE ''pEmail = %'', pEmail;
+    RAISE NOTICE ''pEmailRecipient = %'', pEmailRecipient;
+    RAISE NOTICE ''pSysmsg = %'', pSysmsg;
+    RAISE NOTICE ''pSysmsgRecipient = %'', pSysmsgRecipient;
+    RAISE NOTICE ''pSource = %'', pSource;
+    RAISE NOTICE ''pSourceId = %'', pSourceId;
+    RAISE NOTICE ''pFlag = %'', pFlag;
+  END IF;
+  --Validate
+  IF ((pFlag IS NULL) OR (pFlag = '''') OR (pFlag = ''CHECK'') OR (pFlag = ''CHANGEONE'') OR (pFlag = ''CHANGEALL'')) THEN
+    IF (pFlag='''') THEN
+      _flag := ''CHECK'';
+    ELSE
+      _flag := COALESCE(pFlag,''CHECK'');
+    END IF;
+  ELSE
+       RAISE EXCEPTION ''Invalid Flag (%). Valid flags are CHECK, CHANGEONE or CHANGEALL'', pFlag;
+  END IF;
+  
+  --If there is nothing here get out
+  IF ( (pAlarmId IS NULL OR pAlarmId = -1)
+       AND (pOffset IS NULL)
+        AND (pSourceId IS NULL)
+       AND (COALESCE(pQualifier, '''') = '''')
+       AND (COALESCE(pEventRecipient, '''') = '''')
+       AND (COALESCE(pEmailRecipient, '''') = '''')
+       AND (COALESCE(pSysmsgRecipient, '''') = '''')
+       AND (COALESCE(pSource, '''') = '''') ) THEN
+       
+       RETURN NULL;
+
+  END IF;
+  
+  IF (pAlarmId IS NULL OR pAlarmId = -1) THEN 
+    _isNew := true;
+    _alarmId := nextval(''alarm_alarm_id_seq'');
+    _alarmNumber := fetchNextNumber(''AlarmNumber'');
+  ELSE
+    SELECT COUNT(alarm_id) INTO _alarmCount
+      FROM alarm
+      WHERE ((alarm_id=pAlarmId)
+      AND (alarm_source=pSource)
+      AND (alarm_source_id=pSourceId));
+
+    -- ask whether new or update if name changes
+    -- but only if this isn''t a new record with a pre-allocated id
+    IF (_alarmCount < 1 AND _flag = ''CHECK'') THEN
+      IF (EXISTS(SELECT alarm_id
+                 FROM alarm
+                 WHERE (alarm_id=pAlarmId))) THEN
+        RETURN -10;
+      ELSE
+        _isNew := true;
+        _alarmNumber := fetchNextNumber(''AlarmNumber'');
+      END IF;
+    ELSIF (_flag = ''CHANGEONE'') THEN
+      _isNew := true;
+      _alarmId := nextval(''alarm_alarm_id_seq'');
+      _alarmNumber := fetchNextNumber(''AlarmNumber'');
+    END IF;
+  END IF;
+
+  _alarmNumber := COALESCE(_alarmNumber,pAlarmNumber,fetchNextNumber(''AlarmNumber''));
+
+  _alarmTime := COALESCE(pDate, CURRENT_DATE) + COALESCE(pTime, CURRENT_TIME);
+  IF (COALESCE(pOffset, 0) > 0) THEN
+    _alarmInterval := CASE WHEN (pQualifier IN (''MB'', ''MA'')) THEN CAST(pOffset AS TEXT) || '' minutes''
+                           WHEN (pQualifier IN (''HB'', ''HA'')) THEN CAST(pOffset AS TEXT) || '' hours''
+                           WHEN (pQualifier IN (''DB'', ''DA'')) THEN CAST(pOffset AS TEXT) || '' days''
+                           ELSE ''''
+                     END;
+    _alarmTrigger := CASE WHEN (pQualifier IN (''MB'', ''HB'', ''DB'')) THEN _alarmTime - _alarmInterval 
+                          WHEN (pQualifier IN (''MA'', ''HA'', ''DA'')) THEN _alarmTime + _alarmInterval
+                          ELSE _alarmTime
+                     END; 
+  ELSE
+    _alarmTrigger := _alarmTime;
+  END IF;
+
+  IF (_isNew) THEN
+    _alarmId := COALESCE(_alarmId,pAlarmId,nextval(''alarm_alarm_id_seq''));
+    INSERT INTO alarm (
+      alarm_id,alarm_number,
+      alarm_event, alarm_email, alarm_sysmsg, alarm_trigger,
+      alarm_time, alarm_time_offset, alarm_time_qualifier,
+      alarm_creator, alarm_event_recipient, alarm_email_recipient, alarm_sysmsg_recipient,
+      alarm_source, alarm_source_id )
+    VALUES (
+      _alarmId, _alarmNumber,
+      pEvent, pEmail, pSysmsg, _alarmTrigger,
+      _alarmTime, pOffset, pQualifier,
+      getEffectiveXtUser(), pEventRecipient, pEmailRecipient, pSysmsgRecipient,
+      pSource, pSourceId );
+
+    RETURN _alarmId;
+
+  ELSE
+    UPDATE alarm SET
+      alarm_number=_alarmNumber,
+      alarm_event=COALESCE(pEvent, alarm_event),
+      alarm_email=COALESCE(pEmail, alarm_event),
+      alarm_sysmsg=COALESCE(pSysmsg, alarm_event),
+      alarm_trigger=_alarmTrigger,
+      alarm_time=_alarmTime,
+      alarm_time_offset=COALESCE(pOffset, alarm_time_offset),
+      alarm_time_qualifier=COALESCE(pQualifier, alarm_time_qualifier),
+      alarm_event_recipient=COALESCE(pEventRecipient, alarm_event_recipient),
+      alarm_email_recipient=COALESCE(pEmailRecipient, alarm_email_recipient),
+      alarm_sysmsg_recipient=COALESCE(pSysmsgRecipient, alarm_sysmsg_recipient)
+    WHERE (alarm_id=pAlarmId);
+    
+    RETURN pAlarmId;
+
+  END IF;
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/savebomhead.sql b/foundation-database/public/functions/savebomhead.sql
new file mode 100644 (file)
index 0000000..8890c9e
--- /dev/null
@@ -0,0 +1,119 @@
+CREATE OR REPLACE FUNCTION saveBomHead(integer,text,date,text,numeric,numeric)
+  RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemid ALIAS FOR $1;
+  pRevision ALIAS FOR $2;
+  pRevisionDate ALIAS FOR $3;
+  pDocumentNumber ALIAS FOR $4;
+  pBatchSize ALIAS FOR $5;
+  pRequiredQtyPer ALIAS FOR $6;
+  _seq INTEGER;
+  _p RECORD;
+  _revid INTEGER;
+  
+BEGIN
+
+  IF (NOT fetchMetricBool(''RevControl'')) THEN -- Deal with BOM if Rev Control Turned off
+    SELECT bomhead_id INTO _seq
+    FROM bomhead 
+    WHERE (bomhead_item_id=pItemid);
+
+    IF (NOT FOUND) THEN  -- No bomhead exists
+      _seq := NEXTVAL(''bomhead_bomhead_id_seq'');
+      
+      INSERT INTO bomhead 
+        (bomhead_id,bomhead_item_id,bomhead_docnum,bomhead_revision,
+        bomhead_revisiondate,bomhead_batchsize,bomhead_requiredqtyper,bomhead_rev_id)
+        VALUES 
+        (_seq,pItemid, pDocumentNumber, pRevision, pRevisionDate, pBatchSize, pRequiredQtyPer,-1);   
+    ELSE
+      UPDATE bomhead SET
+        bomhead_revision       = pRevision,
+        bomhead_revisiondate   = pRevisionDate,
+        bomhead_docnum         = pDocumentNumber,
+        bomhead_batchsize      = pBatchSize,
+        bomhead_requiredqtyper = pRequiredQtyPer
+      WHERE (bomhead_id=_seq);
+    END IF;
+    
+    RETURN _seq;
+  ELSE  -- Deal with Revision Control
+    IF (COALESCE(pRevision,'''') = '''' AND getActiveRevId(''BOM'',pItemid) != -1) THEN 
+        RAISE EXCEPTION ''Revision Control records exist for item.  You must provide a new or existing revision number.'';
+    END IF;
+    
+    SELECT * INTO _p
+    FROM bomhead
+      LEFT OUTER JOIN rev ON (bomhead_rev_id=rev_id),
+      item
+    WHERE ((bomhead_item_id=pItemid)
+    AND (COALESCE(bomhead_revision,'''')=COALESCE(pRevision,''''))
+    AND (bomhead_item_id=item_id));
+
+    IF (NOT FOUND) THEN  -- This is a new bomhead record
+      IF LENGTH(pRevision) > 0 THEN  -- We need to create a revision record   
+        SELECT createbomrev(pItemid, pRevision) INTO _revid;
+        
+        UPDATE bomhead SET
+          bomhead_revisiondate         = pRevisiondate,
+          bomhead_docnum               = pDocumentNumber,
+          bomhead_batchsize            = pBatchsize,
+          bomhead_requiredqtyper       = pRequiredqtyper
+        WHERE (bomhead_rev_id=_revid);
+        
+        SELECT bomhead_id INTO _seq
+        FROM bomhead
+        WHERE (bomhead_rev_id=_revid);
+        
+        RETURN _seq;      
+      ELSE  -- Just create a regular bom header record
+       _seq := NEXTVAL(''bomhead_bomhead_id_seq'');
+       
+       INSERT INTO bomhead 
+        (bomhead_id,bomhead_item_id,bomhead_docnum,bomhead_revision,
+        bomhead_revisiondate,bomhead_batchsize,bomhead_requiredqtyper,bomhead_rev_id)
+        VALUES 
+        (_seq,pItemid, pDocumentNumber, pRevision, pRevisionDate, pBatchSize, pRequiredQtyPer,-1);
+        
+        RETURN _seq;      
+        
+      END IF;
+    ELSE  -- We need to update a record
+      IF (_p.rev_status = ''I'') THEN
+        RAISE EXCEPTION ''Revision % for % is inactive.  Update not allowed.'', _p.rev_number, _p.item_number;
+
+      ELSIF (COALESCE(pRevision,'''') = COALESCE(_p.bomhead_revision,'''')) THEN  -- No change, just update
+        UPDATE bomhead SET
+          bomhead_revisiondate         = pRevisiondate,
+          bomhead_docnum               = pDocumentNumber,
+          bomhead_batchsize            = pBatchSize,
+          bomhead_requiredqtyper       = pRequiredqtyper
+        WHERE (bomhead_id=_p.bomhead_id);
+
+        RETURN _p.bomhead_id;
+        
+      ELSE -- Need a new revision
+        SELECT createbomrev(pItemid, pRevision) INTO _revid;
+        
+        UPDATE bomhead SET
+          bomhead_revisiondate         = pRevisiondate,
+          bomhead_docnum               = pDocumentNumber,
+          bomhead_batchsize            = pBatchSize,
+          bomhead_requiredqtyper       = pRequiredqtyper
+        WHERE (bomhead_rev_id=_revid);
+
+        SELECT bomhead_id INTO _seq
+        FROM bomhead
+        WHERE (bomhead_rev_id=_revid);
+        
+        RETURN _seq;
+      END IF;
+    END IF;
+  END IF;
+
+  RETURN _seq;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/savecntct.sql b/foundation-database/public/functions/savecntct.sql
new file mode 100644 (file)
index 0000000..11abaa5
--- /dev/null
@@ -0,0 +1,207 @@
+CREATE OR REPLACE FUNCTION saveCntct( pCntctId         INTEGER,
+                                      pContactNumber   TEXT,
+                                      pCrmAcctId       INTEGER,
+                                      pAddrId          INTEGER,
+                                      pHonorific       TEXT,
+                                      pFirstName       TEXT,
+                                      pMiddleName      TEXT,
+                                      pLastName        TEXT,
+                                      pSuffix          TEXT,
+                                      pInitials        TEXT,
+                                      pActive          BOOL,
+                                      pPhone           TEXT,
+                                      pPhone2          TEXT,
+                                      pFax             TEXT,
+                                      pEmail           TEXT,
+                                      pWebAddr         TEXT,
+                                      pNotes           TEXT,
+                                      pTitle           TEXT,
+                                      pFlag            TEXT,
+                                      pOwnerUsername   TEXT ) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _cntctId INTEGER;
+  _cntctNumber TEXT;
+  _isNew BOOLEAN;
+  _flag TEXT;
+  _contactCount INTEGER := 0;
+
+BEGIN
+  --Validate
+  IF ((pFlag IS NULL) OR (pFlag = '') OR (pFlag = 'CHECK') OR (pFlag = 'CHANGEONE') OR (pFlag = 'CHANGEALL')) THEN
+    IF (pFlag='') THEN
+      _flag := 'CHECK';
+    ELSE
+      _flag := COALESCE(pFlag,'CHECK');
+    END IF;
+  ELSE
+       RAISE EXCEPTION 'Invalid Flag (%). Valid flags are CHECK, CHANGEONE or CHANGEALL', pFlag;
+  END IF;
+  
+  --If there is nothing here get out
+  IF ( (pCntctId IS NULL OR pCntctId = -1)
+       AND (pAddrId IS NULL)
+       AND (COALESCE(pFirstName, '') = '')
+       AND (COALESCE(pMiddleName, '') = '')
+       AND (COALESCE(pLastName, '') = '')
+       AND (COALESCE(pSuffix, '') = '')
+       AND (COALESCE(pHonorific, '') = '')
+       AND (COALESCE(pInitials, '') = '')
+       AND (COALESCE(pPhone, '') = '')
+       AND (COALESCE(pPhone2, '') = '')
+       AND (COALESCE(pFax, '') = '')
+       AND (COALESCE(pEmail, '') = '')
+       AND (COALESCE(pWebAddr, '') = '')
+       AND (COALESCE(pNotes, '') = '')
+       AND (COALESCE(pTitle, '') = '') ) THEN
+       
+       RETURN NULL;
+
+  END IF;
+  
+  IF (pCntctId IS NULL OR pCntctId = -1) THEN 
+    _isNew := true;
+    _cntctId := nextval('cntct_cntct_id_seq');
+    _cntctNumber := COALESCE(pContactNumber,fetchNextNumber('ContactNumber'));
+  ELSE
+    SELECT COUNT(cntct_id) INTO _contactCount
+      FROM cntct
+      WHERE ((cntct_id=pCntctId)
+      AND (cntct_first_name=pFirstName)
+      AND (cntct_last_name=pLastName));
+
+    -- ask whether new or update if name changes
+    -- but only if this isn't a new record with a pre-allocated id
+    IF (_contactCount < 1 AND _flag = 'CHECK') THEN
+      IF (EXISTS(SELECT cntct_id
+                 FROM cntct
+                 WHERE (cntct_id=pCntctId))) THEN
+        RETURN -10;
+      ELSE
+        _isNew := true;
+        _cntctNumber := fetchNextNumber('ContactNumber');
+      END IF;
+    ELSIF (_flag = 'CHANGEONE') THEN
+      _isNew := true;
+      _cntctId := nextval('cntct_cntct_id_seq');
+      _cntctNumber := fetchNextNumber('ContactNumber');
+    ELSIF (_flag = 'CHANGEALL') THEN
+      _isNew := false;
+    END IF;
+  END IF;
+
+  IF (pContactNumber = '') THEN
+    _cntctNumber := fetchNextNumber('ContactNumber');
+  ELSE
+    _cntctNumber := COALESCE(_cntctNumber,pContactNumber,fetchNextNumber('ContactNumber'));
+  END IF;
+
+  IF (_isNew) THEN
+    _cntctId := COALESCE(_cntctId,pCntctId,nextval('cntct_cntct_id_seq'));
+    INSERT INTO cntct (
+      cntct_id,cntct_number,
+      cntct_crmacct_id,cntct_addr_id,cntct_first_name,
+      cntct_last_name,cntct_honorific,cntct_initials,
+      cntct_active,cntct_phone,cntct_phone2,
+      cntct_fax,cntct_email,cntct_webaddr,
+      cntct_notes,cntct_title,cntct_middle,cntct_suffix, cntct_owner_username ) 
+    VALUES (
+      _cntctId, COALESCE(_cntctNumber,fetchNextNumber('ContactNumber')) ,pCrmAcctId,pAddrId,
+      pFirstName,pLastName,pHonorific,
+      pInitials,COALESCE(pActive,true),pPhone,pPhone2,pFax,
+      pEmail,pWebAddr,pNotes,pTitle,pMiddleName,pSuffix,pOwnerUsername );
+
+    RETURN _cntctId;
+
+  ELSE
+    UPDATE cntct SET
+      cntct_number=COALESCE(_cntctNumber,fetchNextNumber('ContactNumber')),
+      cntct_crmacct_id=COALESCE(pCrmAcctId,cntct_crmacct_id),
+      cntct_addr_id=COALESCE(pAddrId,cntct_addr_id),
+      cntct_first_name=COALESCE(pFirstName,cntct_first_name),
+      cntct_last_name=COALESCE(pLastName,cntct_last_name),
+      cntct_honorific=COALESCE(pHonorific,cntct_honorific),
+      cntct_initials=COALESCE(pInitials,cntct_initials),
+      cntct_active=COALESCE(pActive,cntct_active),
+      cntct_phone=COALESCE(pPhone,cntct_phone),
+      cntct_phone2=COALESCE(pPhone2,cntct_phone2),
+      cntct_fax=COALESCE(pFax,cntct_fax),
+      cntct_email=COALESCE(pEmail,cntct_email),
+      cntct_webaddr=COALESCE(pWebAddr,cntct_webaddr),
+      cntct_notes=COALESCE(pNotes,cntct_notes),
+      cntct_title=COALESCE(pTitle,cntct_title),
+      cntct_middle=COALESCE(pMiddleName,cntct_middle),
+      cntct_suffix=COALESCE(pSuffix,cntct_suffix),
+      cntct_owner_username=COALESCE(pOwnerUsername, cntct_owner_username) 
+    WHERE (cntct_id=pCntctId);
+    
+    RETURN pCntctId;
+
+  END IF;
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION saveCntct( pCntctId         INTEGER,
+                                      pContactNumber   TEXT,
+                                      pCrmAcctId       INTEGER,
+                                      pAddrId          INTEGER,
+                                      pHonorific       TEXT,
+                                      pFirstName       TEXT,
+                                      pMiddleName      TEXT,
+                                      pLastName        TEXT,
+                                      pSuffix          TEXT,
+                                      pInitials        TEXT,
+                                      pActive          BOOL,
+                                      pPhone           TEXT,
+                                      pPhone2          TEXT,
+                                      pFax             TEXT,
+                                      pEmail           TEXT,
+                                      pWebAddr         TEXT,
+                                      pNotes           TEXT,
+                                      pTitle           TEXT,
+                                      pFlag            TEXT ) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _returnVal INTEGER;
+
+BEGIN
+  
+  SELECT saveCntct( pCntctId, pContactNumber, pCrmAcctId, pAddrId, pHonorific, pFirstName, pMiddleName, pLastName, pSuffix, pInitials, 
+       pActive, pPhone, pPhone2, pFax, pEmail, pWebAddr, pNotes, pTitle, pFlag, NULL) INTO _returnVal;
+  RETURN _returnVal;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION saveCntct( pCntctId         INTEGER,
+                                      pContactNumber   TEXT,
+                                      pAddrId          INTEGER,
+                                      pHonorific       TEXT,
+                                      pFirstName       TEXT,
+                                      pMiddleName      TEXT,
+                                      pLastName        TEXT,
+                                      pSuffix          TEXT,
+                                      pPhone           TEXT,
+                                      pPhone2          TEXT,
+                                      pFax             TEXT,
+                                      pEmail           TEXT,
+                                      pWebAddr         TEXT,
+                                      pTitle           TEXT,
+                                      pFlag            TEXT ) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _returnVal INTEGER;
+
+BEGIN
+  
+  SELECT saveCntct(pCntctId,pContactNumber,NULL,pAddrId,pHonorific,pFirstName,pMiddleName,pLastName,pSuffix,NULL,
+        NULL,pPhone,pPhone2,pFax,pEmail,pWebAddr,NULL,pTitle,pFlag, NULL) INTO _returnVal;
+  
+  RETURN _returnVal;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/saveimageass.sql b/foundation-database/public/functions/saveimageass.sql
new file mode 100644 (file)
index 0000000..d54297b
--- /dev/null
@@ -0,0 +1,45 @@
+CREATE OR REPLACE FUNCTION saveImageAss(TEXT, INTEGER, CHAR, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSource      ALIAS FOR $1;
+  pSourceId    ALIAS FOR $2;
+  pPurpose     ALIAS FOR $3;
+  pImageid     ALIAS FOR $4;
+  _imageassId INTEGER = 0;
+
+BEGIN
+
+--  See if this link already exists
+  SELECT imageass_id INTO _imageassId
+  FROM imageass
+  WHERE ((imageass_source_id=pSourceId)
+  AND (imageass_source=pSource)
+  AND (imageass_image_id=pImageId)
+  AND (imageass_purpose=pPurpose));
+
+  IF (FOUND) THEN
+    RETURN _imageassId;
+  END IF;
+  
+-- See if a record with this purpose already exists (item only)
+  IF (pSource = 'I' AND pPurpose != 'M') THEN
+    SELECT imageass_id INTO _imageassId
+    FROM imageass
+    WHERE ((imageass_source_id=pSourceId)
+    AND (imageass_source=pSource)
+    AND (imageass_purpose=pPurpose));
+  END IF;
+
+  IF (_imageassId > 0) THEN
+    UPDATE imageass SET
+      imageass_image_id=pImageId
+    WHERE (imageass_id=_imageassId);
+  ELSE
+    _imageassId := NEXTVAL('imageass_imageass_id_seq');
+    INSERT INTO imageass VALUES (_imageassId,pSourceId,pSource,pImageid,CASE WHEN pSource='I' THEN pPurpose ELSE 'M' END);
+  END IF;
+  
+  RETURN _imageassId;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/saveipsitem.sql b/foundation-database/public/functions/saveipsitem.sql
new file mode 100644 (file)
index 0000000..1c7890f
--- /dev/null
@@ -0,0 +1,156 @@
+CREATE OR REPLACE FUNCTION saveIpsItem(pIpsItemId INTEGER,
+                                       pIpsHeadId INTEGER,
+                                       pItemId INTEGER,
+                                       pQtyBreak NUMERIC,
+                                       pPrice NUMERIC,
+                                       pQtyUomId INTEGER,
+                                       pPriceUomId INTEGER,
+                                       pPercent NUMERIC,
+                                       pFixedAmt NUMERIC,
+                                       pType TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _ipsitemid   INTEGER;
+  _new         BOOLEAN;
+BEGIN
+
+  -- Validation
+  IF (SELECT COUNT(item_id)=0 FROM item WHERE (item_id=pItemId)) THEN
+    RAISE EXCEPTION 'You must provide a valid Item';
+  ELSIF (COALESCE(pQtyBreak,0) < 0) THEN
+    RAISE EXCEPTION 'Quantity can not be a negative value';
+  ELSIF (COALESCE(pPrice,0) < 0) THEN
+    RAISE EXCEPTION 'Price must be a negative value';
+  ELSIF ((pQtyUomId IS NOT NULL) AND (SELECT COUNT(item_id)=0 FROM
+        (SELECT item_id
+         FROM item
+         WHERE ((item_id=pItemId)
+           AND (item_inv_uom_id=pQtyUomId))
+         UNION
+         SELECT item_id
+         FROM item,itemuomconv,itemuom,uomtype
+         WHERE ((item_id=pItemId)
+           AND (itemuomconv_item_id=item_id)
+           AND (itemuomconv_from_uom_id=pQtyUomId)
+           AND (itemuom_itemuomconv_id=itemuomconv_id)
+           AND (itemuom_uomtype_id=uomtype_id)
+           AND (uomtype_name='Selling'))
+         UNION
+         SELECT item_id
+         FROM item,itemuomconv,itemuom,uomtype
+         WHERE ((item_id=pItemId)
+           AND (itemuomconv_item_id=item_id)
+           AND (itemuomconv_to_uom_id=pQtyUomId)
+           AND (itemuom_itemuomconv_id=itemuomconv_id)
+           AND (itemuom_uomtype_id=uomtype_id)
+           AND (uomtype_name='Selling'))) AS data)) THEN
+    RAISE EXCEPTION 'Qty UOM Must be a valid Selling UOM for the Item';
+  ELSIF ((pPriceUomId IS NOT NULL) AND (SELECT COUNT(item_id)=0 FROM
+        (SELECT item_id
+         FROM item
+         WHERE ((item_id=pItemId)
+           AND (item_inv_uom_id=pPriceUomId))
+         UNION
+         SELECT item_id
+         FROM item,itemuomconv,itemuom,uomtype
+         WHERE ((item_id=pItemId)
+           AND (itemuomconv_item_id=item_id)
+           AND (itemuomconv_from_uom_id=pPriceUomId)
+           AND (itemuom_itemuomconv_id=itemuomconv_id)
+           AND (itemuom_uomtype_id=uomtype_id)
+           AND (uomtype_name='Selling'))
+         UNION
+         SELECT item_id
+         FROM item,itemuomconv,itemuom,uomtype
+         WHERE ((item_id=pItemId)
+           AND (itemuomconv_item_id=item_id)
+           AND (itemuomconv_to_uom_id=pPriceUomId)
+           AND (itemuom_itemuomconv_id=itemuomconv_id)
+           AND (itemuom_uomtype_id=uomtype_id)
+           AND (uomtype_name='Selling'))) AS data)) THEN
+    RAISE EXCEPTION 'Price UOM Must be a valid Selling UOM for the Item';
+  END IF;
+
+  _new := TRUE;
+
+  IF (pIpsItemId IS NOT NULL) THEN
+    SELECT ipsitem_id INTO _ipsitemid
+    FROM ipsiteminfo
+    WHERE (ipsitem_id=pIpsItemId);
+
+    IF (FOUND) THEN
+      _new := FALSE;
+    ELSE
+      RAISE EXCEPTION 'Pricing Schedule Item not found.';
+    END IF;
+  ELSE
+    SELECT ipsitem_id INTO _ipsitemid
+    FROM ipsiteminfo
+    WHERE ((ipsitem_ipshead_id = pIpsheadId)
+      AND (ipsitem_item_id     = pItemId)
+      AND (ipsitem_qtybreak    = pQtyBreak)
+      AND (ipsitem_qty_uom_id = COALESCE(pQtyUomId,(SELECT item_inv_uom_id FROM item WHERE item_id = pItemId))) 
+      AND (ipsitem_price_uom_id =
+           CASE
+             WHEN (pQtyUomId = (SELECT item_inv_uom_id FROM item WHERE item_id = pItemId)) THEN
+               COALESCE(pPriceUomId,pQtyUomId,(SELECT item_inv_uom_id FROM item WHERE item_id = pItemId))
+             ELSE
+               COALESCE(pQtyUomId,(SELECT item_inv_uom_id FROM item WHERE item_id = pItemId))
+           END));
+  END IF;
+  
+  IF (FOUND) THEN
+    _new := false;
+  END IF;
+  
+  IF (_new) THEN
+    INSERT INTO ipsiteminfo (
+      ipsitem_ipshead_id, 
+      ipsitem_item_id, 
+      ipsitem_qtybreak, 
+      ipsitem_price, 
+      ipsitem_qty_uom_id, 
+      ipsitem_price_uom_id,
+      ipsitem_discntprcnt,
+      ipsitem_fixedamtdiscount,
+      ipsitem_type) 
+    VALUES (
+      pIpsheadId,
+      pItemId,
+      pQtyBreak, 
+      pPrice,
+      COALESCE(pQtyUomId,(SELECT item_inv_uom_id FROM item WHERE item_id = pItemId)), 
+      CASE
+        WHEN (pQtyUomId = (SELECT item_inv_uom_id FROM item WHERE item_id = pItemId)) THEN
+          COALESCE(pPriceUomId,pQtyUomId,(SELECT item_inv_uom_id FROM item WHERE item_id = pItemId))
+        ELSE
+          COALESCE(pQtyUomId,(SELECT item_inv_uom_id FROM item WHERE item_id = pItemId))
+      END,
+      pPercent,
+      pFixedAmt,
+      pType)
+    RETURNING ipsitem_id INTO _ipsitemid;
+  ELSE 
+    UPDATE ipsiteminfo SET 
+      ipsitem_qtybreak = pQtyBreak, 
+      ipsitem_price = pPrice, 
+      ipsitem_qty_uom_id = COALESCE(pQtyUomId,(SELECT item_inv_uom_id FROM item WHERE item_id = pItemId)), 
+      ipsitem_price_uom_id =
+      CASE
+        WHEN (pQtyUomId = (SELECT item_inv_uom_id FROM item WHERE item_id = pItemId)) THEN
+          COALESCE(pPriceUomId,pQtyUomId,(SELECT item_inv_uom_id FROM item WHERE item_id = pItemId))
+        ELSE
+          COALESCE(pQtyUomId,(SELECT item_inv_uom_id FROM item WHERE item_id = pItemId))
+      END,
+      ipsitem_discntprcnt=pPercent,
+      ipsitem_fixedamtdiscount=pFixedAmt,
+      ipsitem_type=pType
+    WHERE (ipsitem_id=_ipsitemid);
+   END IF;
+
+   RETURN _ipsitemid;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/saveipsprodcat.sql b/foundation-database/public/functions/saveipsprodcat.sql
new file mode 100644 (file)
index 0000000..1019076
--- /dev/null
@@ -0,0 +1,82 @@
+CREATE OR REPLACE FUNCTION saveIpsProdcat(pIpsProdcatId INTEGER,
+                                          pIpsHeadId INTEGER,
+                                          pProdCatId INTEGER,
+                                          pQtyBreak NUMERIC,
+                                          pDiscount NUMERIC,
+                                          pFixedAmtDiscount NUMERIC,
+                                          pType TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _ipsitemid   INTEGER;
+  _new         BOOLEAN;
+  
+BEGIN
+
+  -- Validation
+  IF (SELECT COUNT(*)=0 FROM prodcat WHERE (prodcat_id=pProdcatId)) THEN
+    RAISE EXCEPTION 'You must provide a valid Product Category';
+  ELSIF (COALESCE(pQtyBreak,0) < 0) THEN
+    RAISE EXCEPTION 'Quantity can not be a negative value';
+  ELSIF (COALESCE(pDiscount,0) < 0) THEN
+    RAISE EXCEPTION 'Discount must be a negative value';
+  END IF;
+    
+  _new := TRUE;
+
+  IF (pIpsProdcatId IS NOT NULL) THEN
+    SELECT ipsitem_id INTO _ipsitemid
+    FROM ipsiteminfo
+    WHERE (ipsprodcat_id=pIpsprodcatId);
+
+    IF (FOUND) THEN
+      _new := FALSE;
+    ELSE
+      RAISE EXCEPTION 'Pricing Schedule Product Category not found';
+    END IF;
+  ELSE
+    SELECT ipsitem_id INTO _ipsitemid
+    FROM ipsiteminfo
+    WHERE ((ipsitem_ipshead_id=pIpsheadId)
+      AND (ipsitem_prodcat_id=pProdcatId)
+      AND (ipsitem_qtybreak=pQtyBreak));
+
+    IF (FOUND) THEN
+      _new := false;
+    ELSE
+      _ipsitemid := nextval('ipsitem_ipsitem_id_seq');
+    END IF;
+  END IF;
+  
+  IF (_new) THEN
+    INSERT INTO ipsiteminfo (
+      ipsitem_id,
+      ipsitem_ipshead_id, 
+      ipsitem_prodcat_id, 
+      ipsitem_qtybreak,
+      ipsitem_price, 
+      ipsitem_discntprcnt,
+      ipsitem_fixedamtdiscount,
+      ipsitem_type)  
+    VALUES (
+      _ipsitemid,
+      pIpsheadId,
+      pProdcatId,
+      pQtyBreak, 
+      0.0,
+      pDiscount * .01,
+      pFixedAmtDiscount,
+      pType);
+  ELSE 
+    UPDATE ipsiteminfo SET 
+      ipsitem_qtybreak = pQtyBreak, 
+      ipsitem_discntprcnt = pDiscount * .01,
+      ipsitem_fixedamtdiscount = pFixedAmtDiscount,
+      ipsitem_type = pType 
+    WHERE (ipsitem_id=_ipsitemid);
+  END IF;
+
+  RETURN _ipsitemid;
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/saveitemimage.sql b/foundation-database/public/functions/saveitemimage.sql
new file mode 100644 (file)
index 0000000..fb882eb
--- /dev/null
@@ -0,0 +1,28 @@
+CREATE OR REPLACE FUNCTION saveItemImage(INTEGER, CHAR, INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemid ALIAS FOR $1;
+  pPurpose ALIAS FOR $2;
+  pImageid ALIAS FOR $3;
+  _itemimageId INTEGER;
+
+BEGIN
+-- See if a record with this purpose already exists
+  SELECT imageass_id INTO _itemimageId
+  FROM imageass
+  WHERE ( (imageass_source=''I'')
+    AND   (imageass_source_id=pItemid)
+    AND   (imageass_purpose=pPurpose) );
+
+  IF (FOUND) THEN
+    UPDATE imageass SET imageass_image_id=pImageId
+    WHERE (imageass_id=_itemimageId);
+  ELSE
+    _itemimageId := NEXTVAL(''imageass_imageass_id_seq'');
+    INSERT INTO imageass VALUES (_itemimageId,pItemid,''I'',pImageid,pPurpose);
+  END IF;
+  
+  RETURN _itemimageId;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/saveitemuomconv.sql b/foundation-database/public/functions/saveitemuomconv.sql
new file mode 100644 (file)
index 0000000..85fccdf
--- /dev/null
@@ -0,0 +1,110 @@
+CREATE OR REPLACE FUNCTION saveItemUomConv(integer, integer, numeric, integer, numeric, boolean, integer[])
+  RETURNS integer AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemId ALIAS FOR $1;
+  pFromUomId ALIAS FOR $2;
+  pFromValue ALIAS FOR $3;
+  pToUomId ALIAS FOR $4;
+  pToValue ALIAS FOR $5;
+  pFractional ALIAS FOR $6;
+  pUomTypes ALIAS FOR $7;
+  _p RECORD;
+  _fromUomId INTEGER;
+  _fromValue NUMERIC;
+  _toUomId INTEGER;
+  _toValue NUMERIC;
+  _fractional BOOLEAN;
+  _seq INTEGER;
+  _i INTEGER;
+  _uomtype TEXT;
+
+BEGIN
+-- Make sure we have some itemtypes
+  IF (pUomTypes IS NULL) OR (ARRAY_UPPER(pUomTypes,1) = 0) THEN
+       RAISE EXCEPTION ''You must include at least one item type.'';
+  END IF;
+
+-- If this is a global UOM, over-ride with global data.
+  SELECT * INTO _p
+  FROM uomconv
+  WHERE ((((uomconv_from_uom_id=pFromUomId)
+  AND (uomconv_to_uom_id=pToUomId))
+  OR ((uomconv_from_uom_id=pToUomId)
+  AND (uomconv_to_uom_id=pFromUomId))));
+
+  IF (FOUND) THEN
+    _fromUomId := _p.uomconv_from_uom_id;
+    _toUomId := _p.uomconv_to_uom_id;
+    _fromValue := _p.uomconv_from_value;
+    _toValue := _p.uomconv_to_value;
+    _fractional := _p.uomconv_fractional;
+    RAISE NOTICE ''Defaulted to global Unit of Measure conversion ratios.'';
+  ELSE
+    _fromUomId := pFromUomId;
+    _fromValue := pFromValue;
+    _toUomId := pToUomId;
+    _toValue := pToValue;
+    _fractional := pFractional;
+  END IF;
+
+-- See if an item conversion exists going the other way
+  SELECT f.uom_name AS f_uom, t.uom_name as t_uom INTO _p
+  FROM itemuomconv,uom f, uom t
+  WHERE ((itemuomconv_item_id=pItemId)
+  AND (itemuomconv_from_uom_id=_toUomId)
+  AND (itemuomconv_to_uom_id=_fromUomId)
+  AND (f.uom_id=itemuomconv_from_uom_id)
+  AND (t.uom_id=itemuomconv_to_uom_id));
+  IF (FOUND) THEN
+    RAISE EXCEPTION ''Unit of measure conversion already exists going from % to %.'',_p.f_uom,_p.t_uom;
+  END IF;
+
+-- See if an item conversion record exists
+  SELECT * INTO _p
+  FROM itemuomconv
+  WHERE ((itemuomconv_item_id=pItemId)
+  AND (itemuomconv_from_uom_id=_fromUomId)
+  AND (itemuomconv_to_uom_id=_toUomId));
+
+-- Update if found
+  IF (FOUND) THEN
+    UPDATE itemuomconv SET
+      itemuomconv_from_value=_fromValue,
+      itemuomconv_to_value=_toValue,
+      itemuomconv_fractional=_fractional
+    WHERE (itemuomconv_id=_p.itemuomconv_id);
+    _seq := _p.itemuomconv_id;
+    
+    --Delete old type list
+    DELETE FROM itemuom WHERE itemuom_itemuomconv_id=_p.itemuomconv_id;
+  ELSE
+  
+-- Otherwise create a new one
+    SELECT NEXTVAL(''itemuomconv_itemuomconv_id_seq'') INTO _seq;
+    INSERT INTO itemuomconv VALUES
+      (_seq, pItemId,_fromUomId,_fromValue,_toUomId,_toValue,_fractional);
+  END IF;
+  
+-- Build new type list
+  FOR _i IN 1..ARRAY_UPPER(pUomTypes,1)
+  LOOP
+    SELECT uomtype_name INTO _uomtype
+    FROM itemuomconv, itemuom, uomtype
+    WHERE ((itemuom_uomtype_id=uomtype_id)
+    AND (itemuomconv_id=itemuom_itemuomconv_id)
+    AND (itemuomconv_item_id=pItemId)
+    AND (uomtype_name != ''Selling'')
+    AND (itemuom_uomtype_id=pUomTypes[_i]));
+    IF (FOUND) THEN
+      RAISE EXCEPTION ''Unit of Measure Type % is already used on this item'',_uomtype;
+    ELSE
+      INSERT INTO itemuom (itemuom_itemuomconv_id,itemuom_uomtype_id)
+      VALUES (_seq,pUomTypes[_i]);
+    END IF;
+  END LOOP;
+  
+  RETURN _seq;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/savemetasql.sql b/foundation-database/public/functions/savemetasql.sql
new file mode 100644 (file)
index 0000000..d41eee7
--- /dev/null
@@ -0,0 +1,89 @@
+CREATE OR REPLACE FUNCTION saveMetasql(TEXT, TEXT, TEXT, TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN saveMetasql($1, $2, $3, $4, true, NULL, 0);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION saveMetasql(TEXT,TEXT,TEXT,TEXT,BOOL) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN saveMetasql($1, $2, $3, $4, $5, NULL, 0);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION saveMetasql(TEXT,TEXT,TEXT,TEXT,BOOL,TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN saveMetasql($1, $2, $3, $4, $5, $6, 0);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION saveMetasql(TEXT,TEXT,TEXT,TEXT,BOOL,TEXT,INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pGroup       ALIAS FOR $1;
+  pName        ALIAS FOR $2;
+  pNotes       ALIAS FOR $3;
+  pQuery       ALIAS FOR $4;
+  pSystem       ALIAS FOR $5;
+  pSchema       ALIAS FOR $6;
+  pGrade        ALIAS FOR $7;
+  _metasqlid   INTEGER;
+  _debug        BOOL    := false;
+  _grade        INTEGER;
+  _insertstr    TEXT;
+  _table        TEXT;
+  
+BEGIN
+
+  --See if Query already exists
+  SELECT metasql_id INTO _metasqlid
+  FROM metasql
+  WHERE ((metasql_group=pGroup)
+     AND (metasql_name=pName)
+     AND (metasql_grade=pGrade));
+
+  IF (FOUND) THEN
+    IF (_debug) THEN RAISE NOTICE 'update metasql'; END IF;
+    UPDATE metasql SET
+      metasql_group=pGroup,
+      metasql_name=pName,
+      metasql_notes=pNotes,
+      metasql_query=pQuery
+    WHERE (metasql_id=_metasqlid);
+  ELSE
+    IF (COALESCE(pSchema, 'public') = 'public' OR
+        TRIM(pSchema) = '') THEN
+      _table := 'metasql';
+    ELSE
+      _table := pSchema || '.pkgmetasql';
+    END IF;
+
+    IF (pGrade IS NULL) THEN
+      SELECT MAX(metasql_grade) + 1 INTO _grade
+      FROM metasql
+      WHERE ((metasql_group=pGroup)
+         AND (metasql_name=pName));
+    ELSE
+      _grade := pGrade;
+    END IF;
+
+    _insertstr := 'INSERT INTO ' || _table ||
+                  ' (metasql_group, metasql_name, metasql_notes, ' ||
+                  '  metasql_query, metasql_grade) VALUES (' ||
+                  COALESCE(quote_literal(pGroup),'NULL') || ',' || COALESCE(quote_literal(pName), 'NULL') || ',' ||
+                  COALESCE(quote_literal(pNotes), 'NULL') || ',' || COALESCE(quote_literal(pQuery), 'NULL') ||',' ||
+                  COALESCE(quote_literal(_grade), 'NULL') || ') RETURNING metasql_id;' ;
+
+    IF (_debug) THEN RAISE NOTICE '%', _insertstr; END IF;
+    EXECUTE _insertstr INTO _metasqlid;
+  END IF;
+  RETURN _metasqlid;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/scraps.sql b/foundation-database/public/functions/scraps.sql
new file mode 100644 (file)
index 0000000..c7a013a
--- /dev/null
@@ -0,0 +1,15 @@
+CREATE OR REPLACE FUNCTION scraps(TEXT) RETURNS BOOLEAN AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pTransType ALIAS FOR $1;
+
+BEGIN
+  IF (pTransType IN (''SI'', ''SM'', ''EX'')) THEN
+    RETURN TRUE;
+  ELSE
+    RETURN FALSE;
+  END IF;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/scrapwomaterial.sql b/foundation-database/public/functions/scrapwomaterial.sql
new file mode 100644 (file)
index 0000000..18c7372
--- /dev/null
@@ -0,0 +1,100 @@
+
+CREATE OR REPLACE FUNCTION scrapWoMaterial(INTEGER, NUMERIC) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN scrapWoMaterial($1, $2, CURRENT_TIMESTAMP);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION scrapWoMaterial(INTEGER, NUMERIC, TIMESTAMP WITH TIME ZONE) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWomatlid    ALIAS FOR $1;
+  pQty         ALIAS FOR $2;
+  pGlDistTS     ALIAS FOR $3;
+  _costmethod          CHAR(1);
+  _scrapValue          NUMERIC;
+  _r                           RECORD;
+
+BEGIN
+  -- Validate
+  IF (pQty <= 0) THEN
+    RAISE EXCEPTION 'Scrap quantity must be a positive number';
+  ELSIF ( ( SELECT (womatl_qtyiss < pQty)
+            FROM womatl
+            WHERE (womatl_id=pWomatlid) ) ) THEN
+    RAISE EXCEPTION 'You may not scrap more material than has been issued';
+  END IF;
+
+  -- Get the wip G/L account
+  SELECT costcat_wip_accnt_id
+    INTO _r
+    FROM womatl, wo, itemsite, costcat
+   WHERE((womatl_wo_id=wo_id)
+     AND (wo_itemsite_id=itemsite_id)
+     AND (itemsite_costcat_id=costcat_id)
+     AND (womatl_id=pWomatlid));
+
+  -- Calculate scrap value
+  SELECT itemsite_costmethod INTO _costmethod
+  FROM womatl
+    JOIN itemsite ON (womatl_itemsite_id=itemsite_id)
+  WHERE (womatl_id=pWomatlid);
+
+  IF (_costmethod = 'S') THEN
+    SELECT ROUND((stdCost(itemsite_item_id) * itemuomtouom(itemsite_item_id, womatl_uom_id, NULL, pQty)),2)
+    INTO _scrapValue
+    FROM womatl
+      JOIN itemsite ON (womatl_itemsite_id=itemsite_id)
+    WHERE (womatl_id=pWomatlid);
+     
+  ELSIF (_costmethod = 'A') THEN
+    SELECT ROUND((SUM(invhist_invqty * invhist_unitcost)-womatl_scrapvalue)/
+            (CASE WHEN (SUM(invhist_invqty)-itemuomtouom(itemsite_item_id, womatl_uom_id, NULL, womatl_qtywipscrap) = 0) THEN
+              1
+            ELSE
+              SUM(invhist_invqty)-itemuomtouom(itemsite_item_id, womatl_uom_id, NULL, womatl_qtywipscrap)
+            END),2) * itemuomtouom(itemsite_item_id, womatl_uom_id, NULL, pQty)
+      INTO _scrapValue
+    FROM womatl
+        JOIN womatlpost ON (womatl_id=womatlpost_womatl_id)
+        JOIN invhist ON (womatlpost_invhist_id=invhist_id)
+        JOIN itemsite ON (womatl_itemsite_id=itemsite_id)
+    WHERE (womatl_id=pWomatlid)
+    GROUP BY itemsite_item_id,womatl_uom_id,womatl_qtywipscrap,womatl_scrapvalue;
+  ELSE
+    RAISE EXCEPTION 'Cost method not supported to scrap this item';
+  END IF;
+
+  --  Distribute to G/L
+  PERFORM insertGLTransaction( 'W/O', 'WO', formatWoNumber(womatl_wo_id),
+                ('Scrap ' || item_number || ' from Work Order'),
+                getPrjAccntId(wo_prj_id, _r.costcat_wip_accnt_id), getPrjAccntId(wo_prj_id, costcat_mfgscrap_accnt_id), -1,
+                _scrapValue, date(pGlDistTS) )
+  FROM wo, womatl, itemsite, item, costcat
+  WHERE ( (wo_id=womatl_wo_id)
+   AND (womatl_itemsite_id=itemsite_id)
+   AND (itemsite_item_id=item_id)
+   AND (itemsite_costcat_id=costcat_id)
+   AND (womatl_id=pWomatlid) );
+
+  UPDATE womatl
+  SET womatl_qtywipscrap=(womatl_qtywipscrap + pQty),
+    womatl_scrapvalue = womatl_scrapvalue + _scrapValue,
+    womatl_qtyiss=(womatl_qtyiss - pQty)
+  WHERE (womatl_id=pWomatlid);
+
+  UPDATE wo
+  SET wo_wipvalue = wo_wipvalue-_scrapValue,
+    wo_postedvalue = wo_postedvalue-_scrapValue
+  FROM womatl
+  WHERE ((womatl_id=pWomatlid)
+   AND (wo_id=womatl_wo_id));
+
+  RETURN pWomatlid;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/selectbalanceforbilling.sql b/foundation-database/public/functions/selectbalanceforbilling.sql
new file mode 100644 (file)
index 0000000..eff9473
--- /dev/null
@@ -0,0 +1,69 @@
+CREATE OR REPLACE FUNCTION selectBalanceForBilling(INTEGER) RETURNS BOOLEAN AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSoheadid ALIAS FOR $1;
+  _returnval   BOOLEAN := TRUE;
+  _doSelect BOOLEAN;
+  _result INTEGER;
+  _soitem RECORD;
+
+BEGIN
+
+  FOR _soitem IN
+    -- Get the shipments for this SO.  Kits are not shipped
+    SELECT cust_partialship, coitem_id,
+           coitem_linenumber, 'NOTK' AS item_type,
+           SUM(shipitem_qty) AS qty,
+           ( (SUM(shipitem_qty) >= (coitem_qtyord - coitem_qtyshipped + coitem_qtyreturned + SUM(shipitem_qty))) OR
+             (NOT cust_partialship) ) AS toclose
+    FROM cohead JOIN custinfo ON (cust_id=cohead_cust_id)
+                JOIN coitem ON (coitem_cohead_id=cohead_id)
+                JOIN shipitem ON ( (shipitem_orderitem_id=coitem_id) AND (NOT shipitem_invoiced) )
+                JOIN shiphead ON ( (shiphead_id=shipitem_shiphead_id) AND (shiphead_order_type='SO') AND (shiphead_shipped) )
+    WHERE (cohead_id=pSoheadid)
+    GROUP BY cust_partialship, coitem_id, item_type,
+             coitem_linenumber, coitem_qtyord,
+             coitem_qtyshipped, coitem_qtyreturned
+    UNION
+    -- Get the Kits for this SO
+    SELECT cust_partialship, coitem_id,
+           coitem_linenumber, 'K' AS item_type,
+           coitem_qtyord AS qty,
+           TRUE AS toclose
+    FROM cohead JOIN custinfo ON (cust_id=cohead_cust_id)
+                JOIN coitem ON (coitem_cohead_id=cohead_id AND coitem_status='O')
+                JOIN itemsite ON (itemsite_id=coitem_itemsite_id)
+                JOIN item ON ( (item_id=itemsite_item_id) AND (item_type='K') )
+    WHERE (cohead_id=pSoheadid)
+  LOOP
+
+    _doSelect := true;
+    IF(_soitem.item_type = 'K') THEN
+      -- see if all the sub items are shipped
+      SELECT coitem_id
+        INTO _result
+        FROM coitem
+       WHERE((coitem_cohead_id=pSoheadid)
+         AND (coitem_linenumber=_soitem.coitem_linenumber)
+         AND (coitem_subnumber > 0)
+         AND ((coitem_qtyord - coitem_qtyshipped + coitem_qtyreturned) > 0))
+       LIMIT 1;
+      IF( FOUND ) THEN
+        _doSelect := false;
+      END IF;
+    END IF;
+
+    IF (_doSelect) THEN
+      -- do as much as we can but still report errors if they occur
+      IF (selectForBilling(_soitem.coitem_id, _soitem.qty, _soitem.toclose) < 0) THEN
+        _returnval := FALSE;
+      END IF;
+    END IF;
+
+  END LOOP;
+
+  RETURN _returnval;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/selectdiscountitemsforpayment.sql b/foundation-database/public/functions/selectdiscountitemsforpayment.sql
new file mode 100644 (file)
index 0000000..5922273
--- /dev/null
@@ -0,0 +1,32 @@
+CREATE OR REPLACE FUNCTION selectDiscountItemsForPayment(INTEGER, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pVendid ALIAS FOR $1;
+  pBankaccntid ALIAS FOR $2;
+  _currid INTEGER;
+  _r RECORD;
+
+BEGIN
+
+  SELECT bankaccnt_curr_id INTO _currid
+  FROM bankaccnt
+  WHERE (bankaccnt_id=pBankaccntid);
+
+  FOR _r IN SELECT apopen_id
+              FROM apopen, terms
+             WHERE((CURRENT_DATE <= determineDiscountDate(apopen_terms_id, apopen_docdate))
+               AND (terms_discprcnt > 0.0)
+               AND (apopen_terms_id=terms_id)
+               AND (apopen_open)
+               AND (apopen_status = 'O')
+               AND (apopen_doctype IN ('V', 'D'))
+               AND (apopen_vend_id=pVendid)
+               AND (apopen_curr_id=_currid) ) LOOP
+    PERFORM selectPayment(_r.apopen_id, pBankaccntid);
+  END LOOP;
+
+  RETURN 1;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/selectdueitemsforpayment.sql b/foundation-database/public/functions/selectdueitemsforpayment.sql
new file mode 100644 (file)
index 0000000..c44433d
--- /dev/null
@@ -0,0 +1,27 @@
+CREATE OR REPLACE FUNCTION selectDueItemsForPayment(INTEGER, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pVendid ALIAS FOR $1;
+  pBankaccntid ALIAS FOR $2;
+  _currid INTEGER;
+
+BEGIN
+
+  SELECT bankaccnt_curr_id INTO _currid
+  FROM bankaccnt
+  WHERE (bankaccnt_id=pBankaccntid);
+
+  PERFORM selectPayment(apopen_id, pBankaccntid)
+     FROM apopen
+    WHERE((apopen_open)
+      AND (apopen_vend_id=pVendid)
+      AND (apopen_duedate <= CURRENT_DATE)
+      AND (apopen_status = 'O')
+      AND (apopen_doctype IN ('V', 'D'))
+      AND (apopen_curr_id=_currid) );
+
+  RETURN 1;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/selectforbilling.sql b/foundation-database/public/functions/selectforbilling.sql
new file mode 100644 (file)
index 0000000..7520223
--- /dev/null
@@ -0,0 +1,109 @@
+CREATE OR REPLACE FUNCTION selectforbilling(integer, numeric, boolean)
+  RETURNS integer AS
+$BODY$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSoitemid    ALIAS FOR $1;
+  pQty ALIAS FOR $2;
+  pClose       ALIAS FOR $3;
+  _itemid      INTEGER := NULL;
+  _taxzoneid   INTEGER := NULL;
+  _taxid       INTEGER := NULL;
+  _taxtypeid   INTEGER := NULL;
+
+BEGIN
+  SELECT cobmisc_taxzone_id,  item_id, coitem_taxtype_id
+  INTO _taxzoneid,  _itemid, _taxtypeid
+  FROM cobmisc, coitem, itemsite, item
+  WHERE ((cobmisc_cohead_id = coitem_cohead_id)
+  AND   (NOT cobmisc_posted)
+  AND   (coitem_itemsite_id = itemsite_id)
+  AND   (itemsite_item_id = item_id)
+  AND   (coitem_id = pSoitemid) )
+  LIMIT 1;
+
+   RETURN selectforbilling(pSoitemid, pQty, pClose, _taxtypeid);
+END;
+$BODY$ LANGUAGE 'plpgsql' VOLATILE;
+
+---------------------------------------------------------------------
+
+CREATE OR REPLACE FUNCTION selectforbilling(integer, numeric, boolean, integer)
+  RETURNS integer AS
+$BODY$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSoitemid    ALIAS FOR $1;
+  pQty         ALIAS FOR $2;
+  pClose       ALIAS FOR $3;
+  ptaxtypeid   ALIAS FOR $4;
+  _cobillid INTEGER;
+  _r RECORD;
+
+BEGIN
+
+-- Get some information
+  SELECT cobmisc_id, cobmisc_taxzone_id, coitem_id, coitem_price,
+    coitem_price_invuomratio AS invpricerat, coitem_qty_invuomratio, item_id
+  INTO _r
+  FROM cobmisc, coitem, itemsite, item, site()
+  WHERE ((cobmisc_cohead_id = coitem_cohead_id)
+  AND   (NOT cobmisc_posted)
+  AND   (coitem_itemsite_id = itemsite_id)
+  AND   (itemsite_item_id = item_id)
+  AND   (coitem_id = pSoitemid)
+  AND   (itemsite_warehous_id = warehous_id) )
+  LIMIT 1;
+
+-- check to make sure the qty to bill for is not less than
+-- the total un-invoiced shipped amount
+  IF ((SELECT (pQty < SUM(shipitem_qty))
+       FROM shipitem, shiphead, coitem
+       WHERE ( (shipitem_shiphead_id=shiphead_id)
+       AND (shiphead_order_type='SO')
+       AND (shiphead_order_id=coitem_cohead_id)
+       AND (shipitem_orderitem_id=coitem_id)
+       AND (shiphead_shipped)
+       AND (NOT shipitem_invoiced)
+       AND (coitem_id=pSoitemid) ) ) ) THEN
+    RETURN -1;
+  END IF;
+
+  SELECT cobill_id INTO _cobillid
+  FROM cobill, cobmisc, coitem
+  WHERE ((cobill_cobmisc_id = cobmisc_id)
+  AND (cobmisc_cohead_id = coitem_cohead_id)
+  AND (cobill_coitem_id = coitem_id)
+  AND (NOT cobmisc_posted)
+  AND (coitem_id = pSoitemid));
+
+  IF (FOUND) THEN
+    UPDATE cobill
+    SET cobill_selectdate = CURRENT_DATE,
+        cobill_select_username = getEffectiveXtUser(),
+        cobill_qty = pQty,
+        cobill_toclose = pClose,
+       cobill_taxtype_id = ptaxtypeid
+    WHERE (cobill_id=_cobillid);
+
+  ELSE
+    SELECT NEXTVAL('cobill_cobill_id_seq') INTO _cobillid;
+    INSERT INTO cobill
+    (cobill_id, cobill_coitem_id, cobill_cobmisc_id,
+     cobill_selectdate, cobill_select_username,
+     cobill_qty, cobill_toclose,
+     cobill_taxtype_id)
+    VALUES
+    (_cobillid, _r.coitem_id, _r.cobmisc_id,
+      CURRENT_DATE, getEffectiveXtUser(),
+      pQty, pClose,
+      ptaxtypeid);
+  END IF;
+
+  RETURN _cobillid;
+
+END;
+$BODY$
+  LANGUAGE 'plpgsql' VOLATILE;
diff --git a/foundation-database/public/functions/selectpayment.sql b/foundation-database/public/functions/selectpayment.sql
new file mode 100644 (file)
index 0000000..2f902b9
--- /dev/null
@@ -0,0 +1,82 @@
+
+CREATE OR REPLACE FUNCTION selectPayment(INTEGER, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pApopenid ALIAS FOR $1;
+  pBankaccntid ALIAS FOR $2;
+  _p RECORD;
+  _apselectid INTEGER;
+  _amount NUMERIC;
+  _discount NUMERIC;
+BEGIN
+
+  SELECT apopen_amount, apopen_paid,
+         apopen_doctype, apopen_docdate,
+         apopen_curr_id,
+         apopen_amount - apopen_paid
+           - COALESCE((SELECT SUM(currToCurr(checkitem_curr_id,
+                                             apopen_curr_id,
+                                             checkitem_amount + checkitem_discount,
+                                             checkhead_checkdate))
+                         FROM checkitem, checkhead
+                        WHERE((checkitem_checkhead_id=checkhead_id)
+                          AND (checkitem_apopen_id=apopen_id)
+                          AND (NOT checkhead_deleted)
+                          AND (NOT checkhead_replaced)
+                          AND (NOT checkhead_posted)) ),0) AS balance,
+         noNeg(COALESCE(apopen_discountable_amount, 0) *
+               CASE WHEN (CURRENT_DATE <= determineDiscountDate(apopen_terms_id, apopen_docdate)) THEN terms_discprcnt
+                    ELSE 0.0 END - discount_applied) AS discount_available
+    INTO _p
+    FROM apopen LEFT OUTER JOIN terms ON (apopen_terms_id=terms_id),
+         (SELECT COALESCE(SUM(apapply_amount),0) AS discount_applied
+            FROM apapply, apopen
+           WHERE((apapply_target_apopen_id=pApopenid)
+             AND (apapply_source_apopen_id=apopen_id)
+             AND (apopen_discount)) ) AS data
+   WHERE(apopen_id=pApopenid);
+  IF(NOT FOUND OR (NOT _p.apopen_doctype IN ('V','D'))) THEN
+    RETURN -1;
+  END IF;
+
+  _discount := round(_p.discount_available, 2);
+  _amount := noNeg(round(_p.balance, 2) - _discount);
+
+  IF (round(_p.balance,2) < (_discount + _amount)) THEN
+    RETURN -2;
+  END IF;
+
+  IF (_amount > 0) THEN
+    SELECT apselect_id INTO _apselectid
+    FROM apselect
+    WHERE (apselect_apopen_id=pApopenid);
+
+    IF (FOUND) THEN
+      UPDATE apselect
+         SET apselect_amount=_amount,
+             apselect_discount=_discount,
+             apselect_curr_id = _p.apopen_curr_id
+       WHERE(apselect_id=_apselectid);
+    ELSE
+      SELECT NEXTVAL('apselect_apselect_id_seq') INTO _apselectid;
+
+      INSERT INTO apselect
+      ( apselect_id, apselect_apopen_id,
+        apselect_amount, apselect_discount,
+        apselect_bankaccnt_id,
+        apselect_curr_id, apselect_date )
+      VALUES
+      ( _apselectid, pApopenid,
+        _amount, _discount,
+        pBankaccntid,
+        _p.apopen_curr_id, _p.apopen_docdate );
+    END IF;
+  ELSE
+    _apselectid := 0;
+  END IF;
+  
+  RETURN _apselectid;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/selectuninvoicedshipment.sql b/foundation-database/public/functions/selectuninvoicedshipment.sql
new file mode 100644 (file)
index 0000000..3cead58
--- /dev/null
@@ -0,0 +1,110 @@
+CREATE OR REPLACE FUNCTION selectuninvoicedshipment(INTEGER) RETURNS INTEGER AS
+$BODY$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple.
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pShipheadid ALIAS FOR $1;
+  _cobmiscid INTEGER;
+  _coheadid  INTEGER;
+  _r RECORD;
+  _cobillid INTEGER;
+
+BEGIN
+
+  -- make a cobmisc head if it doesn't already exist for this cohead
+  SELECT shiphead_order_id, createBillingHeader(shiphead_order_id)
+    INTO _coheadid, _cobmiscid
+    FROM shiphead
+    JOIN shipitem ON (shipitem_shiphead_id=shiphead_id)
+   WHERE (shiphead_shipped
+      AND NOT shipitem_invoiced
+      AND (shiphead_id=pShipheadid));
+
+  --  Grab all of the uninvoiced shipitem records
+  FOR _r IN SELECT cohead_id, coitem_id, SUM(shipitem_qty) AS qty,
+                   coitem_price, coitem_price_invuomratio AS invpricerat, coitem_qty_invuomratio, item_id,
+                   ( ((coitem_qtyord - coitem_qtyshipped + coitem_qtyreturned) <= 0)
+                    OR (NOT cust_partialship) ) AS toclose, coitem_taxtype_id
+            FROM shiphead, shipitem, coitem, cohead, custinfo, itemsite, item
+            WHERE ( (shipitem_shiphead_id=shiphead_id)
+             AND (shipitem_orderitem_id=coitem_id)
+             AND (coitem_cohead_id=cohead_id)
+             AND (shiphead_shipped)
+             AND (NOT shipitem_invoiced)
+             AND (coitem_itemsite_id=itemsite_id)
+             AND (itemsite_item_id=item_id)
+             AND (cohead_cust_id=cust_id)
+             AND (item_type != 'K')
+             AND (cohead_id=_coheadid)
+             AND (shiphead_id=pShipheadid) )
+            GROUP BY cohead_id, coitem_id, cust_partialship, coitem_taxtype_id,
+                     coitem_qtyord, coitem_qtyshipped, coitem_qtyreturned,
+                     coitem_price, invpricerat, coitem_qty_invuomratio, item_id
+            UNION
+            SELECT cohead_id, coitem_id, coitem_qtyord AS qty,
+                   coitem_price, coitem_price_invuomratio AS invpricerat, coitem_qty_invuomratio, item_id,
+                   true AS toclose, coitem_taxtype_id
+              FROM shiphead, cohead, custinfo, itemsite, item, coitem AS kit
+             WHERE((shiphead_order_id=cohead_id)
+               AND (coitem_cohead_id=cohead_id)
+               AND (coitem_status='O')
+               AND (shiphead_shipped)
+               AND (coitem_itemsite_id=itemsite_id)
+               AND (itemsite_item_id=item_id)
+               AND (cohead_cust_id=cust_id)
+               AND (item_type = 'K')
+               AND (cohead_id=_coheadid)
+               AND (shiphead_id=pShipheadid)
+               AND (coitem_linenumber NOT IN
+                      (SELECT sub.coitem_linenumber
+                       FROM coitem AS sub
+                       WHERE sub.coitem_cohead_id=cohead_id     -- cohead for kit
+                        AND sub.coitem_linenumber=kit.coitem_linenumber
+                        AND sub.coitem_subnumber > 0
+                        AND ((sub.coitem_qtyord - sub.coitem_qtyshipped + sub.coitem_qtyreturned) > 0)
+                        LIMIT 1)
+               ))
+             GROUP BY cohead_id, coitem_id, cust_partialship, coitem_taxtype_id,
+                      coitem_qtyord, coitem_qtyshipped, coitem_qtyreturned,
+                      coitem_price, invpricerat, coitem_qty_invuomratio, item_id, coitem_linenumber
+  LOOP
+
+    SELECT cobill_id INTO _cobillid
+      FROM cobill, cobmisc, coitem
+     WHERE ((cobill_cobmisc_id=cobmisc_id)
+       AND  (cobmisc_cohead_id=coitem_cohead_id)
+       AND  (cobill_coitem_id=coitem_id)
+       AND  (NOT cobmisc_posted)
+       AND  (cobill_cobmisc_id=_cobmiscid)
+       AND  (coitem_id=_r.coitem_id))
+     LIMIT 1;
+
+    IF (FOUND) THEN
+      UPDATE cobill
+         SET cobill_selectdate = CURRENT_DATE,
+             cobill_select_username = getEffectiveXtUser(),
+             cobill_qty = cobill_qty + _r.qty,
+             cobill_toclose = _r.toclose,
+             cobill_taxtype_id = _r.coitem_taxtype_id
+      WHERE (cobill_id=_cobillid);
+    ELSE
+--  Now insert the cobill line
+      INSERT INTO cobill
+      ( cobill_cobmisc_id, cobill_coitem_id,
+        cobill_selectdate, cobill_select_username,
+        cobill_qty, cobill_toclose,
+        cobill_taxtype_id )
+      VALUES
+      ( _cobmiscid, _r.coitem_id,
+        CURRENT_DATE, getEffectiveXtUser(),
+        _r.qty, _r.toclose,
+         _r.coitem_taxtype_id );
+     END IF;
+
+  END LOOP;
+
+  RETURN _cobmiscid;
+
+END;
+$BODY$
+  LANGUAGE 'plpgsql' VOLATILE;
diff --git a/foundation-database/public/functions/selectuninvoicedshipments.sql b/foundation-database/public/functions/selectuninvoicedshipments.sql
new file mode 100644 (file)
index 0000000..4fe979b
--- /dev/null
@@ -0,0 +1,121 @@
+CREATE OR REPLACE FUNCTION selectUninvoicedShipments(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWarehousid ALIAS FOR $1;
+  _r RECORD;
+  _recordCounter INTEGER := 0;
+
+BEGIN
+
+--  Grab all of the uninvoiced shipitem records
+  FOR _r IN SELECT DISTINCT shiphead_id
+            FROM shiphead, shipitem, coitem, itemsite
+            WHERE ( (shiphead_order_type='SO')
+             AND (shipitem_shiphead_id=shiphead_id)
+             AND (shipitem_orderitem_id=coitem_id)
+             AND (coitem_itemsite_id=itemsite_id)
+             AND (coitem_status <> 'C')
+             AND ( (pWarehousid = -1) OR (itemsite_warehous_id=pWarehousid) )
+             AND (shiphead_shipped)
+             AND (NOT shipitem_invoiced)
+             AND (coitem_id NOT IN ( SELECT cobill_coitem_id
+                                     FROM cobmisc, cobill
+                                     WHERE ((cobill_cobmisc_id=cobmisc_id)
+                                      AND (NOT cobmisc_posted) ) ) ) ) LOOP
+
+      PERFORM selectUninvoicedShipment(_r.shiphead_id);
+
+    _recordCounter := _recordCounter + 1;
+
+  END LOOP;
+
+  RETURN _recordCounter;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION selectUninvoicedShipments(INTEGER, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWarehousid ALIAS FOR $1;
+  pCusttypeid ALIAS FOR $2;
+  _r RECORD;
+  _recordCounter INTEGER := 0;
+
+BEGIN
+
+--  Grab all of the uninvoiced shipitem records
+  FOR _r IN SELECT DISTINCT shiphead_id
+            FROM shiphead, shipitem, coitem, itemsite, cohead, custinfo
+            WHERE ( (shiphead_order_type='SO')
+             AND (shipitem_shiphead_id=shiphead_id)
+             AND (shipitem_orderitem_id=coitem_id)
+             AND (coitem_itemsite_id=itemsite_id)
+             AND (coitem_status <> 'C')
+             AND (coitem_cohead_id=cohead_id)
+             AND (cohead_cust_id=cust_id)
+             AND (cust_custtype_id=pCusttypeid)
+             AND ( (pWarehousid = -1) OR (itemsite_warehous_id=pWarehousid) )
+             AND (shiphead_shipped)
+             AND (NOT shipitem_invoiced)
+             AND (coitem_id NOT IN ( SELECT cobill_coitem_id
+                                     FROM cobmisc, cobill
+                                     WHERE ((cobill_cobmisc_id=cobmisc_id)
+                                      AND (NOT cobmisc_posted) ) ) ) ) LOOP
+
+      PERFORM selectUninvoicedShipment(_r.shiphead_id);
+
+    _recordCounter := _recordCounter + 1;
+
+  END LOOP;
+
+  RETURN _recordCounter;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION selectUninvoicedShipments(INTEGER, TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWarehousid ALIAS FOR $1;
+  pCusttype ALIAS FOR $2;
+  _r RECORD;
+  _recordCounter INTEGER := 0;
+
+BEGIN
+
+--  Grab all of the uninvoiced shipitem records
+  FOR _r IN SELECT DISTINCT shiphead_id
+            FROM shiphead, shipitem, coitem, itemsite, cohead, custinfo, custtype
+            WHERE ( (shiphead_order_type='SO')
+             AND (shipitem_shiphead_id=shiphead_id)
+             AND (shipitem_orderitem_id=coitem_id)
+             AND (coitem_itemsite_id=itemsite_id)
+             AND (coitem_status <> 'C')
+             AND (coitem_cohead_id=cohead_id)
+             AND (cohead_cust_id=cust_id)
+             AND (cust_custtype_id=custtype_id)
+             AND ( (pWarehousid = -1) OR (itemsite_warehous_id=pWarehousid) )
+             AND (custtype_code ~ pCusttype)
+             AND (shiphead_shipped)
+             AND (NOT shipitem_invoiced)
+             AND (coitem_id NOT IN ( SELECT cobill_coitem_id
+                                     FROM cobmisc, cobill
+                                     WHERE ((cobill_cobmisc_id=cobmisc_id)
+                                      AND (NOT cobmisc_posted) ) ) ) ) LOOP
+
+      PERFORM selectUninvoicedShipment(_r.shiphead_id);
+
+    _recordCounter := _recordCounter + 1;
+
+  END LOOP;
+
+  RETURN _recordCounter;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/setapjournalnumber.sql b/foundation-database/public/functions/setapjournalnumber.sql
new file mode 100644 (file)
index 0000000..ec989ea
--- /dev/null
@@ -0,0 +1,37 @@
+CREATE OR REPLACE FUNCTION setApJournalNumber() RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _journalNumber INTEGER;
+  _r RECORD;
+
+BEGIN
+
+--  Fetch the next Journal Number
+  SELECT fetchJournalNumber(''A/P'') INTO _journalNumber;
+
+--  Walk through all of the A/P Open Items
+  FOR _r IN SELECT apopen_id, apopen_docnumber
+            FROM apopen
+            WHERE (NOT apopen_posted) LOOP
+
+--  Set the Journal Number for all of the G/L Transactions
+--  for the A/P Open Item
+    UPDATE gltrans
+    SET gltrans_journalnumber=_journalNumber
+    WHERE ( (gltrans_source=''P/O'')
+     AND (gltrans_doctype IN (''VO''))
+     AND (gltrans_docnumber=_r.apopen_docnumber)
+     AND (NOT gltrans_exported) );
+
+--  Set the Journal Number for the A/P Open Item
+    UPDATE apopen
+    SET apopen_journalnumber=_journalNumber
+    WHERE (apopen_id=_r.apopen_id);
+
+  END LOOP;
+
+  RETURN _journalNumber;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/setarjournalnumber.sql b/foundation-database/public/functions/setarjournalnumber.sql
new file mode 100644 (file)
index 0000000..7e49e8f
--- /dev/null
@@ -0,0 +1,39 @@
+
+CREATE OR REPLACE FUNCTION setArJournalNumber() RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _journalNumber INTEGER;
+  _r RECORD;
+
+BEGIN
+
+--  Fetch the next Journal Number
+  SELECT fetchJournalNumber(''A/R'') INTO _journalNumber;
+
+--  Walk through all of the A/R Open Items
+  FOR _r IN SELECT aropen_id, aropen_docnumber
+            FROM aropen
+            WHERE (NOT aropen_posted) LOOP
+
+--  Set the Journal Number for all of the G/L Transactions
+--  for the A/R Open Item
+    UPDATE gltrans
+    SET gltrans_journalnumber=_journalNumber
+    WHERE ( (gltrans_source=''S/O'')
+     AND (gltrans_doctype IN (''CM'', ''IN''))
+     AND (gltrans_docnumber=_r.aropen_docnumber)
+     AND (NOT gltrans_exported) );
+
+--  Set the Journal Number for the A/R Open Item
+    UPDATE aropen
+    SET aropen_journalnumber=_journalNumber
+    WHERE (aropen_id=_r.aropen_id);
+
+  END LOOP;
+
+  RETURN _journalNumber;
+
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/setbudget.sql b/foundation-database/public/functions/setbudget.sql
new file mode 100644 (file)
index 0000000..6c207ea
--- /dev/null
@@ -0,0 +1,48 @@
+
+CREATE OR REPLACE FUNCTION setBudget(INTEGER, INTEGER, NUMERIC) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pPeriodid ALIAS FOR $1;
+  pAccntid ALIAS FOR $2;
+  pAmount ALIAS FOR $3;
+
+BEGIN
+  RETURN setBudget(1, pPeriodid, pAccntid, pAmount);
+END;
+' LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION setBudget(INTEGER, INTEGER, INTEGER, NUMERIC) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pBudgheadid ALIAS FOR $1;
+  pPeriodid ALIAS FOR $2;
+  pAccntid ALIAS FOR $3;
+  pAmount ALIAS FOR $4;
+  _budgetid INTEGER;
+
+BEGIN
+
+  SELECT budgitem_id INTO _budgetid
+    FROM budgitem
+   WHERE ((budgitem_period_id=pPeriodid)
+     AND  (budgitem_budghead_id=pBudgheadid)
+     AND  (budgitem_accnt_id=pAccntid));
+  IF (FOUND) THEN
+    UPDATE budgitem
+       SET budgitem_amount = pAmount
+     WHERE (budgitem_id=_budgetid);
+  ELSE
+    SELECT nextval(''budgitem_budgitem_id_seq'') INTO _budgetid;
+
+    INSERT INTO budgitem
+          (budgitem_id, budgitem_budghead_id, budgitem_period_id, budgitem_accnt_id, budgitem_amount)
+    VALUES(_budgetid, pBudgheadid, pPeriodid, pAccntid, pAmount);
+  END IF;
+
+  RETURN _budgetid;
+
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/setbytea.sql b/foundation-database/public/functions/setbytea.sql
new file mode 100644 (file)
index 0000000..05cecac
--- /dev/null
@@ -0,0 +1,38 @@
+CREATE OR REPLACE FUNCTION setbytea(text)
+  RETURNS bytea AS
+'
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pMetricName ALIAS FOR $1;
+  _value bytea;
+
+BEGIN
+
+  _value := decode(pMetricName, ''escape'');
+
+  RETURN _value;
+
+END;
+'
+  LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION setbytea(bytea)
+  RETURNS bytea AS
+'
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pMetricName ALIAS FOR $1;
+  _value bytea;
+
+BEGIN
+
+  _value := pMetricName;
+
+  RETURN _value;
+
+END;
+'
+  LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/setccbankaccnt.sql b/foundation-database/public/functions/setccbankaccnt.sql
new file mode 100644 (file)
index 0000000..133f30c
--- /dev/null
@@ -0,0 +1,27 @@
+CREATE OR REPLACE FUNCTION setCCBankAccnt(TEXT, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pccardtype   ALIAS FOR $1;
+  pbankaccntid ALIAS FOR $2;
+
+  _ccbankid    INTEGER;
+  _numfound    INTEGER;
+
+BEGIN
+  RAISE DEBUG 'setCCBankAccount(%, %) entered', pccardtype, pbankaccntid;
+  UPDATE ccbank SET ccbank_bankaccnt_id=pbankaccntid
+  WHERE ccbank_ccard_type=pccardtype
+  RETURNING ccbank_id INTO _ccbankid;
+
+  GET DIAGNOSTICS _numfound = ROW_COUNT;
+
+  IF (_numfound <= 0) THEN
+    INSERT INTO ccbank (ccbank_ccard_type, ccbank_bankaccnt_id)
+                VALUES (pccardtype,        pbankaccntid)
+    RETURNING _ccbankid;
+  END IF;
+
+  RETURN _ccbankid;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/seteffectivextuser.sql b/foundation-database/public/functions/seteffectivextuser.sql
new file mode 100644 (file)
index 0000000..323c7b1
--- /dev/null
@@ -0,0 +1,24 @@
+CREATE OR REPLACE FUNCTION setEffectiveXtUser(TEXT) RETURNS BOOL AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pUsername ALIAS FOR $1;
+BEGIN
+  PERFORM initEffectiveXtUser();
+
+  PERFORM *
+     FROM effective_user
+    WHERE effective_key = 'username';
+
+  IF FOUND THEN
+    UPDATE effective_user
+       SET effective_value = pUsername
+     WHERE effective_key = 'username';
+  ELSE
+    INSERT INTO effective_user (effective_key, effective_value)
+         VALUES('username', pUsername);
+  END IF;
+
+  RETURN true;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/setgljournalnumber.sql b/foundation-database/public/functions/setgljournalnumber.sql
new file mode 100644 (file)
index 0000000..f36e6a1
--- /dev/null
@@ -0,0 +1,26 @@
+
+CREATE OR REPLACE FUNCTION setGlJournalNumber(DATE, DATE) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pStartDate ALIAS FOR $1;
+  pEndDate ALIAS FOR $2;
+  _journalNumber INTEGER;
+
+BEGIN
+
+--  Fetch the next Journal Number
+  SELECT fetchJournalNumber(''G/L'') INTO _journalNumber;
+
+--  Set the Journal Number for all of the unposted G/L Transactions
+--  in the passed date range.
+  UPDATE gltrans
+  SET gltrans_journalnumber=_journalNumber
+  WHERE ( (NOT gltrans_exported)
+    AND (gltrans_date BETWEEN pStartDate and pEndDate) );
+
+  RETURN _journalNumber;
+
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/setmetric.sql b/foundation-database/public/functions/setmetric.sql
new file mode 100644 (file)
index 0000000..e6510c9
--- /dev/null
@@ -0,0 +1,29 @@
+CREATE OR REPLACE FUNCTION setMetric(TEXT, TEXT) RETURNS BOOLEAN AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pMetricName ALIAS FOR $1;
+  pMetricValue ALIAS FOR $2;
+  _metricid INTEGER;
+
+BEGIN
+
+  SELECT metric_id INTO _metricid
+  FROM metric
+  WHERE (metric_name=pMetricName);
+
+  IF (FOUND) THEN
+    UPDATE metric
+    SET metric_value=pMetricValue
+    WHERE (metric_id=_metricid);
+
+  ELSE
+    INSERT INTO metric
+    (metric_name, metric_value)
+    VALUES (pMetricName, pMetricValue);
+  END IF;
+
+  RETURN TRUE;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/setmetricenc.sql b/foundation-database/public/functions/setmetricenc.sql
new file mode 100644 (file)
index 0000000..e0b9ae7
--- /dev/null
@@ -0,0 +1,38 @@
+CREATE OR REPLACE FUNCTION setmetricenc(text, text, text)
+  RETURNS bool AS
+'
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pMetricName ALIAS FOR $1;
+  pMetricValue ALIAS FOR $2;
+  pMetricEnc ALIAS FOR $3;
+  _metricid INTEGER;
+  _value bytea;
+  _key bytea;
+
+BEGIN
+
+  _value = decode(pMetricValue, ''escape'');
+  _key = decode(pMetricEnc, ''escape'');
+
+  SELECT metricenc_id INTO _metricid
+  FROM metricenc
+  WHERE (metricenc_name=pMetricName);
+
+  IF (FOUND) THEN
+    UPDATE metricenc
+    SET metricenc_value=encrypt(_value, _key, ''bf'')
+    WHERE (metricenc_id=_metricid);
+
+  ELSE
+    INSERT INTO metricenc
+    (metricenc_name, metricenc_value)
+    VALUES (pMetricName, encrypt(_value, _key, ''bf''));
+  END IF;
+
+  RETURN TRUE;
+
+END;
+'
+  LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/setnextapmemonumber.sql b/foundation-database/public/functions/setnextapmemonumber.sql
new file mode 100644 (file)
index 0000000..06dc9fb
--- /dev/null
@@ -0,0 +1,28 @@
+CREATE OR REPLACE FUNCTION setNextAPMemoNumber(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pNumber ALIAS FOR $1;
+  _orderseqid INTEGER;
+
+BEGIN
+
+  SELECT orderseq_id INTO _orderseqid
+  FROM orderseq
+  WHERE (orderseq_name=''APMemoNumber'');
+  IF (FOUND) THEN
+    UPDATE orderseq
+    SET orderseq_number=pNumber
+    WHERE (orderseq_id=_orderseqid);
+
+  ELSE
+    INSERT INTO orderseq
+    (orderseq_name, orderseq_number)
+    VALUES
+    (''APMemoNumber'', pNumber);
+  END IF;
+
+  RETURN 1;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/setnextarmemonumber.sql b/foundation-database/public/functions/setnextarmemonumber.sql
new file mode 100644 (file)
index 0000000..e828112
--- /dev/null
@@ -0,0 +1,30 @@
+
+CREATE OR REPLACE FUNCTION setNextARMemoNumber(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pNumber ALIAS FOR $1;
+  _orderseqid INTEGER;
+
+BEGIN
+
+  SELECT orderseq_id INTO _orderseqid
+  FROM orderseq
+  WHERE (orderseq_name=''ARMemoNumber'');
+  IF (FOUND) THEN
+    UPDATE orderseq
+    SET orderseq_number=pNumber
+    WHERE (orderseq_id=_orderseqid);
+
+  ELSE
+    INSERT INTO orderseq
+    (orderseq_name, orderseq_number)
+    VALUES
+    (''ARMemoNumber'', pNumber);
+  END IF;
+
+  RETURN 1;
+
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/setnextcashrcptnumber.sql b/foundation-database/public/functions/setnextcashrcptnumber.sql
new file mode 100644 (file)
index 0000000..ffd347b
--- /dev/null
@@ -0,0 +1,30 @@
+
+CREATE OR REPLACE FUNCTION setNextCashRcptNumber(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pNumber ALIAS FOR $1;
+  _orderseqid INTEGER;
+
+BEGIN
+
+  SELECT orderseq_id INTO _orderseqid
+  FROM orderseq
+  WHERE (orderseq_name=''CashRcptNumber'');
+  IF (FOUND) THEN
+    UPDATE orderseq
+    SET orderseq_number=pNumber
+    WHERE (orderseq_id=_orderseqid);
+
+  ELSE
+    INSERT INTO orderseq
+    (orderseq_name, orderseq_number)
+    VALUES
+    (''CashRcptNumber'', pNumber);
+  END IF;
+
+  RETURN 1;
+
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/setnextchecknumber.sql b/foundation-database/public/functions/setnextchecknumber.sql
new file mode 100644 (file)
index 0000000..19cd93c
--- /dev/null
@@ -0,0 +1,19 @@
+
+CREATE OR REPLACE FUNCTION setNextCheckNumber(INTEGER, INTEGER) RETURNS BOOLEAN AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pBankaccntid ALIAS FOR $1;
+  pNextCheckNumber ALIAS FOR $2;
+
+BEGIN
+
+  UPDATE bankaccnt
+  SET bankaccnt_nextchknum=pNextCheckNumber
+  WHERE (bankaccnt_id=pBankaccntid);
+
+  RETURN TRUE;
+
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/setnextcmnumber.sql b/foundation-database/public/functions/setnextcmnumber.sql
new file mode 100644 (file)
index 0000000..a6b72dc
--- /dev/null
@@ -0,0 +1,29 @@
+CREATE OR REPLACE FUNCTION setNextCmNumber(INTEGER) RETURNS INTEGER  AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pNumber ALIAS FOR $1;
+  _orderseqid INTEGER;
+
+BEGIN
+
+  SELECT orderseq_id INTO _orderseqid
+  FROM orderseq
+  WHERE (orderseq_name=''CmNumber'');
+
+  IF (NOT FOUND) THEN
+    SELECT NEXTVAL(''orderseq_orderseq_id_seq'') INTO _orderseqid;
+
+    INSERT INTO orderseq (orderseq_id, orderseq_name, orderseq_number)
+    VALUES (_orderseqid, ''CmNumber'', pNumber);
+
+  ELSE
+    UPDATE orderseq
+    SET orderseq_number=pNumber
+    WHERE (orderseq_name=''CmNumber'');
+  END IF;
+
+  RETURN _orderseqid;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/setnextcrmaccountnumber.sql b/foundation-database/public/functions/setnextcrmaccountnumber.sql
new file mode 100644 (file)
index 0000000..1c64f27
--- /dev/null
@@ -0,0 +1,30 @@
+CREATE OR REPLACE FUNCTION setNextCRMAccountNumber(INTEGER) RETURNS INTEGER  AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pNumber ALIAS FOR $1;
+  _orderseqid INTEGER;
+
+BEGIN
+
+  SELECT orderseq_id INTO _orderseqid
+  FROM orderseq
+  WHERE (orderseq_name='CRMAccountNumber');
+
+  IF (NOT FOUND) THEN
+    SELECT NEXTVAL('orderseq_orderseq_id_seq') INTO _orderseqid;
+
+    INSERT INTO orderseq (orderseq_id, orderseq_name, orderseq_number)
+    VALUES (_orderseqid, 'CRMAccountNumber', pNumber);
+
+  ELSE
+    UPDATE orderseq
+    SET orderseq_number=pNumber
+    WHERE (orderseq_name='CRMAccountNumber');
+  END IF;
+
+  RETURN _orderseqid;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/setnextincidentnumber.sql b/foundation-database/public/functions/setnextincidentnumber.sql
new file mode 100644 (file)
index 0000000..4caf308
--- /dev/null
@@ -0,0 +1,31 @@
+
+CREATE OR REPLACE FUNCTION setNextIncidentNumber(INTEGER) RETURNS INTEGER  AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pNumber ALIAS FOR $1;
+  _orderseqid INTEGER;
+
+BEGIN
+
+  SELECT orderseq_id INTO _orderseqid
+  FROM orderseq
+  WHERE (orderseq_name=''IncidentNumber'');
+
+  IF (NOT FOUND) THEN
+    SELECT NEXTVAL(''orderseq_orderseq_id_seq'') INTO _orderseqid;
+
+    INSERT INTO orderseq (orderseq_id, orderseq_name, orderseq_number)
+    VALUES (_orderseqid, ''IncidentNumber'', pNumber);
+
+  ELSE
+    UPDATE orderseq
+    SET orderseq_number=pNumber
+    WHERE (orderseq_name=''IncidentNumber'');
+  END IF;
+
+  RETURN _orderseqid;
+
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/setnextinvcnumber.sql b/foundation-database/public/functions/setnextinvcnumber.sql
new file mode 100644 (file)
index 0000000..794a115
--- /dev/null
@@ -0,0 +1,29 @@
+CREATE OR REPLACE FUNCTION setNextInvcNumber(INTEGER) RETURNS INTEGER  AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pNumber ALIAS FOR $1;
+  _orderseqid INTEGER;
+
+BEGIN
+
+  SELECT orderseq_id INTO _orderseqid
+  FROM orderseq
+  WHERE (orderseq_name=''InvcNumber'');
+
+  IF (NOT FOUND) THEN
+    SELECT NEXTVAL(''orderseq_orderseq_id_seq'') INTO _orderseqid;
+
+    INSERT INTO orderseq (orderseq_id, orderseq_name, orderseq_number)
+    VALUES (_orderseqid, ''InvcNumber'', pNumber);
+
+  ELSE
+    UPDATE orderseq
+    SET orderseq_number=pNumber
+    WHERE (orderseq_name=''InvcNumber'');
+  END IF;
+
+  RETURN _orderseqid;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/setnextnumber.sql b/foundation-database/public/functions/setnextnumber.sql
new file mode 100644 (file)
index 0000000..09aa916
--- /dev/null
@@ -0,0 +1,61 @@
+CREATE OR REPLACE FUNCTION setNextNumber(TEXT, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  psequence    ALIAS FOR $1;
+  pnumber      ALIAS FOR $2;
+  _orderseqid  INTEGER;
+
+BEGIN
+  SELECT orderseq_id INTO _orderseqid
+  FROM orderseq
+  WHERE (orderseq_name=psequence);
+
+  IF (NOT FOUND) THEN
+    INSERT INTO orderseq (orderseq_name, orderseq_number,
+                         orderseq_table, orderseq_numcol)
+                 VALUES (psequence,     pnumber,
+                         CASE WHEN (psequence='APMemoNumber') THEN 'apopen'
+                              WHEN (psequence='ARMemoNumber') THEN 'aropen'
+                              WHEN (psequence='CmNumber') THEN 'cmhead'
+                              WHEN (psequence='IncidentNumber') THEN 'incdt'
+                              WHEN (psequence='InvcNumber') THEN 'invchead'
+                              WHEN (psequence='JournalNumber') THEN 'gltrans'
+                              WHEN (psequence='PlanNumber') THEN 'planord'
+                              WHEN (psequence='PoNumber') THEN 'pohead'
+                              WHEN (psequence='PrNumber') THEN 'pr'
+                              WHEN (psequence='QuNumber') THEN 'quhead'
+                              WHEN (psequence='ShipmentNumber') THEN 'shiphead'
+                              WHEN (psequence='SoNumber') THEN 'cohead'
+                              WHEN (psequence='ToNumber') THEN 'tohead'
+                              WHEN (psequence='VcNumber') THEN 'vohead'
+                              WHEN (psequence='WoNumber') THEN 'wo'
+                              ELSE ''
+                         END,
+                         CASE WHEN (psequence='APMemoNumber') THEN 'apopen_docnumber'
+                              WHEN (psequence='ARMemoNumber') THEN 'aropen_docnumber'
+                              WHEN (psequence='CmNumber') THEN 'cmhead_number'
+                              WHEN (psequence='IncidentNumber') THEN 'incdt_number'
+                              WHEN (psequence='InvcNumber') THEN 'invchead_invcnumber'
+                              WHEN (psequence='JournalNumber') THEN 'gltrans_journalnumber'
+                              WHEN (psequence='PlanNumber') THEN 'planord_number'
+                              WHEN (psequence='PoNumber') THEN 'pohead_number'
+                              WHEN (psequence='PrNumber') THEN 'pr_number'
+                              WHEN (psequence='QuNumber') THEN 'quhead_number'
+                              WHEN (psequence='ShipmentNumber') THEN 'shiphead_number'
+                              WHEN (psequence='SoNumber') THEN 'cohead_number'
+                              WHEN (psequence='ToNumber') THEN 'tohead_number'
+                              WHEN (psequence='VcNumber') THEN 'vohead_number'
+                              WHEN (psequence='WoNumber') THEN 'wo_number'
+                              ELSE ''
+                         END
+                         );
+  ELSE
+    UPDATE orderseq
+    SET orderseq_number=pnumber
+    WHERE (orderseq_name=psequence);
+  END IF;
+
+  RETURN 0;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/setnextponumber.sql b/foundation-database/public/functions/setnextponumber.sql
new file mode 100644 (file)
index 0000000..e33d738
--- /dev/null
@@ -0,0 +1,28 @@
+CREATE OR REPLACE FUNCTION setNextPoNumber(INTEGER) RETURNS INTEGER  AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pNumber ALIAS FOR $1;
+  _orderseqid INTEGER;
+
+BEGIN
+
+  SELECT orderseq_id INTO _orderseqid
+  FROM orderseq
+  WHERE (orderseq_name=''PoNumber'');
+
+  IF (NOT FOUND) THEN
+    SELECT NEXTVAL(''orderseq_orderseq_id_seq'') INTO _orderseqid;
+    INSERT INTO orderseq (orderseq_id, orderseq_name, orderseq_number)
+    VALUES (_orderseqid, ''PoNumber'', pNumber);
+
+  ELSE
+    UPDATE orderseq
+    SET orderseq_number=pNumber
+    WHERE (orderseq_name=''PoNumber'');
+  END IF;
+
+  RETURN _orderseqid;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/setnextprnumber.sql b/foundation-database/public/functions/setnextprnumber.sql
new file mode 100644 (file)
index 0000000..b5ba0be
--- /dev/null
@@ -0,0 +1,28 @@
+CREATE OR REPLACE FUNCTION setNextPrNumber(INTEGER) RETURNS INTEGER  AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pNumber ALIAS FOR $1;
+  _orderseqid INTEGER;
+
+BEGIN
+
+  SELECT orderseq_id INTO _orderseqid
+  FROM orderseq
+  WHERE (orderseq_name=''PrNumber'');
+
+  IF (NOT FOUND) THEN
+    SELECT NEXTVAL(''orderseq_orderseq_id_seq'') INTO _orderseqid;
+    INSERT INTO orderseq (orderseq_id, orderseq_name, orderseq_number)
+    VALUES (_orderseqid, ''PrNumber'', pNumber);
+
+  ELSE
+    UPDATE orderseq
+    SET orderseq_number=pNumber
+    WHERE (orderseq_name=''PrNumber'');
+  END IF;
+
+  RETURN _orderseqid;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/setnextqunumber.sql b/foundation-database/public/functions/setnextqunumber.sql
new file mode 100644 (file)
index 0000000..9b239a7
--- /dev/null
@@ -0,0 +1,29 @@
+CREATE OR REPLACE FUNCTION setNextQuNumber(INTEGER) RETURNS INTEGER  AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pQuNumber ALIAS FOR $1;
+  _orderseqid INTEGER;
+
+BEGIN
+
+  SELECT orderseq_id INTO _orderseqid
+  FROM orderseq
+  WHERE (orderseq_name=''QuNumber'');
+
+  IF (NOT FOUND) THEN
+    SELECT NEXTVAL(''orderseq_orderseq_id_seq'') INTO _orderseqid;
+
+    INSERT INTO orderseq (orderseq_id, orderseq_name, orderseq_number)
+    VALUES (_orderseqid, ''QuNumber'', pQuNumber);
+
+  ELSE
+    UPDATE orderseq
+    SET orderseq_number=pQuNumber
+    WHERE (orderseq_name=''QuNumber'');
+  END IF;
+
+  RETURN _orderseqid;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/setnextshipmentnumber.sql b/foundation-database/public/functions/setnextshipmentnumber.sql
new file mode 100644 (file)
index 0000000..e679b6c
--- /dev/null
@@ -0,0 +1,29 @@
+CREATE OR REPLACE FUNCTION setNextShipmentNumber(INTEGER) RETURNS INTEGER  AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pShipmentNumber ALIAS FOR $1;
+  _orderseqid INTEGER;
+
+BEGIN
+
+  SELECT orderseq_id INTO _orderseqid
+  FROM orderseq
+  WHERE (orderseq_name=''ShipmentNumber'');
+
+  IF (NOT FOUND) THEN
+    SELECT NEXTVAL(''orderseq_orderseq_id_seq'') INTO _orderseqid;
+
+    INSERT INTO orderseq (orderseq_id, orderseq_name, orderseq_number)
+    VALUES (_orderseqid, ''ShipmentNumber'', pShipmentNumber);
+
+  ELSE
+    UPDATE orderseq
+    SET orderseq_number=pShipmentNumber
+    WHERE (orderseq_name=''ShipmentNumber'');
+  END IF;
+
+  RETURN _orderseqid;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/setnextsonumber.sql b/foundation-database/public/functions/setnextsonumber.sql
new file mode 100644 (file)
index 0000000..33337fc
--- /dev/null
@@ -0,0 +1,29 @@
+CREATE OR REPLACE FUNCTION setNextSoNumber(INTEGER) RETURNS INTEGER  AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSoNumber ALIAS FOR $1;
+  _orderseqid INTEGER;
+
+BEGIN
+
+  SELECT orderseq_id INTO _orderseqid
+  FROM orderseq
+  WHERE (orderseq_name=''SoNumber'');
+
+  IF (NOT FOUND) THEN
+    SELECT NEXTVAL(''orderseq_orderseq_id_seq'') INTO _orderseqid;
+
+    INSERT INTO orderseq (orderseq_id, orderseq_name, orderseq_number)
+    VALUES (_orderseqid, ''SoNumber'', pSoNumber);
+
+  ELSE
+    UPDATE orderseq
+    SET orderseq_number=pSoNumber
+    WHERE (orderseq_name=''SoNumber'');
+  END IF;
+
+  RETURN _orderseqid;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/setnextvcnumber.sql b/foundation-database/public/functions/setnextvcnumber.sql
new file mode 100644 (file)
index 0000000..364e1ba
--- /dev/null
@@ -0,0 +1,28 @@
+CREATE OR REPLACE FUNCTION setNextVcNumber(INTEGER) RETURNS INTEGER  AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pNumber ALIAS FOR $1;
+  _orderseqid INTEGER;
+
+BEGIN
+
+  SELECT orderseq_id INTO _orderseqid
+  FROM orderseq
+  WHERE (orderseq_name=''VcNumber'');
+
+  IF (NOT FOUND) THEN
+    SELECT NEXTVAL(''orderseq_orderseq_id_seq'') INTO _orderseqid;
+    INSERT INTO orderseq (orderseq_id, orderseq_name, orderseq_number)
+    VALUES (_orderseqid, ''VcNumber'', pNumber);
+
+  ELSE
+    UPDATE orderseq
+    SET orderseq_number=pNumber
+    WHERE (orderseq_name=''VcNumber'');
+  END IF;
+
+  RETURN _orderseqid;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/setnextwonumber.sql b/foundation-database/public/functions/setnextwonumber.sql
new file mode 100644 (file)
index 0000000..d8a651f
--- /dev/null
@@ -0,0 +1,29 @@
+CREATE OR REPLACE FUNCTION setNextWoNumber(INTEGER) RETURNS INTEGER  AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pWoNumber ALIAS FOR $1;
+  _orderseqid INTEGER;
+
+BEGIN
+
+  SELECT orderseq_id INTO _orderseqid
+  FROM orderseq
+  WHERE (orderseq_name=''WoNumber'');
+
+  IF (NOT FOUND) THEN
+    SELECT NEXTVAL(''orderseq_orderseq_id_seq'') INTO _orderseqid;
+
+    INSERT INTO orderseq (orderseq_id, orderseq_name, orderseq_number)
+    VALUES (_orderseqid, ''WoNumber'', pWoNumber);
+
+  ELSE
+    UPDATE orderseq
+    SET orderseq_number=pWoNumber
+    WHERE (orderseq_name=''WoNumber'');
+  END IF;
+
+  RETURN _orderseqid;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/setusercancreateusers.sql b/foundation-database/public/functions/setusercancreateusers.sql
new file mode 100644 (file)
index 0000000..3f83778
--- /dev/null
@@ -0,0 +1,13 @@
+DROP FUNCTION IF EXISTS setuserCanCreateUsers(TEXT, BOOLEAN);
+CREATE OR REPLACE FUNCTION setuserCanCreateUsers(pUsername TEXT, pCreateUser BOOLEAN) RETURNS BOOLEAN AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  IF (pCreateUser) THEN
+    EXECUTE 'ALTER USER "' || pUsername || '" CREATEROLE;';
+  ELSE
+    EXECUTE 'ALTER USER "' || pUsername || '" NOCREATEROLE;';
+  END IF;
+  RETURN TRUE;
+END;
+$$ LANGUAGE PLPGSQL;
diff --git a/foundation-database/public/functions/setuserpreference.sql b/foundation-database/public/functions/setuserpreference.sql
new file mode 100644 (file)
index 0000000..2b82882
--- /dev/null
@@ -0,0 +1,45 @@
+
+CREATE OR REPLACE FUNCTION setUserPreference(TEXT, TEXT) RETURNS BOOLEAN AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pPrefName ALIAS FOR $1;
+  pPrefValue ALIAS FOR $2;
+
+BEGIN
+  RETURN setUserPreferences(getEffectiveXtUser(), pPrefName, pPrefValue);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION setUserPreference(TEXT, TEXT, TEXT) RETURNS BOOLEAN AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pUsername ALIAS FOR $1;
+  pPrefName ALIAS FOR $2;
+  pPrefValue ALIAS FOR $3;
+  _usrprefid INTEGER;
+
+BEGIN
+
+  SELECT usrpref_id INTO _usrprefid
+  FROM usrpref
+  WHERE ( (usrpref_username=pUsername)
+   AND (usrpref_name=pPrefName) );
+
+  IF (FOUND) THEN
+    UPDATE usrpref
+    SET usrpref_value=pPrefValue
+    WHERE (usrpref_id=_usrprefid);
+
+  ELSE
+    INSERT INTO usrpref
+    (usrpref_username, usrpref_name, usrpref_value)
+    VALUES
+    (pUsername, pPrefName, pPrefValue);
+  END IF;
+
+  RETURN TRUE;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/shipments.sql b/foundation-database/public/functions/shipments.sql
new file mode 100644 (file)
index 0000000..b4bb01f
--- /dev/null
@@ -0,0 +1,15 @@
+CREATE OR REPLACE FUNCTION shipments(TEXT) RETURNS BOOLEAN AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pTransType ALIAS FOR $1;
+
+BEGIN
+  IF (pTransType IN (''SC'', ''SV'', ''SH'', ''RS'', ''TS'')) THEN
+    RETURN TRUE;
+  ELSE
+    RETURN FALSE;
+  END IF;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/shipshipment.sql b/foundation-database/public/functions/shipshipment.sql
new file mode 100644 (file)
index 0000000..13b34e2
--- /dev/null
@@ -0,0 +1,347 @@
+CREATE OR REPLACE FUNCTION shipShipment(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+  SELECT shipShipment($1, CURRENT_TIMESTAMP);
+$$ LANGUAGE SQL;
+
+CREATE OR REPLACE FUNCTION shipShipment(INTEGER, TIMESTAMP WITH TIME ZONE) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pshipheadid          ALIAS FOR $1;
+  _timestamp           TIMESTAMP WITH TIME ZONE := $2;
+
+  _billedQty           NUMERIC;
+  _c                   RECORD;
+  _coholdtype          TEXT;
+  _gldate              DATE;
+  _invhistid           INTEGER;
+  _itemlocSeries       INTEGER;
+  _lineitemsToClose     INTEGER[];
+  _newQty              NUMERIC;
+  _result              INTEGER;
+  _s                   RECORD;
+  _shipcomplete                BOOLEAN;
+  _shiphead            RECORD;
+  _ti                  RECORD;
+  _to                  RECORD;
+  _variance            NUMERIC;
+  _k                    RECORD;
+
+BEGIN
+
+  IF (_timestamp IS NULL) THEN
+    _timestamp := CURRENT_TIMESTAMP;
+  END IF;
+  _gldate := _timestamp::DATE;
+
+  SELECT * INTO _shiphead
+  FROM shiphead WHERE (shiphead_id=pshipheadid);
+  IF (NOT FOUND) THEN
+    RETURN -50;
+  END IF;
+
+  IF (_shiphead.shiphead_order_type = 'SO') THEN
+
+    SELECT cohead_shipcomplete, cohead_holdtype INTO _shipcomplete, _coholdtype
+      FROM cohead, shiphead
+     WHERE ((shiphead_order_id=cohead_id)
+       AND  (NOT shiphead_shipped)
+       AND  (shiphead_order_type=_shiphead.shiphead_order_type)
+       AND  (shiphead_id=pshipheadid));
+
+    IF (_coholdtype = 'C') THEN
+      RETURN -12;
+    ELSIF (_coholdtype = 'P') THEN
+      RETURN -13;
+    ELSIF (_coholdtype = 'R') THEN
+      RETURN -14;
+    ELSIF (_coholdtype = 'S') THEN
+      RETURN -15;
+    END IF;
+
+---Must Ship Kit components (coitem_subnumber <> 0 complete---------------
+    IF ((
+         --  Test to see if order's customer accepts backorders and partials 
+         --  If not then test for shipping kit components complete 
+        SELECT cohead_number
+        FROM shiphead, cohead, custinfo
+        WHERE 
+          (shiphead_order_id = cohead_id) AND
+          (cohead_cust_id = cust_id) AND
+          (shiphead_order_type = 'SO') AND 
+          (cust_partialship) AND
+          (cust_backorder) AND
+          (shiphead_id = pshipheadid)
+         ) IS NULL) THEN
+      FOR _k IN SELECT (coitem_qtyord -
+                       (COALESCE(SUM(shipitem_qty),0) +
+                        (coitem_qtyshipped - coitem_qtyreturned))) AS remain
+                 FROM (coitem LEFT OUTER JOIN (itemsite JOIN item ON (itemsite_item_id=item_id)) ON (coitem_itemsite_id=itemsite_id)) LEFT OUTER JOIN
+                      shipitem ON (shipitem_orderitem_id=coitem_id
+                               AND shipitem_shiphead_id=pshipheadid)
+                WHERE ((coitem_status NOT IN ('C','X'))
+                   AND  (item_type != 'K')
+                  AND  (coitem_cohead_id=_shiphead.shiphead_order_id)
+                   AND  (coitem_subnumber <> 0)
+                  )
+             GROUP BY coitem_id, coitem_qtyshipped, coitem_qtyord,
+                      coitem_qtyreturned LOOP
+       IF (_k.remain > 0) THEN
+         RAISE EXCEPTION 'Kit component item not shipped complete.  Kits must be shipped and shipped complete or closed on the order.';
+       END IF;
+      END LOOP;
+    END IF;
+---End--------------------------------------------------------------------
+
+    IF ( _shipcomplete ) THEN
+      FOR _c IN SELECT (coitem_qtyord -
+                       (COALESCE(SUM(shipitem_qty),0) +
+                        (coitem_qtyshipped - coitem_qtyreturned))) AS remain
+                 FROM (coitem LEFT OUTER JOIN (itemsite JOIN item ON (itemsite_item_id=item_id)) ON (coitem_itemsite_id=itemsite_id)) LEFT OUTER JOIN
+                      shipitem ON (shipitem_orderitem_id=coitem_id
+                               AND shipitem_shiphead_id=pshipheadid)
+                WHERE ((coitem_status<>'X')
+                   AND  (item_type != 'K')
+                  AND  (coitem_cohead_id=_shiphead.shiphead_order_id))
+             GROUP BY coitem_id, coitem_qtyshipped, coitem_qtyord,
+                      coitem_qtyreturned LOOP
+       IF (_c.remain > 0) THEN
+         RETURN -99;
+       END IF;
+      END LOOP;
+    END IF;
+
+    FOR _c IN SELECT coitem_id, cohead_number, cohead_cust_id, cohead_billtoname, cohead_prj_id,
+                     cohead_saletype_id, cohead_shipzone_id,
+                    itemsite_id, itemsite_item_id,
+                     coitem_qty_invuomratio,
+                     coitem_warranty, coitem_cos_accnt_id,
+                    SUM(shipitem_qty) AS _qty,
+                     SUM(shipitem_value) AS _value
+             FROM coitem, cohead, shiphead, shipitem, itemsite
+             WHERE ( (coitem_cohead_id=cohead_id)
+              AND (coitem_itemsite_id=itemsite_id)
+              AND (shiphead_order_id=cohead_id)
+              AND (shipitem_shiphead_id=shiphead_id)
+              AND (shipitem_orderitem_id=coitem_id)
+              AND (NOT shiphead_shipped)
+              AND (shiphead_id=pshipheadid) )
+             GROUP BY coitem_id, coitem_qty_invuomratio, cohead_number, cohead_cust_id, cohead_billtoname,
+           itemsite_id, itemsite_item_id, coitem_warranty, coitem_cos_accnt_id, cohead_prj_id, cohead_saletype_id, cohead_shipzone_id
+    LOOP
+
+      IF _c._value > 0 THEN
+  --    Distribute to G/L, credit Shipping Asset, debit COS
+       SELECT MIN(insertGLTransaction( 'S/R', 'SH', _shiphead.shiphead_number,
+                                        ('Ship Order ' || _c.cohead_number || ' for Customer ' || _c.cohead_billtoname),
+                                        getPrjAccntId(_c.cohead_prj_id, costcat_shipasset_accnt_id),
+                                        CASE WHEN (COALESCE(_c.coitem_cos_accnt_id, -1) != -1)
+                                               THEN getPrjAccntId(_c.cohead_prj_id, _c.coitem_cos_accnt_id)
+                                             WHEN (_c.coitem_warranty=TRUE)
+                                               THEN getPrjAccntId(_c.cohead_prj_id, resolveCOWAccount(itemsite_id, _c.cohead_cust_id, _c.cohead_saletype_id, _c.cohead_shipzone_id))
+                                             ELSE getPrjAccntId(_c.cohead_prj_id, resolveCOSAccount(itemsite_id, _c.cohead_cust_id, _c.cohead_saletype_id, _c.cohead_shipzone_id))
+                                        END,
+                                        -1, _c._value, _gldate )) INTO _result
+       FROM itemsite, costcat
+       WHERE ( (itemsite_costcat_id=costcat_id)
+       AND (itemsite_id=_c.itemsite_id) );
+
+       IF (_result < 0 AND _result != -3) THEN -- ignore -3 as it just means it's not posting a 0 value
+         RETURN _result;
+       END IF;
+
+      END IF;
+
+      UPDATE coitem
+      SET coitem_qtyshipped = (coitem_qtyshipped + _c._qty)
+      WHERE (coitem_id=_c.coitem_id);
+
+      -- check to see if we have more invoiced than shipped items
+      -- if we do we will need to mark some of these records as invoiced
+      SELECT noNeg(( SELECT COALESCE(SUM(cobill_qty), 0.0)
+                    FROM cobill, cobmisc, coitem
+                    WHERE ( (cobill_cobmisc_id=cobmisc_id)
+                     AND (cobmisc_cohead_id=coitem_cohead_id)
+                     AND (cobill_coitem_id=coitem_id)
+                     AND (cobmisc_posted)
+                     AND (coitem_id=_c.coitem_id) )
+                  ) - ( SELECT COALESCE(SUM(shipitem_qty), 0.0)
+                        FROM shipitem, shiphead, coitem
+                        WHERE ( (shipitem_shiphead_id=shiphead_id)
+                         AND (shiphead_order_id=coitem_cohead_id)
+                         AND (shipitem_orderitem_id=coitem_id)
+                         AND (shiphead_order_type=_shiphead.shiphead_order_type)
+                         AND (shiphead_shipped)
+                         AND (coitem_id=_c.coitem_id) )
+                      ) ) INTO _billedQty;
+
+      IF (_billedQty > 0.0) THEN
+       FOR _s IN SELECT shipitem_id, shipitem_qty
+                 FROM shipitem, shiphead
+                 WHERE ( (shipitem_shiphead_id=shiphead_id)
+                  AND (shipitem_orderitem_id=_c.coitem_id)
+                  AND (shiphead_order_type=_shiphead.shiphead_order_type)
+                  AND (NOT shiphead_shipped)
+                  AND (shiphead_id=pshipheadid) )
+                 ORDER BY shipitem_qty LOOP
+
+         IF (_billedQty > 0.0) THEN
+
+           IF (_billedQty >= _s.shipitem_qty) THEN
+             UPDATE shipitem SET shipitem_invoiced=TRUE WHERE shipitem_id=_s.shipitem_id;
+              -- must wait to close coitems until after shiphead_shipped -> true
+              _lineitemsToClose := _lineitemsToClose || _c.coitem_id;
+           ELSE
+             _newQty := _s.shipitem_qty - _billedQty;
+             UPDATE shipitem SET shipitem_invoiced=TRUE, shipitem_qty=_billedQty WHERE shipitem_id=_s.shipitem_id;
+             INSERT INTO shipitem ( shipitem_orderitem_id, shipitem_shipdate,
+               shipitem_qty, shipitem_transdate, shipitem_invoiced,
+               shipitem_shiphead_id, shipitem_trans_username)
+             SELECT shipitem_orderitem_id, shipitem_shipdate,
+               _newQty, shipitem_transdate, FALSE,
+               shipitem_shiphead_id, shipitem_trans_username
+             FROM shipitem
+             WHERE (shipitem_id=_s.shipitem_id);
+           END IF;
+
+           _billedQty := _billedQty - _s.shipitem_qty;
+         END IF;
+       END LOOP;
+
+      END IF;
+    END LOOP;
+
+  ELSEIF (_shiphead.shiphead_order_type = 'TO') THEN
+    IF (_shiphead.shiphead_shipped) THEN
+      RETURN -8;
+    END IF;
+
+    SELECT tohead.* INTO _to
+      FROM tohead
+     WHERE (tohead_id=_shiphead.shiphead_order_id);
+
+    IF ( _to.tohead_shipcomplete ) THEN
+      -- use sufficientInventory...()?
+      FOR _ti IN SELECT (toitem_qty_ordered -
+                        (COALESCE(SUM(shipitem_qty),0) + toitem_qty_shipped)) AS remain
+                 FROM toitem LEFT OUTER JOIN
+                      shipitem ON (shipitem_orderitem_id=toitem_id)
+                WHERE ((toitem_status<>'X')
+                  AND  (toitem_tohead_id=_shiphead.shiphead_order_id))
+             GROUP BY toitem_qty_shipped, toitem_qty_ordered LOOP
+       IF (_ti.remain > 0) THEN
+         RETURN -99;
+       END IF;
+      END LOOP;
+    END IF;
+
+    FOR _ti IN SELECT toitem_id, toitem_item_id, SUM(shipitem_qty) AS qty, SUM(shipitem_value) AS value
+               FROM toitem, shipitem
+               WHERE ((toitem_tohead_id=_to.tohead_id)
+                 AND  (shipitem_orderitem_id=toitem_id)
+                 AND  (shipitem_shiphead_id=pshipheadid))
+               GROUP BY toitem_id, toitem_item_id LOOP
+
+      IF (NOT EXISTS(SELECT itemsite_id
+                    FROM itemsite
+                    WHERE ((itemsite_item_id=_ti.toitem_item_id)
+                    AND  (itemsite_warehous_id = _to.tohead_trns_warehous_id))
+                    )) THEN
+       RETURN -6;
+      END IF;
+
+      _itemlocSeries := NEXTVAL('itemloc_series_seq');
+
+      SELECT postInvTrans(si.itemsite_id, 'TS', _ti.qty,
+                          'I/M', _shiphead.shiphead_order_type,
+                          formatToNumber(_ti.toitem_id), _to.tohead_number,
+                         'Ship from Src to Transit Warehouse',
+                         tc.costcat_asset_accnt_id,
+                         sc.costcat_shipasset_accnt_id,
+                         _itemlocSeries, _timestamp, _ti.value) INTO _invhistid
+      FROM itemsite AS ti, costcat AS tc,
+          itemsite AS si, costcat AS sc
+      WHERE ( (ti.itemsite_costcat_id=tc.costcat_id)
+        AND  (si.itemsite_costcat_id=sc.costcat_id)
+        AND  (ti.itemsite_item_id=_ti.toitem_item_id)
+        AND  (si.itemsite_item_id=_ti.toitem_item_id)
+        AND  (ti.itemsite_warehous_id=_to.tohead_trns_warehous_id)
+        AND  (si.itemsite_warehous_id=_to.tohead_src_warehous_id) );
+
+      --We do not need to distribute lot/serial info for transit, post trans and discard dist detail
+      PERFORM postIntoTrialBalance(itemlocpost_glseq) FROM itemlocpost WHERE (itemlocpost_itemlocseries=_itemlocSeries);
+      PERFORM postInvHist(_invhistid);
+      DELETE FROM itemlocdist WHERE (itemlocdist_series=_itemlocSeries);
+      DELETE FROM itemlocpost WHERE (itemlocpost_itemlocSeries=_itemlocSeries);
+
+      IF (_result < 0) THEN
+       RETURN _result;
+      END IF;
+
+      -- record inventory history and qoh changes at transit warehouse but
+      -- there is only one g/l account to touch
+      SELECT postInvTrans(ti.itemsite_id, 'TR', _ti.qty,
+                          'I/M', _shiphead.shiphead_order_type,
+                          formatToNumber(_ti.toitem_id), _to.tohead_number,
+                         'Receive into Transit from Src Warehouse',
+                         tc.costcat_asset_accnt_id,
+                         tc.costcat_asset_accnt_id,
+                         _itemlocSeries, _timestamp, 
+                         _ti.value) INTO _invhistid
+      FROM itemsite AS ti, costcat AS tc
+      WHERE ((ti.itemsite_costcat_id=tc.costcat_id)
+        AND  (ti.itemsite_item_id=_ti.toitem_item_id)
+        AND  (ti.itemsite_warehous_id=_to.tohead_trns_warehous_id));
+      --We do not need to distribute lot/serial info for transit, post trans and discard dist detail
+      PERFORM postIntoTrialBalance(itemlocpost_glseq) FROM itemlocpost WHERE (itemlocpost_itemlocseries=_itemlocSeries);
+      PERFORM postInvHist(_invhistid);
+      DELETE FROM itemlocdist WHERE (itemlocdist_series=_itemlocSeries);
+      DELETE FROM itemlocpost WHERE (itemlocpost_itemlocSeries=_itemlocSeries);
+
+      --See if there was a change in values during the transfer, if so record the variance
+      SELECT (invhist_invqty * invhist_unitcost - _ti.value) INTO _variance
+      FROM invhist
+      WHERE (invhist_id=_invhistid);
+
+      IF (_variance > 0) THEN
+        PERFORM insertGLTransaction( 'S/R', _shiphead.shiphead_order_type, _to.tohead_number, 
+                                     'Transfer Order - Transfer Variance',
+                                     tc.costcat_invcost_accnt_id, tc.costcat_asset_accnt_id, _invhistid,
+                                     _variance,
+                                     CAST(_timestamp AS DATE) )
+        FROM itemsite AS ti, costcat AS tc
+        WHERE ( (ti.itemsite_costcat_id=tc.costcat_id)
+        AND  (ti.itemsite_item_id=_ti.toitem_item_id)
+        AND  (ti.itemsite_warehous_id=_to.tohead_trns_warehous_id) );
+      END IF;
+
+      IF (_result < 0) THEN
+       RETURN _result;
+      END IF;
+
+      UPDATE shipitem SET shipitem_shipdate=_timestamp, shipitem_shipped=TRUE
+      WHERE ((shipitem_orderitem_id=_ti.toitem_id)
+        AND  (shipitem_shiphead_id=pshipheadid));
+
+      UPDATE toitem
+      SET toitem_qty_shipped = (toitem_qty_shipped + _ti.qty)
+      WHERE (toitem_id=_ti.toitem_id);
+    END LOOP;
+  END IF;
+
+  UPDATE shiphead
+  SET shiphead_shipped=TRUE, shiphead_shipdate=_gldate
+  WHERE (shiphead_id=pshipheadid);
+
+  -- now try to close line items that are fully shipped and invoiced
+  IF (_shiphead.shiphead_order_type = 'SO') THEN
+    UPDATE coitem SET coitem_status='C'
+    WHERE ((coitem_id = ANY (_lineitemsToClose))
+      AND  (coitem_qtyshipped >= coitem_qtyord));
+  END IF;
+
+  RETURN _itemlocSeries;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/singlecharacteristicstostring.sql b/foundation-database/public/functions/singlecharacteristicstostring.sql
new file mode 100644 (file)
index 0000000..486b7dd
--- /dev/null
@@ -0,0 +1,31 @@
+CREATE OR REPLACE FUNCTION singleCharacteristicsToString(TEXT, INTEGER, TEXT, TEXT, INTEGER) RETURNS text AS
+$$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pTargetType ALIAS FOR $1;
+  pTargetId ALIAS FOR $2;
+  pValKeySep ALIAS FOR $3;
+  pPairSep ALIAS FOR $4;
+  pCharId ALIAS FOR $5;
+  _string TEXT := '';
+  _extra BOOLEAN := false;
+  _r RECORD;
+BEGIN
+  FOR _r IN SELECT char_name, charass_value
+              FROM charass, char
+             WHERE ((charass_char_id=char_id)
+               AND  (charass_char_id=pCharId)
+               AND  (charass_target_type=pTargetType)
+               AND  (charass_target_id=pTargetId)) LOOP
+    IF(_extra) THEN
+      _string := _string || pPairSep;
+    END IF;
+    _extra := true;
+
+    _string := _string || _r.char_name || pValKeySep || _r.charass_value;
+  END LOOP;
+
+  RETURN _string;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/singlelevelbom.sql b/foundation-database/public/functions/singlelevelbom.sql
new file mode 100644 (file)
index 0000000..8fce947
--- /dev/null
@@ -0,0 +1,217 @@
+CREATE OR REPLACE FUNCTION singlelevelBOM(INTEGER, INTEGER, INTEGER, INTEGER) RETURNS SETOF bomdata AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemid ALIAS FOR $1;
+  pRevisionid ALIAS FOR $2;
+  pExpiredDays ALIAS FOR $3;
+  pFutureDays ALIAS FOR $4;
+  _row bomdata%ROWTYPE;
+  _bomworksetid INTEGER;
+  _x RECORD;
+  _check CHAR(1);
+  _inactive BOOLEAN;
+  _batchsize NUMERIC;
+
+BEGIN
+
+  _inactive := FALSE;
+
+  IF (pRevisionid != -1) THEN
+    --Is this a deactivated revision?
+    SELECT rev_status INTO _check
+    FROM rev
+    WHERE ((rev_id=pRevisionid)
+    AND (rev_status='I'));
+    IF (FOUND) THEN
+      _inactive := TRUE;
+    END IF;
+  END IF;
+  -- Get the batch quantity
+  SELECT COALESCE( (
+    SELECT bomhead_batchsize
+    FROM bomhead
+    WHERE ((bomhead_item_id=pItemId)
+    AND (bomhead_rev_id=pRevisionid))),1) INTO _batchsize;
+  IF NOT (_inactive) THEN
+    FOR _x IN
+        SELECT bomitem_id, bomitem_seqnumber, bomitem_seqnumber AS f_bomitem_seqnumber,
+               item_id, item_number, uom_name,
+               item_descrip1, item_descrip2,
+               (item_descrip1 || ' ' || item_descrip2) AS itemdescription,
+               (itemuomtouomratio(bomitem_item_id, bomitem_uom_id, NULL) * bomitem_qtyfxd) AS qtyfxd,
+               (itemuomtouomratio(bomitem_item_id, bomitem_uom_id, NULL) * bomitem_qtyper) AS qtyper,
+               bomitem_scrap, bomitem_createwo,
+               CASE WHEN (bomitem_issuemethod='S') THEN 'Push'
+                 WHEN (bomitem_issuemethod='L') THEN 'Pull'
+                 WHEN (bomitem_issuemethod='M') THEN 'Mixed'
+                 ELSE 'Special'
+               END AS issuemethod,
+               bomitem_effective, bomitem_expires,
+               CASE WHEN (bomitem_expires <= CURRENT_DATE) THEN TRUE
+                 ELSE FALSE
+               END AS expired,
+               CASE WHEN (bomitem_effective > CURRENT_DATE) THEN TRUE
+                 ELSE FALSE
+               END AS future,
+               actcost(bomitem_item_id, bomitem_id) AS actunitcost,
+               stdcost(bomitem_item_id, bomitem_id) AS stdunitcost,
+               CASE WHEN item_type NOT IN ('R','T') THEN
+                 itemuomtouom(bomitem_item_id, bomitem_uom_id, NULL,
+                              (bomitem_qtyfxd/_batchsize + bomitem_qtyper) * (1 + bomitem_scrap), 'qtyper') * actcost(bomitem_item_id, bomitem_id)
+               ELSE 0.0 END AS actextendedcost,
+               CASE WHEN item_type NOT IN ('R','T') THEN
+                 itemuomtouom(bomitem_item_id, bomitem_uom_id, NULL,
+                              (bomitem_qtyfxd/_batchsize + bomitem_qtyper) * (1 + bomitem_scrap), 'qtyper') * stdcost(bomitem_item_id, bomitem_id)
+               ELSE 0.0 END AS stdextendedcost,
+               bomitem_char_id, bomitem_value, bomitem_notes, bomitem_ref 
+       FROM bomitem(pItemid,pRevisionid), item, uom 
+       WHERE ( (item_inv_uom_id=uom_id)
+       AND (bomitem_item_id=item_id)
+       AND (bomitem_expires > (CURRENT_DATE - pExpiredDays))
+       AND (bomitem_effective <= (CURRENT_DATE + pFutureDays)) )
+       UNION
+       SELECT -1, -1, NULL, -1, costelem_type AS bomdata_item_number, '',
+              '', '',
+              '',
+              NULL,
+              NULL,
+              NULL, NULL,
+              NULL,
+              NULL, NULL,
+              false,false,
+              currToBase(itemcost_curr_id, itemcost_actcost, CURRENT_DATE) AS actunitcost,
+              itemcost_stdcost AS stdunitcost,
+              currToBase(itemcost_curr_id, itemcost_actcost, CURRENT_DATE) AS actextendedcost,
+              itemcost_stdcost AS stdextendedcost,
+              NULL, NULL, NULL, NULL
+       FROM itemcost, costelem 
+       WHERE ( (itemcost_costelem_id=costelem_id)
+       AND (NOT itemcost_lowlevel)
+       AND (itemcost_item_id=pItemid) )
+       ORDER BY bomitem_seqnumber, bomitem_effective, item_number
+    LOOP
+        _row.bomdata_bomitem_id := _x.bomitem_id;
+        _row.bomdata_bomwork_seqnumber := _x.f_bomitem_seqnumber;
+        _row.bomdata_item_id := _x.item_id;
+        _row.bomdata_item_number := _x.item_number;
+        _row.bomdata_uom_name := _x.uom_name;
+        _row.bomdata_item_descrip1 := _x.item_descrip1;
+        _row.bomdata_item_descrip2 := _x.item_descrip2;
+        _row.bomdata_itemdescription := _x.itemdescription;
+        _row.bomdata_batchsize := _batchsize;
+        _row.bomdata_qtyfxd := _x.qtyfxd;
+        _row.bomdata_qtyper := _x.qtyper;
+        _row.bomdata_scrap := _x.bomitem_scrap;
+        _row.bomdata_createchild := _x.bomitem_createwo;
+        _row.bomdata_issuemethod := _x.issuemethod;
+        _row.bomdata_effective := _x.bomitem_effective;
+        _row.bomdata_expires := _x.bomitem_expires;
+        _row.bomdata_expired := _x.expired;
+        _row.bomdata_future := _x.future;
+        _row.bomdata_actunitcost := _x.actunitcost;
+        _row.bomdata_stdunitcost := _x.stdunitcost;
+        _row.bomdata_actextendedcost := _x.actextendedcost;
+        _row.bomdata_stdextendedcost := _x.stdextendedcost;
+        _row.bomdata_char_id := _x.bomitem_char_id;
+        _row.bomdata_value := _x.bomitem_value;
+        _row.bomdata_notes := _x.bomitem_notes;
+        _row.bomdata_ref := _x.bomitem_ref;
+        RETURN NEXT _row;
+    END LOOP;
+
+   ELSE
+
+-- Use historical snapshot for inactive revisions
+    FOR _x IN
+        SELECT bomitem_id, bomitem_seqnumber, bomitem_seqnumber AS f_bomitem_seqnumber,
+               item_id, item_number, uom_name,
+               item_descrip1, item_descrip2,
+               (item_descrip1 || ' ' || item_descrip2) AS itemdescription,
+               (itemuomtouomratio(bomitem_item_id, bomitem_uom_id, NULL) * bomitem_qtyfxd) AS qtyfxd,
+               (itemuomtouomratio(bomitem_item_id, bomitem_uom_id, NULL) * bomitem_qtyper) AS qtyper,
+               bomitem_scrap, bomitem_createwo,
+               CASE WHEN (bomitem_issuemethod='S') THEN 'Push'
+                 WHEN (bomitem_issuemethod='L') THEN 'Pull'
+                 WHEN (bomitem_issuemethod='M') THEN 'Mixed'
+                 ELSE 'Special'
+               END AS issuemethod,
+               bomitem_effective, bomitem_expires,
+               CASE WHEN (bomitem_expires <= CURRENT_DATE) THEN TRUE
+                 ELSE FALSE
+               END AS expired,
+               CASE WHEN (bomitem_effective > CURRENT_DATE) THEN TRUE
+                 ELSE FALSE
+               END AS future,
+               actcost(bomitem_item_id) AS actunitcost,
+               stdcost(bomitem_item_id) AS stdunitcost,
+               CASE WHEN item_type NOT IN ('R','T') THEN
+                 itemuomtouom(bomitem_item_id, bomitem_uom_id, NULL,
+                              (bomitem_qtyfxd/_batchsize + bomitem_qtyper) * (1 + bomitem_scrap), 'qtyper') * actcost(bomitem_item_id)
+               ELSE 0.0 END AS actextendedcost,
+               CASE WHEN item_type NOT IN ('R','T') THEN
+                 itemuomtouom(bomitem_item_id, bomitem_uom_id, NULL,
+                              (bomitem_qtyfxd/_batchsize + bomitem_qtyper) * (1 + bomitem_scrap), 'qtyper') * stdcost(bomitem_item_id)
+               ELSE 0.0 END AS stdextendedcost,
+               bomitem_char_id, bomitem_value, bomitem_notes, bomitem_ref 
+       FROM bomitem(pItemid,pRevisionid), item, uom 
+       WHERE ( (item_inv_uom_id=uom_id)
+       AND (bomitem_item_id=item_id)
+       AND (bomitem_expires > (CURRENT_DATE - pExpiredDays))
+       AND (bomitem_effective <= (CURRENT_DATE + pFutureDays)) )
+       UNION
+       SELECT -1, -1, NULL, -1, costelem_type AS bomdata_item_number, '',
+              '', '',
+              '',
+              NULL,
+              NULL,
+              NULL, NULL,
+              NULL,
+              NULL, NULL,
+              false,false,
+              bomhist_actunitcost AS actunitcost,
+              bomhist_stdunitcost AS stdunitcost,
+              bomhist_actunitcost AS actextendedcost,
+              bomhist_stdunitcost AS stdextendedcost,
+              NULL, NULL, NULL, NULL
+       FROM bomhist, costelem 
+       WHERE ( (bomhist_item_id=costelem_id)
+       AND (bomhist_item_type='E')
+       AND (bomhist_rev_id=pRevisionid) )
+       ORDER BY bomitem_seqnumber, bomitem_effective, item_number
+    LOOP
+        _row.bomdata_bomitem_id := _x.bomitem_id;
+        _row.bomdata_bomwork_seqnumber := _x.f_bomitem_seqnumber;
+        _row.bomdata_item_id := _x.item_id;
+        _row.bomdata_item_number := _x.item_number;
+        _row.bomdata_uom_name := _x.uom_name;
+        _row.bomdata_item_descrip1 := _x.item_descrip1;
+        _row.bomdata_item_descrip2 := _x.item_descrip2;
+        _row.bomdata_itemdescription := _x.itemdescription;
+        _row.bomdata_batchsize := _batchsize;
+        _row.bomdata_qtyfxd := _x.qtyfxd;
+        _row.bomdata_qtyper := _x.qtyper;
+        _row.bomdata_scrap := _x.bomitem_scrap;
+        _row.bomdata_createchild := _x.bomitem_createwo;
+        _row.bomdata_issuemethod := _x.issuemethod;
+        _row.bomdata_effective := _x.bomitem_effective;
+        _row.bomdata_expires := _x.bomitem_expires;
+        _row.bomdata_expired := _x.expired;
+        _row.bomdata_future := _x.future;
+        _row.bomdata_actunitcost := _x.actunitcost;
+        _row.bomdata_stdunitcost := _x.stdunitcost;
+        _row.bomdata_actextendedcost := _x.actextendedcost;
+        _row.bomdata_stdextendedcost := _x.stdextendedcost;
+        _row.bomdata_char_id := _x.bomitem_char_id;
+        _row.bomdata_value := _x.bomitem_value;
+        _row.bomdata_notes := _x.bomitem_notes;
+        _row.bomdata_ref := _x.bomitem_ref;
+        RETURN NEXT _row;
+    END LOOP;
+  END IF;
+
+  RETURN;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/site.sql b/foundation-database/public/functions/site.sql
new file mode 100644 (file)
index 0000000..2322183
--- /dev/null
@@ -0,0 +1,96 @@
+CREATE OR REPLACE FUNCTION site() RETURNS SETOF whsinfo AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _row whsinfo%ROWTYPE;
+  _r RECORD;
+
+BEGIN
+
+  IF ( (fetchMetricBool(''MultiWhs'')) AND
+       (SELECT (COUNT(usrpref_id)=1)
+        FROM usrpref
+        WHERE ((usrpref_name=''selectedSites'')
+        AND (usrpref_value=''t'')
+        AND (usrpref_username=getEffectiveXtUser()))) ) THEN
+
+    FOR _r IN SELECT *
+            FROM whsinfo,usrsite
+            WHERE ((warehous_id=usrsite_warehous_id)
+            AND (usrsite_username=getEffectiveXtUser()))
+    LOOP
+      _row.warehous_id:=_r.warehous_id;
+      _row.warehous_code:=_r.warehous_code;
+      _row.warehous_descrip:=_r.warehous_descrip;
+      _row.warehous_fob:=_r.warehous_fob;
+      _row.warehous_active:=_r.warehous_active;
+      _row.warehous_counttag_prefix:=_r.warehous_counttag_prefix;
+      _row.warehous_counttag_number:=_r.warehous_counttag_number;
+      _row.warehous_bol_prefix:=_r.warehous_bol_prefix;
+      _row.warehous_bol_number:=_r.warehous_bol_number;
+      _row.warehous_shipping:=_r.warehous_shipping;
+      _row.warehous_useslips:=_r.warehous_useslips;
+      _row.warehous_usezones:=_r.warehous_usezones;
+      _row.warehous_aislesize:=_r.warehous_aislesize;
+      _row.warehous_racksize:=_r.warehous_racksize;
+      _row.warehous_binsize:=_r.warehous_binsize;
+      _row.warehous_binalpha:=_r.warehous_binalpha;
+      _row.warehous_locationsize:=_r.warehous_locationsize;
+      _row.warehous_locationalpha:=_r.warehous_locationalpha;
+      _row.warehous_enforcearbl:=_r.warehous_enforcearbl;
+      _row.warehous_default_accnt_id:=_r.warehous_default_accnt_id;
+      _row.warehous_shipping_commission:=_r.warehous_shipping_commission;
+      _row.warehous_cntct_id:=_r.warehous_cntct_id;
+      _row.warehous_addr_id:=_r.warehous_addr_id;
+      _row.warehous_taxzone_id:=_r.warehous_taxzone_id;
+      _row.warehous_transit:=_r.warehous_transit;
+      _row.warehous_shipform_id:=_r.warehous_shipform_id;
+      _row.warehous_shipvia_id:=_r.warehous_shipvia_id;
+      _row.warehous_shipcomments:=_r.warehous_shipcomments;
+      _row.warehous_costcat_id:=_r.warehous_costcat_id;
+      _row.warehous_sitetype_id:=_r.warehous_sitetype_id;
+             
+      RETURN NEXT _row;
+    END LOOP;
+  ELSE
+    FOR _r IN SELECT *
+            FROM whsinfo
+    LOOP
+      _row.warehous_id:=_r.warehous_id;
+      _row.warehous_code:=_r.warehous_code;
+      _row.warehous_descrip:=_r.warehous_descrip;
+      _row.warehous_fob:=_r.warehous_fob;
+      _row.warehous_active:=_r.warehous_active;
+      _row.warehous_counttag_prefix:=_r.warehous_counttag_prefix;
+      _row.warehous_counttag_number:=_r.warehous_counttag_number;
+      _row.warehous_bol_prefix:=_r.warehous_bol_prefix;
+      _row.warehous_bol_number:=_r.warehous_bol_number;
+      _row.warehous_shipping:=_r.warehous_shipping;
+      _row.warehous_useslips:=_r.warehous_useslips;
+      _row.warehous_usezones:=_r.warehous_usezones;
+      _row.warehous_aislesize:=_r.warehous_aislesize;
+      _row.warehous_racksize:=_r.warehous_racksize;
+      _row.warehous_binsize:=_r.warehous_binsize;
+      _row.warehous_binalpha:=_r.warehous_binalpha;
+      _row.warehous_locationsize:=_r.warehous_locationsize;
+      _row.warehous_locationalpha:=_r.warehous_locationalpha;
+      _row.warehous_enforcearbl:=_r.warehous_enforcearbl;
+      _row.warehous_default_accnt_id:=_r.warehous_default_accnt_id;
+      _row.warehous_shipping_commission:=_r.warehous_shipping_commission;
+      _row.warehous_cntct_id:=_r.warehous_cntct_id;
+      _row.warehous_addr_id:=_r.warehous_addr_id;
+      _row.warehous_taxzone_id:=_r.warehous_taxzone_id;
+      _row.warehous_transit:=_r.warehous_transit;
+      _row.warehous_shipform_id:=_r.warehous_shipform_id;
+      _row.warehous_shipvia_id:=_r.warehous_shipvia_id;
+      _row.warehous_shipcomments:=_r.warehous_shipcomments;
+      _row.warehous_costcat_id:=_r.warehous_costcat_id;
+      _row.warehous_sitetype_id:=_r.warehous_sitetype_id;
+             
+      RETURN NEXT _row;
+    END LOOP;
+  END IF;
+  
+  RETURN;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/snoozemessage.sql b/foundation-database/public/functions/snoozemessage.sql
new file mode 100644 (file)
index 0000000..d3ae7d9
--- /dev/null
@@ -0,0 +1,19 @@
+
+CREATE OR REPLACE FUNCTION snoozeMessage(INTEGER) RETURNS BOOLEAN AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pMsgid ALIAS FOR $1;
+  snooze INTERVAL := ''10 minutes'';
+
+BEGIN
+
+  UPDATE msg
+  SET msg_scheduled=(msg_scheduled + snooze)
+  WHERE (msg_id=pMsgid);
+
+  RETURN TRUE;
+
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/spellamount.sql b/foundation-database/public/functions/spellamount.sql
new file mode 100644 (file)
index 0000000..c24601e
--- /dev/null
@@ -0,0 +1,203 @@
+
+CREATE OR REPLACE FUNCTION spellAmount(NUMERIC) RETURNS TEXT AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN spellAmount($1, baseCurrId());
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION spellAmount(NUMERIC, INTEGER) RETURNS text AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pN ALIAS FOR $1;
+  pCurrId ALIAS FOR $2;
+  _t text;
+  _dollars text;
+  _cents text;
+  _l integer;
+  _p integer;
+  _words text;
+  _word text;
+  _hundreds char;
+  _tens char;
+  _ones char;
+  _fractionalPartName text;
+  _curr curr_symbol%ROWTYPE;
+BEGIN
+
+  _t := ltrim(to_char(pN, '999999999990D99'),' ');
+  IF strpos(_t, '.') > 0 THEN
+    _dollars := split_part(_t, '.', 1);
+    _cents := split_part(_t, '.', 2);
+  ELSIF strpos(_t, ',') > 0 THEN
+    _dollars := split_part(_t, ',', 1);
+    _cents := split_part(_t, ',', 2);
+  END IF;
+
+  _p := 0;
+  _l := length(_dollars);
+
+  _words := '';
+  WHILE (_p < _l) LOOP
+    IF((_l - _p - 2) < 1) THEN
+      _hundreds := '0';
+    ELSE
+      _hundreds := substr(_dollars, _l - _p - 2, 1);
+    END IF;
+    IF((_l - _p - 1) < 1) THEN
+      _tens := '0';
+    ELSE
+      _tens := substr(_dollars, _l - _p - 1, 1);
+    END IF;
+    IF((_l - _p) < 1) THEN
+      _ones := '0';
+    ELSE
+      _ones := substr(_dollars, _l - _p, 1);
+    END IF;
+
+    IF(_hundreds != '0' OR _tens != '0' OR _ones != '0') THEN
+      IF (_p = 3) THEN
+        _words := 'thousand ' || _words;
+      ELSIF (_p = 6) THEN
+        _words := 'million ' || _words;
+      ELSIF (_p = 9) THEN
+        _words := 'billion ' || _words;
+      END IF;
+
+      _word := '';
+      IF(_tens = '1') THEN
+        IF(_ones = '0') THEN
+          _word := 'ten';
+        ELSIF(_ones = '1') THEN
+          _word := 'eleven';
+        ELSIF(_ones = '2') THEN
+          _word := 'twelve';
+        ELSIF(_ones = '3') THEN
+          _word := 'thirteen';
+        ELSIF(_ones = '4') THEN
+          _word := 'fourteen';
+        ELSIF(_ones = '5') THEN
+          _word := 'fifteen';
+        ELSIF(_ones = '6') THEN
+          _word := 'sixteen';
+        ELSIF(_ones = '7') THEN
+          _word := 'seventeen';
+        ELSIF(_ones = '8') THEN
+          _word := 'eighteen';
+        ELSIF(_ones = '9') THEN
+          _word := 'nineteen';
+        ELSE
+          _word := 'ERROR';
+        END IF;
+      ELSE
+        IF(_ones = '1') THEN
+          _word := 'one';
+        ELSIF(_ones = '2') THEN
+          _word := 'two';
+        ELSIF(_ones = '3') THEN
+          _word := 'three';
+        ELSIF(_ones = '4') THEN
+          _word := 'four';
+        ELSIF(_ones = '5') THEN
+          _word := 'five';
+        ELSIF(_ones = '6') THEN
+          _word := 'six';
+        ELSIF(_ones = '7') THEN
+          _word := 'seven';
+        ELSIF(_ones = '8') THEN
+          _word := 'eight';
+        ELSIF(_ones = '9') THEN
+          _word := 'nine';
+        ELSIF(_ones != '0') THEN
+          _word := 'ERROR';
+        END IF;
+
+        if(_tens != '0') THEN
+          _word := '-' || _word;
+        END IF;
+
+        IF(_tens = '2') THEN
+          _word := 'twenty' || _word;
+        ELSIF(_tens = '3') THEN
+          _word := 'thirty' || _word;
+        ELSIF(_tens = '4') THEN
+          _word := 'forty' || _word;
+        ELSIF(_tens = '5') THEN
+          _word := 'fifty' || _word;
+        ELSIF(_tens = '6') THEN
+          _word := 'sixty' || _word;
+        ELSIF(_tens = '7') THEN
+          _word := 'seventy' || _word;
+        ELSIF(_tens = '8') THEN
+          _word := 'eighty' || _word;
+        ELSIF(_tens = '9') THEN
+          _word := 'ninety' || _word;
+        ELSIF(_tens != '0' AND _tens != '1') THEN
+          _word := 'ERROR' || _word;
+        END IF;
+      END IF;
+      if(_word != '') THEN
+        _words := _word || ' ' || _words;
+      END IF;
+
+      _word := '';
+      IF(_hundreds = '1') THEN
+        _word := 'one hundred';
+      ELSIF(_hundreds = '2') THEN
+        _word := 'two hundred';
+      ELSIF(_hundreds = '3') THEN
+        _word := 'three hundred';
+      ELSIF(_hundreds = '4') THEN
+        _word := 'four hundred';
+      ELSIF(_hundreds = '5') THEN
+        _word := 'five hundred';
+      ELSIF(_hundreds = '6') THEN
+        _word := 'six hundred';
+      ELSIF(_hundreds = '7') THEN
+        _word := 'seven hundred';
+      ELSIF(_hundreds = '8') THEN
+        _word := 'eight hundred';
+      ELSIF(_hundreds = '9') THEN
+        _word := 'nine hundred';
+      ELSIF(_hundreds != '0') THEN
+        _words := 'ERROR';
+      END IF;
+      if(_word != '') THEN
+        _words := _word || ' ' || _words;
+      END IF;
+    END IF;
+
+    _p := _p + 3;
+  END LOOP;
+
+  _words := rtrim(_words, ' ');
+  IF(_words = '') THEN
+    _words := 'zero';
+  END IF;
+
+  SELECT * INTO _curr
+    FROM curr_symbol
+    WHERE curr_id = pCurrId;
+
+  IF(_words = 'one') AND TRIM(_curr.curr_name) ~ '.*s' THEN
+    _word := rtrim(_curr.curr_name, ' s');
+  ELSE
+    _word := trim(_curr.curr_name);
+  END IF;
+
+  IF _curr.curr_abbr = 'USD' OR _curr.curr_abbr = 'CAD' THEN
+      IF (_cents = '1') THEN
+        _fractionalPartName = ' cent';
+      ELSE
+        _fractionalPartName = ' cents';
+      END IF;
+  ELSE
+    _fractionalPartName = ' / 100 ';
+  END IF;
+
+  RETURN _words || ' ' || _word || ' and ' || _cents || _fractionalPartName;
+END;
+$$ LANGUAGE 'plpgsql' VOLATILE;
+
diff --git a/foundation-database/public/functions/splitreceipt.sql b/foundation-database/public/functions/splitreceipt.sql
new file mode 100644 (file)
index 0000000..fc1842d
--- /dev/null
@@ -0,0 +1,62 @@
+CREATE OR REPLACE FUNCTION splitReceipt(INTEGER, NUMERIC, NUMERIC) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  precvid      ALIAS FOR $1;
+  pqty         ALIAS FOR $2;
+  pfreight     ALIAS FOR $3;
+  _check               RECORD;
+  _seq         INTEGER;
+
+BEGIN
+  -- validate
+  IF (COALESCE(pQty,0) <= 0) THEN
+    RETURN -7;
+  END IF;
+  
+  SELECT * INTO _check
+  FROM recv
+  WHERE (recv_id=precvid);
+
+  IF (FOUND) THEN
+    IF (_check.recv_order_type != ''PO'') THEN
+      RETURN -1;
+    ELSIF ( NOT _check.recv_posted) THEN
+      RETURN -2;
+    ELSIF ( (_check.recv_invoiced)
+         OR (_check.recv_vohead_id IS NOT NULL)
+         OR (_check.recv_voitem_id IS NOT NULL) ) THEN
+      RETURN -3;
+    ELSIF (pqty >= _check.recv_qty) THEN
+      RETURN -4;
+    ELSIF (COALESCE(pfreight,0) > _check.recv_freight) THEN
+      RETURN -5;
+    END IF;
+  ELSE
+    RETURN -6;
+  END IF;
+
+  -- Create new receipt record
+  _seq := nextval(''recv_recv_id_seq'');
+  
+  INSERT INTO recv
+  SELECT _seq, recv_order_type,recv_order_number,
+         recv_orderitem_id, recv_agent_username, recv_itemsite_id, recv_vend_id,
+         recv_vend_item_number, recv_vend_item_descrip, recv_vend_uom,
+         recv_purchcost, recv_purchcost_curr_id, recv_duedate, pqty, 
+         recv_recvcost, recv_recvcost_curr_id, COALESCE(pfreight,0), recv_freight_curr_id, recv_date, 
+         ROUND(recv_value/recv_qty * pqty, 2), TRUE, FALSE, NULL, NULL,
+         recv_trans_usr_name, recv_notes, recv_gldistdate, precvid
+  FROM recv
+  WHERE (recv_id=precvid);
+
+  --  Update qty and value of old record
+  UPDATE recv SET
+    recv_qty = recv_qty-pqty,
+    recv_value = recv_value - ROUND(recv_value/recv_qty * pqty, 2),
+    recv_freight = recv_freight - COALESCE(pfreight,0)
+  WHERE (recv_id=precvid);
+
+  RETURN _seq;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/splitrecurrence.sql b/foundation-database/public/functions/splitrecurrence.sql
new file mode 100644 (file)
index 0000000..e799104
--- /dev/null
@@ -0,0 +1,84 @@
+CREATE OR REPLACE FUNCTION splitRecurrence(INTEGER, TEXT, TIMESTAMP WITH TIME ZONE) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pParentid     ALIAS FOR $1;
+  pType         TEXT := UPPER($2);
+  pDatetime     TIMESTAMP WITH TIME ZONE := COALESCE($3, CURRENT_TIMESTAMP);
+
+  _count         INTEGER;
+  _newrecurid    INTEGER;
+  _newparentid   INTEGER;
+  _newparentstmt TEXT;
+  _rt            RECORD;
+  _updchildstmt  TEXT;
+
+BEGIN
+  IF (pParentid IS NULL) THEN
+    RETURN -11;
+  END IF;
+
+  SELECT * INTO _rt FROM recurtype WHERE (UPPER(recurtype_type)=pType);
+  GET DIAGNOSTICS _count = ROW_COUNT;
+  IF (_count <= 0) THEN
+    RETURN -10;
+  END IF;
+
+  _newparentstmt := 'SELECT [table]_id FROM [fulltable]'
+                 || ' WHERE (([table]_recurring_[table]_id=$1)'
+                 || '    AND NOT ([done])'
+                 || '    AND ([schedcol]>=''$2''))'
+                 || ' ORDER BY [schedcol]'
+                 || ' LIMIT 1;';
+  _newparentstmt := REPLACE(_newparentstmt, '[fulltable]', _rt.recurtype_table);
+  _newparentstmt := REPLACE(_newparentstmt, '[table]',
+                            REGEXP_REPLACE(_rt.recurtype_table, E'.*\\.', ''));
+  _newparentstmt := REPLACE(_newparentstmt, '[done]',  _rt.recurtype_donecheck);
+  _newparentstmt := REPLACE(_newparentstmt, '[schedcol]', _rt.recurtype_schedcol);
+  _updchildstmt := 'UPDATE [fulltable] SET [table]_recurring_[table]_id=$1'
+                || ' WHERE (([table]_recurring_[table]_id=$2)'
+                || '   AND NOT ([done])'
+                || '   AND ([schedcol] > ''$3''));';
+  _updchildstmt := REPLACE(_updchildstmt, '[fulltable]', _rt.recurtype_table);
+  _updchildstmt := REPLACE(_updchildstmt, '[table]',
+                           REGEXP_REPLACE(_rt.recurtype_table, E'.*\\.', ''));
+  _updchildstmt := REPLACE(_updchildstmt, '[done]',  _rt.recurtype_donecheck);
+  _updchildstmt := REPLACE(_updchildstmt, '[schedcol]', _rt.recurtype_schedcol);
+
+  -- 8.4+: EXECUTE _newparentstmt INTO _newparentid USING pParentid, pDatetime;
+  EXECUTE REPLACE(REPLACE(_newparentstmt, '$1', pParentid::TEXT),
+                                          '$2', pDatetime::TEXT)
+          INTO _newparentid;
+
+  -- if nothing to split
+  IF (_newparentid = pParentid OR _newparentid IS NULL) THEN
+    SELECT recur_id INTO _newrecurid
+      FROM recur
+     WHERE ((recur_parent_id=pParentid)
+        AND (recur_parent_type=pType));
+
+  ELSE
+    INSERT INTO recur (recur_parent_id, recur_parent_type, recur_period,
+                       recur_freq,      recur_start,       recur_end,
+                       recur_max,       recur_data
+             ) SELECT _newparentid,     pType,             recur_period,
+                      recur_freq,       pDatetime,         recur_end,
+                      recur_max,        recur_data
+                 FROM recur
+                WHERE ((recur_parent_id=pParentid)
+                   AND (recur_parent_type=pType))
+      RETURNING recur_id INTO _newrecurid;
+
+    UPDATE recur SET recur_end=pDatetime
+    WHERE ((recur_parent_id=pParentid)
+       AND (recur_parent_type=pType));
+
+    -- 8.4+: EXECUTE _updchildstmt USING _newparentid, pParentid, pDatetime;
+    EXECUTE REPLACE(REPLACE(REPLACE(_updchildstmt, '$1', _newparentid::TEXT),
+                                                   '$2', pParentid::TEXT),
+                                                   '$3', pDatetime::TEXT);
+  END IF;
+
+  RETURN _newrecurid;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/startoftime.sql b/foundation-database/public/functions/startoftime.sql
new file mode 100644 (file)
index 0000000..f759024
--- /dev/null
@@ -0,0 +1,7 @@
+
+CREATE OR REPLACE FUNCTION startoftime() RETURNS DATE IMMUTABLE AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+SELECT DATE('1970-01-01') AS return;
+$$ LANGUAGE sql;
+
diff --git a/foundation-database/public/functions/stdcost.sql b/foundation-database/public/functions/stdcost.sql
new file mode 100644 (file)
index 0000000..1795108
--- /dev/null
@@ -0,0 +1,31 @@
+CREATE OR REPLACE FUNCTION stdCost(INTEGER) RETURNS NUMERIC AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN stdCost($1, NULL);
+END;
+' LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION stdCost(INTEGER, INTEGER) RETURNS NUMERIC AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemid ALIAS FOR $1;
+  pBomitemid ALIAS FOR $2;
+  _cost NUMERIC;
+
+BEGIN
+
+  SELECT SUM(COALESCE(bomitemcost_stdcost, itemcost_stdcost)) INTO _cost
+  FROM itemcost
+    LEFT OUTER JOIN bomitemcost ON (bomitemcost_bomitem_id=pBomitemid AND bomitemcost_costelem_id=itemcost_costelem_id)
+  WHERE (itemcost_item_id=pItemid);
+
+  IF (_cost IS NULL) THEN
+    RETURN 0;
+  ELSE
+    RETURN _cost;
+  END IF;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/sufficientinventorytoshipitem.sql b/foundation-database/public/functions/sufficientinventorytoshipitem.sql
new file mode 100644 (file)
index 0000000..35f0927
--- /dev/null
@@ -0,0 +1,130 @@
+CREATE OR REPLACE FUNCTION sufficientInventoryToShipItem(TEXT, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pordertype    ALIAS FOR $1;
+  porderitemid  ALIAS FOR $2;
+
+BEGIN
+  RETURN sufficientInventoryToShipItem(pordertype, porderitemid, NULL);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION sufficientInventoryToShipItem(TEXT, INTEGER, NUMERIC) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pordertype           ALIAS FOR $1;
+  porderitemid         ALIAS FOR $2;
+  pqty                  ALIAS FOR $3;
+  _returnVal           INTEGER;
+  _isqtyavail          BOOLEAN;
+
+BEGIN
+  IF (porderitemid IS NULL) THEN
+    RETURN -1;
+  END IF;
+
+  IF (pordertype = 'SO') THEN
+    IF ( SELECT fetchMetricBool('EnableSOReservations') ) THEN
+      IF (SELECT (itemsite_costmethod = 'J')
+          FROM coitem JOIN itemsite ON (coitem_itemsite_id=itemsite_id)
+          WHERE (coitem_id=porderitemid)) THEN
+        RETURN 0;
+      END IF;
+      SELECT (((COALESCE(pqty, roundQty(item_fractional,
+                     noNeg(coitem_qtyord - coitem_qtyshipped +
+                           coitem_qtyreturned - qtyAtShipping(pordertype, coitem_id)
+                          ))) - coitem_qtyreserved) * coitem_qty_invuomratio
+                     ) <= itemsite_qtyonhand)
+              AND 
+             (((COALESCE(pqty, roundQty(item_fractional,
+                     noNeg(coitem_qtyord - coitem_qtyshipped +
+                           coitem_qtyreturned - qtyAtShipping(pordertype, coitem_id)
+                          ))) - coitem_qtyreserved) * coitem_qty_invuomratio
+                     ) <= qtyunreserved(itemsite_id))
+        INTO _isqtyavail
+        FROM coitem, itemsite, item
+       WHERE ((coitem_itemsite_id=itemsite_id) 
+         AND (coitem_status <> 'X')
+         AND  (NOT ((item_type IN ('R','J')) OR (itemsite_controlmethod = 'N'))) 
+         AND (itemsite_item_id=item_id) 
+         AND (coitem_id=porderitemid));
+    ELSE
+      SELECT (COALESCE(pqty, roundQty(item_fractional,
+                                     noNeg(coitem_qtyord - coitem_qtyshipped +
+                                     coitem_qtyreturned - qtyAtShipping(pordertype, coitem_id) - coitem_qtyreserved
+                                     ) * coitem_qty_invuomratio
+                     )
+              ) <= itemsite_qtyonhand)
+        INTO _isqtyavail
+        FROM coitem, itemsite, item
+       WHERE ((coitem_itemsite_id=itemsite_id) 
+         AND (coitem_status <> 'X')
+         AND  (NOT ((item_type IN ('R','J')) OR (itemsite_controlmethod = 'N'))) 
+         AND (itemsite_item_id=item_id) 
+         AND (coitem_id=porderitemid));
+    END IF;
+  ELSEIF (pordertype = 'TO') THEN
+    SELECT (COALESCE(pqty, roundQty(item_fractional,
+                                   noNeg(toitem_qty_ordered - toitem_qty_shipped - 
+                                   qtyAtShipping(pordertype, toitem_id)
+                                   )
+                   )
+           ) <= itemsite_qtyonhand) INTO _isqtyavail  
+      FROM toitem, tohead, itemsite, item
+     WHERE ((toitem_tohead_id=tohead_id)
+       AND  (tohead_src_warehous_id=itemsite_warehous_id) 
+       AND  (toitem_item_id=itemsite_item_id) 
+       AND  (itemsite_warehous_id=tohead_src_warehous_id) 
+       AND  (itemsite_item_id=item_id) 
+       AND  (toitem_status <> 'X')
+         AND  (NOT ((item_type IN ('R','J')) OR (itemsite_controlmethod = 'N'))) 
+       AND  (toitem_id=porderitemid));
+  ELSE
+    RETURN -11;
+  END IF;
+
+  IF (NOT _isqtyavail) THEN
+    RETURN -2;
+  END IF;
+
+  IF (pordertype = 'SO') THEN
+    SELECT (COALESCE((SELECT SUM(itemloc_qty) 
+                       FROM itemloc 
+                      WHERE (itemloc_itemsite_id=itemsite_id)), 0.0) >= roundQty(item_fractional, 
+                             COALESCE(pQty, noNeg( coitem_qtyord - coitem_qtyshipped + coitem_qtyreturned - 
+                             qtyAtShipping(pordertype, coitem_id) )) * coitem_qty_invuomratio
+                            )) INTO _isqtyavail 
+      FROM coitem, itemsite, item
+     WHERE ((coitem_itemsite_id=itemsite_id) 
+       AND (itemsite_item_id=item_id) 
+       AND (NOT ((item_type ='R') OR (itemsite_controlmethod = 'N'))) 
+       AND ((itemsite_controlmethod IN ('L', 'S')) OR (itemsite_loccntrl)) 
+       AND (coitem_id=porderitemid)); 
+
+  ELSEIF (pordertype = 'TO') THEN
+    SELECT (COALESCE((SELECT SUM(itemloc_qty) 
+                       FROM itemloc 
+                      WHERE (itemloc_itemsite_id=itemsite_id)), 0.0) >= roundQty(item_fractional, 
+                             noNeg( toitem_qty_ordered - toitem_qty_shipped - 
+                             qtyAtShipping(pordertype, toitem_id) )
+                            )) INTO _isqtyavail 
+      FROM toitem, tohead, itemsite, item
+     WHERE ((toitem_tohead_id=tohead_id)
+       AND  (tohead_src_warehous_id=itemsite_warehous_id) 
+       AND  (toitem_item_id=itemsite_item_id) 
+       AND  (itemsite_item_id=item_id) 
+       AND  (toitem_status <> 'X')
+       AND  (NOT ((item_type ='R') OR (itemsite_costmethod = 'J') OR (itemsite_controlmethod = 'N'))) 
+       AND  ((itemsite_controlmethod IN ('L', 'S')) OR (itemsite_loccntrl)) 
+       AND  (toitem_id=porderitemid)); 
+  END IF;
+  
+  IF (NOT _isqtyavail) THEN
+    RETURN -3;
+  END IF;
+
+  RETURN 0;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/sufficientinventorytoshiporder.sql b/foundation-database/public/functions/sufficientinventorytoshiporder.sql
new file mode 100644 (file)
index 0000000..97f2bb9
--- /dev/null
@@ -0,0 +1,31 @@
+CREATE OR REPLACE FUNCTION sufficientInventoryToShipOrder(TEXT, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pordertype   ALIAS FOR $1;
+  porderid     ALIAS FOR $2;
+  _s           RECORD;
+  _returnVal   INTEGER := 0;
+
+BEGIN
+  IF (pordertype = 'SO') THEN
+    FOR _s IN SELECT coitem_id
+               FROM coitem
+                JOIN itemsite ON (coitem_itemsite_id=itemsite_id)
+              WHERE((coitem_cohead_id=porderid) 
+               AND (itemsite_costmethod != 'J')) LOOP
+      _returnVal := sufficientInventoryToShipItem(pordertype, _s.coitem_id);
+      EXIT WHEN (_returnVal < 0);
+    END LOOP;
+  ELSEIF (pordertype = 'TO') THEN
+    FOR _s IN SELECT toitem_id
+               FROM toitem
+              WHERE(toitem_tohead_id=porderid) LOOP
+      _returnVal := sufficientInventoryToShipItem(pordertype, _s.toitem_id);
+      EXIT WHEN (_returnVal < 0);
+    END LOOP;
+  END IF;
+
+  RETURN _returnVal;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/summarizedbom.sql b/foundation-database/public/functions/summarizedbom.sql
new file mode 100644 (file)
index 0000000..87809d9
--- /dev/null
@@ -0,0 +1,246 @@
+CREATE OR REPLACE FUNCTION summarizedBOM(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemid ALIAS FOR $1;
+  _revid INTEGER;
+
+BEGIN
+
+  SELECT getActiveRevId('BOM',pItemid) INTO _revid;
+  
+  RETURN summarizedBOM(pItemid, _revid);
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION summarizedBOM(INTEGER, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemid ALIAS FOR $1;
+  pRevisionid ALIAS FOR $2;
+  _bomworkid INTEGER;
+  _indexid INTEGER;
+  _r RECORD;
+
+BEGIN
+
+--  Check on the temporary workspace
+--  PERFORM maintainBOMWorkspace();
+
+--  Grab a new index for this bomwork set
+  SELECT NEXTVAL('misc_index_seq') INTO _indexid;
+
+--  Step through all of the components of the passed pItemid
+  FOR _r IN SELECT bomitem.*,
+                   item_id,
+                   (itemuomtouomratio(bomitem_item_id, bomitem_uom_id, NULL)
+                              * bomitem_qtyfxd) AS qtyfxd,
+                   (itemuomtouomratio(bomitem_item_id, bomitem_uom_id, NULL)
+                              * bomitem_qtyper) AS qtyper,
+                   stdcost(item_id, bomitem_id) AS standardcost,
+                   actcost(item_id, bomitem_id) AS actualcost
+  FROM bomitem(pItemid, pRevisionid), item
+  WHERE (bomitem_item_id=item_id) LOOP
+
+--  Insert the component and bomitem parameters
+    SELECT NEXTVAL('bomwork_bomwork_id_seq') INTO _bomworkid;
+    INSERT INTO bomwork
+    ( bomwork_id, bomwork_set_id, bomwork_parent_id, bomwork_level,
+      bomwork_parent_seqnumber, bomwork_seqnumber,
+      bomwork_item_id, bomwork_createwo, bomwork_qtyreq,
+      bomwork_qtyfxd, bomwork_qtyper, bomwork_scrap, bomwork_issuemethod,
+      bomwork_effective, bomwork_expires,
+      bomwork_stdunitcost, bomwork_actunitcost )
+    VALUES
+    ( _bomworkid, _indexid, -1, 1,
+      0, _r.bomitem_seqnumber,
+      _r.item_id, _r.bomitem_createwo, (_r.qtyfxd + _r.qtyper),
+      _r.qtyfxd, _r.qtyper, _r.bomitem_scrap, _r.bomitem_issuemethod,
+      _r.bomitem_effective, _r.bomitem_expires,
+      _r.standardcost, _r.actualcost,
+      _r.bomitem_char_id, _r.bomitem_value, _r.bomitem_notes, _r.bomitem_ref,
+      _r.bomitem_id, _r.bomitem_ecn );
+
+--  Explode the components of the current component
+    PERFORM explodeBOM(_r.item_id, _bomworkid, 1);
+
+  END LOOP;
+
+--  Return a key to the result
+  RETURN _indexid;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION summarizedBOM(INTEGER, INTEGER, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemid ALIAS FOR $1;
+  pExpired ALIAS FOR $2;
+  pFuture ALIAS FOR $3;
+  _revid INTEGER;
+
+BEGIN
+
+  SELECT getActiveRevId('BOM',pItemid) INTO _revid;
+  
+  RETURN summarizedBOM(pItemid, _revid, pExpired, pFuture);
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION summarizedBOM(INTEGER, INTEGER, INTEGER, INTEGER) RETURNS SETOF bomdata AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemid ALIAS FOR $1;
+  pRevisionid ALIAS FOR $2;
+  pExpiredDays  INTEGER := COALESCE($3, 0);
+  pFutureDays   INTEGER := COALESCE($4, 0);
+  _row bomdata%ROWTYPE;
+  _bomworksetid INTEGER;
+  _x RECORD;
+  _check CHAR(1);
+  _inactive BOOLEAN := FALSE;
+  _batchsize NUMERIC;
+
+BEGIN
+
+  IF (pRevisionid != -1) THEN
+    --Is this a deactivated revision?
+    SELECT rev_status INTO _check
+    FROM rev
+    WHERE ((rev_id=pRevisionid)
+    AND (rev_status='I'));
+    IF (FOUND) THEN
+      _inactive := TRUE;
+    END IF;
+  END IF;
+  -- Get the batch quantity
+  SELECT COALESCE( (
+    SELECT bomhead_batchsize
+    FROM bomhead
+    WHERE ((bomhead_item_id=pItemId)
+    AND (bomhead_rev_id=pRevisionid))),1) INTO _batchsize;
+  IF NOT (_inactive) THEN
+
+    --We can explode this out based on current data
+    SELECT indentedBOM(pItemid, pRevisionid) INTO _bomworksetid;  
+
+    FOR _x IN
+       SELECT item_number, uom_name,
+               item_descrip1, item_descrip2,
+               (item_descrip1 || ' ' || item_descrip2) AS itemdescription,
+               SUM(bomwork_qtyreq) AS qtyreq,
+               SUM(bomwork_qtyfxd * (1 + bomwork_scrap)) AS qtyfxd,
+               SUM(bomwork_qtyper * (1 + bomwork_scrap)) AS qtyper,
+       MAX(bomwork_actunitcost) AS actunitcost,
+       MAX(bomwork_stdunitcost) AS stdunitcost,
+       CASE WHEN item_type NOT IN ('R','T') THEN
+         SUM(bomwork_actunitcost * bomwork_qtyreq)
+       ELSE 0 END AS actextendedcost,
+       CASE WHEN item_type NOT IN ('R','T') THEN
+         SUM(bomwork_stdunitcost * bomwork_qtyreq)
+       ELSE 0 END AS stdextendedcost,
+       bomwork_effective,
+       bomwork_expires,
+       bomwork_effective > CURRENT_DATE AS future,
+       bomwork_expires  <= CURRENT_DATE AS expired
+       FROM ( SELECT item_number, item_type, uom_name,
+                     item_descrip1, item_descrip2,
+                     bomwork_qtyreq, bomwork_qtyfxd,
+                     bomwork_qtyper, bomwork_scrap,
+                     bomwork_actunitcost, bomwork_stdunitcost,
+                     CASE WHEN (bomwork_effective > CURRENT_DATE) THEN (CURRENT_DATE + 1)
+                          ELSE CURRENT_DATE END AS bomwork_effective,
+                     CASE WHEN (bomwork_expires <= CURRENT_DATE) THEN (CURRENT_DATE - 1)
+                          ELSE (CURRENT_DATE + 1) END AS bomwork_expires
+                     FROM bomwork, item, uom 
+                     WHERE ( (bomwork_item_id=item_id)
+                       AND (item_inv_uom_id=uom_id)
+                       AND (bomwork_set_id=_bomworksetid) )
+                       AND (bomwork_expires > (CURRENT_DATE - pExpiredDays))
+                       AND (bomwork_effective <= (CURRENT_DATE + pFutureDays)) ) AS data
+       GROUP BY item_number, uom_name, item_type,
+                item_descrip1, item_descrip2,
+                bomwork_effective, bomwork_expires
+       ORDER BY item_number
+    LOOP
+        _row.bomdata_item_number := _x.item_number;
+        _row.bomdata_uom_name := _x.uom_name;
+        _row.bomdata_item_descrip1 := _x.item_descrip1;
+        _row.bomdata_item_descrip2 := _x.item_descrip2;
+        _row.bomdata_itemdescription := _x.itemdescription;
+        _row.bomdata_qtyreq := _x.qtyreq;
+        _row.bomdata_qtyfxd := _x.qtyfxd;
+        _row.bomdata_qtyper := _x.qtyper;
+        _row.bomdata_actunitcost := _x.actunitcost;
+        _row.bomdata_stdunitcost := _x.stdunitcost;
+        _row.bomdata_actextendedcost := _x.actextendedcost;
+        _row.bomdata_stdextendedcost := _x.stdextendedcost;
+        _row.bomdata_effective := _x.bomwork_effective;
+        _row.bomdata_expires := _x.bomwork_expires;
+        _row.bomdata_future := _x.future;
+        _row.bomdata_expired := _x.expired;
+        RETURN NEXT _row;
+    END LOOP;
+    
+    PERFORM deleteBOMWorkset(_bomworksetid);
+
+  ELSE
+   
+-- Use historical snapshot for inactive revisions
+    FOR _x IN
+       SELECT item_number, uom_name,
+               item_descrip1, item_descrip2,
+               (item_descrip1 || ' ' || item_descrip2) AS itemdescription,
+               SUM(bomhist_qtyreq * (1 + bomhist_scrap)) AS qtyreq,
+               SUM(bomhist_qtyfxd * (1 + bomhist_scrap)) AS qtyfxd,
+               SUM(bomhist_qtyper * (1 + bomhist_scrap)) AS qtyper,
+       MAX(bomhist_actunitcost) AS actunitcost,
+       MAX(bomhist_stdunitcost) AS stdunitcost,
+       CASE WHEN item_type NOT IN ('R','T') THEN
+         MAX(bomhist_actunitcost) * SUM((bomhist_qtyfxd/_batchsize + bomhist_qtyper) * (1 + bomhist_scrap))
+       ELSE 0 END AS actextendedcost,
+       CASE WHEN item_type NOT IN ('R','T') THEN
+         MAX(bomhist_stdunitcost) * SUM((bomhist_qtyfxd/_batchsize + bomhist_qtyper) * (1 + bomhist_scrap)) 
+       ELSE 0 END AS stdextendedcost
+       FROM bomhist, item, uom 
+       WHERE ( (bomhist_item_id=item_id)
+       AND (item_inv_uom_id=uom_id)
+       AND (bomhist_rev_id=pRevisionid) )
+       AND (bomhist_expires > (CURRENT_DATE - pExpiredDays))
+       AND (bomhist_effective <= (CURRENT_DATE + pFutureDays))
+       GROUP BY item_number, uom_name, item_type,
+                item_descrip1, item_descrip2
+       ORDER BY item_number
+    LOOP
+        _row.bomdata_item_number := _x.item_number;
+        _row.bomdata_uom_name := _x.uom_name;
+        _row.bomdata_item_descrip1 := _x.item_descrip1;
+        _row.bomdata_item_descrip2 := _x.item_descrip2;
+        _row.bomdata_itemdescription := _x.itemdescription;
+        _row.bomdata_qtyreq := _x.qtyreq;
+        _row.bomdata_qtyfxd := _x.qtyfxd;
+        _row.bomdata_qtyper := _x.qtyper;
+        _row.bomdata_actunitcost := _x.actunitcost;
+        _row.bomdata_stdunitcost := _x.stdunitcost;
+        _row.bomdata_actextendedcost := _x.actextendedcost;
+        _row.bomdata_stdextendedcost := _x.stdextendedcost;
+        RETURN NEXT _row;
+    END LOOP;
+
+  END IF;
+
+  RETURN;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/summarizetransactions.sql b/foundation-database/public/functions/summarizetransactions.sql
new file mode 100644 (file)
index 0000000..be70b05
--- /dev/null
@@ -0,0 +1,91 @@
+CREATE OR REPLACE FUNCTION summarizeTransactions(INTEGER, DATE, DATE) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  pStartDate ALIAS FOR $2;
+  pEndDate ALIAS FOR $3;
+  _startDate DATE;
+  _endDate DATE;
+  _invhist RECORD;
+  _itemuom TEXT;
+  _transCounter INTEGER;
+  _itemlocSeries INTEGER;
+
+BEGIN
+
+--  Cache the uom_name
+  SELECT uom_name INTO _itemuom
+  FROM itemsite, item, uom
+  WHERE ((itemsite_item_id=item_id)
+    AND (item_inv_uom_id=uom_id)
+    AND (itemsite_id=pItemsiteid));
+
+--  Can't summarize into the future...
+  IF (pEndDate > CURRENT_DATE) THEN
+    _endDate := CURRENT_DATE;
+  ELSE
+    _endDate := pEndDate;
+  END IF;
+
+--  Verify date bounds
+  IF (pStartDate > pEndDate) THEN
+    _startDate := pEndDate;
+  ELSE
+    _startDate := pStartDate;
+  END IF;
+
+--  Verify that history is not referenced elsewhere
+  SELECT invhist_id INTO _transCounter
+  FROM invhist JOIN womatlpost ON (womatlpost_invhist_id=invhist_id)
+  WHERE ((invhist_itemsite_id=pItemsiteid)
+    AND (invhist_transdate::DATE BETWEEN _startDate AND _endDate))
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN 0;
+  END IF;
+
+  SELECT invhist_id INTO _transCounter
+  FROM invhist JOIN shipitem ON (shipitem_invhist_id=invhist_id)
+  WHERE ((invhist_itemsite_id=pItemsiteid)
+    AND (invhist_transdate::DATE BETWEEN _startDate AND _endDate))
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN 0;
+  END IF;
+
+  _transCounter := 0;
+  _itemlocSeries := NEXTVAL('itemloc_series_seq');
+
+  FOR _invhist IN SELECT invhist_transtype, invhist_costmethod, SUM(invhist_invqty) AS qty
+                  FROM invhist
+                  WHERE ((invhist_itemsite_id=pItemsiteid)
+                   AND (invhist_transdate::DATE BETWEEN _startDate AND _endDate))
+                  GROUP BY invhist_transtype, invhist_costmethod LOOP
+
+    DELETE FROM invhist
+    WHERE ((invhist_transdate::DATE BETWEEN _startDate AND _endDate)
+     AND (invhist_transtype=_invhist.invhist_transtype)
+     AND (invhist_itemsite_id=pItemsiteid));
+
+    INSERT INTO invhist
+    ( invhist_itemsite_id, invhist_transdate, invhist_transtype,
+      invhist_invqty, invhist_qoh_before, invhist_qoh_after,
+      invhist_invuom, invhist_user, invhist_ordnumber,
+      invhist_costmethod, invhist_value_before, invhist_value_after,
+      invhist_series )
+    VALUES
+    ( pItemsiteid, _endDate, _invhist.invhist_transtype,
+      _invhist.qty, 0, 0,
+      _itemuom, getEffectiveXtUser(), 'Summary',
+      _invhist.invhist_costmethod, 0, 0,
+      _itemlocSeries );
+
+    _transCounter := (_transCounter + 1);
+
+  END LOOP;
+
+  RETURN _transCounter;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/summdemand.sql b/foundation-database/public/functions/summdemand.sql
new file mode 100644 (file)
index 0000000..479423a
--- /dev/null
@@ -0,0 +1,43 @@
+CREATE OR REPLACE FUNCTION summDemand(INTEGER, DATE, DATE) RETURNS NUMERIC AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  pStartDate ALIAS FOR $2;
+  pEndDate ALIAS FOR $3;
+  _value NUMERIC;
+
+BEGIN
+
+  SELECT SUM(wo_qtyord - wo_qtyrcv) INTO _value
+  FROM wo
+  WHERE ( (wo_itemsite_id=pItemsiteid)
+   AND (wo_status IN (''R'', ''I''))
+   AND (wo_startdate::DATE BETWEEN pStartDate AND pEndDate) );
+
+  IF (_value IS NULL) THEN
+    _value := 0;
+  END IF;
+
+  RETURN _value;
+
+END;
+' LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION summDemand(INTEGER, INTEGER) RETURNS NUMERIC AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  pCalitemid ALIAS FOR $2;
+  _value NUMERIC;
+
+BEGIN
+
+  SELECT summDemand(pItemsiteid, findPeriodStart(pCalitemid), findPeriodEnd(pCalitemid)) INTO _value;
+
+  RETURN _value;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/summprod.sql b/foundation-database/public/functions/summprod.sql
new file mode 100644 (file)
index 0000000..fcad159
--- /dev/null
@@ -0,0 +1,43 @@
+CREATE OR REPLACE FUNCTION summProd(INTEGER, DATE, DATE) RETURNS NUMERIC AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  pStartDate ALIAS FOR $2;
+  pEndDate ALIAS FOR $3;
+  _value NUMERIC;
+
+BEGIN
+
+  SELECT SUM(invhist_invqty) INTO _value
+  FROM invhist
+  WHERE ( (invhist_itemsite_id=pItemsiteid)
+   AND (invhist_transtype IN (''RM'', ''RB''))
+   AND (invhist_transdate::DATE BETWEEN pStartDate AND pEndDate) );
+
+  IF (_value IS NULL) THEN
+    _value := 0;
+  END IF;
+
+  RETURN _value;
+
+END;
+' LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION summProd(INTEGER, INTEGER) RETURNS NUMERIC AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  pCalitemid ALIAS FOR $2;
+  _value NUMERIC;
+
+BEGIN
+
+  SELECT summProd(pItemsiteid, findPeriodStart(pCalitemid), findPeriodEnd(pCalitemid)) INTO _value;
+
+  RETURN _value;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/summtransa.sql b/foundation-database/public/functions/summtransa.sql
new file mode 100644 (file)
index 0000000..2e07772
--- /dev/null
@@ -0,0 +1,43 @@
+CREATE OR REPLACE FUNCTION summTransA (INTEGER, DATE, DATE) RETURNS NUMERIC AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  pStartDate ALIAS FOR $2;
+  pEndDate ALIAS FOR $3;
+  _value NUMERIC;
+
+BEGIN
+
+  SELECT SUM(invhist_invqty) INTO _value
+  FROM invhist
+  WHERE ((invhist_transdate::DATE BETWEEN pStartDate AND pEndDate)
+   AND (invhist_transtype IN (''AD'', ''CC''))
+   AND (invhist_itemsite_id=pItemsiteid));
+
+  IF (_value IS NULL) THEN
+    _value := 0;
+  END IF;
+
+  RETURN _value;
+
+END;
+' LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION summTransA(INTEGER, INTEGER) RETURNS NUMERIC AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  pCalitemid ALIAS FOR $2;
+  _value NUMERIC;
+
+BEGIN
+
+  SELECT summTransA(pItemsiteid, findPeriodStart(pCalitemid), findPeriodEnd(pCalitemid)) INTO _value;
+
+  RETURN _value;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/summtransc.sql b/foundation-database/public/functions/summtransc.sql
new file mode 100644 (file)
index 0000000..defa51a
--- /dev/null
@@ -0,0 +1,43 @@
+CREATE OR REPLACE FUNCTION summTransC (INTEGER, DATE, DATE) RETURNS NUMERIC AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  pStartDate ALIAS FOR $2;
+  pEndDAte ALIAS FOR $3;
+  _value NUMERIC;
+
+BEGIN
+
+  SELECT SUM(invhist_invqty) INTO _value
+  FROM invhist
+  WHERE ((invhist_transdate::DATE BETWEEN pStartDate AND pEndDate)
+   AND (invhist_transtype IN (''SI''))
+   AND (invhist_itemsite_id=pItemsiteid));
+
+  IF (_value IS NULL) THEN
+    _value := 0;
+  END IF;
+
+  RETURN _value;
+
+END;
+' LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION summTransC(INTEGER, INTEGER) RETURNS NUMERIC AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  pCalitemid ALIAS FOR $2;
+  _value NUMERIC;
+
+BEGIN
+
+  SELECT summTransC(pItemsiteid, findPeriodStart(pCalitemid), findPeriodEnd(pCalitemid)) INTO _value;
+
+  RETURN _value;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/summtransi.sql b/foundation-database/public/functions/summtransi.sql
new file mode 100644 (file)
index 0000000..5161bfc
--- /dev/null
@@ -0,0 +1,43 @@
+CREATE OR REPLACE FUNCTION summTransI (INTEGER, DATE, DATE) RETURNS NUMERIC AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  pStartDate ALIAS FOR $2;
+  pEndDate ALIAS FOR $3;
+  _value NUMERIC;
+
+BEGIN
+
+  SELECT SUM(invhist_invqty) INTO _value
+  FROM invhist
+  WHERE ((invhist_transdate::DATE BETWEEN pStartDate AND pEndDate)
+   AND (invhist_transtype IN (''IM'', ''IC''))
+   AND (invhist_itemsite_id=pItemsiteid));
+
+  IF (_value IS NULL) THEN
+    _value := 0;
+  END IF;
+
+  RETURN _value;
+
+END;
+' LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION summTransI(INTEGER, INTEGER) RETURNS NUMERIC AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  pCalitemid ALIAS FOR $2;
+  _value NUMERIC;
+
+BEGIN
+
+  SELECT summTransI(pItemsiteid, findPeriodStart(pCalitemid), findPeriodEnd(pCalitemid)) INTO _value;
+
+  RETURN _value;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/summtransr.sql b/foundation-database/public/functions/summtransr.sql
new file mode 100644 (file)
index 0000000..d2d6c76
--- /dev/null
@@ -0,0 +1,43 @@
+CREATE OR REPLACE FUNCTION summTransR (INTEGER, DATE, DATE) RETURNS NUMERIC AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  pStartDate ALIAS FOR $2;
+  pEndDate ALIAS FOR $3;
+  _value NUMERIC;
+
+BEGIN
+
+  SELECT SUM(invhist_invqty) INTO _value
+  FROM invhist
+  WHERE ((invhist_transdate::DATE BETWEEN pStartDate AND pEndDate)
+   AND (invhist_transtype IN (''RM'', ''RP'', ''RX''))
+   AND (invhist_itemsite_id=pItemsiteid) );
+
+  IF (_value IS NULL) THEN
+    _value := 0;
+  END IF;
+
+  RETURN _value;
+
+END;
+' LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION summTransR(INTEGER, INTEGER) RETURNS NUMERIC AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  pCalitemid ALIAS FOR $2;
+  _value NUMERIC;
+
+BEGIN
+
+  SELECT summTransR(pItemsiteid, findPeriodStart(pCalitemid), findPeriodEnd(pCalitemid)) INTO _value;
+
+  RETURN _value;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/summtranss.sql b/foundation-database/public/functions/summtranss.sql
new file mode 100644 (file)
index 0000000..ba7fb64
--- /dev/null
@@ -0,0 +1,46 @@
+CREATE OR REPLACE FUNCTION summTransS (pItemsiteid INTEGER,
+                                       pStartDate DATE,
+                                       pEndDate DATE) RETURNS NUMERIC AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _value NUMERIC;
+
+BEGIN
+
+  SELECT SUM( CASE WHEN (invhist_transtype = 'RS') THEN (invhist_invqty * -1)
+                   ELSE (invhist_invqty)
+              END ) INTO _value
+  FROM invhist
+  WHERE ( (invhist_transdate::DATE BETWEEN pStartDate AND pEndDate)
+   AND (invhist_transtype IN ('SC', 'SH', 'SV', 'RS'))
+   AND (invhist_ordtype != 'TO')
+   AND (invhist_itemsite_id=pItemsiteid) );
+
+  IF (_value IS NULL) THEN
+    _value := 0;
+  END IF;
+
+  RETURN _value;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION summTransS(pItemsiteid INTEGER,
+                                      pCalitemid INTEGER) RETURNS NUMERIC AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  pCalitemid ALIAS FOR $2;
+  _value NUMERIC;
+
+BEGIN
+
+  SELECT summTransS(pItemsiteid, findPeriodStart(pCalitemid), findPeriodEnd(pCalitemid)) INTO _value;
+
+  RETURN _value;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/summtranst.sql b/foundation-database/public/functions/summtranst.sql
new file mode 100644 (file)
index 0000000..02951a5
--- /dev/null
@@ -0,0 +1,45 @@
+CREATE OR REPLACE FUNCTION summTransT (INTEGER, DATE, DATE) RETURNS NUMERIC AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  pStartDate ALIAS FOR $2;
+  pEndDate ALIAS FOR $3;
+  _value NUMERIC;
+
+BEGIN
+
+  SELECT SUM( CASE WHEN (invhist_transtype = 'TS') THEN (invhist_invqty * -1)
+                   ELSE (invhist_invqty)
+              END ) INTO _value
+  FROM invhist
+  WHERE ( (invhist_transdate::DATE BETWEEN pStartDate AND pEndDate)
+   AND (invhist_transtype IN ('TS', 'TR', 'TW'))
+   AND (invhist_itemsite_id=pItemsiteid) );
+
+  IF (_value IS NULL) THEN
+    _value := 0;
+  END IF;
+
+  RETURN _value;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION summTransT(INTEGER, INTEGER) RETURNS NUMERIC AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  pCalitemid ALIAS FOR $2;
+  _value NUMERIC;
+
+BEGIN
+
+  SELECT summTransT(pItemsiteid, findPeriodStart(pCalitemid), findPeriodEnd(pCalitemid)) INTO _value;
+
+  RETURN _value;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/taxassignments.sql b/foundation-database/public/functions/taxassignments.sql
new file mode 100644 (file)
index 0000000..b93be0b
--- /dev/null
@@ -0,0 +1,92 @@
+
+CREATE OR REPLACE FUNCTION taxassignments(integer, integer)
+  RETURNS SETOF taxassign AS
+$BODY$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pTaxZoneId ALIAS FOR $1;
+  pTaxTypeId ALIAS FOR $2;
+  _row taxassign%ROWTYPE;
+  _qry text;
+  _x RECORD;
+  _y RECORD;
+  _z RECORD;
+
+BEGIN
+  _qry = 'SELECT DISTINCT COALESCE(taxass_taxzone_id, -1) AS taxass_taxzone_id, COALESCE(taxass_taxtype_id, -1) AS taxass_taxtype_id, ';
+  _qry = _qry || 'taxzone_code, taxtype_name FROM taxass LEFT OUTER JOIN taxzone ON (taxass_taxzone_id=taxzone_id) ';
+  _qry = _qry || 'LEFT OUTER JOIN taxtype ON (taxass_taxtype_id=taxtype_id) ';
+
+  IF ((pTaxZoneId > 0) OR (pTaxTypeId > 0)) THEN
+    _qry := _qry || ' WHERE ';
+    IF (pTaxZoneId > 0) THEN
+      _qry := _qry || ' (taxass_taxzone_id = ' || pTaxZoneId ||')';
+      IF (pTaxTypeId > 0) THEN
+        _qry := _qry || ' AND ';
+      END IF;
+    END IF;
+    IF (pTaxTypeId > 0) THEN
+      _qry := _qry || ' (taxass_taxtype_id = ' || pTaxTypeId || ')';
+    END IF;
+  END IF;
+
+  --This first query gets all the distinct tax zone and type groupings as if it were its own table.
+  --This allows us to have a level 0 record as pictured in Tax Assignments window that code assignements will
+  --Subordinate to.
+  FOR _x IN  EXECUTE _qry
+  LOOP
+    --Map values to _row here
+    _row.taxassign_taxzone_id = _x.taxass_taxzone_id;
+    _row.taxassign_taxtype_id = _x.taxass_taxtype_id;
+    _row.taxassign_level = 0;
+    _row.taxassign_zone_code = _x.taxzone_code;
+    _row.taxassign_type_descrip = _x.taxtype_name;
+    _row.taxassign_taxclass_code = '';
+    _row.taxassign_taxclass_sequence = NULL;
+    RETURN NEXT _row; --so we get a level tax zone/type 0 record.
+  
+    -- Now get all the tax code assignments that belong to this Zone and Type pair
+    FOR _y IN
+      SELECT taxass_id, COALESCE(taxzone_id, -1) AS taxzone_id, tax_id,
+      tax_code, tax_descrip, COALESCE(taxtype_id, -1) AS taxtype_id, taxzone_code, 
+      taxtype_descrip, taxclass_code, 
+      COALESCE(taxclass_sequence, 0) AS taxclass_sequence
+      FROM taxass JOIN tax 
+         LEFT OUTER JOIN taxclass ON (tax_taxclass_id = taxclass_id)
+      ON (taxass_tax_id = tax_id)
+      LEFT OUTER JOIN taxzone ON (taxass_taxzone_id = taxzone_id)
+      LEFT OUTER JOIN taxtype ON (taxass_taxtype_id = taxtype_id)
+      WHERE COALESCE(taxass_taxzone_id, -1) = _x.taxass_taxzone_id
+      AND   COALESCE(taxass_taxtype_id, -1) = _x.taxass_taxtype_id
+    LOOP
+      --Map results to _row
+      _row.taxassign_taxzone_id = _y.taxzone_id;
+      _row.taxassign_taxtype_id = _y.taxtype_id;
+      _row.taxassign_level = 1;
+      _row.taxassign_zone_code = _y.tax_code;
+      _row.taxassign_type_descrip = _y.tax_descrip;
+      _row.taxassign_taxclass_code = _y.taxclass_code;
+      _row.taxassign_taxclass_sequence = _y.taxclass_sequence;
+      RETURN NEXT _row; --to get code detail record;
+      
+      FOR _z IN SELECT * FROM getsubtax(_y.tax_id, 1) --a new recursive function described above
+      LOOP
+        --Map results to _row
+        _row.taxassign_taxzone_id = _y.taxzone_id;
+        _row.taxassign_taxtype_id = _y.taxtype_id;
+        _row.taxassign_level = _z.subtax_taxcode_level;
+        _row.taxassign_zone_code = _z.subtax_taxcode_code;
+        _row.taxassign_type_descrip = _z.subtax_taxcode_descrip;
+        _row.taxassign_taxclass_code = _y.taxclass_code;
+        _row.taxassign_taxclass_sequence = _y.taxclass_sequence;
+        RETURN NEXT _row;
+      END  LOOP;
+
+    END LOOP;
+
+  END LOOP;
+
+END;
+$BODY$
+  LANGUAGE 'plpgsql' VOLATILE;
diff --git a/foundation-database/public/functions/thawaccountingperiod.sql b/foundation-database/public/functions/thawaccountingperiod.sql
new file mode 100644 (file)
index 0000000..0ab3521
--- /dev/null
@@ -0,0 +1,44 @@
+
+CREATE OR REPLACE FUNCTION thawAccountingPeriod(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pPeriodid ALIAS FOR $1;
+  _r RECORD;
+
+BEGIN
+
+--  Check to make sure that the period is frozen
+  IF ( ( SELECT (NOT period_freeze)
+         FROM period
+         WHERE (period_id=pPeriodid) ) ) THEN
+    RETURN -2;
+  END IF;
+
+--  Check to make sure that the period is not closed
+  IF ( ( SELECT (period_closed)
+         FROM period
+         WHERE (period_id=pPeriodid) ) ) THEN
+    RETURN -1;
+  END IF;
+
+--  Reset the period_freeze flag
+  UPDATE period
+  SET period_freeze=FALSE
+  WHERE (period_id=pPeriodid);
+
+--  Post any unposted G/L Transactions into the period
+  FOR _r IN SELECT DISTINCT gltrans_sequence
+            FROM gltrans, accnt, period
+            WHERE ( (gltrans_accnt_id=accnt_id)
+             AND (NOT gltrans_posted)
+             AND (gltrans_date BETWEEN period_start AND period_end)
+             AND (period_id=pPeriodid) ) LOOP
+    PERFORM postIntoTrialBalance(_r.gltrans_sequence);
+  END LOOP;
+
+  RETURN pPeriodid;
+
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/thawitemsite.sql b/foundation-database/public/functions/thawitemsite.sql
new file mode 100644 (file)
index 0000000..86aa2cf
--- /dev/null
@@ -0,0 +1,182 @@
+CREATE OR REPLACE FUNCTION thawItemSite(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  _qoh            NUMERIC := 0;
+  _netable_qoh    NUMERIC := 0;
+  _nonnetable_qoh NUMERIC := 0;
+  _value          NUMERIC := 0;
+  _itemlocid INTEGER;
+  _itemloc RECORD;
+  _invhist RECORD;
+  _coarse RECORD;
+  _fine RECORD;
+
+BEGIN
+
+  IF ( SELECT itemsite_freeze
+       FROM itemsite
+       WHERE (itemsite_id=pItemsiteid) ) THEN
+
+    SELECT invhist_id INTO _invhist
+    FROM invhist
+    WHERE ( (invhist_itemsite_id=pItemsiteid)
+     AND (NOT invhist_posted) )
+    LIMIT 1;
+    IF (NOT FOUND) THEN
+      UPDATE itemsite
+      SET itemsite_freeze=FALSE
+      WHERE (itemsite_id=pItemsiteid);
+   END IF;
+
+--  Run through any invdetail if this itemsite is still MLC and/or Lot/Serial
+    IF ( SELECT ( (itemsite_loccntrl) OR
+                  (itemsite_controlmethod IN ('L', 'S')) )
+         FROM itemsite
+         WHERE (itemsite_id=pItemsiteid) ) THEN
+
+--  Grab all of the itemsite/location/lot/serial combinations
+--  that have unposted detail
+      FOR _coarse IN SELECT DISTINCT invdetail_location_id, invdetail_ls_id,
+                                     invdetail_expiration, invdetail_warrpurc
+                     FROM invhist, invdetail
+                     WHERE ( (invdetail_invhist_id=invhist_id)
+                      AND (NOT invhist_posted)
+                      AND (invhist_itemsite_id=pItemsiteid) )
+                     ORDER BY invdetail_location_id, invdetail_ls_id LOOP
+
+--  Cache the initial qty of the itemloc specified by the
+--  itemsite/location/lot/serial
+        SELECT itemloc_id, itemloc_qty, COALESCE(location_netable, TRUE) AS location_netable
+        INTO _itemloc
+        FROM itemloc LEFT OUTER JOIN location ON (location_id=itemloc_location_id)
+        WHERE ( (itemloc_itemsite_id=pItemsiteid)
+         AND (itemloc_location_id=_coarse.invdetail_location_id)
+         AND (COALESCE(itemloc_ls_id,-1)=COALESCE(_coarse.invdetail_ls_id,-1))
+         AND (COALESCE(itemloc_expiration,endOfTime())=COALESCE(_coarse.invdetail_expiration,endOfTime()))
+         AND (COALESCE(itemloc_warrpurc,endOfTime())=COALESCE(_coarse.invdetail_warrpurc,endOfTime())) );
+
+--  If the itemloc in question cannot be found, create it
+        IF (NOT FOUND) THEN
+          SELECT NEXTVAL('itemloc_itemloc_id_seq') INTO _itemlocid;
+          INSERT INTO itemloc
+          ( itemloc_id, itemloc_itemsite_id,
+            itemloc_location_id, itemloc_ls_id,
+            itemloc_qty, itemloc_expiration )
+          VALUES
+          ( _itemlocid, pItemsiteid,
+            _coarse.invdetail_location_id, _coarse.invdetail_ls_id,
+            0, endOfTime() );
+
+        _qoh := 0.0;
+        _netable_qoh := 0.0;
+        _nonnetable_qoh := 0.0;
+
+        ELSE
+          _itemlocid := _itemloc.itemloc_id;
+          _qoh := _itemloc.itemloc_qty;
+          IF (_itemloc.location_netable) THEN
+            _netable_qoh := _itemloc.itemloc_qty;
+          ELSE
+            _nonnetable_qoh := _itemloc.itemloc_qty;
+          END IF;
+        END IF;
+
+--  Now step through each unposted invdetail record for a given
+--  itemsite/location/lot/serial
+        FOR _fine IN SELECT invdetail_id, invdetail_qty
+                     FROM invhist, invdetail
+                     WHERE ( (invdetail_invhist_id=invhist_id)
+                      AND (NOT invhist_posted)
+                      AND (invhist_itemsite_id=pItemsiteid)
+                      AND (invdetail_location_id=_coarse.invdetail_location_id)
+                      AND (COALESCE(invdetail_ls_id,-1)=COALESCE(_coarse.invdetail_ls_id,-1))
+                      AND (COALESCE(invdetail_expiration,endOfTime())=COALESCE(_coarse.invdetail_expiration,endOfTime()))
+                      AND (COALESCE(invdetail_warrpurc,endOfTime())=COALESCE(_coarse.invdetail_warrpurc,endOfTime())) )
+                     ORDER BY invhist_transdate LOOP
+
+--  Update the running qoh fields in the detail record
+          UPDATE invdetail
+          SET invdetail_qty_before = _qoh,
+              invdetail_qty_after = (_qoh + invdetail_qty)
+          WHERE (invdetail_id=_fine.invdetail_id);
+
+--  Update the running qoh
+          _qoh = (_qoh + _fine.invdetail_qty);
+          IF (_itemloc.location_netable) THEN
+            _netable_qoh := (_netable_qoh + _fine.invdetail_qty);
+          ELSE
+            _nonnetable_qoh := (_nonnetable_qoh + _fine.invdetail_qty);
+          END IF;
+
+        END LOOP;
+
+--  If the running qoh end up at 0, delete the itemloc in question
+        IF (_qoh = 0) THEN
+          DELETE FROM itemloc
+          WHERE (itemloc_id=_itemlocid);
+
+--  Otherwise, update the itemloc in question with the resultant qty
+        ELSE
+          UPDATE itemloc
+          SET itemloc_qty=_qoh
+          WHERE (itemloc_id=_itemlocid);
+        END IF;
+
+      END LOOP;
+
+    END IF; 
+
+--  Cache the inital qoh of the itemsite
+    SELECT itemsite_qtyonhand, itemsite_value INTO _qoh, _value
+    FROM itemsite
+    WHERE (itemsite_id=pItemsiteid);
+
+--  We have to un-freeze the itemsite before update-ing its QOH
+--  so that that itemsite trigger won't block the QOH update.
+--  Also so the invhist trigger won't block the posted update.
+
+    UPDATE itemsite
+    SET itemsite_freeze=FALSE
+    WHERE (itemsite_id=pItemsiteid);
+
+    FOR _invhist IN SELECT invhist_id,
+                           invhist_qoh_before, invhist_qoh_after,
+                           invhist_value_before, invhist_value_after
+                      FROM invhist
+                     WHERE((invhist_itemsite_id=pItemsiteid)
+                       AND (NOT invhist_posted))
+                     ORDER BY invhist_transdate LOOP
+
+      UPDATE invhist
+      SET invhist_qoh_before = _qoh,
+          invhist_qoh_after = ( _qoh +
+                                _invhist.invhist_qoh_after -
+                                _invhist.invhist_qoh_before ),
+          invhist_value_before = _value,
+          invhist_value_after = ( _value +
+                                  _invhist.invhist_value_after -
+                                  _invhist.invhist_value_before ),
+          invhist_posted = TRUE
+      WHERE (invhist_id=_invhist.invhist_id);
+
+      _qoh := (_qoh + (_invhist.invhist_qoh_after - _invhist.invhist_qoh_before));
+      _value := (_value + (_invhist.invhist_value_after - _invhist.invhist_value_before));
+
+    END LOOP;
+
+-- _qoh can be used for the netable qoh because of the negative NN transactions
+    UPDATE itemsite
+       SET itemsite_qtyonhand = _qoh,
+           itemsite_nnqoh = _nonnetable_qoh,
+           itemsite_value = CASE WHEN ((itemsite_costmethod='A') AND (_value < 0.0)) THEN 0.0
+                                 ELSE _value END
+     WHERE(itemsite_id=pItemsiteid);
+
+  END IF;
+
+  RETURN pItemsiteid;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/todoitem.sql b/foundation-database/public/functions/todoitem.sql
new file mode 100644 (file)
index 0000000..f22e462
--- /dev/null
@@ -0,0 +1,39 @@
+CREATE OR REPLACE FUNCTION todoitem() RETURNS SETOF todoitem AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _row todoitem%ROWTYPE;
+  _priv TEXT;
+  _grant BOOLEAN;
+
+BEGIN
+  -- This query will give us the most permissive privilege the user has been granted
+  SELECT privilege, granted INTO _priv, _grant
+  FROM privgranted 
+  WHERE privilege IN ('MaintainAllToDoItems','ViewAllToDoItems','MaintainPersonalToDoItems','ViewPersonalToDoItems')
+  ORDER BY granted DESC, sequence
+  LIMIT 1;
+
+  -- If have an 'All' privilege return all results
+  IF (_priv ~ 'All' AND _grant) THEN
+    FOR _row IN 
+      SELECT * FROM todoitem
+    LOOP
+      RETURN NEXT _row;
+    END LOOP;
+  -- Otherwise if have any other grant, must be personal privilege.
+  ELSIF (_grant) THEN
+    FOR _row IN 
+      SELECT * FROM todoitem 
+      WHERE getEffectiveXtUser() IN (todoitem_owner_username, todoitem_username)
+    LOOP
+      RETURN NEXT _row;
+    END LOOP;
+  END IF;
+
+  RETURN;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+COMMENT ON FUNCTION todoitem() IS 'A table function that returns To Do Items results according to privilege settings.';
diff --git a/foundation-database/public/functions/todoitemmove.sql b/foundation-database/public/functions/todoitemmove.sql
new file mode 100644 (file)
index 0000000..398ced8
--- /dev/null
@@ -0,0 +1,38 @@
+
+CREATE OR REPLACE FUNCTION todoItemMove(INTEGER, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  ptodoItemId ALIAS FOR $1;
+  pHowFar     ALIAS FOR $2;   -- -1 moves toward front of list, +1 toward back
+  _howFar     INTEGER := pHowFar;
+  _username   TEXT;
+  _currseq    INTEGER;
+BEGIN
+  SELECT todoitem_username, todoitem_seq INTO _username, _currseq
+  FROM todoitem
+  WHERE todoitem_id = ptodoItemId;
+
+  IF NOT FOUND THEN
+    RETURN -1;
+  END IF;
+
+  IF (_currseq + pHowFar <= 0) THEN
+    _howFar = 1 - _currseq;   -- move to beginning
+  END IF;
+
+  UPDATE todoitem
+  SET todoitem_seq=todoitem_seq - _howFar
+  WHERE todoitem_seq >= _currseq + _howFar
+    AND todoitem_id != ptodoItemId
+    AND todoitem_username = _username
+    AND todoitem_status != 'C';
+
+  UPDATE todoitem
+  SET todoitem_seq=_currseq + _howFar
+  WHERE todoitem_id = ptodoItemId;
+
+  RETURN 0;
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/todoitemmovedown.sql b/foundation-database/public/functions/todoitemmovedown.sql
new file mode 100644 (file)
index 0000000..96aecd7
--- /dev/null
@@ -0,0 +1,11 @@
+
+CREATE OR REPLACE FUNCTION todoItemMoveDown(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  ptodoItemId ALIAS FOR $1;
+BEGIN
+  RETURN todoItemMove(ptodoItemId, 1);        -- move toward end of list
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/todoitemmoveup.sql b/foundation-database/public/functions/todoitemmoveup.sql
new file mode 100644 (file)
index 0000000..8dd2d40
--- /dev/null
@@ -0,0 +1,11 @@
+
+CREATE OR REPLACE FUNCTION todoItemMoveUp(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  ptodoItemId ALIAS FOR $1;
+BEGIN
+  RETURN todoItemMove(ptodoItemId, -1);       -- move toward front of list
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/togglebankreccleared.sql b/foundation-database/public/functions/togglebankreccleared.sql
new file mode 100644 (file)
index 0000000..f37c15c
--- /dev/null
@@ -0,0 +1,40 @@
+
+CREATE OR REPLACE FUNCTION toggleBankrecCleared(INTEGER, TEXT, INTEGER, NUMERIC, NUMERIC) RETURNS BOOLEAN AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pBankrecid ALIAS FOR $1;
+  pSource    ALIAS FOR $2;
+  pSourceid  ALIAS FOR $3;
+  pCurrrate  ALIAS FOR $4;
+  pAmount    ALIAS FOR $5;
+  _cleared BOOLEAN;
+  _r RECORD;
+
+BEGIN
+
+  SELECT bankrecitem_id, bankrecitem_cleared INTO _r
+    FROM bankrecitem
+   WHERE ( (bankrecitem_bankrec_id=pBankrecid)
+     AND   (bankrecitem_source=pSource)
+     AND   (bankrecitem_source_id=pSourceid) );
+  IF ( NOT FOUND ) THEN
+    _cleared := TRUE;
+    INSERT INTO bankrecitem
+    (bankrecitem_bankrec_id, bankrecitem_source,
+     bankrecitem_source_id, bankrecitem_cleared,
+     bankrecitem_curr_rate, bankrecitem_amount)
+    VALUES
+    (pBankrecid, pSource,
+     pSourceid, _cleared,
+     pCurrrate, pAmount);
+  ELSE
+    _cleared := FALSE;
+    DELETE FROM bankrecitem 
+    WHERE bankrecitem_id = _r.bankrecitem_id;
+  END IF;
+
+  RETURN _cleared;
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/togglebomitemcost.sql b/foundation-database/public/functions/togglebomitemcost.sql
new file mode 100644 (file)
index 0000000..dec6c85
--- /dev/null
@@ -0,0 +1,32 @@
+
+CREATE OR REPLACE FUNCTION toggleBomitemCost(INTEGER, BOOLEAN) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pBomitemid ALIAS FOR $1;
+  pEnabled   ALIAS FOR $2;
+
+BEGIN
+
+  IF (pEnabled) THEN
+    INSERT INTO bomitemcost
+    (bomitemcost_bomitem_id, bomitemcost_costelem_id,
+     bomitemcost_lowlevel, bomitemcost_stdcost,
+     bomitemcost_posted, bomitemcost_actcost,
+     bomitemcost_updated, bomitemcost_curr_id)
+    SELECT
+     bomitem_id, itemcost_costelem_id,
+     itemcost_lowlevel, itemcost_stdcost,
+     itemcost_posted, itemcost_actcost,
+     itemcost_updated, itemcost_curr_id
+    FROM bomitem JOIN itemcost ON (itemcost_item_id=bomitem_item_id)
+    WHERE (bomitem_id=pBomitemid);
+  ELSE
+    DELETE FROM bomitemcost
+    WHERE (bomitemcost_bomitem_id=pBomitemid);
+  END IF;
+
+  RETURN 0;
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/tonumeric.sql b/foundation-database/public/functions/tonumeric.sql
new file mode 100644 (file)
index 0000000..973a100
--- /dev/null
@@ -0,0 +1,19 @@
+
+CREATE OR REPLACE FUNCTION toNumeric(TEXT, NUMERIC) RETURNS NUMERIC IMMUTABLE AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pText ALIAS FOR $1;
+  pDefault ALIAS FOR $2;
+
+BEGIN
+
+  IF (isNumeric(pText)) THEN
+    RETURN TO_NUMBER(pText, ''999999999999'');
+  ELSE
+    RETURN pDefault;
+  END IF;
+
+END;
+' LANGUAGE plpgsql;
+
diff --git a/foundation-database/public/functions/transitwhs.sql b/foundation-database/public/functions/transitwhs.sql
new file mode 100644 (file)
index 0000000..c6b2f92
--- /dev/null
@@ -0,0 +1,82 @@
+CREATE OR REPLACE FUNCTION transitWhs() RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _id  INTEGER;
+
+BEGIN
+  _id = fetchMetricValue(''TransitWarehouse'');
+
+  IF (_id IS NOT NULL
+      AND EXISTS(SELECT warehous_id FROM whsinfo WHERE (warehous_id=_id)) ) THEN
+    RETURN _id;
+  END IF;
+
+  SELECT warehous_id INTO _id FROM whsinfo WHERE warehous_transit;
+
+  IF (NOT FOUND) THEN
+    _id := NEXTVAL(''warehous_warehous_id_seq'');
+
+    INSERT INTO whsinfo (
+      warehous_id,
+      warehous_code,
+      warehous_descrip,
+      --warehous_fob,
+      warehous_active,
+      --warehous_counttag_prefix,
+      --warehous_counttag_number,
+      --warehous_bol_prefix,
+      --warehous_bol_number,
+      warehous_shipping,
+      warehous_useslips,
+      warehous_usezones,
+      --warehous_aislesize,
+      --warehous_aislealpha,
+      --warehous_racksize,
+      --warehous_rackalpha,
+      --warehous_binsize,
+      --warehous_binalpha,
+      --warehous_locationsize,
+      --warehous_locationalpha,
+      warehous_enforcearbl,
+      warehous_default_accnt_id,
+      --warehous_shipping_commission,
+      --warehous_cntct_id,
+      --warehous_addr_id,
+      warehous_taxzone_id
+     ) VALUES (
+       _id,
+      ''TRANSIT'',
+      ''Intermediate Warehouse for Inter-Warehouse Transfers'',
+      --text,
+      TRUE,
+      --text, 
+      --integer, 
+      --text, 
+      --integer, 
+      TRUE,
+      FALSE,
+      FALSE,
+      --integer, 
+      --boolean, 
+      --integer, 
+      --boolean, 
+      --integer, 
+      --boolean, 
+      --integer, 
+      --boolean, 
+      FALSE,
+      fetchMetricValue(''UnassignedAccount''),
+      --numeric(8,4) default 0.00,
+      --integer, 
+      --integer, 
+      NULL
+    );
+  END IF;
+
+  PERFORM setMetric(''TransitWarehouse'', _id);
+
+  RETURN _id;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/transtype.sql b/foundation-database/public/functions/transtype.sql
new file mode 100644 (file)
index 0000000..f0c408e
--- /dev/null
@@ -0,0 +1,29 @@
+CREATE OR REPLACE FUNCTION transType(TEXT, INTEGER) RETURNS BOOLEAN AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pTransType ALIAS FOR $1;
+  pTargetType ALIAS FOR $2;
+
+BEGIN
+
+  IF (pTargetType = 255) THEN
+    RETURN TRUE;
+  ELSIF (pTargetType = 1) THEN
+    RETURN receipts(pTransType);
+  ELSIF (pTargetType = 2) THEN
+    RETURN issues(pTransType);
+  ELSIF (pTargetType = 4) THEN
+    RETURN shipments(pTransType);
+  ELSIF (pTargetType = 8) THEN
+    RETURN adjustments(pTransType);
+  ELSIF (pTargetType = 16) THEN
+    RETURN transfers(pTransType);
+  ELSIF (pTargetType = 32) THEN
+    RETURN scraps(pTransType);
+  ELSE
+    RETURN TRUE;
+  END IF;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/trylock.sql b/foundation-database/public/functions/trylock.sql
new file mode 100644 (file)
index 0000000..5902859
--- /dev/null
@@ -0,0 +1,25 @@
+CREATE OR REPLACE FUNCTION tryLock(integer, integer) RETURNS boolean AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pKey1 ALIAS FOR $1;
+  pKey2 ALIAS FOR $2;
+  _pid integer;
+BEGIN
+
+  /* The standard try lock ignores locks made by the current user in same
+     session.  Check for ANY lock on this id, whether by this user or not */
+  SELECT pid INTO _pid
+  FROM pg_locks
+  WHERE ((classid=pKey1)
+   AND (objid=pKey2)
+   AND (objsubid=2));
+
+  IF (FOUND) THEN
+    RETURN false;
+  ELSE
+    RETURN pg_try_advisory_lock(pKey1,pKey2);
+  END IF;
+   
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/undomerge.sql b/foundation-database/public/functions/undomerge.sql
new file mode 100644 (file)
index 0000000..0216f92
--- /dev/null
@@ -0,0 +1,49 @@
+CREATE OR REPLACE FUNCTION undomerge(TEXT, TEXT, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pSchema       ALIAS FOR $1;
+  pTable        ALIAS FOR $2;
+  pId           ALIAS FOR $3;
+  _qry          TEXT;
+  _r            RECORD;
+  _result       INTEGER;
+
+BEGIN
+  FOR _r IN
+    SELECT *
+      FROM mrgundo
+     WHERE mrgundo_base_schema = pSchema
+       AND mrgundo_base_table  = pTable
+       AND mrgundo_base_id     = pId
+       AND mrgundo_col IS NOT NULL  -- NULL mrgundo_col signals a row to delete on purge
+  LOOP
+    IF (_r.mrgundo_value IS NULL) THEN
+      _qry := 'UPDATE ' || quote_ident(_r.mrgundo_schema)  ||
+                    '.' || quote_ident(_r.mrgundo_table)   ||
+                ' SET ' || quote_ident(_r.mrgundo_col)     || '= NULL
+               WHERE (' || _r.mrgundo_pkey_col || '=' || _r.mrgundo_pkey_id || ');';
+    ELSE
+      _qry := 'UPDATE ' || quote_ident(_r.mrgundo_schema)  ||
+                    '.' || quote_ident(_r.mrgundo_table)   ||
+                ' SET ' || quote_ident(_r.mrgundo_col)     ||
+              '= CAST(' || quote_literal(_r.mrgundo_value) || ' AS '
+                        || quote_ident(_r.mrgundo_type)    || ')
+               WHERE (' || _r.mrgundo_pkey_col || '=' || _r.mrgundo_pkey_id || ');';
+    END IF;
+
+    EXECUTE _qry;
+  END LOOP;
+
+  DELETE FROM mrgundo
+   WHERE mrgundo_base_schema = pSchema
+     AND mrgundo_base_table  = pTable
+     AND mrgundo_base_id     = pId;
+
+  GET DIAGNOSTICS _result = ROW_COUNT;
+
+  RETURN _result;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/uomusedforitem.sql b/foundation-database/public/functions/uomusedforitem.sql
new file mode 100644 (file)
index 0000000..c11d207
--- /dev/null
@@ -0,0 +1,84 @@
+CREATE OR REPLACE FUNCTION uomusedforitem(INTEGER) RETURNS SETOF uom AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pitemid       ALIAS FOR $1;
+  _row          uom%ROWTYPE;
+BEGIN
+  FOR _row IN SELECT DISTINCT *
+              FROM uom
+              WHERE uom_id IN (
+                SELECT bomitem_uom_id AS uom_id 
+                FROM bomitem 
+                WHERE (bomitem_item_id=pitemid)
+                UNION 
+                SELECT cmitem_qty_uom_id 
+                FROM cmitem, itemsite 
+                WHERE ((cmitem_itemsite_id=itemsite_id)
+                   AND (itemsite_item_id=pitemid))
+                UNION 
+                SELECT cmitem_price_uom_id 
+                FROM cmitem, itemsite 
+                WHERE ((cmitem_itemsite_id=itemsite_id)
+                   AND (itemsite_item_id=pitemid))
+                UNION 
+                SELECT coitem_qty_uom_id 
+                FROM coitem, itemsite 
+                WHERE ((coitem_itemsite_id=itemsite_id)
+                   AND (itemsite_item_id=pitemid))
+                UNION 
+                SELECT coitem_price_uom_id 
+                FROM coitem, itemsite 
+                WHERE ((coitem_itemsite_id=itemsite_id)
+                   AND (itemsite_item_id=pitemid))
+                UNION 
+                SELECT invcitem_qty_uom_id 
+                FROM invcitem 
+                WHERE ((invcitem_item_id=pitemid))
+                UNION 
+                SELECT invcitem_price_uom_id 
+                FROM invcitem 
+                WHERE ((invcitem_item_id=pitemid))
+                UNION 
+                SELECT ipsitem_qty_uom_id 
+                FROM ipsiteminfo 
+                WHERE (ipsitem_item_id=pitemid)
+                UNION 
+                SELECT ipsitem_price_uom_id 
+                FROM ipsiteminfo 
+                WHERE (ipsitem_item_id=pitemid)
+                UNION 
+                SELECT quitem_qty_uom_id 
+                FROM quitem, itemsite 
+                WHERE ((quitem_itemsite_id=itemsite_id)
+                   AND (itemsite_item_id=pitemid))
+                UNION 
+                SELECT quitem_price_uom_id 
+                FROM quitem, itemsite 
+                WHERE ((quitem_itemsite_id=itemsite_id)
+                   AND (itemsite_item_id=pitemid))
+                UNION 
+                SELECT womatl_uom_id 
+                FROM womatl, itemsite 
+                WHERE ((womatl_itemsite_id=itemsite_id)
+                   AND (itemsite_item_id=pitemid))
+  ) LOOP
+    RETURN NEXT _row;
+  END LOOP;
+
+  IF (fetchmetricbool(''MultiWhs'')) THEN
+    FOR _row IN SELECT DISTINCT *
+                FROM uom
+                WHERE uom_id IN (
+                  SELECT rahist_uom_id 
+                  FROM rahist, itemsite 
+                  WHERE ((rahist_itemsite_id=itemsite_id)
+                     AND (itemsite_item_id=pitemid))
+    ) LOOP
+      RETURN NEXT _row;
+    END LOOP;
+  END IF;
+
+  RETURN;
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/updateabcclass.sql b/foundation-database/public/functions/updateabcclass.sql
new file mode 100644 (file)
index 0000000..d14404c
--- /dev/null
@@ -0,0 +1,246 @@
+CREATE OR REPLACE FUNCTION updateABCClass(TEXT, NUMERIC, NUMERIC, DATE, DATE) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pClassCodePattern ALIAS FOR $1;
+  pACutoff ALIAS FOR $2;
+  pBCutoff ALIAS FOR $3;
+  pStartDate ALIAS FOR $4;
+  pEndDate ALIAS FOR $5;
+  _result INTEGER;
+
+BEGIN
+
+  SELECT updateABCClass(pClassCodePattern, -1, pACutoff, pBCutoff, pStartDate, pEndDate) INTO _result;
+  RETURN _result;
+
+END;
+' LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION updateABCClass(TEXT, INTEGER, NUMERIC, NUMERIC, DATE, DATE) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pClassCodePattern ALIAS FOR $1;
+  pWarehousid ALIAS FOR $2;
+  pACutoff ALIAS FOR $3;
+  pBCutoff ALIAS FOR $4;
+  pStartDate ALIAS FOR $5;
+  pEndDate ALIAS FOR $6;
+  _updateCount INTEGER;
+  _totalValue NUMERIC;
+  _cumulativeValue NUMERIC;
+  _itemsite RECORD;
+
+BEGIN
+
+  SELECT COUNT(*) INTO _updateCount
+  FROM itemsite, item, classcode
+  WHERE ( (itemsite_item_id=item_id)
+   AND (item_classcode_id=classcode_id)
+   AND (itemsite_autoabcclass)
+   AND (classcode_code ~ pClassCodePattern)
+   AND ((itemsite_warehous_id=pWarehousid) OR (pWarehousid=-1)) );
+
+  IF (_updateCount IS NULL) THEN
+    RETURN 0;
+  ELSE
+
+    UPDATE itemsite
+    SET itemsite_abcclass=''T''
+    FROM item, classcode
+    WHERE ( (itemsite_item_id=item_id)
+     AND (item_classcode_id=classcode_id)
+     AND (itemsite_autoabcclass)
+     AND (classcode_code ~ pClassCodePattern)
+     AND ((itemsite_warehous_id=pWarehousid) OR (pWarehousid=-1)) );
+
+    SELECT SUM(ABS(invhist_qoh_before - invhist_qoh_after) * invhist_unitcost) INTO _totalValue
+    FROM invhist, itemsite, item, classcode
+    WHERE ( (invhist_itemsite_id=itemsite_id)
+     AND (itemsite_item_id=item_id)
+     AND (item_classcode_id=classcode_id)
+     AND (invhist_analyze)
+     AND (invhist_transtype ~ ''^[IR]'')
+     AND (itemsite_autoabcclass)
+     AND (classcode_code ~ pClassCodePattern)
+     AND (invhist_transdate::DATE BETWEEN pStartDate AND pEndDate)
+     AND ((itemsite_warehous_id=pWarehousid) OR (pWarehousid=-1)) );
+
+    IF ( (_totalValue IS NULL) OR (_totalValue = 0) ) THEN
+      UPDATE itemsite
+      SET itemsite_abcclass=''A''
+      WHERE (itemsite_abcclass=''T'');
+    ELSE
+
+      _cumulativeValue := 0;
+
+      FOR _itemsite IN SELECT itemsite_id, item_number,
+                              SUM(ABS(invhist_qoh_before - invhist_qoh_after) * invhist_unitcost) AS value
+                       FROM invhist, itemsite, item, classcode
+                       WHERE ( (invhist_itemsite_id=itemsite_id)
+                        AND (itemsite_item_id=item_id)
+                        AND (item_classcode_id=classcode_id)
+                        AND (invhist_analyze)
+                        AND (invhist_transtype ~ ''^[IR]'')
+                        AND (itemsite_autoabcclass)
+                        AND (classcode_code ~ pClassCodePattern)
+                        AND (invhist_transdate::DATE BETWEEN pStartDate AND pEndDate)
+                        AND ((itemsite_warehous_id=pWarehousid) OR (pWarehousid=-1)) )
+                       GROUP BY itemsite_id, item_number
+                       ORDER BY value DESC LOOP
+
+        IF (_itemsite.value IS NOT NULL) THEN
+          _cumulativeValue := _cumulativeValue + _itemsite.value;
+        END IF;
+
+        IF ((_cumulativeValue / _totalValue) <= pACutoff) THEN
+          UPDATE itemsite
+          SET itemsite_abcclass=''A''
+          WHERE (itemsite_id=_itemsite.itemsite_id);
+        ELSE
+          IF ((_cumulativeValue / _totalValue) <= pBCutoff) THEN
+            UPDATE itemsite
+            SET itemsite_abcclass=''B''
+            WHERE (itemsite_id=_itemsite.itemsite_id);
+          ELSE
+            UPDATE itemsite
+            SET itemsite_abcclass=''C''
+            WHERE (itemsite_id=_itemsite.itemsite_id);
+          END IF;
+        END IF;
+
+      END LOOP;
+
+      UPDATE itemsite
+      SET itemsite_abcclass=''C''
+      WHERE (itemsite_abcclass=''T'');
+    END IF;
+
+  END IF;
+
+  RETURN _updateCount;
+
+END;
+' LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION updateABCClass(INTEGER, NUMERIC, NUMERIC, DATE, DATE) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pClasscodeid ALIAS FOR $1;
+  pACutoff ALIAS FOR $2;
+  pBCutoff ALIAS FOR $3;
+  pStartDate ALIAS FOR $4;
+  pEndDate ALIAS FOR $5;
+  _result INTEGER;
+
+BEGIN
+
+  SELECT updateABCClass(pClassCodeid, -1, pACutoff, pBCutoff, pStartDate, pEndDate) INTO _result;
+  RETURN _result;
+
+END;
+' LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION updateABCClass(INTEGER, INTEGER, NUMERIC, NUMERIC, DATE, DATE) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pClasscodeid ALIAS FOR $1;
+  pWarehousid ALIAS FOR $2;
+  pACutoff ALIAS FOR $3;
+  pBCutoff ALIAS FOR $4;
+  pStartDate ALIAS FOR $5;
+  pEndDate ALIAS FOR $6;
+  _updateCount INTEGER;
+  _totalValue NUMERIC;
+  _cumulativeValue NUMERIC;
+  _itemsite RECORD;
+
+BEGIN
+
+  SELECT COUNT(*) INTO _updateCount
+  FROM itemsite, item
+  WHERE ( (itemsite_item_id=item_id)
+   AND ((item_classcode_id=pClasscodeid) OR (pClasscodeid=-1))
+   AND ((itemsite_warehous_id=pWarehousid) OR (pWarehousid=-1)) );
+
+  IF (_updateCount IS NULL) THEN
+    _updateCount := 0;
+  ELSE
+
+    UPDATE itemsite
+    SET itemsite_abcclass=''T''
+    FROM item
+    WHERE ( (itemsite_item_id=item_id)
+     AND ((item_classcode_id=pClasscodeid) OR (pClasscodeid=-1))
+     AND ((itemsite_warehous_id=pWarehousid) OR (pWarehousid=-1)) );
+
+    SELECT SUM(ABS(invhist_qoh_before - invhist_qoh_after) * invhist_unitcost) INTO _totalValue
+    FROM invhist, itemsite, item
+    WHERE ( (invhist_itemsite_id=itemsite_id)
+     AND (itemsite_item_id=item_id)
+     AND (invhist_analyze)
+     AND (invhist_transtype ~ ''^[IR]'')
+     AND ((item_classcode_id=pClasscodeid) OR (pClasscodeid=-1))
+     AND (invhist_transdate::DATE BETWEEN pStartDate AND pEndDate)
+     AND ((itemsite_warehous_id=pWarehousid) OR (pWarehousid=-1)) );
+
+    IF ( (_totalValue IS NULL) OR (_totalValue = 0) ) THEN
+      UPDATE itemsite
+      SET itemsite_abcclass=''A''
+      WHERE (itemsite_abcclass=''T'');
+    ELSE
+
+      _cumulativeValue := 0;
+
+      FOR _itemsite IN SELECT itemsite_id, item_number,
+                              SUM(ABS(invhist_qoh_before - invhist_qoh_after) * invhist_unitcost) AS value
+                       FROM invhist, itemsite, item
+                       WHERE ( (invhist_itemsite_id=itemsite_id)
+                        AND (itemsite_item_id=item_id)
+                        AND (invhist_analyze)
+                        AND (invhist_transtype ~ ''^[IR]'')
+                        AND ((item_classcode_id=pClasscodeid) OR (pClasscodeid=-1))
+                        AND (invhist_transdate::DATE BETWEEN pStartDate AND pEndDate)
+                        AND ((itemsite_warehous_id=pWarehousid) OR (pWarehousid=-1)) )
+                       GROUP BY itemsite_id, item_number
+                       ORDER BY value DESC LOOP
+
+        IF (_itemsite.value IS NOT NULL) THEN
+          _cumulativeValue := _cumulativeValue + _itemsite.value;
+        END IF;
+
+        IF ((_cumulativeValue / _totalValue) <= pACutoff) THEN
+          UPDATE itemsite
+          SET itemsite_abcclass=''A''
+          WHERE (itemsite_id=_itemsite.itemsite_id);
+        ELSE
+          IF ((_cumulativeValue / _totalValue) <= pBCutoff) THEN
+            UPDATE itemsite
+            SET itemsite_abcclass=''B''
+            WHERE (itemsite_id=_itemsite.itemsite_id);
+          ELSE
+            UPDATE itemsite
+            SET itemsite_abcclass=''C''
+            WHERE (itemsite_id=_itemsite.itemsite_id);
+          END IF;
+        END IF;
+
+      END LOOP;
+
+      UPDATE itemsite
+      SET itemsite_abcclass=''C''
+      WHERE (itemsite_abcclass=''T'');
+    END IF;
+
+  END IF;
+
+  RETURN _updateCount;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/updatecharassignment.sql b/foundation-database/public/functions/updatecharassignment.sql
new file mode 100644 (file)
index 0000000..1fc3f61
--- /dev/null
@@ -0,0 +1,86 @@
+CREATE OR REPLACE FUNCTION updateCharAssignment(TEXT, INTEGER, INTEGER, TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pTargetType ALIAS FOR $1;
+  pTargetId ALIAS FOR $2;
+  pCharId ALIAS FOR $3;
+  pValue ALIAS FOR $4;
+  _charassid INTEGER;
+
+BEGIN
+
+  SELECT updateCharAssignment(pTargetType, pTargetId, pCharId, pValue, 0) INTO _charassid;
+
+  RETURN _charassid;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION updateCharAssignment(TEXT, INTEGER, INTEGER, TEXT, NUMERIC) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pTargetType ALIAS FOR $1;
+  pTargetId ALIAS FOR $2;
+  pCharId ALIAS FOR $3;
+  pValue ALIAS FOR $4;
+  pPrice ALIAS FOR $5;
+  _charassid INTEGER;
+  _charassprice NUMERIC;
+  _explodedJob BOOLEAN = FALSE;
+  _value TEXT;
+
+BEGIN
+
+  -- Check for Valid Assignment
+  IF (pTargetType='SI') THEN
+    SELECT (item_config AND wo_status != 'O') INTO _explodedJob
+        FROM coitem,itemsite,item,wo
+        WHERE ((coitem_id=pTargetId)
+        AND (itemsite_id=coitem_itemsite_id)
+        AND (item_id=itemsite_item_id)
+        AND (wo_ordtype='S')
+        AND (wo_ordid=coitem_id));
+  END IF;
+  
+  SELECT charass_id,charass_value INTO _charassid, _value
+    FROM charass
+   WHERE ((charass_target_type=pTargetType)
+     AND  (charass_target_id=pTargetId)
+     AND  (charass_char_id=pCharId) )
+   LIMIT 1;
+  IF (FOUND) THEN
+    IF (_explodedJob AND pValue != _value) THEN
+      RAISE EXCEPTION  'Characteristic may not be updated for Configured Item with exploded Work Order.';
+    ELSIF(COALESCE(pValue, '')!='') THEN
+        UPDATE charass
+        SET charass_value = pValue,
+            charass_price = pPrice
+        WHERE (charass_id=_charassid);
+    ELSE
+      DELETE
+        FROM charass
+       WHERE (charass_id=_charassid);
+      _charassid := 0;
+    END IF;
+  ELSE
+    IF ( (_explodedJob) AND (COALESCE(pValue, '')!='') ) THEN
+      RAISE EXCEPTION  'Characteristics may not be updated for Configured Item with exploded Work Order.';
+    ELSIF(COALESCE(pValue, '')!='') THEN
+      SELECT nextval('charass_charass_id_seq') INTO _charassid;
+      INSERT INTO charass
+            (charass_id, charass_target_type, charass_target_id,
+             charass_char_id, charass_value, charass_price)
+      VALUES(_charassid, pTargetType, pTargetId,
+             pCharId, pValue, pPrice);
+    ELSE
+      _charassid := 0;
+    END IF;
+  END IF;
+
+  RETURN _charassid;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/updatecost.sql b/foundation-database/public/functions/updatecost.sql
new file mode 100644 (file)
index 0000000..dc962c4
--- /dev/null
@@ -0,0 +1,213 @@
+CREATE OR REPLACE FUNCTION updateCost(INTEGER, NUMERIC) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemcostid ALIAS FOR $1;
+  pCost ALIAS FOR $2;
+
+BEGIN
+  RETURN updateCost(pItemcostid, pCost, baseCurrId());
+END;
+' LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION updateCost(INTEGER, NUMERIC, INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+    pItemcostid ALIAS FOR $1;
+    pCost ALIAS FOR $2;
+    pCurrId ALIAS FOR $3;
+
+BEGIN
+  IF ( ( SELECT (itemcost_stdcost > 0)
+           FROM itemcost
+          WHERE (itemcost_id=pItemcostid) )  OR
+        (pCost > 0) ) THEN
+    UPDATE itemcost
+       SET itemcost_actcost=pCost, itemcost_updated=CURRENT_DATE,
+           itemcost_curr_id=pCurrId
+     WHERE (itemcost_id=pItemcostid);
+
+    RETURN pItemcostid;
+
+  ELSE
+    DELETE FROM itemcost
+     WHERE (itemcost_id=pItemcostid);
+
+    RETURN -1;
+  END IF;
+
+END;
+' LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION updateCost(INTEGER, TEXT, BOOLEAN, NUMERIC) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  returnVal INTEGER;
+
+BEGIN
+  SELECT updateCost($1, $2, $3, $4, baseCurrId()) INTO returnVal;
+  RETURN returnVal;
+END;
+' LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION updateCost(INTEGER, INTEGER, BOOLEAN, NUMERIC) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  returnVal INTEGER;
+
+BEGIN
+  SELECT updateCost($1, $2, $3, $4, baseCurrId()) INTO returnVal;
+  RETURN returnVal;
+END;
+' LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION updateCost(INTEGER, TEXT, BOOLEAN, NUMERIC, INTEGER) RETURNS integer AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemid ALIAS FOR $1;
+  pCosttype ALIAS FOR $2;
+  pLevel ALIAS FOR $3;
+  pCost ALIAS FOR $4;
+  pCurrid ALIAS FOR $5;
+  _cost NUMERIC;
+  _currId INTEGER;
+  _p RECORD;
+  _itemcostid INTEGER;
+
+BEGIN
+
+  IF (pCost IS NULL) THEN
+    _cost = 0;
+  ELSE
+    _cost = pCost;
+  END IF;
+
+  IF (pCurrId IS NULL) THEN
+    _currId := baseCurrID();
+  ELSE
+    _currId := pCurrId;
+  END IF;
+
+  SELECT itemcost_id, itemcost_stdcost INTO _p
+  FROM itemcost, costelem
+  WHERE ( (itemcost_costelem_id=costelem_id)
+   AND (itemcost_item_id=pItemid)
+   AND (itemcost_lowlevel=pLevel)
+   AND (costelem_type=pCosttype) );
+
+  IF (NOT FOUND) THEN
+    IF (_cost > 0) THEN
+      SELECT NEXTVAL(''itemcost_itemcost_id_seq'') INTO _itemcostid;
+      INSERT INTO itemcost
+      ( itemcost_id, itemcost_item_id, itemcost_costelem_id, itemcost_lowlevel,
+        itemcost_stdcost, itemcost_posted, itemcost_actcost, itemcost_updated,
+        itemcost_curr_id )
+      SELECT _itemcostid, pItemid, costelem_id, pLevel,
+             0, startOfTime(), _cost, CURRENT_DATE,
+             _currId
+      FROM costelem
+      WHERE (costelem_type=pCosttype);
+
+      RETURN _itemcostid;
+
+    ELSE
+      RETURN -1;
+    END IF;
+
+  ELSIF ( (_p.itemcost_stdcost > 0) OR (_cost > 0) ) THEN
+    UPDATE itemcost
+    SET itemcost_actcost=_cost,
+        itemcost_curr_id = _currId,
+        itemcost_updated=CURRENT_DATE
+    WHERE (itemcost_id=_p.itemcost_id);
+
+    RETURN _p.itemcost_id;
+
+  ELSE
+    DELETE FROM itemcost
+    WHERE (itemcost_id=_p.itemcost_id);
+    RETURN -1;
+  END IF;
+
+END;
+' LANGUAGE 'plpgsql'; 
+
+
+CREATE OR REPLACE FUNCTION updateCost(INTEGER, INTEGER, BOOLEAN, NUMERIC, INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemid ALIAS FOR $1;
+  pCostelemid ALIAS FOR $2;
+  pLevel ALIAS FOR $3;
+  pCost ALIAS FOR $4;
+  pCurrid ALIAS FOR $5;
+  _cost NUMERIC;
+  _currId INTEGER;
+  _p RECORD;
+  _itemcostid INTEGER;
+
+BEGIN
+
+  IF (pCost IS NULL) THEN
+    _cost = 0;
+  ELSE
+    _cost = pCost;
+  END IF;
+
+  IF (pCurrId IS NULL) THEN
+    _currId := baseCurrID();
+  ELSE
+    _currId := pCurrId;
+  END IF;
+
+  SELECT itemcost_id, itemcost_stdcost INTO _p
+  FROM itemcost
+  WHERE ((itemcost_costelem_id=pCostelemid)
+   AND (itemcost_item_id=pItemid)
+   AND (itemcost_lowlevel=pLevel) );
+
+  IF (NOT FOUND) THEN
+    IF (_cost > 0) THEN
+      SELECT NEXTVAL(''itemcost_itemcost_id_seq'') INTO _itemcostid;
+      INSERT INTO itemcost
+      ( itemcost_id, itemcost_item_id, itemcost_costelem_id, itemcost_lowlevel,
+        itemcost_stdcost, itemcost_posted, itemcost_actcost, itemcost_updated,
+        itemcost_curr_id )
+      SELECT _itemcostid, pItemid, costelem_id, pLevel,
+             0, startOfTime(), _cost, CURRENT_DATE,
+             _currId
+      FROM costelem
+      WHERE (costelem_id=pCostelemid);
+
+      RETURN _itemcostid;
+
+    ELSE
+      RETURN -1;
+    END IF;
+
+  ELSIF ( (_p.itemcost_stdcost > 0) OR (_cost > 0) ) THEN
+    UPDATE itemcost
+    SET itemcost_actcost=_cost,
+        itemcost_curr_id = _currId,
+        itemcost_updated=CURRENT_DATE
+    WHERE (itemcost_id=_p.itemcost_id);
+
+    RETURN _p.itemcost_id;
+
+  ELSE
+    DELETE FROM itemcost
+    WHERE (itemcost_id=_p.itemcost_id);
+    RETURN -1;
+  END IF;
+
+END;
+' LANGUAGE 'plpgsql'; 
diff --git a/foundation-database/public/functions/updatecustomprivs.sql b/foundation-database/public/functions/updatecustomprivs.sql
new file mode 100644 (file)
index 0000000..554727d
--- /dev/null
@@ -0,0 +1,38 @@
+
+CREATE OR REPLACE FUNCTION updateCustomPrivs() RETURNS BOOLEAN AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _r RECORD;
+BEGIN
+
+  FOR _r IN SELECT priv_id
+              FROM priv
+             WHERE ((priv_name IN (SELECT priv_name
+                                     FROM priv
+                                    WHERE (priv_module=''Custom'')
+                                   EXCEPT
+                                   SELECT (''Custom''||cmd_privname)
+                                     FROM cmd))
+               AND  (priv_module=''Custom'')) LOOP
+    -- TODO: something here
+    DELETE FROM grppriv WHERE grppriv_priv_id=_r.priv_id;
+    DELETE FROM usrpriv WHERE usrpriv_priv_id=_r.priv_id;
+    DELETE FROM priv WHERE priv_id=_r.priv_id;
+  END LOOP;
+
+  FOR _r IN SELECT (''Custom''||cmd_privname) AS privname
+              FROM cmd
+            EXCEPT
+            SELECT priv_name
+              FROM priv
+             WHERE (priv_module=''Custom'') LOOP
+    -- TODO: something here
+    INSERT INTO priv (priv_module, priv_name, priv_descrip)
+              VALUES (''Custom'', _r.privname, ''Auto Generated Custom Priv.'');
+  END LOOP;
+
+  RETURN TRUE;
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/updateitemcost.sql b/foundation-database/public/functions/updateitemcost.sql
new file mode 100644 (file)
index 0000000..0d2f7f8
--- /dev/null
@@ -0,0 +1,54 @@
+CREATE OR REPLACE FUNCTION UpdateItemCost(INTEGER, INTEGER, INTEGER, NUMERIC, BOOLEAN) RETURNS INTEGER AS $BODY$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+pItemId ALIAS FOR $1;
+pCostElemId ALIAS FOR $2;
+pCurrId ALIAS FOR $3;
+pCost ALIAS FOR $4;
+pPostToStandard ALIAS FOR $5;
+_itemcost_id INTEGER;
+_update_return INTEGER;
+_postcost_return BOOLEAN;
+
+--This function is used with the api.itemcost View for updating
+--the itemcost table
+
+BEGIN
+  SELECT itemcost_id INTO _itemcost_id
+  FROM itemcost
+  WHERE ( (itemcost_item_id = pItemId) AND (itemcost_costelem_id = pCostElemId) );
+
+  IF (NOT FOUND) THEN
+    RAISE EXCEPTION 'itemcost % not found for. ', pItemId || ' & ' || pCostElemId;
+  END IF;
+
+  IF (pCost IS NULL OR pCost < 0) THEN
+    RAISE EXCEPTION 'itemcost Actual Cost Invalid ', pCost;
+  END IF;
+  
+  IF (pCost > 0) THEN
+    UPDATE itemcost
+    SET itemcost_actcost=pCost,
+        itemcost_curr_id = pCurrId
+    WHERE (itemcost_id=_itemcost_id);
+
+    --Only Post Cost to standard if the parameter is set to true
+    IF (pPostToStandard) THEN
+      IF (NOT checkPrivilege('PostStandardCosts')) THEN
+        RAISE EXCEPTION 'You do not have privileges to poststandard itemcosts. Set api.itemcost post_to_standard to false';
+      END IF;
+      SELECT postcost(_itemcost_id) INTO _postcost_return;       
+      IF (NOT _postcost_return) THEN
+        RETURN -2;
+      END IF;
+    END IF;
+  ELSE 
+    RETURN -1;
+  END IF;
+
+  RETURN _itemcost_id;
+  
+END;
+$BODY$
+  LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/updateitemsiteleadtime.sql b/foundation-database/public/functions/updateitemsiteleadtime.sql
new file mode 100644 (file)
index 0000000..8f81f3b
--- /dev/null
@@ -0,0 +1,58 @@
+CREATE OR REPLACE FUNCTION updateItemSiteLeadTime(INTEGER, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  pPad ALIAS FOR $2;
+  _p RECORD;
+  _materialLeadTime INTEGER;
+  _productionLeadTime INTEGER;
+  _leadTime INTEGER;
+
+BEGIN
+
+  SELECT item_type, itemsite_wosupply INTO _p
+  FROM item, itemsite
+  WHERE ( (itemsite_item_id=item_id)
+   AND (itemsite_id=pitemsiteid) );
+  
+  IF ( (_p.item_type IN ('M', 'P')) ) THEN
+
+    IF (_p.item_type = 'M') THEN
+      SELECT COALESCE(MAX(component.itemsite_leadtime), 0) INTO _materialLeadTime
+      FROM bomitem, itemsite AS parent, itemsite AS component
+      WHERE ( (bomitem_parent_item_id=parent.itemsite_item_id)
+       AND (bomitem_rev_id=getActiveRevId('BOM',bomitem_parent_item_id))
+       AND (bomitem_item_id=component.itemsite_item_id)
+       AND (parent.itemsite_warehous_id=component.itemsite_warehous_id)
+       AND (parent.itemsite_id=pItemsiteid) );
+
+      SELECT COALESCE(MAX(booitem_execday), 0) INTO _productionLeadTime
+      FROM xtmfg.booitem, itemsite
+      WHERE ( (booitem_item_id=itemsite_item_id)
+       AND (booitem_rev_id=getActiveRevId('BOO',booitem_item_id))
+       AND (itemsite_id=pItemsiteid) );
+
+      _leadTime := (_materialLeadTime + _productionLeadTime + pPad);
+
+    ELSIF (_p.item_type IN ('P')) THEN
+      SELECT COALESCE(MAX(itemsrc_leadtime), 0) INTO _leadTime
+      FROM itemsrc, itemsite
+      WHERE ( (itemsite_item_id=itemsrc_item_id)
+       AND (itemsite_id=pItemsiteid) );
+
+      _leadTime := (_leadTime + pPad);
+    END IF;
+
+  ELSE
+    _leadTime = pPad;
+  END IF;
+
+  UPDATE itemsite
+  SET itemsite_leadtime=_leadTime
+  WHERE (itemsite_id=pItemsiteid);
+
+  RETURN _leadTime;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/updatelistprice.sql b/foundation-database/public/functions/updatelistprice.sql
new file mode 100644 (file)
index 0000000..bd33cbc
--- /dev/null
@@ -0,0 +1,17 @@
+CREATE OR REPLACE FUNCTION updateListPrice(INTEGER, NUMERIC) RETURNS NUMERIC AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemid ALIAS FOR $1;
+  pUpdateBy ALIAS FOR $2;
+
+BEGIN
+
+  UPDATE item
+  SET item_listprice = (item_listprice * pUpdateBy)
+  WHERE (item_id=pItemid);
+
+  RETURN 1;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/updatelowerusercosts.sql b/foundation-database/public/functions/updatelowerusercosts.sql
new file mode 100644 (file)
index 0000000..b598867
--- /dev/null
@@ -0,0 +1,84 @@
+CREATE OR REPLACE FUNCTION updateLowerUserCosts(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemid ALIAS FOR $1;
+
+BEGIN
+    RETURN updateLowerUserCosts(pItemid, TRUE);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION updateLowerUserCosts(INTEGER, BOOLEAN) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemid      ALIAS FOR $1;
+  pUpdateActual        ALIAS FOR $2;
+  _bomitem RECORD;
+  _type CHAR(1);
+
+BEGIN
+
+  SELECT item_type INTO _type
+  FROM item
+  WHERE (item_id=pItemid);
+
+  IF (_type IN ('M', 'F', 'B', 'T')) THEN
+    FOR _bomitem IN SELECT DISTINCT costelem_type
+                    FROM ( SELECT COALESCE(bc.costelem_type, ic.costelem_type) AS costelem_type
+                           FROM bomitem(pItemid)
+                             JOIN item ON (item_id=bomitem_item_id AND item_type <> 'T')
+                             JOIN itemcost ON (itemcost_item_id=bomitem_item_id)
+                             JOIN costelem ic ON (ic.costelem_id=itemcost_costelem_id AND NOT ic.costelem_sys)
+                             LEFT OUTER JOIN bomitemcost ON (bomitemcost_bomitem_id=bomitem_id)
+                             LEFT OUTER JOIN costelem bc ON (bc.costelem_id=bomitemcost_costelem_id AND NOT bc.costelem_sys)
+                           WHERE ( CURRENT_DATE BETWEEN bomitem_effective AND (bomitem_expires - 1) )
+
+                           UNION SELECT costelem_type
+                           FROM itemcost, costelem
+                           WHERE ( (itemcost_costelem_id=costelem_id)
+                            AND (itemcost_item_id=pItemid) ) ) AS data LOOP
+
+      PERFORM updateSorACost( pItemid, _bomitem.costelem_type,
+                             TRUE, lowerCost(pItemid, _bomitem.costelem_type, pUpdateActual),
+                             pUpdateActual);
+    END LOOP;
+
+  ELSIF (_type = 'C') THEN
+    FOR _bomitem IN SELECT DISTINCT costelem_type
+                    FROM ( SELECT costelem_type
+                           FROM itemcost, costelem,
+                                xtmfg.bbomitem
+                           WHERE ( (bbomitem_item_id=pItemid)
+                            AND ( CURRENT_DATE BETWEEN bbomitem_effective
+                                               AND (bbomitem_expires - 1) )
+                            AND (NOT costelem_sys)
+                            AND (bbomitem_item_id=itemcost_item_id)
+                            AND (itemcost_costelem_id=costelem_id) ) 
+
+                           UNION SELECT costelem_type
+                           FROM itemcost, costelem,
+                                xtmfg.bbomitem AS t,
+                                xtmfg.bbomitem AS s
+                           WHERE ( (t.bbomitem_item_id=pItemid)
+                            AND ( CURRENT_DATE BETWEEN s.bbomitem_effective
+                                               AND (s.bbomitem_expires - 1) )
+                            AND ( CURRENT_DATE BETWEEN t.bbomitem_effective
+                                               AND (t.bbomitem_expires - 1) )
+                            AND (s.bbomitem_parent_item_id=t.bbomitem_parent_item_id)
+                            AND (NOT costelem_sys)
+                            AND (s.bbomitem_item_id=itemcost_item_id)
+                            AND (itemcost_costelem_id=costelem_id) ) ) AS data LOOP
+
+      PERFORM updateSorACost( pItemid, _bomitem.costelem_type,
+                             TRUE, lowerCost(pItemid, _bomitem.costelem_type, pUpdateActual),
+                             pUpdateActual);
+    END LOOP;
+  END IF;
+
+  RETURN 1;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/updatelowlevel.sql b/foundation-database/public/functions/updatelowlevel.sql
new file mode 100644 (file)
index 0000000..d0d5ce4
--- /dev/null
@@ -0,0 +1,27 @@
+CREATE OR REPLACE FUNCTION updateLowlevel(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+
+  pLowlevel ALIAS FOR $1;
+  _num_back INTEGER;
+
+BEGIN
+
+  UPDATE costUpdate SET costUpdate_lowlevel_code = (pLowlevel + 1)
+    WHERE costUpdate_item_id IN (
+         SELECT bomitem_item_id
+         FROM bomitem JOIN costUpdate ON (bomitem_parent_item_id = costUpdate_item_id)
+         WHERE ((costUpdate_lowlevel_code = pLowlevel)
+            AND  (bomitem_rev_id=getActiveRevId('BOM',bomitem_parent_item_id))
+           AND  (CURRENT_DATE BETWEEN bomitem_effective
+                                               AND (bomitem_expires - 1))))
+      AND costUpdate_lowlevel_code >= pLowlevel;
+
+  GET DIAGNOSTICS _num_back = ROW_COUNT;
+
+  RETURN _num_back;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/updateoutlevel.sql b/foundation-database/public/functions/updateoutlevel.sql
new file mode 100644 (file)
index 0000000..cfbe42d
--- /dev/null
@@ -0,0 +1,59 @@
+CREATE OR REPLACE FUNCTION updateOUTLevel(INTEGER, INTEGER, INTEGER[]) RETURNS boolean AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  pDays ALIAS FOR $2;
+  pPeriods ALIAS FOR $3;
+  _cursor INTEGER;
+  _periodid INTEGER;
+  _usage NUMERIC;
+  _totalUsage NUMERIC;
+  _totalDays INTEGER;
+  _outLevel NUMERIC;
+  _averageUsage NUMERIC;
+
+BEGIN
+
+  _cursor := 1;
+  _totalUsage := 0;
+  _totalDays := 0;
+
+  _periodid = pPeriods[_cursor];
+  WHILE (_periodid IS NOT NULL) LOOP
+    SELECT COALESCE(SUM(invhist_invqty), 0) INTO _usage
+    FROM invhist
+    WHERE ( (invhist_itemsite_id=pItemsiteid)
+     AND ( invhist_transdate::DATE BETWEEN findPeriodStart(_periodid)
+                                   AND findPeriodEnd(_periodid) )
+     AND (invhist_transtype IN (''SH'', ''IM'')) );
+
+    _totalUsage := (_totalUsage + _usage);
+
+    _totalDays := ( _totalDays + ( findPeriodEnd(_periodid) -
+                                   findPeriodStart(_periodid) + 1 ) );
+
+    _cursor := (_cursor + 1);
+    _periodid = pPeriods[_cursor];
+  END LOOP;
+
+  IF (_totalDays > 0) THEN
+    _outLevel := round(_totalUsage / _totalDays * pDays);
+
+    IF (_outLevel > 0) THEN
+      UPDATE itemsite
+      SET itemsite_ordertoqty = _outLevel
+      WHERE (itemsite_id=pItemsiteid);
+    ELSE
+      UPDATE itemsite
+      SET itemsite_ordertoqty = 0
+      WHERE (itemsite_id=pItemsiteid);
+    END IF;
+
+    RETURN TRUE;
+  ELSE
+    RETURN FALSE;
+  END IF;
+
+END;
+' LANGUAGE plpgsql;
diff --git a/foundation-database/public/functions/updatereorderlevel.sql b/foundation-database/public/functions/updatereorderlevel.sql
new file mode 100644 (file)
index 0000000..cccc602
--- /dev/null
@@ -0,0 +1,174 @@
+
+CREATE OR REPLACE FUNCTION updateReorderLevel(INTEGER, INTEGER, INTEGER[]) RETURNS boolean AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  pDays ALIAS FOR $2;
+  pPeriods ALIAS FOR $3;
+  _cursor INTEGER;
+  _periodid INTEGER;
+  _usage NUMERIC;
+  _totalUsage NUMERIC;
+  _totalDays INTEGER;
+  _reorderLevel NUMERIC;
+  _averageUsage NUMERIC;
+  _result      TEXT;
+
+BEGIN
+
+  _cursor := 1;
+  _totalUsage := 0;
+  _totalDays := 0;
+
+  _periodid = pPeriods[_cursor];
+  WHILE (_periodid IS NOT NULL) LOOP
+    SELECT COALESCE(SUM(invhist_invqty), 0) INTO _usage
+    FROM invhist
+    WHERE ( (invhist_itemsite_id=pItemsiteid)
+     AND ( invhist_transdate::DATE BETWEEN findPeriodStart(_periodid)
+                                   AND findPeriodEnd(_periodid) )
+     AND (invhist_transtype IN ('SH', 'IM')) );
+
+    _totalUsage := (_totalUsage + _usage);
+
+    _totalDays := ( _totalDays + ( findPeriodEnd(_periodid) -
+                                   findPeriodStart(_periodid) + 1 ) );
+
+    _cursor := (_cursor + 1);
+    _periodid = pPeriods[_cursor];
+  END LOOP;
+
+  IF (_totalDays > 0) THEN
+    _reorderLevel := round(_totalUsage / _totalDays * pDays);
+
+    SELECT itemsite_stocked INTO _result from itemsite WHERE (itemsite_id=pItemsiteid);
+    IF (_reorderLevel = 0 AND _result='t') THEN
+      _reorderLevel := 1;
+    END IF;
+    
+    IF (_reorderLevel > 0) THEN
+      UPDATE itemsite
+      SET itemsite_reorderlevel = _reorderLevel
+      WHERE (itemsite_id=pItemsiteid);
+    ELSE
+      UPDATE itemsite
+      SET itemsite_reorderlevel = 0
+      WHERE (itemsite_id=pItemsiteid);
+    END IF;
+
+    RETURN TRUE;
+  ELSE
+    RETURN FALSE;
+  END IF;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION updateReorderLevel(INTEGER[], INTEGER, BOOLEAN, INTEGER[]) RETURNS SETOF reordlvl AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteids                 ALIAS FOR $1;
+  pDays                ALIAS FOR $2;
+  pAddLeadtime         ALIAS FOR $3;
+  pPeriodIds           ALIAS FOR $4;
+  _icursor             INTEGER := 1;
+  _pcursor             INTEGER := 1;
+  _totalUsage          NUMERIC := 0;
+  _totalDays           INTEGER := 0;
+  _reorderLevel        NUMERIC := 0;
+  _result              TEXT;
+  _usage               NUMERIC;
+  _averageUsage        NUMERIC;
+  _row reordlvl                %ROWTYPE;
+
+BEGIN
+  -- Validate
+  IF (pItemsiteIds[1] IS NULL OR pPeriodIds[1] IS NULL) THEN
+    RETURN;
+  END IF;
+  
+  -- Calculate total days
+  FOR _pcursor IN 1..ARRAY_UPPER(pPeriodIds,1) 
+  LOOP
+    _totalDays := ( _totalDays + ( findPeriodEnd(pPeriodIds[_pcursor]) -
+                                      findPeriodStart(pPeriodIds[_pcursor]) + 1 ) );
+  END LOOP;
+
+  --  Loop through each itemsite id
+  FOR _icursor IN 1..ARRAY_UPPER(pItemsiteIds,1)
+  LOOP
+      -- Get itemsite data
+    SELECT itemsite_id,
+      item_id,
+      warehous_code,
+      item_number,
+      item_descrip1,
+      itemsite_leadtime,
+      0,
+      itemsite_reorderlevel,
+      0,
+      0,
+      0
+      INTO _row
+    FROM itemsite
+      JOIN item ON (itemsite_item_id=item_id)
+      JOIN whsinfo ON (itemsite_warehous_id=warehous_id)
+    WHERE (itemsite_id=pItemsiteIds[_icursor]);
+
+    IF (FOUND) THEN
+      IF (pAddLeadtime) THEN
+        _row.reordlvl_daysofstock := pDays + _row.reordlvl_leadtime;
+      ELSE
+        _row.reordlvl_daysofstock := pDays;
+      END IF;
+      
+      --  Loop through each period id
+      FOR _pcursor IN 1..ARRAY_UPPER(pPeriodIds,1) 
+      LOOP
+        -- Sum days and usage shipping and inventory transactions
+        SELECT COALESCE(SUM(invhist_invqty), 0) INTO _usage
+        FROM invhist
+        WHERE ( (invhist_itemsite_id=pItemsiteIds[_icursor])
+         AND ( invhist_transdate::DATE BETWEEN findPeriodStart(pPeriodIds[_pcursor])
+                                       AND findPeriodEnd(pPeriodIds[_pcursor]) )
+         AND (invhist_transtype IN ('SH', 'IM')) );
+
+        _totalUsage := (_totalUsage + _usage);
+
+      END LOOP;
+
+      -- Calculate reorder level
+      IF (_totalDays > 0) THEN
+        _reorderLevel := round(_totalUsage / _totalDays * _row.reordlvl_daysofstock);
+      END IF;
+  
+      IF (_reorderLevel <= 0) THEN
+        _reorderLevel := 0;
+      END IF;
+
+      SELECT itemsite_stocked INTO _result from itemsite WHERE (itemsite_id=pItemsiteIds[_icursor]);
+      IF (_reorderLevel = 0 AND _result='t') THEN
+        _reorderLevel := 1;
+      END IF;
+
+      -- Set values
+      _row.reordlvl_total_days         := _totalDays;
+      _row.reordlvl_total_usage        := _totalUsage;
+      _row.reordlvl_calc_level         := _reorderLevel;
+
+      -- Return result
+      RETURN NEXT _row;
+    END IF;
+
+    _usage             := 0;
+    _averageUsage      := 0;
+    _totalUsage                := 0;
+    _reorderLevel      := 0;
+  END LOOP;
+
+  RETURN;
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/updateretainedearnings.sql b/foundation-database/public/functions/updateretainedearnings.sql
new file mode 100644 (file)
index 0000000..1577612
--- /dev/null
@@ -0,0 +1,155 @@
+
+CREATE OR REPLACE FUNCTION updateRetainedEarnings(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pYearPeriodid ALIAS FOR $1;
+  _r RECORD;
+  _n RECORD;
+  _c RECORD;
+  _beginningPeriodid INTEGER;
+  _trialbalid INTEGER;
+  _accntid INTEGER;
+  _totalProfitLoss      NUMERIC;
+  _periodid INTEGER;
+  _forwardupdate INTEGER;
+BEGIN
+
+--  First thing we need to do is to get the yearperiod
+
+  SELECT * INTO _r FROM yearperiod where yearperiod_id = pYearPeriodid;
+
+  IF (NOT FOUND) THEN
+    RETURN -6;
+  END IF;
+
+--  Now we need to find the next yearperiod
+
+  SELECT * INTO _n FROM yearperiod WHERE yearperiod_start = _r.yearperiod_end + interval '1 day';
+
+  IF (NOT FOUND) THEN
+    RETURN -4;
+  END IF;
+
+--  Now we have to find where to stick the end of year data
+
+  SELECT period_id INTO _periodid FROM period WHERE period_start = _n.yearperiod_start;
+
+  IF (NOT FOUND) THEN
+    RETURN -8;
+  END IF;
+
+--  Loop through companies and process each one
+
+  IF (coalesce(fetchMetricValue('GLCompanySize'),0) = 0) THEN
+  --  Process for installs not using company segment
+  --  Now we need to get the default account number for year end closing
+
+    SELECT CAST ( metric_value AS integer ) INTO _accntid FROM metric WHERE metric_name = 'YearEndEquityAccount';
+
+    IF (NOT FOUND) THEN
+      RETURN -7;
+    END IF;
+
+  --  So far so good.  Now we need to calculate the profit-loss for the year that we are closing
+
+    SELECT SUM(gltrans_amount) INTO _totalProfitLoss
+     FROM gltrans, accnt
+     WHERE ( (gltrans_accnt_id = accnt_id)
+       AND   (accnt_type IN ( 'R', 'E' ) )
+       AND   (gltrans_posted)
+       AND   (NOT gltrans_deleted)
+       AND   (gltrans_date between _r.yearperiod_start and _r.yearperiod_end ) );
+    IF (_totalProfitLoss IS NULL) THEN
+      _totalProfitLoss := 0;
+    END IF;
+
+  -- Get the trailbal_id
+
+    SELECT trialbal_id INTO _trialbalid
+      FROM trialbal
+     WHERE ( (trialbal_period_id = _periodid )
+       AND   (trialbal_accnt_id = _accntid) );
+
+    IF (NOT FOUND) THEN
+      RETURN -9;
+    END IF;
+
+  -- Lets do the update for the trialbal
+
+    UPDATE trialbal
+       SET trialbal_beginning = trialbal_beginning - trialbal_yearend + _totalProfitLoss,
+           trialbal_ending = trialbal_beginning - trialbal_yearend - trialbal_debits + trialbal_credits + _totalProfitLoss,
+           trialbal_yearend = _totalProfitLoss
+     WHERE trialbal_id = _trialbalid;
+
+  -- Now the forward update
+
+    SELECT forwardupdatetrialbalance(_trialbalid) INTO _forwardupdate;
+    
+  ELSE  -- Process for a multi-company set up
+  
+    FOR _c IN
+      SELECT company_number, company_yearend_accnt_id
+      FROM company
+    LOOP
+  --  Calculate the profit-loss for the year that we are closing
+
+      SELECT SUM(gltrans_amount) INTO _totalProfitLoss
+       FROM gltrans, accnt
+       WHERE ( (gltrans_accnt_id = accnt_id)
+         AND   (accnt_type IN ( 'R', 'E' ) )
+         AND   (gltrans_posted)
+         AND   (NOT gltrans_deleted)
+         AND   (accnt_company = _c.company_number)
+         AND   (gltrans_date between _r.yearperiod_start and _r.yearperiod_end ) );
+      IF(_totalProfitLoss IS NULL) THEN
+        _totalProfitLoss := 0;
+      END IF;
+
+    -- Get the trailbal_id
+
+      SELECT trialbal_id INTO _trialbalid
+        FROM trialbal
+          JOIN accnt ON (trialbal_accnt_id=accnt_id)
+       WHERE ( (trialbal_period_id = _periodid )
+         AND   (trialbal_accnt_id = _c.company_yearend_accnt_id) );
+
+      IF (NOT FOUND) THEN
+        -- Create a trial balance record
+        SELECT NEXTVAL('trialbal_trialbal_id_seq') INTO _trialbalid;
+        INSERT INTO trialbal
+          ( trialbal_id, trialbal_accnt_id, trialbal_period_id,
+            trialbal_beginning, trialbal_dirty,
+            trialbal_ending,
+            trialbal_credits,
+            trialbal_debits,
+            trialbal_yearend )
+        VALUES
+          ( _trialbalid, _c.company_yearend_accnt_id, _periodid,
+            _totalProfitLoss, TRUE,
+            _totalProfitLoss,
+            0,
+            0,
+            _totalProfitLoss );
+      ELSE
+        -- Lets do the update for the trialbal
+        UPDATE trialbal
+           SET trialbal_beginning = trialbal_beginning - trialbal_yearend + _totalProfitLoss,
+               trialbal_ending = trialbal_beginning - trialbal_yearend - trialbal_debits + trialbal_credits + _totalProfitLoss,
+               trialbal_yearend = _totalProfitLoss
+         WHERE trialbal_id = _trialbalid;
+      END IF;
+
+    -- Now the forward update
+
+      SELECT forwardupdatetrialbalance(_trialbalid) INTO _forwardupdate;
+
+    END LOOP;
+  END IF;
+
+  RETURN 0;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/updatesoracost.sql b/foundation-database/public/functions/updatesoracost.sql
new file mode 100644 (file)
index 0000000..9636f02
--- /dev/null
@@ -0,0 +1,19 @@
+CREATE OR REPLACE FUNCTION updateSorACost(INTEGER, TEXT, BOOLEAN, NUMERIC, BOOLEAN) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemid      ALIAS FOR $1;
+  pCosttype    ALIAS FOR $2;
+  pLevel       ALIAS FOR $3;
+  pCost                ALIAS FOR $4;
+  pUpdateActual        ALIAS FOR $5;
+
+BEGIN
+    IF (pUpdateActual) THEN
+       RETURN updateCost(pItemid, pCosttype, pLevel, pCost);
+    ELSE
+       RETURN updateStdCost(pItemid, pCosttype, pLevel, pCost);
+    END IF;
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/updatestdcost.sql b/foundation-database/public/functions/updatestdcost.sql
new file mode 100644 (file)
index 0000000..20b4e1a
--- /dev/null
@@ -0,0 +1,156 @@
+CREATE OR REPLACE FUNCTION updateStdCost(INTEGER, NUMERIC, NUMERIC, TEXT, TEXT) RETURNS BOOLEAN AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+    pItemcostid        ALIAS FOR $1;
+    pNewcost   ALIAS FOR $2;
+    pOldcost   ALIAS FOR $3;
+    pDocNumber ALIAS FOR $4;
+    pNotes     ALIAS FOR $5;
+    _itemcostid        INTEGER;
+    _r         RECORD;
+    _newcost   NUMERIC;
+    _oldcost   NUMERIC;
+
+BEGIN
+  IF (pNewcost IS NULL) THEN
+    _newcost := 0;
+  ELSE
+    _newcost := pNewcost;
+  END IF;
+  IF (pOldcost IS NULL) THEN
+    _oldcost := 0;
+  ELSE
+    _oldcost := pOldcost;
+  END IF;
+
+  IF (_newcost > 0) THEN
+    UPDATE itemcost
+    SET itemcost_stdcost=_newcost,
+        itemcost_posted=CURRENT_DATE
+    WHERE (itemcost_id=pItemcostid);
+  END IF;
+
+--  Distribute to G/L, debit Inventory Asset, credit Inventory Cost Variance
+  FOR _r IN SELECT itemsite_id, (itemsite_qtyonhand + itemsite_nnqoh) AS totalQty,
+                   costcat_invcost_accnt_id, costcat_asset_accnt_id,
+                   itemsite_costmethod
+            FROM itemcost, itemsite, costcat
+            WHERE ( (itemsite_item_id=itemcost_item_id)
+             AND (itemsite_costcat_id=costcat_id)
+             AND (itemsite_costmethod != 'A')
+             AND ((itemsite_qtyonhand + itemsite_nnqoh) <> 0)
+             AND (itemcost_id=pItemcostid) ) LOOP
+--    IF (_newcost <> _oldcost) THEN
+--      RAISE NOTICE 'itemcost_id = %, Qty = %, Old Cost = %, New Cost = %', pItemcostid, _r.totalQty, _oldcost, _newcost;
+--    END IF;
+    PERFORM insertGLTransaction( 'P/D', '', pDocNumber, pNotes,
+                                 _r.costcat_invcost_accnt_id, _r.costcat_asset_accnt_id, _r.itemsite_id,
+                                 ((_newcost - _oldcost) * _r.totalQty),
+                                 CURRENT_DATE );
+--  Update Itemsite Value if not Average Cost
+    IF (_r.itemsite_costmethod <> 'A') THEN
+--      RAISE NOTICE 'itemsite_id = %, Qty = %, New Cost = %', _r.itemsite_id, _r.totalQty, _newcost;
+      UPDATE itemsite SET itemsite_value=(_r.totalQty * stdCost(itemsite_item_id))
+      WHERE (itemsite_id=_r.itemsite_id);
+    END IF;
+  END LOOP;
+
+  IF (_newcost = 0) THEN
+    DELETE FROM itemcost
+    WHERE (itemcost_id=pItemcostid);
+
+    RETURN FALSE;
+  END IF;
+
+  IF ( SELECT metric_value
+        FROM metric
+        WHERE ((metric_name = 'EnableAsOfQOH')
+        AND (metric_value = 't'))) THEN
+    IF (pNewcost IS NOT NULL) THEN
+      _newcost := pNewcost;
+    END IF;
+    IF (pOldcost IS NULL) THEN
+      _oldcost := 0;
+    ELSE
+      _oldcost := pOldcost;
+    END IF;
+  --  Distribute to G/L, debit Inventory Asset, credit Inventory Cost Variance
+    PERFORM postValueIntoInvBalance(
+                  itemsite_id,
+                  current_date,
+                  asofinvqty(itemsite_id,current_date),
+                  asofinvnn(itemsite_id,current_date),
+                  _oldcost,
+                  _newcost)
+       FROM itemsite
+       JOIN item ON (itemsite_item_id=item_id)
+       JOIN itemcost ON (itemcost_item_id=item_id)
+      WHERE((itemsite_costmethod = 'S')
+        AND (itemcost_id=pItemcostid));
+  END IF;
+
+  RETURN TRUE;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION updateStdCost(INTEGER, TEXT, BOOLEAN, NUMERIC) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+    pItemid    ALIAS FOR $1;
+    pCostType  ALIAS FOR $2;
+    pLevel     ALIAS FOR $3;
+    pCost      ALIAS FOR $4;
+    _newCost   NUMERIC;
+    _oldCost   NUMERIC := 0;
+    _itemcostid        INTEGER;
+    _updateRet BOOLEAN;
+    _itemNumber TEXT;
+
+BEGIN
+    IF (pCost IS NULL) THEN
+       _newCost = 0;
+    ELSE
+       _newCost = pCost;
+    END IF;
+
+    SELECT itemcost_id, itemcost_stdCost, item_number
+       INTO _itemcostid, _oldCost, _itemNumber
+    FROM itemcost, costelem, item
+    WHERE ((itemcost_costelem_id=costelem_id)
+      AND  (itemcost_item_id=item_id)
+      AND  (item_id=pItemid)
+      AND  (itemcost_lowlevel=pLevel)
+      AND  (costelem_type=pCosttype));
+--    RAISE NOTICE 'updateStdCost(%, %, %, %) has itemcost_id % and stdcost %',
+--                             pItemid, pCostType, plevel, _newCost, _itemcostid, _oldCost;
+
+    IF (NOT FOUND) AND (_newCost > 0) THEN
+       SELECT NEXTVAL('itemcost_itemcost_id_seq') INTO _itemcostid;
+       RAISE NOTICE 'updateStdCost() inserting itemcost_id %', _itemcostid;
+       INSERT INTO itemcost
+           (itemcost_id, itemcost_item_id, itemcost_costelem_id,
+            itemcost_lowlevel, itemcost_stdcost, itemcost_posted,
+            itemcost_actcost, itemcost_updated)
+       SELECT
+             _itemcostid, pItemid, costelem_id,
+             pLevel, _newCost, CURRENT_DATE,
+             0, CURRENT_DATE
+       FROM costelem
+       WHERE (costelem_type=pCosttype);
+    END IF;
+
+    IF (_itemcostid IS NOT NULL) THEN
+       SELECT updateStdCost(_itemcostid, _newCost, _oldCost, 'Post Cost',
+               ('Set Standard Cost - ' || pCosttype || ' for item ' || _itemNumber)) INTO _updateRet;
+       IF (_updateRet) THEN
+           RETURN _itemcostid;
+       END IF;
+    END IF;
+
+    RETURN -1;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/updatetodoitem.sql b/foundation-database/public/functions/updatetodoitem.sql
new file mode 100644 (file)
index 0000000..85c2643
--- /dev/null
@@ -0,0 +1,105 @@
+
+CREATE OR REPLACE FUNCTION updateTodoItem(INTEGER, TEXT, TEXT, TEXT, INTEGER, INTEGER, INTEGER, DATE, DATE, CHARACTER(1), DATE, DATE, INTEGER, TEXT, BOOLEAN, TEXT) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple.
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN updateTodoItem($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, NULL);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION updateTodoItem(INTEGER, TEXT, TEXT, TEXT, INTEGER, INTEGER, INTEGER, DATE, DATE, CHARACTER(1), DATE, DATE, INTEGER, TEXT, BOOLEAN, TEXT, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple.
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  ptodoitemid ALIAS FOR  $1;
+  pusername   ALIAS FOR  $2;
+  pname       ALIAS FOR  $3;
+  pdesc       ALIAS FOR  $4;
+  pincdtid    ALIAS FOR  $5;
+  pcrmacctid  ALIAS FOR  $6;
+  pOpheadid   ALIAS FOR  $7;
+  pstarted    ALIAS FOR  $8;
+  pdue        ALIAS FOR  $9;
+  pstatus     ALIAS FOR $10;
+  passigned   ALIAS FOR $11;
+  pcompleted  ALIAS FOR $12;
+  ppriority   ALIAS FOR $13;
+  pnotes      ALIAS FOR $14;
+  pactive     ALIAS FOR $15;
+  powner       ALIAS FOR $16;
+  pcntctid     ALIAS FOR $17;
+
+  _priority   INTEGER         := ppriority;
+  _status     CHARACTER(1)    := pstatus;
+  _incdtid    INTEGER         := pincdtid;
+  _crmacctid  INTEGER         := pcrmacctid;
+  _opheadid   INTEGER         := pOpheadid;
+  _assigned   DATE            := passigned;
+  _active     BOOL            := pactive;
+  _result     INTEGER;
+
+BEGIN
+  IF (pusername IS NULL OR pusername = '') THEN
+    RETURN -1;
+  END IF;
+
+  IF (pname IS NULL OR pname = '') THEN
+    RETURN -2;
+  END IF;
+
+  IF (pdue IS NULL) THEN
+    RETURN -3;
+  END IF;
+
+  IF (ptodoitemid IS NULL OR ptodoitemid <= 0) THEN
+    RETURN -10;
+  END IF;
+
+  IF (pcompleted IS NOT NULL) THEN
+    _status := 'C';
+  ELSIF (pstatus IS NULL AND pstarted IS NOT NULL) THEN
+    _status := 'I';
+  ELSIF (pstatus IS NULL) THEN
+    _status := 'N';
+  END IF;
+
+  IF (_incdtid <= 0) THEN
+    _incdtid := NULL;
+  END IF;
+
+  IF (_crmacctid <= 0) THEN
+    _crmacctid := NULL;
+  END IF;
+
+  IF (_opheadid <= 0) THEN
+    _opheadid := NULL;
+  END IF;
+
+  IF (_priority <= 0) THEN
+    _priority := NULL;
+  END IF;
+
+  IF (_assigned IS NULL) THEN
+    _assigned := CURRENT_DATE;
+  END IF;
+
+  IF (_active IS NULL) THEN
+    _active := TRUE;
+  END IF;
+
+  UPDATE todoitem SET
+      todoitem_username=pusername, todoitem_name=pname, todoitem_description=pdesc,
+      todoitem_incdt_id=_incdtid, todoitem_status=_status,
+      todoitem_active=_active, todoitem_start_date=pstarted,
+      todoitem_due_date=pdue, todoitem_assigned_date=_assigned,
+      todoitem_completed_date=pcompleted, todoitem_priority_id=_priority,
+      todoitem_notes=pnotes, todoitem_crmacct_id=_crmacctid,
+      todoitem_ophead_id=_opheadid, todoitem_owner_username=powner,
+      todoitem_cntct_id=pcntctid
+  WHERE (todoitem_id=ptodoitemid);
+
+  RETURN ptodoitemid;
+END;
+$$ LANGUAGE 'plpgsql';
+
+
diff --git a/foundation-database/public/functions/usedefaultlocation.sql b/foundation-database/public/functions/usedefaultlocation.sql
new file mode 100644 (file)
index 0000000..ecc5708
--- /dev/null
@@ -0,0 +1,29 @@
+CREATE OR REPLACE FUNCTION useDefaultLocation(INTEGER) RETURNS BOOLEAN AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  _p RECORD;
+
+BEGIN
+
+  SELECT itemsite_location_id,
+         LENGTH(itemsite_location) AS locationlength INTO _p
+  FROM itemsite
+  WHERE (itemsite_id=pItemsiteid);
+
+  IF (NOT FOUND) THEN
+    RETURN FALSE;
+
+  ELSIF (_p.itemsite_location_id <> -1) THEN
+    RETURN TRUE;
+
+  ELSIF (_p.locationlength > 0) THEN
+    RETURN TRUE;
+
+  ELSE
+    RETURN FALSE;
+  END IF;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/usercancreateusers.sql b/foundation-database/public/functions/usercancreateusers.sql
new file mode 100644 (file)
index 0000000..625a375
--- /dev/null
@@ -0,0 +1,7 @@
+CREATE OR REPLACE FUNCTION userCanCreateUsers(TEXT) RETURNS BOOLEAN AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+SELECT rolcreaterole OR rolsuper
+  FROM pg_roles
+ WHERE rolname=($1);
+$$ LANGUAGE SQL;
diff --git a/foundation-database/public/functions/usercanlogin.sql b/foundation-database/public/functions/usercanlogin.sql
new file mode 100644 (file)
index 0000000..a989560
--- /dev/null
@@ -0,0 +1,37 @@
+DROP VIEW     IF EXISTS usr;
+DROP FUNCTION IF EXISTS userCanLogin(TEXT);
+CREATE OR REPLACE FUNCTION userCanLogin(pUsername TEXT) RETURNS BOOLEAN AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _isactive  BOOLEAN;
+  _mode      TEXT;
+BEGIN
+  IF (isDBA(pUsername) OR userCanCreateUsers(pUsername)) THEN
+    RETURN TRUE;
+
+  ELSIF (pg_has_role(pUsername, 'xtrole', 'member')) THEN
+    _mode := COALESCE(fetchMetricText('AllowedUserLogins'), '');
+
+    IF (_mode = 'AdminOnly') THEN
+      RETURN FALSE; -- administrators were checked above
+    END IF;
+
+    IF (_mode NOT IN ('AdminOnly','ActiveOnly','Any')) THEN
+      _mode := 'ActiveOnly';
+    END IF;
+
+    SELECT (usrpref_value = 't') INTO _isactive
+      FROM usrpref
+     WHERE usrpref_username = pUsername
+       AND usrpref_name = 'active';
+
+    IF (_isactive OR _mode = 'Any') THEN
+      RETURN TRUE;
+    END IF;
+  END IF;
+
+  RETURN FALSE;
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/userid.sql b/foundation-database/public/functions/userid.sql
new file mode 100644 (file)
index 0000000..9e7957c
--- /dev/null
@@ -0,0 +1,21 @@
+CREATE OR REPLACE FUNCTION userId(TEXT) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pUsername ALIAS FOR $1;
+  _userId INTEGER;
+
+BEGIN
+
+  SELECT usesysid INTO _userId
+  FROM pg_user
+  WHERE (usename=pUsername);
+
+  IF (FOUND) THEN
+    RETURN _userId;
+  ELSE
+    RETURN -1;
+  END IF;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/validateorderqty.sql b/foundation-database/public/functions/validateorderqty.sql
new file mode 100644 (file)
index 0000000..82de3c7
--- /dev/null
@@ -0,0 +1,44 @@
+
+CREATE OR REPLACE FUNCTION validateOrderQty(INTEGER, NUMERIC, BOOLEAN) RETURNS NUMERIC AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pItemsiteid ALIAS FOR $1;
+  pQty ALIAS FOR $2;
+  pManual ALIAS FOR $3;
+  _p RECORD;
+  _qty NUMERIC;
+
+BEGIN
+
+  _qty := pQty;
+
+  SELECT itemsite_useparams,
+         CASE WHEN (itemsite_useparams) THEN itemsite_useparamsmanual ELSE FALSE END AS itemsite_useparamsmanual,
+         CASE WHEN (itemsite_useparams) THEN itemsite_minordqty ELSE 0.0 END AS itemsite_minordqty,
+         CASE WHEN (itemsite_useparams) THEN itemsite_multordqty ELSE 0.0 END AS itemsite_multordqty,
+         item_fractional, item_type INTO _p
+  FROM itemsite, item
+  WHERE ( (itemsite_item_id=item_id)
+   AND (itemsite_id=pItemsiteid) );
+
+  IF ( (pManual AND (_p.itemsite_useparamsmanual)) OR
+       ((NOT pManual) AND (_p.itemsite_useparams)) ) THEN
+
+    IF (_qty < _p.itemsite_minordqty) THEN
+      _qty := _p.itemsite_minordqty;
+    END IF;
+
+    IF ( (_p.itemsite_multordqty > 0) AND ((_qty % _p.itemsite_multordqty) > 0) ) THEN
+      _qty := ((TRUNC(_qty / _p.itemsite_multordqty) * _p.itemsite_multordqty) + _p.itemsite_multordqty);
+    END IF;
+
+  END IF;
+
+  _qty := roundQty(_p.item_fractional, _qty);
+
+  RETURN _qty;
+
+END;
+' LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/validlocation.sql b/foundation-database/public/functions/validlocation.sql
new file mode 100644 (file)
index 0000000..2a592a5
--- /dev/null
@@ -0,0 +1,84 @@
+CREATE OR REPLACE FUNCTION validLocation(INTEGER, INTEGER) RETURNS BOOLEAN AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pLocationid ALIAS FOR $1;
+  pItemsiteid ALIAS FOR $2;
+  _p RECORD;
+  
+BEGIN
+
+  SELECT location_restrict INTO _p
+  FROM location, itemsite
+  WHERE ( (location_warehous_id=itemsite_warehous_id)
+    AND (itemsite_id=pItemsiteid)
+    AND (location_id=pLocationid) );
+
+  IF (FOUND) THEN
+    IF (_p.location_restrict) THEN
+
+      SELECT locitem_id INTO _p
+      FROM locitem, itemsite
+      WHERE ( (locitem_item_id=itemsite_item_id)
+       AND (itemsite_id=pItemsiteid)
+       AND (locitem_location_id=pLocationid) );
+
+      IF (FOUND) THEN
+        RETURN TRUE;
+      END IF;
+
+    ELSE
+      RETURN TRUE;
+    END IF;
+
+  ELSE
+    RETURN FALSE;
+  END IF;
+
+  RETURN FALSE;
+
+END;
+' LANGUAGE 'plpgsql';
+
+-- i found the following in a different batch script than the version above.
+-- i like the second version better but the 1st is the one that was in dev
+-- at the time i did the split so it''s most likely the one that we have been
+-- testing.
+-- TODO: uncomment the following and test it. if it works, drop the one above
+-- CREATE OR REPLACE FUNCTION validLocation(INTEGER, INTEGER) RETURNS BOOLEAN AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+-- DECLARE
+--   pLocationid ALIAS FOR $1;
+--   pItemsiteid ALIAS FOR $2;
+--   _valid BOOLEAN;
+--   _check INTEGER;
+-- 
+-- BEGIN
+-- 
+-- --  Check to see if the passed location is even restricted
+--   SELECT (NOT(location_restrict)) INTO _valid
+--   FROM location, itemsite
+--   WHERE ( (location_warehous_id=itemsite_warehous_id)
+--     AND (location_id=pLocationid)
+--     AND (itemsite_id=pItemsiteid) );
+--   IF ( (FOUND) AND (_valid) ) THEN
+--     RETURN TRUE;
+--   END IF;
+-- 
+-- --  Check to see if the passed itemsite_id is allowed in the passed restricted location_id
+--   SELECT location_id INTO _check
+--   FROM location, locitem, itemsite
+--   WHERE ( (locitem_location_id=location_id)
+--    AND (locitem_item_id=itemsite_item_id)
+--    AND (location_warehous_id=itemsite_warehous_id)
+--    AND (location_id=pLocationid)
+--    AND (itemsite_id=pItemsiteid) );
+--   IF (FOUND) THEN
+--     RETURN TRUE;
+--   END IF;
+-- 
+--   RETURN FALSE;
+-- 
+-- END;
+-- ' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/valueatshipping.sql b/foundation-database/public/functions/valueatshipping.sql
new file mode 100644 (file)
index 0000000..0727733
--- /dev/null
@@ -0,0 +1,31 @@
+CREATE OR REPLACE FUNCTION valueAtShipping(plineitemid INTEGER) RETURNS NUMERIC AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RETURN valueAtShipping('SO', plineitemid);
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION valueAtShipping(pordertype TEXT,
+                                           plineitemid INTEGER) RETURNS NUMERIC AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _value NUMERIC := 0.0;
+
+BEGIN
+
+  IF (pordertype NOT IN ('SO', 'TO')) THEN
+    RAISE EXCEPTION '% is not a valid order type', pordertype;
+  END IF;
+
+  SELECT COALESCE(SUM(shipitem_value), 0.0) INTO _value
+  FROM shipitem JOIN shiphead ON (shipitem_shiphead_id=shiphead_id)
+  WHERE ( (NOT shiphead_shipped)
+    AND  (shiphead_order_type=pordertype)
+    AND  (shipitem_orderitem_id=plineitemid) );
+
+  RETURN _value;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/voidapcheck.sql b/foundation-database/public/functions/voidapcheck.sql
new file mode 100644 (file)
index 0000000..6d33c0b
--- /dev/null
@@ -0,0 +1,8 @@
+CREATE OR REPLACE FUNCTION voidAPCheck(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RAISE NOTICE ''voidAPCheck() is deprecated - use voidCheck() instead'';
+  RETURN voidCheck($1);
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/voidapopenvoucher.sql b/foundation-database/public/functions/voidapopenvoucher.sql
new file mode 100644 (file)
index 0000000..49984b2
--- /dev/null
@@ -0,0 +1,350 @@
+CREATE OR REPLACE FUNCTION voidApopenVoucher(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pApopenid ALIAS FOR $1;
+BEGIN
+  RETURN voidApopenVoucher(pApopenid, fetchJournalNumber('AP-VO'));
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION voidApopenVoucher(INTEGER, INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pApopenid ALIAS FOR $1;
+  pJournalNumber ALIAS FOR $2;
+  _apopenid INTEGER;
+  _apcreditapplyid INTEGER;
+  _reference    TEXT;
+  _result INTEGER;
+  _sequence INTEGER;
+  _totalAmount_base NUMERIC;
+  _totalAmount NUMERIC;
+  _itemAmount_base NUMERIC;
+  _itemAmount NUMERIC;
+  _test INTEGER;
+  _a RECORD;
+  _d RECORD;
+  _g RECORD;
+  _p RECORD;
+  _n RECORD;
+  _r RECORD;
+  _costx RECORD;
+  _pExplain BOOLEAN;
+  _pLowLevel BOOLEAN;
+  _exchGainFreight NUMERIC;
+  _firstExchDateFreight        DATE;
+  _tmpTotal            NUMERIC;
+  _glDate              DATE;
+
+BEGIN
+
+  _totalAmount_base := 0;
+  _totalAmount := 0;
+  SELECT fetchGLSequence() INTO _sequence;
+
+--  Cache APOpen Information
+  SELECT apopen.* INTO _n
+  FROM apopen
+  WHERE ( (apopen_doctype='V')
+    AND   (apopen_id=pApopenid) );
+  IF (NOT FOUND) THEN
+    RAISE EXCEPTION 'Cannot Void Voucher #% as apopen not found', pApopenid;
+  END IF;
+
+--  Cache Voucher Infomation
+  SELECT vohead.*,
+        vend_number || '-' || vend_name || ' ' || vohead_reference
+                                                         AS glnotes,
+        COALESCE(pohead_orderdate, vohead_docdate) AS pohead_orderdate,
+        COALESCE(pohead_curr_id, vohead_curr_id) AS pohead_curr_id INTO _p
+  FROM vohead JOIN vendinfo ON (vend_id=vohead_vend_id)
+              LEFT OUTER JOIN pohead ON (vohead_pohead_id = pohead_id)
+  WHERE (vohead_number=_n.apopen_docnumber);
+  IF (NOT FOUND) THEN
+    RAISE EXCEPTION 'Cannot Void Voucher #% as vohead not found', _n.apopen_docnumber;
+  END IF;
+
+  _glDate := COALESCE(_p.vohead_gldistdate, _p.vohead_distdate);
+
+-- there is no currency gain/loss on items, see issue 3892,
+-- but there might be on freight, which is first encountered at p/o receipt
+  SELECT recv_date::DATE INTO _firstExchDateFreight
+  FROM recv
+  WHERE (recv_vohead_id = _p.vohead_id);
+
+--  Start by handling taxes
+  FOR _r IN SELECT tax_sales_accnt_id, 
+              round(sum(taxdetail_tax),2) AS tax,
+              currToBase(_p.vohead_curr_id, round(sum(taxdetail_tax),2), _p.vohead_docdate) AS taxbasevalue
+            FROM tax 
+             JOIN calculateTaxDetailSummary('VO', _p.vohead_id, 'T') ON (taxdetail_tax_id=tax_id)
+           GROUP BY tax_id, tax_sales_accnt_id LOOP
+
+    PERFORM insertIntoGLSeries( _sequence, 'A/P', 'VO', _p.vohead_number,
+                                _r.tax_sales_accnt_id, 
+                                (_r.taxbasevalue * -1),
+                                _glDate, _p.glnotes );
+
+    _totalAmount_base := (_totalAmount_base - _r.taxbasevalue);
+    _totalAmount := (_totalAmount - _r.tax);
+     
+  END LOOP;
+
+--  Loop through the vodist records for the passed vohead that
+--  are posted against a P/O Item
+  FOR _g IN SELECT DISTINCT poitem_id, voitem_qty, poitem_expcat_id,
+                            poitem_invvenduomratio,
+                            COALESCE(itemsite_id, -1) AS itemsiteid,
+                            COALESCE(itemsite_costcat_id, -1) AS costcatid,
+                            COALESCE(itemsite_item_id, -1) AS itemsite_item_id,
+                            (SELECT SUM(value) 
+                             FROM (
+                                SELECT SUM(recv_value) AS value
+                                FROM recv
+                                WHERE (recv_voitem_id=voitem_id)
+                             UNION
+                                SELECT SUM(poreject_value)*-1 AS value
+                                FROM poreject
+                                WHERE (poreject_voitem_id=voitem_id)) as data)
+                            AS value_base,
+                            (poitem_freight_vouchered / poitem_qty_vouchered) * voitem_qty AS vouchered_freight,
+                            currToBase(_p.pohead_curr_id, (poitem_freight_vouchered / poitem_qty_vouchered) * voitem_qty, _firstExchDateFreight ) AS vouchered_freight_base,
+                           voitem_freight,
+                           currToBase(_p.vohead_curr_id, voitem_freight,
+                                       _p.vohead_distdate) AS voitem_freight_base
+            FROM vodist, voitem,
+                 poitem LEFT OUTER JOIN itemsite ON (poitem_itemsite_id=itemsite_id)
+            WHERE ( (vodist_poitem_id=poitem_id)
+             AND (voitem_poitem_id=poitem_id)
+             AND (voitem_vohead_id=vodist_vohead_id)
+             AND (vodist_vohead_id=_p.vohead_id)) LOOP
+
+--  Grab the G/L Accounts
+    IF (_g.costcatid = -1) THEN
+      SELECT pp.accnt_id AS pp_accnt_id,
+             lb.accnt_id AS lb_accnt_id INTO _a
+      FROM expcat, accnt AS pp, accnt AS lb
+      WHERE ( (expcat_purchprice_accnt_id=pp.accnt_id)
+       AND (expcat_liability_accnt_id=lb.accnt_id)
+       AND (expcat_id=_g.poitem_expcat_id) );
+      IF (NOT FOUND) THEN
+        RAISE EXCEPTION 'Cannot Void Voucher #% due to unassigned G/L Accounts.', _p.vohead_number;
+      END IF;
+    ELSE
+      SELECT pp.accnt_id AS pp_accnt_id,
+             lb.accnt_id AS lb_accnt_id INTO _a
+      FROM costcat, accnt AS pp, accnt AS lb
+      WHERE ( (costcat_purchprice_accnt_id=pp.accnt_id)
+       AND (costcat_liability_accnt_id=lb.accnt_id)
+       AND (costcat_id=_g.costcatid) );
+      IF (NOT FOUND) THEN
+        RAISE EXCEPTION 'Cannot Void Voucher #% due to unassigned G/L Accounts.', _p.vohead_number;
+      END IF;
+    END IF;
+
+--  Clear the Item Amount accumulator
+    _itemAmount_base := 0;
+    _itemAmount := 0;
+
+--  Figure out the total posted value for this line item
+    FOR _d IN SELECT vodist_id, vodist_amount,
+                    _p.vohead_curr_id, vodist_costelem_id,
+                    currToBase(_p.vohead_curr_id, vodist_amount,
+                               _p.vohead_distdate) AS vodist_amount_base
+              FROM vodist
+              WHERE ( (vodist_vohead_id=_p.vohead_id)
+               AND (vodist_poitem_id=_g.poitem_id) ) LOOP
+
+       _pExplain := TRUE;
+       SELECT * INTO _costx
+         FROM itemcost
+        WHERE ( (itemcost_item_id = _g.itemsite_item_id)
+          AND   (itemcost_costelem_id = _d.vodist_costelem_id) );
+
+       IF (FOUND) THEN
+         _pExplain := _costx.itemcost_lowlevel;
+       END IF;
+
+--  Add the Distribution Amount to the Item Amount
+      _itemAmount_base := _itemAmount_base + ROUND(_d.vodist_amount_base, 2);
+      _itemAmount := _itemAmount + _d.vodist_amount;
+
+    END LOOP;
+
+--  Distribute from the clearing account
+    PERFORM insertIntoGLSeries( _sequence, 'A/P', 'VO', text(_p.vohead_number),
+                                _a.lb_accnt_id,
+                                round(_g.value_base + _g.vouchered_freight_base, 2),
+                                _glDate, _p.glnotes );
+
+--  Attribute the correct portion to currency gain/loss
+    _exchGainFreight := 0;
+    SELECT currGain(_p.pohead_curr_id, _g.vouchered_freight,
+                   _firstExchDateFreight, _p.vohead_distdate )
+                   INTO _exchGainFreight;
+    IF (round(_exchGainFreight, 2) <> 0) THEN
+      PERFORM insertIntoGLSeries(_sequence, 'A/P', 'VO',
+                                 text(_p.vohead_number),
+                                 getGainLossAccntId(_a.lb_accnt_id), round(_exchGainFreight, 2) * -1,
+                                 _glDate, _p.glnotes);
+    END IF;
+
+--  Distribute the remaining variance to the Purchase Price Variance account
+    IF (round(_itemAmount_base, 2) <> round(_g.value_base, 2)) THEN
+      _tmpTotal := round(_itemAmount_base, 2) - round(_g.value_base, 2);
+      PERFORM insertIntoGLSeries( _sequence, 'A/P', 'VO', text(_p.vohead_number),
+                                  _a.pp_accnt_id,
+                                  _tmpTotal,
+                                  _glDate, _p.glnotes );
+    END IF;
+
+--  Distribute the remaining freight variance to the Purchase Price Variance account
+    IF (round(_g.voitem_freight_base + _exchGainFreight, 2) <> round(_g.vouchered_freight_base, 2)) THEN
+      _tmpTotal := round(_g.voitem_freight_base + _exchGainFreight, 2) - round(_g.vouchered_freight_base, 2);
+      PERFORM insertIntoGLSeries( _sequence, 'A/P', 'VO', text(_p.vohead_number),
+                                  _a.pp_accnt_id,
+                                  _tmpTotal,
+                                  _glDate, _p.glnotes );
+    END IF;
+
+--  Add the distribution amount to the total amount to distribute
+    _totalAmount_base := (_totalAmount_base + _itemAmount_base + _g.voitem_freight_base);
+    _totalAmount := (_totalAmount + _itemAmount + _g.voitem_freight);
+
+--  Reverse the posting for all the Tagged Receivings for this P/O Item
+    UPDATE recv
+    SET recv_invoiced=FALSE,
+        recv_recvcost_curr_id=basecurrid(),
+        recv_recvcost=0,
+        recv_vohead_id=NULL,
+        recv_voitem_id=NULL
+    FROM poitem
+    WHERE ( (recv_orderitem_id=poitem_id)
+      AND   (recv_order_type='PO')
+      AND   (recv_orderitem_id=_g.poitem_id)
+      AND   (recv_vohead_id=_p.vohead_id) );
+
+--  Reverse the posting for all the Tagged Rejections for this P/O Item
+    UPDATE poreject
+    SET poreject_invoiced=FALSE,
+        poreject_vohead_id=NULL,
+        poreject_voitem_id=NULL
+    WHERE ( (poreject_poitem_id=_g.poitem_id)
+      AND   (poreject_vohead_id=_p.vohead_id) );
+
+--  Update the qty and freight vouchered fields
+    UPDATE poitem
+       SET poitem_qty_vouchered = (poitem_qty_vouchered - _g.voitem_qty),
+           poitem_freight_vouchered = (poitem_freight_vouchered - _g.voitem_freight)
+    WHERE (poitem_id=_g.poitem_id);
+
+  END LOOP;
+
+--  Loop through the vodist records for the passed vohead that
+--  are not posted against a P/O Item
+--  Skip the tax distributions
+  FOR _d IN SELECT vodist_id,
+                  currToBase(_p.vohead_curr_id, vodist_amount,
+                             _p.vohead_distdate) AS vodist_amount_base,
+                  vodist_amount,
+                  vodist_accnt_id, vodist_expcat_id
+            FROM vodist
+            WHERE ( (vodist_vohead_id=_p.vohead_id)
+              AND   (vodist_poitem_id=-1)
+              AND   (vodist_tax_id=-1) ) LOOP
+
+--  Distribute from the misc. account
+    IF (_d.vodist_accnt_id = -1) THEN
+      PERFORM insertIntoGLSeries( _sequence, 'A/P', 'VO', text(_p.vohead_number),
+                                  expcat_exp_accnt_id,
+                                  round(_d.vodist_amount_base, 2),
+                                  _glDate, _p.glnotes )
+      FROM expcat
+      WHERE (expcat_id=_d.vodist_expcat_id);
+    ELSE
+      PERFORM insertIntoGLSeries( _sequence, 'A/P', 'VO', text(_p.vohead_number),
+                                  _d.vodist_accnt_id,
+                                  round(_d.vodist_amount_base, 2),
+                                  _glDate, _p.glnotes );
+    END IF;
+
+--  Add the Distribution Amount to the Total Amount
+    _totalAmount_base := _totalAmount_base + ROUND(_d.vodist_amount_base, 2);
+    _totalAmount := _totalAmount + _d.vodist_amount;
+
+  END LOOP;
+
+  SELECT insertIntoGLSeries( _sequence, 'A/P', 'VO', text(vohead_number),
+                             accnt_id, round(_totalAmount_base, 2) * -1,
+                             _glDate, _p.glnotes ) INTO _test
+  FROM vohead LEFT OUTER JOIN accnt ON (accnt_id=findAPAccount(vohead_vend_id))
+  WHERE ( (findAPAccount(vohead_vend_id)=0 OR accnt_id > 0) -- G/L interface might be disabled
+    AND   (vohead_id=_p.vohead_id) );
+  IF (NOT FOUND) THEN
+    RAISE EXCEPTION 'Cannot Void Voucher #% due to an unassigned A/P Account.', _p.vohead_number;
+  END IF;
+
+  PERFORM postGLSeries(_sequence, pJournalNumber);
+
+--  Create the A/P Open Item
+  SELECT NEXTVAL('apopen_apopen_id_seq') INTO _apopenid;
+  _reference := ('Void Voucher #' || _n.apopen_docnumber);
+  INSERT INTO apopen
+  ( apopen_id, apopen_username, apopen_journalnumber,
+    apopen_vend_id, apopen_docnumber, apopen_doctype, apopen_ponumber,
+    apopen_docdate, apopen_duedate, apopen_distdate, apopen_terms_id, apopen_curr_id,
+    apopen_amount, apopen_paid, apopen_open, apopen_notes, apopen_discount, apopen_curr_rate )
+  SELECT _apopenid, getEffectiveXtUser(), pJournalnumber,
+         apopen_vend_id, apopen_docnumber, 'C', apopen_ponumber,
+         CURRENT_DATE, CURRENT_DATE, CURRENT_DATE, -1, apopen_curr_id,
+         apopen_amount - apopen_paid, 0, TRUE, _reference, TRUE, apopen_curr_rate
+    FROM apopen
+   WHERE (apopen_id=_n.apopen_id);
+
+  SELECT apcreditapply_id INTO _apcreditapplyid
+    FROM apcreditapply
+   WHERE ( (apcreditapply_source_apopen_id=_apopenid)
+     AND   (apcreditapply_target_apopen_id=_n.apopen_id) );
+  IF (FOUND) THEN
+    UPDATE apcreditapply
+       SET apcreditapply_amount=_n.apopen_amount-_n.apopen_paid
+     WHERE (apcreditapply_id=_apcreditapplyid);
+  ELSE
+    SELECT nextval('apcreditapply_apcreditapply_id_seq') INTO _apcreditapplyid;
+    INSERT INTO apcreditapply
+           ( apcreditapply_id, apcreditapply_source_apopen_id,
+             apcreditapply_target_apopen_id, apcreditapply_amount,
+             apcreditapply_curr_id )
+    VALUES ( _apcreditapplyid, _apopenid, _n.apopen_id, _n.apopen_amount-_n.apopen_paid, _n.apopen_curr_id );
+  END IF;
+
+  SELECT postAPCreditMemoApplication(_apopenid) INTO _result;
+
+  IF (_result < 0) THEN
+    RAISE EXCEPTION 'Credit application failed with result %.', _result;
+  END IF;
+
+--  Reopen all of the P/O Items that were closed by this Voucher
+  UPDATE poitem
+  SET poitem_status='O'
+  FROM voitem
+  WHERE ( (voitem_poitem_id=poitem_id)
+    AND   (voitem_close)
+    AND   (voitem_vohead_id=_p.vohead_id) );
+
+--  Reopen the P/O
+  UPDATE pohead
+  SET pohead_status='O'
+  WHERE (pohead_id=_p.vohead_pohead_id);
+
+--  Mark as voided
+  UPDATE apopen
+  SET apopen_void=TRUE
+  WHERE (apopen_id=_n.apopen_id);
+
+  RETURN pJournalNumber;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/voidcheck.sql b/foundation-database/public/functions/voidcheck.sql
new file mode 100644 (file)
index 0000000..4217fea
--- /dev/null
@@ -0,0 +1,22 @@
+CREATE OR REPLACE FUNCTION voidCheck(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCheckid ALIAS FOR $1;
+
+BEGIN
+
+  IF ( SELECT (checkhead_void OR checkhead_posted OR checkhead_replaced)
+       FROM checkhead
+       WHERE (checkhead_id=pCheckid) ) THEN
+    RETURN -1;
+  END IF;
+
+  UPDATE checkhead
+  SET checkhead_void=TRUE
+  WHERE (checkhead_id=pCheckid);
+
+  RETURN 1;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/voidcreditmemo.sql b/foundation-database/public/functions/voidcreditmemo.sql
new file mode 100644 (file)
index 0000000..7669aed
--- /dev/null
@@ -0,0 +1,249 @@
+CREATE OR REPLACE FUNCTION voidCreditMemo(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCmheadid ALIAS FOR $1;
+  _r RECORD;
+  _p RECORD;
+  _n RECORD;
+  _glSequence INTEGER := 0;
+  _glJournal INTEGER := 0;
+  _itemlocSeries INTEGER := 0;
+  _invhistid INTEGER;
+  _test INTEGER;
+  _amount NUMERIC;
+  _roundedBase NUMERIC;
+  _totalAmount NUMERIC   := 0;
+  _totalRoundedBase NUMERIC := 0;
+  _commissionDue NUMERIC := 0;
+  _toApply NUMERIC;
+  _toClose BOOLEAN;
+  _glDate      DATE;
+  _taxBaseValue        NUMERIC := 0;
+
+BEGIN
+
+--  Cache C/M information
+  SELECT cmhead.*,
+         findARAccount(cmhead_cust_id) AS ar_accnt_id,
+         ( SELECT COALESCE(SUM(taxhist_tax), 0)
+           FROM cmheadtax
+           WHERE ( (taxhist_parent_id = cmhead_id)
+             AND   (taxhist_taxtype_id = getAdjustmentTaxtypeId()) ) ) AS adjtax
+         INTO _p
+  FROM cmhead
+  WHERE (cmhead_id=pCmheadid);
+  IF (NOT FOUND) THEN
+    RAISE EXCEPTION 'Cannot Void Credit Memo as cmhead not found';
+  END IF;
+  IF (NOT _p.cmhead_posted) THEN
+    RETURN -10;
+  END IF;
+
+--  Cache AROpen Information
+  SELECT aropen.* INTO _n
+  FROM aropen
+  WHERE ( (aropen_doctype='C')
+    AND   (aropen_docnumber=_p.cmhead_number) );
+  IF (NOT FOUND) THEN
+    RAISE EXCEPTION 'Cannot Void Credit Memo as aropen not found';
+  END IF;
+
+--  Check for ARApplications
+  SELECT arapply_id INTO _test
+  FROM arapply
+  WHERE (arapply_target_aropen_id=_n.aropen_id)
+     OR (arapply_source_aropen_id=_n.aropen_id)
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -20;
+  END IF;
+
+  _glDate := COALESCE(_p.cmhead_gldistdate, _p.cmhead_docdate);
+
+  SELECT fetchGLSequence() INTO _glSequence;
+  SELECT fetchJournalNumber('AR-IN') INTO _glJournal;
+
+--  Start by handling taxes (reverse sense)
+  FOR _r IN SELECT tax_sales_accnt_id, 
+              round(sum(taxdetail_tax),2) AS tax,
+              currToBase(_p.cmhead_curr_id, round(sum(taxdetail_tax),2), _p.cmhead_docdate) AS taxbasevalue
+            FROM tax 
+             JOIN calculateTaxDetailSummary('CM', _p.cmhead_id, 'T') ON (taxdetail_tax_id=tax_id)
+           GROUP BY tax_id, tax_sales_accnt_id LOOP
+
+    PERFORM insertIntoGLSeries( _glSequence, 'A/R', 'CM', _p.cmhead_number,
+                                _r.tax_sales_accnt_id, 
+                                (_r.taxbasevalue * -1.0),
+                                _glDate, ('Void-' || _p.cmhead_billtoname) );
+
+    _totalAmount := _totalAmount + _r.tax * -1;
+    _totalRoundedBase := _totalRoundedBase + _r.taxbasevalue * -1;  
+  END LOOP;
+
+-- Process line items
+  FOR _r IN SELECT *
+            FROM creditmemoitem
+            WHERE ( (cmitem_cmhead_id=_p.cmhead_id)
+              AND   (cmitem_qtycredit <> 0 ) ) LOOP
+
+    IF (_r.extprice <> 0) THEN
+--  Debit the Sales Account for the current cmitem (reverse sense)
+      _roundedBase := round(currToBase(_p.cmhead_curr_id, _r.extprice, _p.cmhead_docdate), 2);
+      SELECT insertIntoGLSeries( _glSequence, 'A/R', 'CM', _p.cmhead_number,
+                                 CASE WHEN _p.cmhead_rahead_id IS NULL THEN
+                                   getPrjAccntId(_p.cmhead_prj_id, salesaccnt_credit_accnt_id)
+                                 ELSE
+                                   getPrjAccntId(_p.cmhead_prj_id, salesaccnt_returns_accnt_id)
+                                 END,
+                                 _roundedBase,
+                                 _glDate, ('Void-' || _p.cmhead_billtoname) ) INTO _test
+      FROM salesaccnt
+      WHERE (salesaccnt_id=findSalesAccnt(_r.cmitem_itemsite_id, 'IS', _p.cmhead_cust_id,
+                                          _p.cmhead_saletype_id, _p.cmhead_shipzone_id));
+      IF (NOT FOUND) THEN
+        PERFORM deleteGLSeries(_glSequence);
+        RETURN -11;
+      END IF;
+    END IF;
+
+    _totalAmount := _totalAmount + round(_r.extprice, 2);
+    _totalRoundedBase := _totalRoundedBase + _roundedBase;
+
+  END LOOP;
+
+--  Credit the Misc. Account for Miscellaneous Charges (reverse sense)
+  IF (_p.cmhead_misc <> 0) THEN
+    _roundedBase := round(currToBase(_p.cmhead_curr_id, _p.cmhead_misc, _p.cmhead_docdate), 2);
+    SELECT insertIntoGLSeries( _glSequence, 'A/R', 'CM', _p.cmhead_number,
+                               getPrjAccntId(_p.cmhead_prj_id, accnt_id),
+                               _roundedBase,
+                               _glDate, ('Void-' ||_p.cmhead_billtoname) ) INTO _test
+    FROM accnt
+    WHERE (accnt_id=_p.cmhead_misc_accnt_id);
+
+--  If the Misc. Charges Account was not found then punt
+    IF (NOT FOUND) THEN
+      PERFORM deleteGLSeries(_glSequence);
+      RETURN _test;
+    END IF;
+
+--  Cache the Misc. Amount distributed
+    _totalAmount := _totalAmount + _p.cmhead_misc;
+    _totalRoundedBase := _totalRoundedBase + _roundedBase;
+  END IF;
+
+--  Debit the Freight Account (reverse sense)
+  IF (_p.cmhead_freight <> 0) THEN
+    _roundedBase := round(currToBase(_p.cmhead_curr_id, _p.cmhead_freight, _p.cmhead_docdate), 2);
+    SELECT insertIntoGLSeries( _glSequence, 'A/R', 'CM', _p.cmhead_number,
+                               getPrjAccntId(_p.cmhead_prj_id, accnt_id),
+                               _roundedBase,
+                               _glDate, ('Void-' || _p.cmhead_billtoname) ) INTO _test
+    FROM accnt
+    WHERE (accnt_id=findFreightAccount(_p.cmhead_cust_id));
+
+--  If the Freight Charges Account was not found then punt
+    IF (NOT FOUND) THEN
+      PERFORM deleteGLSeries(_glSequence);
+      RETURN _test;
+    END IF;
+
+--  Cache the Amount Distributed to Freight
+    _totalAmount := _totalAmount + _p.cmhead_freight;
+    _totalRoundedBase := _totalRoundedBase + _roundedBase;
+  END IF;
+
+  _totalAmount := _totalAmount;
+
+--  Credit the A/R for the total Amount (reverse sense)
+  IF (_totalAmount <> 0) THEN
+    IF (_p.ar_accnt_id != -1) THEN
+      SELECT insertIntoGLSeries( _glSequence, 'A/R', 'CM', _p.cmhead_number,
+                                 _p.ar_accnt_id,
+                                 (_totalRoundedBase * -1.0),
+                                 _glDate, ('Void-' || _p.cmhead_billtoname) ) INTO _test;
+    ELSE
+      PERFORM deleteGLSeries(_glSequence);
+      RETURN _test;
+    END IF;
+  END IF;
+
+--  Commit the GLSeries;
+  SELECT postGLSeries(_glSequence, _glJournal) INTO _test;
+  IF (_test < 0) THEN
+    PERFORM deleteGLSeries(_glSequence);
+    RETURN _test;
+  END IF;
+
+--  Delete sales history
+  DELETE FROM cohisttax
+  WHERE (taxhist_parent_id IN (SELECT cohist_id
+                               FROM cohist
+                               WHERE (cohist_doctype='C' AND cohist_ordernumber=_p.cmhead_number)));
+
+  DELETE FROM cohist
+  WHERE (cohist_doctype='C' AND cohist_ordernumber=_p.cmhead_number);
+
+--  Delete the Invoice aropen item
+  DELETE FROM aropen
+  WHERE (aropen_doctype='C' AND aropen_docnumber=_p.cmhead_number);
+
+-- Handle the Inventory and G/L Transactions for any returned Inventory where cmitem_updateinv is true (reverse sense)
+  FOR _r IN SELECT cmitem_itemsite_id AS itemsite_id, cmitem_id,
+                   (cmitem_qtyreturned * cmitem_qty_invuomratio) AS qty,
+                   cmhead_number, cmhead_cust_id AS cust_id, item_number,
+                   cmhead_prj_id AS prj_id, cmhead_saletype_id AS saletype_id,
+                   cmhead_shipzone_id AS shipzone_id
+            FROM cmhead, cmitem, itemsite, item
+            WHERE ( (cmitem_cmhead_id=cmhead_id)
+             AND (cmitem_itemsite_id=itemsite_id)
+             AND (itemsite_item_id=item_id)
+             AND (cmitem_qtyreturned <> 0)
+             AND (cmitem_updateinv)
+             AND (cmhead_id=_p.cmhead_id) ) LOOP
+
+--  Return credited stock to inventory
+    IF (_itemlocSeries = 0) THEN
+      SELECT NEXTVAL('itemloc_series_seq') INTO _itemlocSeries;
+    END IF;
+    SELECT postInvTrans( itemsite_id, 'RS', (_r.qty * -1),
+                         'S/O', 'CM', _r.cmhead_number, '',
+                         ('Credit Voided ' || _r.item_number),
+                         costcat_asset_accnt_id,
+                         getPrjAccntId(_r.prj_id, resolveCOSAccount(itemsite_id, _r.cust_id, _r.saletype_id, _r.shipzone_id)),  
+                         _itemlocSeries, _glDate) INTO _invhistid
+    FROM itemsite, costcat
+    WHERE ( (itemsite_costcat_id=costcat_id)
+     AND (itemsite_id=_r.itemsite_id) );
+
+  END LOOP;
+
+--  Update coitem to reflect the returned qty where cmitem_updateinv is true (reverse sense)
+  FOR _r IN SELECT cmitem_qtyreturned, cmitem_itemsite_id, cohead_id
+            FROM cmitem, cmhead, invchead, cohead
+            WHERE ( (cmitem_cmhead_id=cmhead_id)
+             AND (cmhead_invcnumber=invchead_invcnumber)
+             AND (invchead_ordernumber=cohead_number)
+             AND (cmitem_qtyreturned <> 0)
+             AND (cmitem_updateinv)
+             AND (cmhead_id=_p.cmhead_id) ) LOOP
+    UPDATE coitem
+    SET coitem_qtyreturned = (coitem_qtyreturned + (_r.cmitem_qtyreturned * -1.0))
+    WHERE coitem_id IN ( SELECT coitem_id
+                         FROM coitem
+                         WHERE ( (coitem_cohead_id=_r.cohead_id)
+                          AND (coitem_itemsite_id = _r.cmitem_itemsite_id) )
+                         LIMIT 1 );
+  END LOOP;
+
+--  Mark the cmhead as voided
+  UPDATE cmhead
+  SET cmhead_void=TRUE
+  WHERE (cmhead_id=_p.cmhead_id);
+
+  RETURN _itemlocSeries;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/voidinvoice.sql b/foundation-database/public/functions/voidinvoice.sql
new file mode 100644 (file)
index 0000000..a793d33
--- /dev/null
@@ -0,0 +1,372 @@
+CREATE OR REPLACE FUNCTION voidInvoice(INTEGER) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pInvcheadid ALIAS FOR $1;
+  _glSequence INTEGER := 0;
+  _glJournal INTEGER := 0;
+  _itemlocSeries INTEGER := 0;
+  _aropenid INTEGER := 0;
+  _invhistid INTEGER := 0;
+  _amount NUMERIC;
+  _roundedBase NUMERIC;
+  _r RECORD;
+  _p RECORD;
+  _n RECORD;
+  _test INTEGER;
+  _totalAmount          NUMERIC := 0;
+  _totalRoundedBase     NUMERIC := 0;
+  _totalAmountBase      NUMERIC := 0;
+  _appliedAmount        NUMERIC := 0;
+  _commissionDue        NUMERIC := 0;
+  _tmpAccntId INTEGER;
+  _tmpCurrId  INTEGER;
+  _firstExchDate        DATE;
+  _glDate              DATE;
+  _exchGain             NUMERIC := 0;
+
+BEGIN
+
+--  Cache Invoice information
+  SELECT invchead.*,
+         findFreightAccount(invchead_cust_id) AS freightaccntid,
+         findARAccount(invchead_cust_id) AS araccntid,
+         aropen_id,
+         ( SELECT COALESCE(SUM(taxhist_tax), 0)
+           FROM invcheadtax
+           WHERE ( (taxhist_parent_id = invchead_id)
+             AND   (taxhist_taxtype_id = getFreightTaxtypeId()) ) ) AS freighttax,
+         ( SELECT COALESCE(SUM(taxhist_tax), 0)
+           FROM invcheadtax
+           WHERE ( (taxhist_parent_id = invchead_id)
+             AND   (taxhist_taxtype_id = getAdjustmentTaxtypeId()) ) ) AS adjtax
+       INTO _p 
+  FROM invchead JOIN aropen ON (aropen_doctype='I' AND aropen_docnumber=invchead_invcnumber)
+  WHERE (invchead_id=pInvcheadid);
+  IF (NOT FOUND) THEN
+    RAISE EXCEPTION 'Cannot Void Invoice as invchead not found';
+  END IF;
+  IF (NOT _p.invchead_posted) THEN
+    RETURN -10;
+  END IF;
+
+--  Cache AROpen Information
+  SELECT aropen.* INTO _n
+  FROM aropen
+  WHERE ( (aropen_doctype='I')
+    AND   (aropen_docnumber=_p.invchead_invcnumber) );
+  IF (NOT FOUND) THEN
+    RAISE EXCEPTION 'Cannot Void Invoice as aropen not found';
+  END IF;
+
+--  Check for ARApplications
+  SELECT arapply_id INTO _test
+  FROM arapply
+  WHERE (arapply_target_aropen_id=_n.aropen_id)
+  LIMIT 1;
+  IF (FOUND) THEN
+    RETURN -20;
+  END IF;
+
+  SELECT fetchGLSequence() INTO _glSequence;
+  SELECT fetchJournalNumber('AR-IN') INTO _glJournal;
+
+  _glDate := COALESCE(_p.invchead_gldistdate, _p.invchead_invcdate);
+
+-- the 1st MC iteration used the cohead_orderdate so we could get curr exch
+-- gain/loss between the sales and invoice dates, but see issue 3892.  leave
+-- this condition TRUE until we make this configurable or decide not to.
+  IF TRUE THEN
+      _firstExchDate := _p.invchead_invcdate;
+  ELSE
+-- can we save a select by using: _firstExchDate := _p.invchead_orderdate;
+      SELECT cohead_orderdate INTO _firstExchDate
+      FROM cohead
+      WHERE (cohead_number = _p.invchead_ordernumber);
+  END IF;
+
+--  Start by handling taxes (reverse sense)
+  FOR _r IN SELECT tax_sales_accnt_id, 
+              round(sum(taxdetail_tax),2) AS tax,
+              currToBase(_p.invchead_curr_id, round(sum(taxdetail_tax),2), _firstExchDate) AS taxbasevalue
+            FROM tax 
+             JOIN calculateTaxDetailSummary('I', _p.invchead_id, 'T') ON (taxdetail_tax_id=tax_id)
+           GROUP BY tax_id, tax_sales_accnt_id LOOP
+
+    PERFORM insertIntoGLSeries( _glSequence, 'A/R', 'IN', _p.invchead_invcnumber,
+                                _r.tax_sales_accnt_id, 
+                                (_r.taxbasevalue * -1.0),
+                                _glDate, ('Void-' || _p.invchead_billto_name) );
+
+    _totalAmount := _totalAmount + _r.tax;
+    _totalRoundedBase := _totalRoundedBase + _r.taxbasevalue;  
+  END LOOP;
+
+--  March through the Non-Misc. Invcitems
+  FOR _r IN SELECT *
+            FROM invoiceitem
+            WHERE ( (invcitem_invchead_id = _p.invchead_id)
+              AND   (invcitem_item_id <> -1) ) LOOP
+
+--  Cache the amount due for this line
+    _amount := _r.extprice;
+
+    IF (_amount > 0) THEN
+--  Credit the Sales Account for the invcitem item (reverse sense)
+      IF (_r.itemsite_id IS NULL) THEN
+       SELECT getPrjAccntId(_p.invchead_prj_id, salesaccnt_sales_accnt_id) 
+       INTO _tmpAccntId
+       FROM salesaccnt
+       WHERE (salesaccnt_id=findSalesAccnt(_r.invcitem_item_id, 'I', _p.invchead_cust_id,
+                                            _p.invchead_saletype_id, _p.invchead_shipzone_id));
+      ELSE
+       SELECT getPrjAccntId(_p.invchead_prj_id, salesaccnt_sales_accnt_id) 
+       INTO _tmpAccntId
+       FROM salesaccnt
+       WHERE (salesaccnt_id=findSalesAccnt(_r.itemsite_id, 'IS', _p.invchead_cust_id,
+                                            _p.invchead_saletype_id, _p.invchead_shipzone_id));
+      END IF;
+
+--  If the Sales Account Assignment was not found then punt
+      IF (NOT FOUND) THEN
+        PERFORM deleteGLSeries(_glSequence);
+        RETURN -11;
+      END IF;
+
+      _roundedBase := round(currToBase(_p.invchead_curr_id, _amount, _firstExchDate), 2);
+      SELECT insertIntoGLSeries( _glSequence, 'A/R', 'IN', _p.invchead_invcnumber,
+                                 _tmpAccntId,
+                                 (_roundedBase * -1.0),
+                                 _glDate, ('Void-' || _p.invchead_billto_name) ) INTO _test;
+
+      _totalAmount := (_totalAmount + _amount);
+      _totalRoundedBase := _totalRoundedBase + _roundedBase;
+      _commissionDue := (_commissionDue + (_amount * _p.invchead_commission));
+    END IF;
+
+    _totalAmount := _totalAmount;
+    _totalRoundedBase := _totalRoundedBase;
+
+  END LOOP;
+
+--  March through the Misc. Invcitems
+  FOR _r IN SELECT *
+            FROM invoiceitem JOIN salescat ON (salescat_id = invcitem_salescat_id)
+            WHERE ( (invcitem_item_id = -1)
+              AND   (invcitem_invchead_id=_p.invchead_id) ) LOOP
+
+--  Cache the amount due for this line and the commission due for such
+    _amount := _r.extprice;
+
+    IF (_amount > 0) THEN
+--  Credit the Sales Account for the invcitem item (reverse sense)
+      _roundedBase = round(currToBase(_p.invchead_curr_id, _amount,
+                                      _firstExchDate), 2);
+      SELECT insertIntoGLSeries( _glSequence, 'A/R', 'IN', _p.invchead_invcnumber,
+                                 getPrjAccntId(_p.invchead_prj_id, _r.salescat_sales_accnt_id), 
+                                 (_roundedBase * -1.0),
+                                 _glDate, ('Void-' || _p.invchead_billto_name) ) INTO _test;
+
+      IF (_test < 0) THEN
+        PERFORM deleteGLSeries(_glSequence);
+        RETURN _test;
+      END IF;
+
+      _totalAmount := (_totalAmount + _amount);
+      _totalRoundedBase :=  _totalRoundedBase + _roundedBase;
+      _commissionDue := (_commissionDue + (_amount * _p.invchead_commission));
+    END IF;
+
+  END LOOP;
+
+--  Credit the Freight Account for Freight Charges (reverse sense)
+  IF (_p.invchead_freight <> 0) THEN
+    IF (_p.freightaccntid <> -1) THEN
+      _roundedBase = round(currToBase(_p.invchead_curr_id, _p.invchead_freight,
+                                      _firstExchDate), 2);
+      SELECT insertIntoGLSeries( _glSequence, 'A/R', 'IN', _p.invchead_invcnumber,
+                                 getPrjAccntId(_p.invchead_prj_id,_p.freightaccntid), 
+                                 (_roundedBase * -1.0),
+                                 _glDate, ('Void-' || _p.invchead_billto_name) ) INTO _test;
+
+--  Cache the Freight Amount distributed
+        _totalAmount := (_totalAmount + _p.invchead_freight);
+        _totalRoundedBase := _totalRoundedBase + _roundedBase;
+    ELSE
+      _test := -14;
+    END IF;
+
+--  If the Freight Account was not found then punt
+    IF (_test < 0) THEN
+      PERFORM deleteGLSeries(_glSequence);
+      RETURN _test;
+    END IF;
+
+  END IF;
+
+--  Credit the Misc. Account for Miscellaneous Charges (reverse sense)
+  IF (_p.invchead_misc_amount <> 0) THEN
+    _roundedBase := round(currToBase(_p.invchead_curr_id, _p.invchead_misc_amount,
+                                     _firstExchDate), 2);
+    SELECT insertIntoGLSeries( _glSequence, 'A/R', 'IN', _p.invchead_invcnumber,
+                               getPrjAccntId(_p.invchead_prj_id, _p.invchead_misc_accnt_id), 
+                               (_roundedBase * -1.0),
+                               _glDate, ('Void-' || _p.invchead_billto_name) ) INTO _test;
+
+--  If the Misc. Charges Account was not found then punt
+    IF (_test < 0) THEN
+      PERFORM deleteGLSeries(_glSequence);
+      RETURN _test;
+    END IF;
+
+--  Cache the Misc. Amount distributed
+    _totalAmount := (_totalAmount + _p.invchead_misc_amount);
+    _totalRoundedBase := _totalRoundedBase + _roundedBase;
+
+  END IF;
+
+-- ToDo: handle rounding errors (reverse sense)
+    _exchGain := currGain(_p.invchead_curr_id, _totalAmount,
+                          _firstExchDate, _glDate);
+    IF (_exchGain <> 0) THEN
+        SELECT insertIntoGLSeries( _glSequence, 'A/R', 'IN', _p.invchead_invcnumber,
+                                   getGainLossAccntId(_p.araccntid),
+                                   round(_exchGain, 2),
+                                   _glDate, ('Void-' || _p.invchead_billto_name) ) INTO _test ;
+        IF (_test < 0) THEN
+          PERFORM deleteGLSeries(_glSequence);
+          RETURN _test;
+        END IF;
+    END IF;
+
+--  Debit A/R for the total Amount (reverse sense)
+  IF (_totalRoundedBase <> 0) THEN
+    IF (_p.araccntid != -1) THEN
+      SELECT insertIntoGLSeries( _glSequence, 'A/R', 'IN', _p.invchead_invcnumber,
+                                 _p.araccntid,
+                                 round(_totalRoundedBase, 2),
+                                 _glDate, ('Void-' || _p.invchead_billto_name) ) INTO _test;
+    ELSE
+      PERFORM deleteGLSeries(_glSequence);
+      RETURN _test;
+    END IF;
+  END IF;
+
+--  Commit the GLSeries;
+  SELECT postGLSeries(_glSequence, _glJournal) INTO _test;
+  IF (_test < 0) THEN
+    PERFORM deleteGLSeries(_glSequence);
+    RETURN _test;
+  END IF;
+
+--  Delete sales history
+  DELETE FROM cohisttax
+  WHERE (taxhist_parent_id IN (SELECT cohist_id
+                               FROM cohist
+                               WHERE (cohist_doctype='I' AND cohist_invcnumber=_p.invchead_invcnumber)));
+
+  DELETE FROM cohist
+  WHERE (cohist_doctype='I' AND cohist_invcnumber=_p.invchead_invcnumber);
+
+--  Create the Credit aropen item
+  SELECT nextval('aropen_aropen_id_seq') INTO _aropenid;
+  INSERT INTO aropen
+  ( aropen_id, aropen_username, aropen_journalnumber,
+    aropen_open, aropen_posted,
+    aropen_cust_id, aropen_ponumber,
+    aropen_docnumber, aropen_applyto, aropen_doctype,
+    aropen_docdate, aropen_duedate, aropen_distdate, aropen_terms_id,
+    aropen_amount, aropen_paid,
+    aropen_salesrep_id, aropen_commission_due, aropen_commission_paid,
+    aropen_ordernumber, aropen_notes, aropen_cobmisc_id,
+    aropen_curr_id )
+  VALUES
+  ( _aropenid, getEffectiveXtUser(), _glJournal,
+    TRUE, FALSE,
+    _p.invchead_cust_id, _p.invchead_ponumber,
+    _p.invchead_invcnumber, _p.invchead_invcnumber, 'C',
+    _p.invchead_invcdate, determineDueDate(_p.invchead_terms_id, _p.invchead_invcdate), _glDate, _p.invchead_terms_id,
+    round(_totalAmount, 2), round(_totalAmount, 2), 
+    _p.invchead_salesrep_id, _commissionDue, FALSE,
+    _p.invchead_ordernumber::text, _p.invchead_notes, pInvcheadid,
+    _p.invchead_curr_id );
+
+--  Alter the Invoice A/R Open Item to reflect the application
+    UPDATE aropen
+    SET aropen_paid = round(_totalAmount, 2)
+    WHERE (aropen_id=_p.aropen_id);
+
+--  Record the application
+    INSERT INTO arapply
+    ( arapply_cust_id,
+      arapply_source_aropen_id, arapply_source_doctype, arapply_source_docnumber,
+      arapply_target_aropen_id, arapply_target_doctype, arapply_target_docnumber,
+      arapply_fundstype, arapply_refnumber,
+      arapply_applied, arapply_closed,
+      arapply_postdate, arapply_distdate, arapply_journalnumber, arapply_curr_id )
+    VALUES
+    ( _p.invchead_cust_id,
+      _aropenid, 'C', _p.invchead_invcnumber,
+      _p.aropen_id, 'I', _p.invchead_invcnumber,
+      '', '',
+      round(_totalAmount, 2), TRUE,
+      CURRENT_DATE, _p.invchead_invcdate, 0, _p.invchead_curr_id );
+
+-- Handle the Inventory and G/L Transactions for any billed Inventory where invcitem_updateinv is true (reverse sense)
+  FOR _r IN SELECT itemsite_id AS itemsite_id, invcitem_id,
+                   (invcitem_billed * invcitem_qty_invuomratio) AS qty,
+                   invchead_invcnumber, invchead_cust_id AS cust_id, item_number,
+                   invchead_prj_id AS prj_id, invchead_saletype_id AS saletype_id,
+                   invchead_shipzone_id AS shipzone_id
+            FROM invchead JOIN invcitem ON ( (invcitem_invchead_id=invchead_id) AND
+                                             (invcitem_billed <> 0) AND
+                                             (invcitem_updateinv) )
+                          JOIN itemsite ON ( (itemsite_item_id=invcitem_item_id) AND
+                                             (itemsite_warehous_id=invcitem_warehous_id) )
+                          JOIN item ON (item_id=invcitem_item_id)
+            WHERE (invchead_id=_p.invchead_id) LOOP
+
+--  Issue billed stock from inventory
+    IF (_itemlocSeries = 0) THEN
+      SELECT NEXTVAL('itemloc_series_seq') INTO _itemlocSeries;
+    END IF;
+    SELECT postInvTrans( itemsite_id, 'SH', (_r.qty * -1.0),
+                         'S/O', 'IN', _r.invchead_invcnumber, '',
+                         ('Invoice Voided ' || _r.item_number),
+                         getPrjAccntId(_r.prj_id, resolveCOSAccount(itemsite_id, _r.cust_id, _r.saletype_id, _r.shipzone_id)),
+                         costcat_asset_accnt_id, _itemlocSeries, _glDate) INTO _invhistid
+    FROM itemsite, costcat
+    WHERE ( (itemsite_costcat_id=costcat_id)
+     AND (itemsite_id=_r.itemsite_id) );
+
+  END LOOP;
+
+--  Reopen Billing
+  UPDATE shipitem
+  SET shipitem_invoiced=FALSE,
+      shipitem_invcitem_id=NULL
+  WHERE (shipitem_invcitem_id IN (SELECT invcitem_id
+                                  FROM invcitem
+                                  WHERE (invcitem_invchead_id=_p.invchead_id)));
+  UPDATE cobill
+  SET cobill_invcnum=NULL,
+      cobill_invcitem_id=NULL
+  WHERE (cobill_invcitem_id IN (SELECT invcitem_id
+                                FROM invcitem
+                                WHERE (invcitem_invchead_id=_p.invchead_id)));
+  UPDATE cobmisc
+  SET cobmisc_posted=FALSE,
+      cobmisc_invcnumber=NULL,
+      cobmisc_invchead_id=NULL
+  WHERE (cobmisc_invchead_id=_p.invchead_id);
+
+--  Mark the invoice as voided
+  UPDATE invchead
+  SET invchead_void=TRUE,
+      invchead_notes=(invchead_notes || 'Voided on ' || current_date || ' by ' || getEffectiveXtUser())
+  WHERE (invchead_id=_p.invchead_id);
+  RETURN _itemlocSeries;
+
+END;
+$$ LANGUAGE plpgsql;
diff --git a/foundation-database/public/functions/voidpostedapcheck.sql b/foundation-database/public/functions/voidpostedapcheck.sql
new file mode 100644 (file)
index 0000000..6b8ec1a
--- /dev/null
@@ -0,0 +1,27 @@
+CREATE OR REPLACE FUNCTION voidPostedAPCheck(INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RAISE NOTICE ''voidPostedAPCheck() is deprecated - use voidPostedCheck() instead'';
+  RETURN voidPostedCheck($1, fetchJournalNumber(''AP-CK''), CURRENT_DATE);
+END;
+' LANGUAGE 'plpgsql';
+
+
+CREATE OR REPLACE FUNCTION voidPostedAPCheck(INTEGER, INTEGER) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RAISE NOTICE ''voidPostedAPCheck() is deprecated - use voidPostedCheck() instead'';
+  RETURN voidPostedCheck($1, $2, CURRENT_DATE);
+END;
+' LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION voidPostedAPCheck(INTEGER, INTEGER, DATE) RETURNS INTEGER AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+BEGIN
+  RAISE NOTICE ''voidPostedAPCheck() is deprecated - use voidPostedCheck() instead'';
+  RETURN voidPostedCheck($1, $2, $3);
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/voidpostedcheck.sql b/foundation-database/public/functions/voidpostedcheck.sql
new file mode 100644 (file)
index 0000000..6e5af58
--- /dev/null
@@ -0,0 +1,308 @@
+CREATE OR REPLACE FUNCTION voidPostedCheck(INTEGER, INTEGER, DATE) RETURNS INTEGER AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pCheckid             ALIAS FOR $1;
+  pJournalNumber       ALIAS FOR $2;
+  pVoidDate            ALIAS FOR $3;
+  _amount_base         NUMERIC := 0;
+  _result               INTEGER;
+  _apopenid            INTEGER;
+  _credit_glaccnt      INTEGER;
+  _docnumber           TEXT;
+  _exchGain            NUMERIC := 0;
+  _exchGainTmp         NUMERIC := 0;
+  _gltransNote         TEXT;
+  _p                   RECORD;
+  _r                   RECORD;
+  _sequence            INTEGER;
+  _amount_check         NUMERIC := 0;
+
+BEGIN
+
+  SELECT fetchGLSequence() INTO _sequence;
+
+  SELECT checkhead.*,
+         checkhead_amount / checkhead_curr_rate AS checkhead_amount_base,
+         bankaccnt_accnt_id AS bankaccntid,
+         findPrepaidAccount(checkhead_recip_id) AS prepaidaccntid,
+        checkrecip.* INTO _p
+  FROM bankaccnt, checkhead LEFT OUTER JOIN
+       checkrecip ON ((checkrecip_type=checkhead_recip_type)
+                 AND (checkrecip_id=checkhead_recip_id))
+  WHERE ((checkhead_bankaccnt_id=bankaccnt_id)
+    AND  (checkhead_id=pCheckid));
+
+  IF (NOT _p.checkhead_posted) THEN
+    RETURN -10;
+  END IF;
+
+  IF (_p.checkrecip_id IS NULL) THEN   -- outer join failed
+    RETURN -11;
+  END IF;
+
+  -- Cannot void if already reconciled
+  SELECT trans_id INTO _result
+  FROM ( SELECT gltrans_id AS trans_id
+         FROM gltrans
+         WHERE ((gltrans_doctype='CK')
+           AND  (gltrans_misc_id=_p.checkhead_id)
+           AND  (gltrans_rec))
+         UNION ALL
+         SELECT sltrans_id AS trans_id
+         FROM sltrans
+         WHERE ((sltrans_doctype='CK')
+           AND  (sltrans_misc_id=_p.checkhead_id)
+           AND  (sltrans_rec)) ) AS data;
+  IF (FOUND) THEN
+    RETURN -14;
+  END IF;
+
+  _gltransNote := 'Void Posted Check #' || _p.checkhead_number || ' ' ||
+                 _p.checkrecip_number || '-' || _p.checkrecip_name;
+
+  IF (_p.checkhead_misc) THEN
+    IF (COALESCE(_p.checkhead_expcat_id, -1) < 0) THEN
+      IF (_p.checkhead_recip_type = 'V') THEN
+       PERFORM createAPDebitMemo(_p.checkhead_recip_id, pJournalNumber,
+                                 CAST(fetchAPMemoNumber() AS text), '',
+                                 pVoidDate, _p.checkhead_amount,
+                                 _gltransNote || ' '|| _p.checkhead_notes,
+                                 -1, pVoidDate, -1, _p.checkhead_curr_id );
+       _credit_glaccnt := findAPPrepaidAccount(_p.checkhead_recip_id);
+
+      ELSIF (_p.checkhead_recip_type = 'C') THEN
+       PERFORM createARCreditMemo(NULL, _p.checkhead_recip_id,
+                                 fetchARMemoNumber(), '', 
+                                 pVoidDate, _p.checkhead_amount,
+                                 _gltransNote || ' '|| _p.checkhead_notes,
+                                 -1, -1, -1, pVoidDate, -1, NULL, 0.0,
+                                 pJournalNumber, _p.checkhead_curr_id );
+       _credit_glaccnt := _p.prepaidaccntid;
+
+      ELSIF (_p.checkhead_recip_type = 'T') THEN
+       -- TODO: should we create a debit memo for the tax authority? how?
+       _credit_glaccnt := _p.checkrecip_accnt_id;
+
+      END IF; -- recip type
+
+    ELSE
+      SELECT expcat_exp_accnt_id INTO _credit_glaccnt
+      FROM expcat
+      WHERE (expcat_id=_p.checkhead_expcat_id);
+      IF (NOT FOUND) THEN
+        RETURN -12;
+      END IF;
+    END IF;
+
+    IF (COALESCE(_credit_glaccnt, -1) < 0) THEN
+      RETURN -13;
+    END IF;
+
+    PERFORM insertIntoGLSeries( _sequence, _p.checkrecip_gltrans_source, 'CK',
+                               text(_p.checkhead_number),
+                               _credit_glaccnt,
+                               round(_p.checkhead_amount_base, 2),
+                               pVoidDate, _gltransNote, pCheckid);
+
+    _amount_base := _p.checkhead_amount_base;
+
+  ELSE
+    FOR _r IN SELECT checkitem_amount, checkitem_discount,
+                     CASE WHEN (checkitem_apopen_id IS NOT NULL) THEN
+                       checkitem_amount / apopen_curr_rate
+                     ELSE
+                       currToBase(checkitem_curr_id,
+                                  checkitem_amount,
+                                  COALESCE(checkitem_docdate, _p.checkhead_checkdate)) 
+                     END AS checkitem_amount_base,
+                       checkitem_amount / checkitem_curr_rate   AS amount_check,
+                     apopen_id, apopen_doctype, apopen_docnumber, apopen_curr_rate, apopen_docdate,
+                     aropen_id, aropen_doctype, aropen_docnumber,
+                     checkitem_curr_id, checkitem_curr_rate,
+                     COALESCE(checkitem_docdate, _p.checkhead_checkdate) AS docdate
+              FROM (checkitem LEFT OUTER JOIN
+                   apopen ON (checkitem_apopen_id=apopen_id)) LEFT OUTER JOIN
+                   aropen ON (checkitem_aropen_id=aropen_id)
+              WHERE (checkitem_checkhead_id=pcheckid) LOOP
+
+      _exchGainTmp := 0;
+      IF (_r.apopen_id IS NOT NULL) THEN
+       -- undo the APDiscount Credit Memo if a discount was taken
+        IF(_r.checkitem_discount > 0) THEN
+          SELECT NEXTVAL('apopen_apopen_id_seq') INTO _apopenid;
+          SELECT fetchAPMemoNumber() INTO _docnumber;
+          INSERT INTO apopen
+          ( apopen_id, apopen_username, apopen_journalnumber,
+            apopen_vend_id, apopen_docnumber, apopen_doctype, apopen_ponumber,
+            apopen_docdate, apopen_duedate, apopen_distdate, apopen_terms_id,
+            apopen_amount, apopen_paid, apopen_open,
+           apopen_notes,
+           apopen_accnt_id, apopen_curr_id, apopen_discount, apopen_curr_rate,
+            apopen_closedate )
+          VALUES
+          ( _apopenid, getEffectiveXtUser(), pJournalNumber,
+            _p.checkhead_recip_id, _docnumber, 'D', '',
+            pVoidDate, pVoidDate, pVoidDate, -1,
+            _r.checkitem_discount, _r.checkitem_discount, FALSE,
+            ('Reverse Posted Discount ' || _r.apopen_doctype || ' ' ||
+             _r.apopen_docnumber),
+           -1, _p.checkhead_curr_id, TRUE, _r.apopen_curr_rate,
+            current_date );
+
+
+          PERFORM insertIntoGLSeries( _sequence, _p.checkrecip_gltrans_source,
+                                     'DS', _r.apopen_docnumber,
+                                      findAPDiscountAccount(_p.checkhead_recip_id),
+                                      round(_r.checkitem_discount / _r.apopen_curr_rate, 2) * -1,
+                                      pVoidDate, _gltransNote, pCheckid);
+
+          PERFORM insertIntoGLSeries( _sequence, _p.checkrecip_gltrans_source,
+                                     'DS', _r.apopen_docnumber,
+                                      findAPAccount(_p.checkhead_recip_id),
+                                      round(_r.checkitem_discount / _r.apopen_curr_rate, 2),
+                                      pVoidDate, _gltransNote, pCheckid);
+
+         --  Post the application
+          INSERT INTO apapply
+          ( apapply_vend_id, apapply_postdate, apapply_username,
+            apapply_source_apopen_id, apapply_source_doctype, apapply_source_docnumber,
+            apapply_target_apopen_id, apapply_target_doctype, apapply_target_docnumber,
+            apapply_journalnumber, apapply_amount, apapply_curr_id )
+          VALUES
+          ( _p.checkhead_recip_id, pVoidDate, getEffectiveXtUser(),
+            _apopenid, 'D', _docnumber,
+            _r.apopen_id, _r.apopen_doctype, _r.apopen_docnumber,
+            pJournalNumber, (_r.checkitem_discount * -1), _r.checkitem_curr_id );
+        END IF; -- discount was taken
+
+        UPDATE apopen
+       SET apopen_paid = round(apopen_paid -
+                               (_r.checkitem_amount + noNeg(_r.checkitem_discount)), 2),
+            apopen_open = round(apopen_amount, 2) >
+                         round(apopen_paid -
+                               (_r.checkitem_amount + noNeg(_r.checkitem_discount)), 2),
+            apopen_closedate = CASE WHEN (round(apopen_amount, 2) >
+                                         round(apopen_paid -
+                                          (_r.checkitem_amount + noNeg(_r.checkitem_discount)))) THEN NULL ELSE apopen_closedate END
+        WHERE (apopen_id=_r.apopen_id);
+
+       --  Post the application
+        INSERT INTO apapply
+        ( apapply_vend_id, apapply_postdate, apapply_username,
+          apapply_source_apopen_id, apapply_source_doctype, apapply_source_docnumber,
+          apapply_target_apopen_id, apapply_target_doctype, apapply_target_docnumber,
+          apapply_journalnumber, apapply_amount, apapply_curr_id )
+        VALUES
+        ( _p.checkhead_recip_id, pVoidDate, getEffectiveXtUser(),
+          -1, 'K', _p.checkhead_number,
+          _r.apopen_id, _r.apopen_doctype, _r.apopen_docnumber,
+          pJournalNumber, (_r.checkitem_amount * -1), _r.checkitem_curr_id );
+      END IF; -- if check item's apopen_id is not null
+
+      IF (_r.aropen_id IS NOT NULL) THEN
+        UPDATE aropen
+        SET aropen_paid = round(aropen_paid -_r.checkitem_amount, 2),
+            aropen_open = round(aropen_amount, 2) >
+                         round(aropen_paid - _r.checkitem_amount, 2)
+        WHERE (aropen_id=_r.aropen_id);
+
+       --  Post the application
+        INSERT INTO arapply
+        ( arapply_cust_id, arapply_postdate, arapply_distdate, arapply_username,
+          arapply_source_aropen_id, arapply_source_doctype, arapply_source_docnumber,
+          arapply_target_aropen_id, arapply_target_doctype, arapply_target_docnumber,
+          arapply_journalnumber, arapply_applied, arapply_curr_id )
+        VALUES
+        ( _p.checkhead_recip_id, pVoidDate, pVoidDate, getEffectiveXtUser(),
+          -1, 'K', _p.checkhead_number,
+          _r.aropen_id, _r.aropen_doctype, _r.aropen_docnumber,
+          pJournalNumber, (_r.checkitem_amount * -1), _r.checkitem_curr_id );
+
+      END IF; -- if check item's aropen_id is not null
+
+--  calculate currency gain/loss
+      IF (_r.apopen_id IS NOT NULL) THEN
+        IF (_p.checkhead_curr_id = _r.checkitem_curr_id) THEN
+          IF (_r.apopen_docdate > _p.checkhead_checkdate) THEN
+            _exchGainTmp := ((_r.checkitem_amount/_p.checkhead_curr_rate) - (_r.checkitem_amount / _r.apopen_curr_rate)) * -1;
+          ELSE
+            _exchGainTmp := ((_r.checkitem_amount / _r.apopen_curr_rate) - (_r.checkitem_amount/_p.checkhead_curr_rate));
+          END IF;
+        ELSE
+          -- unusual condition where bank overridden and different currency from voucher
+          IF (_r.apopen_docdate > _p.checkhead_checkdate) THEN
+            _exchGainTmp := ((_r.checkitem_amount/_r.checkitem_curr_rate) - (_r.checkitem_amount / _r.apopen_curr_rate)) * -1;
+          ELSE
+            _exchGainTmp := ((_r.checkitem_amount / _r.apopen_curr_rate) - (_r.checkitem_amount/_r.checkitem_curr_rate));
+          END IF;
+        END IF;
+      ELSE
+        SELECT arCurrGain(_r.aropen_id,_r.checkitem_curr_id, _r.checkitem_amount,
+                        _p.checkhead_checkdate)
+              INTO _exchGainTmp;
+      END IF;
+      _exchGain := _exchGain + _exchGainTmp;
+
+      PERFORM insertIntoGLSeries( _sequence, _p.checkrecip_gltrans_source,
+                                 'CK', text(_p.checkhead_number),
+                                  _p.checkrecip_accnt_id,
+                                  round(_r.checkitem_amount_base, 2),
+                                  pVoidDate, _gltransNote, pCheckid);
+      IF (_exchGainTmp <> 0) THEN
+          PERFORM insertIntoGLSeries( _sequence, _p.checkrecip_gltrans_source,
+                                     'CK', text(_p.checkhead_number),
+                                     getGainLossAccntId(_p.checkrecip_accnt_id),
+                                     round(_exchGainTmp, 2) * -1,
+                                     pVoidDate, _gltransNote, pCheckid);
+      END IF;
+
+      _amount_check := (_amount_check + _r.amount_check);
+      _amount_base := (_amount_base + _r.checkitem_amount_base);
+
+    END LOOP;
+
+    IF( (_amount_check - _p.checkhead_amount) <> 0.0 ) THEN 
+      _exchGainTmp :=  (_amount_check - _p.checkhead_amount) / _p.checkhead_curr_rate;
+      _exchGain := _exchGain + _exchGainTmp;
+    END IF;
+
+    --  ensure that the check balances, attribute rounding errors to gain/loss
+    IF round(_amount_base, 2) - round(_exchGain, 2) <> round(_p.checkhead_amount_base, 2) THEN
+      IF round(_amount_base - _exchGain, 2) = round(_p.checkhead_amount_base, 2) THEN
+       PERFORM insertIntoGLSeries( _sequence, _p.checkrecip_gltrans_source,
+                                   'CK',
+                                   text(_p.checkhead_number), getGainLossAccntId(_p.bankaccntid),
+                                   (round(_amount_base, 2) -
+                                    round(_exchGain, 2) -
+                                    round(_p.checkhead_amount_base, 2)) * -1,
+                                   pVoidDate, _gltransNote, pCheckid);
+      ELSE
+       RAISE EXCEPTION 'checkhead_id % does not balance (% - % <> %)', pCheckid,
+             _amount_base, _exchGain, _p.checkhead_amount_base;
+      END IF;
+    END IF;
+  END IF;
+
+  PERFORM insertIntoGLSeries( _sequence, _p.checkrecip_gltrans_source, 'CK',
+                             text(_p.checkhead_number),
+                              _p.bankaccntid,
+                             round(_p.checkhead_amount_base, 2) * -1,
+                              pVoidDate, _gltransNote, pCheckid);
+
+  PERFORM postGLSeries(_sequence, pJournalNumber);
+
+  UPDATE gltrans
+     SET gltrans_misc_id=pCheckid
+   WHERE gltrans_sequence=_sequence;
+
+  UPDATE checkhead
+  SET checkhead_posted=false,
+      checkhead_void=true,
+      checkhead_journalnumber=pJournalNumber
+  WHERE (checkhead_id=pCheckid);
+
+  RETURN pJournalNumber;
+
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/woeffectivedate.sql b/foundation-database/public/functions/woeffectivedate.sql
new file mode 100644 (file)
index 0000000..880efd3
--- /dev/null
@@ -0,0 +1,16 @@
+CREATE OR REPLACE FUNCTION woEffectiveDate(DATE) RETURNS DATE AS '
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  pStartDate ALIAS FOR $1;
+
+BEGIN
+
+  IF (explodeWoEffective() = ''E'') THEN
+    RETURN CURRENT_DATE;
+  ELSE
+    RETURN pStartDate;
+  END IF;
+
+END;
+' LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/woinvavail.sql b/foundation-database/public/functions/woinvavail.sql
new file mode 100644 (file)
index 0000000..4ef05f1
--- /dev/null
@@ -0,0 +1,249 @@
+
+CREATE OR REPLACE FUNCTION woinvavail(integer, boolean, boolean, boolean, boolean)
+  RETURNS SETOF woinvav AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+   pwoid ALIAS FOR $1;   
+   pshowchildindent ALIAS FOR $2;    
+   pshowchildsum ALIAS FOR $3;
+   pshowshortage ALIAS FOR $4;
+   pshowlowinventory ALIAS FOR $5;
+  _row woinvav%ROWTYPE;
+  _subrow woinvav%ROWTYPE;
+  _wonumber TEXT;
+  _x RECORD;
+  _subx RECORD;  
+  _qry TEXT;
+   
+BEGIN   
+    
+    IF(pshowchildindent) THEN 
+      --get top level order   
+      FOR _x IN
+          SELECT wo_id,
+                itemsite_id,
+                item_type,
+                wo_number,
+                wo_subnumber,                             
+                item_number,
+                item_descrip1, 
+                item_descrip2, 
+                uom_name,
+                qoh, 
+                wobalance, 
+                allocated, 
+                ordered,                        
+                reorderlevel,
+                (qoh + ordered - wobalance) AS woavail,
+                (qoh + ordered - allocated) AS totalavail 
+         FROM(SELECT wo_id,
+                itemsite_id,
+                item_type,
+                wo_number,
+                wo_subnumber,                             
+                item_number,
+                item_descrip1, 
+                item_descrip2, 
+                uom_name,
+                noNeg(itemsite_qtyonhand) AS qoh, 
+                noNeg(wo_qtyord - wo_qtyrcv) AS wobalance, 
+                qtyAllocated(itemsite_id, wo_duedate) AS allocated, 
+                qtyOrdered(itemsite_id, wo_duedate) AS ordered,                        
+                CASE WHEN(itemsite_useparams) THEN itemsite_reorderlevel ELSE 0.0 END AS reorderlevel 
+          FROM wo, itemsite, item, uom     
+         WHERE ((wo_id = pwoid)          
+           AND (itemsite_id = wo_itemsite_id)
+           AND (itemsite_item_id=item_id)
+           AND (item_inv_uom_id=uom_id))               
+         ORDER BY wo_number, wo_subnumber) AS data
+       LOOP       
+         _row.woinvav_itemsite_id := _x.itemsite_id;            
+         _row.woinvav_womatl_id := -1;
+         _row.woinvav_type := _x.item_type;          
+         _row.woinvav_item_wo_number := _x.wo_number || '-' || _x.wo_subnumber;
+         _row.woinvav_descrip := _x.item_descrip1 || ' ' || _x.item_descrip2;
+         _row.woinvav_uomname := _x.uom_name;
+         _row.woinvav_qoh := _x.qoh;
+         _row.woinvav_balance := _x.wobalance;
+         _row.woinvav_allocated := _x.allocated;     
+         _row.woinvav_ordered := _x.ordered;         
+         _row.woinvav_woavail := _x.woavail;
+         _row.woinvav_totalavail := _x.totalavail;
+         _row.woinvav_reorderlevel := _x.reorderlevel;
+         _row.woinvav_level := 0;                       
+         RETURN NEXT _row;                
+        --get materials for this level        
+        FOR _subx IN
+          SELECT * FROM woinvavailmatl(_x.wo_id, 1, pshowshortage, pshowlowinventory) 
+        LOOP                                            
+         RETURN NEXT _subx;
+       END LOOP;  
+       FOR _subx IN
+          SELECT * FROM woinvavail(_x.wo_id, 1, pshowshortage, pshowlowinventory)
+        LOOP                                            
+         RETURN NEXT _subx;
+       END LOOP;  
+     END LOOP;             
+    ELSE
+       SELECT wo_number FROM wo WHERE wo_id=pwoid LIMIT 1 INTO _wonumber;   
+       --display a single level sum of work order requirements
+       _qry := 'SELECT  wo_id,
+                        itemsite_id, 
+                       womatl_id,
+                       item_type,
+                       wo_number,                         
+                        item_number, 
+                        item_descrip1, 
+                        item_descrip2, 
+                        uom_name,                         
+                        qoh, 
+                        wobalance, 
+                        allocated, 
+                        ordered,                        
+                        reorderlevel,
+                        (qoh + ordered - wobalance) AS woavail,
+                        (qoh + ordered - allocated) AS totalavail
+                 FROM (SELECT wo_id,
+                       itemsite_id, 
+                       womatl_id,
+                       item_type,                      
+                       wo_number,                         
+                        item_number, 
+                        item_descrip1, 
+                        item_descrip2, 
+                        uom_name,                         
+                        noNeg(itemsite_qtyonhand) AS qoh, 
+                        noNeg(itemuomtouom(itemsite_item_id, womatl_uom_id, NULL, womatl_qtyreq - womatl_qtyiss)) AS wobalance, 
+                        qtyAllocated(itemsite_id, womatl_duedate) AS allocated, 
+                        qtyOrdered(itemsite_id, womatl_duedate) AS ordered,                        
+                        CASE WHEN(itemsite_useparams) THEN itemsite_reorderlevel ELSE 0.0 END AS reorderlevel 
+                   FROM wo, womatl, itemsite, item, uom  
+                 WHERE (womatl_wo_id=wo_id) 
+                   AND (womatl_itemsite_id=itemsite_id) 
+                   AND (itemsite_item_id=item_id) 
+                   AND (item_inv_uom_id=uom_id) ';
+                IF(pshowchildsum) THEN
+                  _qry := _qry || ' AND (wo_number=' || _wonumber || ')';
+                ELSE  
+                  _qry := _qry || ' AND (womatl_wo_id=' || pwoid || ')';                  
+                END IF; 
+                 _qry := _qry || ' ORDER BY item_number) AS data ';  
+                IF(pshowshortage) THEN
+                  _qry := _qry || ' WHERE (((qoh + ordered - allocated) < 0) OR ((qoh + ordered - wobalance) < 0)) '; 
+                END IF;
+                IF(pshowlowinventory AND NOT pshowshortage) THEN                 
+                  _qry := _qry || ' WHERE (((qoh - allocated) < 0) OR ((qoh - wobalance) < 0)) '; 
+                END IF;                 
+                
+      FOR _x IN   
+         EXECUTE _qry
+      LOOP        
+        _row.woinvav_itemsite_id := _x.itemsite_id;            
+        _row.woinvav_womatl_id := _x.womatl_id;  
+        _row.woinvav_type := _x.item_type;      
+        _row.woinvav_item_wo_number := _x.item_number;
+        _row.woinvav_descrip := _x.item_descrip1 || ' ' || _x.item_descrip2;
+        _row.woinvav_uomname := _x.uom_name;
+        _row.woinvav_qoh := _x.qoh;
+        _row.woinvav_balance := _x.wobalance;
+        _row.woinvav_allocated := _x.allocated;     
+        _row.woinvav_ordered := _x.ordered;         
+        _row.woinvav_woavail := _x.woavail;
+        _row.woinvav_totalavail := _x.totalavail;
+        _row.woinvav_reorderlevel := _x.reorderlevel;
+        _row.woinvav_level := 0;                
+        RETURN NEXT _row;  
+  END LOOP;
+  END IF;                     
+  RETURN;
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION woinvavail(integer, integer, boolean, boolean)
+  RETURNS SETOF woinvav AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+   pwoid ALIAS FOR $1; 
+   plevel ALIAS FOR $2;
+   pshowshortage ALIAS FOR $3;
+   pshowlowinventory ALIAS FOR $4;
+  _row woinvav%ROWTYPE;
+  _x RECORD;
+  _subx RECORD;
+  _index INTEGER;
+  _level INTEGER;
+  _qry TEXT;   
+BEGIN   
+    FOR _x IN
+          SELECT wo_id,
+                itemsite_id,
+                item_type,
+                wo_number,
+                wo_subnumber,                             
+                item_number,
+                item_descrip1, 
+                item_descrip2, 
+                uom_name,
+                qoh, 
+                wobalance, 
+                allocated, 
+                ordered,                        
+                reorderlevel,
+                (qoh + ordered - wobalance) AS woavail,
+                (qoh + ordered - allocated) AS totalavail 
+         FROM(SELECT wo_id,
+                itemsite_id,
+                item_type,
+                wo_number,
+                wo_subnumber,                             
+                item_number,
+                item_descrip1, 
+                item_descrip2, 
+                uom_name,
+                noNeg(itemsite_qtyonhand) AS qoh, 
+                noNeg(wo_qtyord - wo_qtyrcv) AS wobalance, 
+                qtyAllocated(itemsite_id, wo_duedate) AS allocated, 
+                qtyOrdered(itemsite_id, wo_duedate) AS ordered,                        
+                CASE WHEN(itemsite_useparams) THEN itemsite_reorderlevel ELSE 0.0 END AS reorderlevel 
+          FROM wo, itemsite, item, uom     
+         WHERE ((wo_ordid = pwoid)
+           AND NOT (wo_status = 'C')          
+           AND (itemsite_id = wo_itemsite_id)
+           AND (itemsite_item_id=item_id)
+           AND (item_inv_uom_id=uom_id))               
+         ORDER BY wo_number, wo_subnumber) AS data
+       LOOP       
+         _row.woinvav_itemsite_id := _x.itemsite_id;            
+         _row.woinvav_womatl_id := -1;
+         _row.woinvav_type := _x.item_type;          
+         _row.woinvav_item_wo_number := _x.wo_number || '-' || _x.wo_subnumber;
+         _row.woinvav_descrip := _x.item_descrip1 || ' ' || _x.item_descrip2;
+         _row.woinvav_uomname := _x.uom_name;
+         _row.woinvav_qoh := _x.qoh;
+         _row.woinvav_balance := _x.wobalance;
+         _row.woinvav_allocated := _x.allocated;     
+         _row.woinvav_ordered := _x.ordered;         
+         _row.woinvav_woavail := _x.woavail;
+         _row.woinvav_totalavail := _x.totalavail;
+         _row.woinvav_reorderlevel := _x.reorderlevel;               
+         _row.woinvav_level := plevel;                       
+         RETURN NEXT _row;         
+        --get materials for this level
+        FOR _subx IN
+          SELECT * FROM woinvavailmatl(_x.wo_id, plevel + 1, pshowshortage, pshowlowinventory) 
+        LOOP                                            
+         RETURN NEXT _subx;
+       END LOOP;
+        --get next level wo
+        FOR _subx IN
+          SELECT * FROM woinvavail(_x.wo_id, plevel + 1, pshowshortage, pshowlowinventory) 
+        LOOP                                            
+         RETURN NEXT _subx;
+       END LOOP;
+      END LOOP;   
+  RETURN;
+END;
+$$ LANGUAGE 'plpgsql';
+
diff --git a/foundation-database/public/functions/woinvavailmatl.sql b/foundation-database/public/functions/woinvavailmatl.sql
new file mode 100644 (file)
index 0000000..c94d0f2
--- /dev/null
@@ -0,0 +1,86 @@
+CREATE OR REPLACE FUNCTION woinvavailmatl(integer, integer, boolean, boolean)
+  RETURNS SETOF woinvav AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+   pwoid ALIAS FOR $1;    
+   plevel ALIAS FOR $2;
+   pshowshortage ALIAS FOR $3;
+   pshowlowinventory ALIAS FOR $4;
+  _subrow woinvav%ROWTYPE;  
+  _subx RECORD;
+  _qry TEXT;
+BEGIN
+  
+   _qry := 'SELECT itemsite_id, 
+           womatl_id,
+           item_type, 
+           wo_number, 
+           wo_subnumber, 
+           womatl_ref, 
+           womatl_notes, 
+           item_number,
+           item_descrip1, 
+           item_descrip2, 
+           uom_name,
+           qoh, 
+           wobalance, 
+           allocated, 
+           ordered,
+           (qoh + ordered - wobalance) AS woavail,
+           (qoh + ordered - allocated) AS totalavail,
+           reorderlevel 
+    FROM(SELECT itemsite_id, 
+                womatl_id,
+                item_type,                 
+                wo_number, 
+                wo_subnumber, 
+                womatl_ref, 
+                womatl_notes, 
+                item_number,
+                item_descrip1, 
+                item_descrip2, 
+                uom_name,
+                noNeg(itemsite_qtyonhand) AS qoh, 
+                noNeg(itemuomtouom(itemsite_item_id, womatl_uom_id, NULL, womatl_qtyreq - womatl_qtyiss)) AS wobalance, 
+                qtyAllocated(itemsite_id, womatl_duedate) AS allocated, 
+                qtyOrdered(itemsite_id, womatl_duedate) AS ordered,
+                CASE WHEN(itemsite_useparams) THEN itemsite_reorderlevel ELSE 0.0 END AS reorderlevel
+    FROM womatl, wo, itemsite, item, uom
+    WHERE ((wo_id = womatl_wo_id)     
+     AND (womatl_itemsite_id = itemsite_id)
+     AND (itemsite_item_id=item_id)
+     AND (womatl_uom_id=uom_id)
+     AND (NOT womatl_createwo OR womatl_createwo IS NULL))';
+     _qry := _qry || ' AND (wo_id=' || pwoid || ') ORDER BY item_number) AS data';
+     IF(pshowshortage) THEN
+     _qry := _qry || ' WHERE (((qoh + ordered - allocated) < 0) OR ((qoh + ordered - wobalance) < 0)) '; 
+     END IF;
+     IF(pshowlowinventory AND NOT pshowshortage) THEN                 
+     _qry := _qry || ' WHERE (((qoh - allocated) < 0) OR ((qoh - wobalance) < 0)) '; 
+     END IF;
+        
+     
+     
+  FOR _subx IN
+      EXECUTE _qry
+  LOOP
+     _subrow.woinvav_itemsite_id := _subx.itemsite_id;            
+     _subrow.woinvav_womatl_id := _subx.womatl_id;
+     _subrow.woinvav_type := _subx.item_type;            
+     _subrow.woinvav_item_wo_number := _subx.item_number;
+     _subrow.woinvav_descrip := _subx.item_descrip1 || ' ' || _subx.item_descrip2;
+     _subrow.woinvav_uomname := _subx.uom_name;
+     _subrow.woinvav_qoh := _subx.qoh;
+     _subrow.woinvav_balance := _subx.wobalance;
+     _subrow.woinvav_allocated := _subx.allocated;     
+     _subrow.woinvav_ordered := _subx.ordered;         
+     _subrow.woinvav_woavail := _subx.woavail;
+     _subrow.woinvav_totalavail := _subx.totalavail;
+     _subrow.woinvav_reorderlevel := _subx.reorderlevel;
+     _subrow.woinvav_level := plevel;                                     
+    RETURN NEXT _subrow; 
+  END LOOP;            
+  RETURN;
+END;
+$$ LANGUAGE 'plpgsql';
diff --git a/foundation-database/public/functions/wostarted.sql b/foundation-database/public/functions/wostarted.sql
new file mode 100644 (file)
index 0000000..cad4178
--- /dev/null
@@ -0,0 +1,15 @@
+CREATE OR REPLACE FUNCTION wostarted(pWoid INTEGER) RETURNS BOOLEAN AS $$
+-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple. 
+-- See www.xtuple.com/CPAL for the full text of the software license.
+DECLARE
+  _result BOOLEAN := FALSE;
+   
+BEGIN   
+  -- is it really this simple?
+  SELECT (wo_wipvalue > 0) INTO _result
+    FROM wo
+   WHERE wo_id=pWoid;
+  
+  RETURN COALESCE(_result, FALSE);
+END;
+$$ LANGUAGE 'plpgsql';