-- Name: savemetasql(text, text, text, text); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION savemetasql(text, text, text, text) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.savemetasql(text, text, text, text) OWNER TO admin;

--
-- Name: savemetasql(text, text, text, text, boolean); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION savemetasql(text, text, text, text, boolean) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.savemetasql(text, text, text, text, boolean) OWNER TO admin;

--
-- Name: savemetasql(text, text, text, text, boolean, text); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION savemetasql(text, text, text, text, boolean, text) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.savemetasql(text, text, text, text, boolean, text) OWNER TO admin;

--
-- Name: savemetasql(text, text, text, text, boolean, text, integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION savemetasql(text, text, text, text, boolean, text, integer) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.savemetasql(text, text, text, text, boolean, text, integer) OWNER TO admin;

--
-- Name: scraps(text); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION scraps(text) RETURNS boolean
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.scraps(text) OWNER TO admin;

--
-- Name: scrapwomaterial(integer, numeric); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION scrapwomaterial(integer, numeric) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.scrapwomaterial(integer, numeric) OWNER TO admin;

--
-- Name: scrapwomaterial(integer, numeric, timestamp with time zone); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION scrapwomaterial(integer, numeric, timestamp with time zone) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.scrapwomaterial(integer, numeric, timestamp with time zone) OWNER TO admin;

--
-- Name: selectbalanceforbilling(integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION selectbalanceforbilling(integer) RETURNS boolean
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.selectbalanceforbilling(integer) OWNER TO admin;

--
-- Name: selectdiscountitemsforpayment(integer, integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION selectdiscountitemsforpayment(pvendid integer, pbankaccntid integer) RETURNS integer
    LANGUAGE plpgsql
    AS $$
-- Copyright (c) 1999-2014 by OpenMFG LLC, d/b/a xTuple. 
-- See www.xtuple.com/CPAL for the full text of the software license.
DECLARE
  _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;
$$;


ALTER FUNCTION public.selectdiscountitemsforpayment(pvendid integer, pbankaccntid integer) OWNER TO admin;

--
-- Name: selectdueitemsforpayment(integer, integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION selectdueitemsforpayment(pvendid integer, pbankaccntid integer) RETURNS integer
    LANGUAGE plpgsql
    AS $$
-- Copyright (c) 1999-2014 by OpenMFG LLC, d/b/a xTuple. 
-- See www.xtuple.com/CPAL for the full text of the software license.
DECLARE
  _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;
$$;


ALTER FUNCTION public.selectdueitemsforpayment(pvendid integer, pbankaccntid integer) OWNER TO admin;

--
-- Name: selectforbilling(integer, numeric, boolean); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION selectforbilling(integer, numeric, boolean) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.selectforbilling(integer, numeric, boolean) OWNER TO admin;

--
-- Name: selectforbilling(integer, numeric, boolean, integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION selectforbilling(integer, numeric, boolean, integer) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.selectforbilling(integer, numeric, boolean, integer) OWNER TO admin;

--
-- Name: selectpayment(integer, integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION selectpayment(papopenid integer, pbankaccntid integer) RETURNS integer
    LANGUAGE plpgsql
    AS $$
-- Copyright (c) 1999-2014 by OpenMFG LLC, d/b/a xTuple. 
-- See www.xtuple.com/CPAL for the full text of the software license.
DECLARE
  _p RECORD;
  _amount NUMERIC;
  _discount NUMERIC;

BEGIN

  SELECT apopen_amount, apopen_paid,
         apopen_doctype, apopen_docdate,
         apopen_curr_id,
         apopen_amount - apopen_paid - apCheckPending(apopen_id) 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','C'))) THEN
    RAISE EXCEPTION 'Apopen not found or invalid document type [xtuple: selectPayment, -1]';
  END IF;

  _discount := round(_p.discount_available, 2);
  _amount := noNeg(round(_p.balance, 2) - _discount);

  RETURN selectPayment(pApopenid, pBankaccntid, _p.apopen_docdate,
                       _p.apopen_curr_id, _amount, _discount);

END;
$$;


ALTER FUNCTION public.selectpayment(papopenid integer, pbankaccntid integer) OWNER TO admin;

--
-- Name: selectpayment(integer, integer, date, integer, numeric, numeric); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION selectpayment(papopenid integer, pbankaccntid integer, pdocdate date, pcurrid integer, pamount numeric, pdiscount numeric) RETURNS integer
    LANGUAGE plpgsql
    AS $$
-- Copyright (c) 1999-2014 by OpenMFG LLC, d/b/a xTuple. 
-- See www.xtuple.com/CPAL for the full text of the software license.
DECLARE
  _p RECORD;
  _bankcompany TEXT;
  _distcompany TEXT;
  _apselectid INTEGER;
  _amount NUMERIC;
  _discount NUMERIC;

BEGIN

  SELECT apopen_amount, apopen_paid,
         apopen_doctype, apopen_docdate,
         apopen_docnumber, apopen_curr_id,
         apopen_amount - apopen_paid - apCheckPending(apopen_id) AS balance
    INTO _p
    FROM apopen
   WHERE(apopen_id=pApopenid);

  IF(NOT FOUND OR (NOT _p.apopen_doctype IN ('V','D','C'))) THEN
    RAISE EXCEPTION 'Apopen not found or invalid document type [xtuple: selectPayment, -1]';
  END IF;

  IF (round(_p.balance,2) < (pDiscount + pAmount)) THEN
    RAISE EXCEPTION 'Apopen balance less than amount plus discount [xtuple: selectPayment, -2]';
  END IF;

  IF (_p.apopen_doctype = 'V') THEN
    SELECT COALESCE(accnt_company, '') INTO _bankcompany
    FROM bankaccnt JOIN accnt ON (accnt_id=bankaccnt_accnt_id)
    WHERE (bankaccnt_id=pBankaccntid);

    SELECT COALESCE(accnt_company, '') INTO _distcompany
    FROM vohead JOIN vodist ON (vodist_vohead_id=vohead_id)
                JOIN accnt ON (accnt_id=vodist_accnt_id)
    WHERE (vohead_number=_p.apopen_docnumber)
      AND (COALESCE(accnt_company, '') != _bankcompany);

    IF (FOUND) THEN
      RAISE EXCEPTION 'Bank account company does not match Voucher distribution company [xtuple: selectPayment, -3]';
    END IF;

    SELECT COALESCE(accnt_company, '') INTO _distcompany
    FROM vohead JOIN vodist ON (vodist_vohead_id=vohead_id)
                JOIN expcat ON (expcat_id=vodist_expcat_id)
                JOIN accnt ON (accnt_id=expcat_exp_accnt_id)
    WHERE (vohead_number=_p.apopen_docnumber)
      AND (COALESCE(accnt_company, '') != _bankcompany);

    IF (FOUND) THEN
      RAISE EXCEPTION 'Bank account company does not match Voucher distribution company [xtuple: selectPayment, -3]';
    END IF;

  END IF;

  IF (pAmount > 0.0) THEN
    SELECT apselect_id INTO _apselectid
    FROM apselect
    WHERE (apselect_apopen_id=pApopenid);

    IF (FOUND) THEN
      UPDATE apselect
         SET apselect_amount=pAmount,
             apselect_discount=pDiscount,
             apselect_curr_id=pCurrid
       WHERE(apselect_id=_apselectid);
    ELSE
      INSERT INTO apselect
      ( apselect_apopen_id,
        apselect_amount, apselect_discount,
        apselect_bankaccnt_id,
        apselect_curr_id, apselect_date )
      VALUES
      ( pApopenid,
        pAmount, pDiscount,
        pBankaccntid,
        pCurrid, pDocdate )
      RETURNING apselect_id INTO _apselectid;
    END IF;
  ELSE
    _apselectid := 0;
  END IF;
  
  RETURN _apselectid;

END;
$$;


ALTER FUNCTION public.selectpayment(papopenid integer, pbankaccntid integer, pdocdate date, pcurrid integer, pamount numeric, pdiscount numeric) OWNER TO admin;

--
-- Name: selectuninvoicedshipment(integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION selectuninvoicedshipment(integer) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.selectuninvoicedshipment(integer) OWNER TO admin;

--
-- Name: selectuninvoicedshipments(integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION selectuninvoicedshipments(integer) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.selectuninvoicedshipments(integer) OWNER TO admin;

--
-- Name: selectuninvoicedshipments(integer, text); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION selectuninvoicedshipments(integer, text) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.selectuninvoicedshipments(integer, text) OWNER TO admin;

--
-- Name: selectuninvoicedshipments(integer, integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION selectuninvoicedshipments(integer, integer) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.selectuninvoicedshipments(integer, integer) OWNER TO admin;

--
-- Name: setapjournalnumber(); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION setapjournalnumber() RETURNS integer
    LANGUAGE plpgsql
    AS $$
-- Copyright (c) 1999-2014 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;
$$;


ALTER FUNCTION public.setapjournalnumber() OWNER TO admin;

--
-- Name: setarjournalnumber(); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION setarjournalnumber() RETURNS integer
    LANGUAGE plpgsql
    AS $$
-- Copyright (c) 1999-2014 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;
$$;


ALTER FUNCTION public.setarjournalnumber() OWNER TO admin;

--
-- Name: setbudget(integer, integer, numeric); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION setbudget(integer, integer, numeric) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.setbudget(integer, integer, numeric) OWNER TO admin;

--
-- Name: setbudget(integer, integer, integer, numeric); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION setbudget(integer, integer, integer, numeric) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.setbudget(integer, integer, integer, numeric) OWNER TO admin;

--
-- Name: setbytea(text); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION setbytea(text) RETURNS bytea
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.setbytea(text) OWNER TO admin;

--
-- Name: setbytea(bytea); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION setbytea(bytea) RETURNS bytea
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.setbytea(bytea) OWNER TO admin;

--
-- Name: setccbankaccnt(text, integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION setccbankaccnt(text, integer) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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 ccbank_id INTO _ccbankid;
  END IF;

  RETURN _ccbankid;
END;
$_$;


ALTER FUNCTION public.setccbankaccnt(text, integer) OWNER TO admin;

--
-- Name: seteffectivextuser(text); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION seteffectivextuser(text) RETURNS boolean
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.seteffectivextuser(text) OWNER TO admin;

--
-- Name: setgljournalnumber(date, date); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION setgljournalnumber(date, date) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.setgljournalnumber(date, date) OWNER TO admin;

--
-- Name: setmetric(text, text); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION setmetric(text, text) RETURNS boolean
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.setmetric(text, text) OWNER TO admin;

--
-- Name: setmetricenc(text, text, text); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION setmetricenc(text, text, text) RETURNS boolean
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.setmetricenc(text, text, text) OWNER TO admin;

--
-- Name: setnextapmemonumber(integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION setnextapmemonumber(integer) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.setnextapmemonumber(integer) OWNER TO admin;

--
-- Name: setnextarmemonumber(integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION setnextarmemonumber(integer) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.setnextarmemonumber(integer) OWNER TO admin;

--
-- Name: setnextcashrcptnumber(integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION setnextcashrcptnumber(integer) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.setnextcashrcptnumber(integer) OWNER TO admin;

--
-- Name: setnextchecknumber(integer, integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION setnextchecknumber(pbankaccntid integer, pnextchecknumber integer) RETURNS boolean
    LANGUAGE plpgsql
    AS $$
-- Copyright (c) 1999-2014 by OpenMFG LLC, d/b/a xTuple. 
-- See www.xtuple.com/CPAL for the full text of the software license.
DECLARE
  _nextChkNumber INTEGER;
  _checkheadid INTEGER;

BEGIN

  _nextChkNumber := pNextCheckNumber;

  WHILE (TRUE) LOOP
    SELECT checkhead_id INTO _checkheadid
    FROM checkhead
    WHERE (checkhead_number=_nextChkNumber)
      AND (checkhead_bankaccnt_id=pBankaccntid);
    IF (NOT FOUND) THEN
      EXIT;
    ELSE
      _nextChkNumber := _nextChkNumber + 1;
    END IF;
  END LOOP;

  UPDATE bankaccnt
  SET bankaccnt_nextchknum=_nextChkNumber
  WHERE (bankaccnt_id=pBankaccntid);

  RETURN TRUE;

END;
$$;


ALTER FUNCTION public.setnextchecknumber(pbankaccntid integer, pnextchecknumber integer) OWNER TO admin;

--
-- Name: setnextcmnumber(integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION setnextcmnumber(integer) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.setnextcmnumber(integer) OWNER TO admin;

--
-- Name: setnextcrmaccountnumber(integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION setnextcrmaccountnumber(integer) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.setnextcrmaccountnumber(integer) OWNER TO admin;

--
-- Name: setnextincidentnumber(integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION setnextincidentnumber(integer) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.setnextincidentnumber(integer) OWNER TO admin;

--
-- Name: setnextinvcnumber(integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION setnextinvcnumber(integer) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.setnextinvcnumber(integer) OWNER TO admin;

--
-- Name: setnextnumber(text, integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION setnextnumber(text, integer) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.setnextnumber(text, integer) OWNER TO admin;

--
-- Name: setnextponumber(integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION setnextponumber(integer) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.setnextponumber(integer) OWNER TO admin;

--
-- Name: setnextprnumber(integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION setnextprnumber(integer) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.setnextprnumber(integer) OWNER TO admin;

--
-- Name: setnextqunumber(integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION setnextqunumber(integer) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.setnextqunumber(integer) OWNER TO admin;

--
-- Name: setnextshipmentnumber(integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION setnextshipmentnumber(integer) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.setnextshipmentnumber(integer) OWNER TO admin;

--
-- Name: setnextsonumber(integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION setnextsonumber(integer) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.setnextsonumber(integer) OWNER TO admin;

--
-- Name: setnextvcnumber(integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION setnextvcnumber(integer) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.setnextvcnumber(integer) OWNER TO admin;

--
-- Name: setnextwonumber(integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION setnextwonumber(integer) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.setnextwonumber(integer) OWNER TO admin;

--
-- Name: setusercancreateusers(text, boolean); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION setusercancreateusers(pusername text, pcreateuser boolean) RETURNS boolean
    LANGUAGE plpgsql
    AS $$
-- Copyright (c) 1999-2014 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;
$$;


ALTER FUNCTION public.setusercancreateusers(pusername text, pcreateuser boolean) OWNER TO admin;

--
-- Name: setuserpreference(text, text); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION setuserpreference(text, text) RETURNS boolean
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.setuserpreference(text, text) OWNER TO admin;

--
-- Name: setuserpreference(text, text, text); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION setuserpreference(text, text, text) RETURNS boolean
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.setuserpreference(text, text, text) OWNER TO admin;

--
-- Name: shipments(text); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION shipments(text) RETURNS boolean
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.shipments(text) OWNER TO admin;

--
-- Name: shipshipment(integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION shipshipment(integer) RETURNS integer
    LANGUAGE sql
    AS $_$
-- Copyright (c) 1999-2014 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);
$_$;


ALTER FUNCTION public.shipshipment(integer) OWNER TO admin;

--
-- Name: shipshipment(integer, timestamp with time zone); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION shipshipment(integer, timestamp with time zone) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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);
      IF (_invhistid > 0) THEN
        PERFORM postInvHist(_invhistid);
      END IF;
      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);
      IF (_invhistid > 0) THEN
        PERFORM postInvHist(_invhistid);
      END IF;
      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;
$_$;


ALTER FUNCTION public.shipshipment(integer, timestamp with time zone) OWNER TO admin;

--
-- Name: show_search_path(); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION show_search_path() RETURNS text
    LANGUAGE plpgsql
    AS $$
declare
  path text;
  rec record;
begin
  -- This exists because show search_path command doesn't work in plv8
  for rec in execute 'show search_path'
  loop
      return rec.search_path::text;
  end loop;
end;
$$;


ALTER FUNCTION public.show_search_path() OWNER TO admin;

--
-- Name: singlecharacteristicstostring(text, integer, text, text, integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION singlecharacteristicstostring(text, integer, text, text, integer) RETURNS text
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.singlecharacteristicstostring(text, integer, text, text, integer) OWNER TO admin;

--
-- Name: singlelevelbom(integer, integer, integer, integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION singlelevelbom(integer, integer, integer, integer) RETURNS SETOF bomdata
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.singlelevelbom(integer, integer, integer, integer) OWNER TO admin;

--
-- Name: site(); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION site() RETURNS SETOF whsinfo
    LANGUAGE plpgsql
    AS $$
-- Copyright (c) 1999-2014 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;
$$;


ALTER FUNCTION public.site() OWNER TO admin;

--
-- Name: snoozemessage(integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION snoozemessage(integer) RETURNS boolean
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.snoozemessage(integer) OWNER TO admin;

--
-- Name: spellamount(numeric); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION spellamount(numeric) RETURNS text
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.spellamount(numeric) OWNER TO admin;

--
-- Name: spellamount(numeric, integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION spellamount(numeric, integer) RETURNS text
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.spellamount(numeric, integer) OWNER TO admin;

--
-- Name: splitreceipt(integer, numeric, numeric); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION splitreceipt(integer, numeric, numeric) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.splitreceipt(integer, numeric, numeric) OWNER TO admin;

--
-- Name: splitrecurrence(integer, text, timestamp with time zone); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION splitrecurrence(integer, text, timestamp with time zone) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.splitrecurrence(integer, text, timestamp with time zone) OWNER TO admin;

--
-- Name: startoftime(); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION startoftime() RETURNS date
    LANGUAGE sql IMMUTABLE
    AS $$
-- Copyright (c) 1999-2014 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;
$$;


ALTER FUNCTION public.startoftime() OWNER TO admin;

--
-- Name: stdcost(integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION stdcost(integer) RETURNS numeric
    LANGUAGE plpgsql STABLE
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.stdcost(integer) OWNER TO admin;

--
-- Name: stdcost(integer, integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION stdcost(integer, integer) RETURNS numeric
    LANGUAGE plpgsql STABLE
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.stdcost(integer, integer) OWNER TO admin;

--
-- Name: sufficientinventorytoshipitem(text, integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION sufficientinventorytoshipitem(text, integer) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.sufficientinventorytoshipitem(text, integer) OWNER TO admin;

--
-- Name: sufficientinventorytoshipitem(text, integer, numeric); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION sufficientinventorytoshipitem(text, integer, numeric) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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
		      ) <= qtyAvailable(itemsite_id))
              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
		      )
              ) <= qtyAvailable(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));
    END IF;
  ELSEIF (pordertype = 'TO') THEN
    SELECT (COALESCE(pqty, roundQty(item_fractional,
		                    noNeg(toitem_qty_ordered - toitem_qty_shipped - 
			            qtyAtShipping(pordertype, toitem_id)
		                    )
		    )
           ) <= qtyAvailable(itemsite_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_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;

  IF (pordertype = 'SO') THEN
    IF ( SELECT fetchMetricBool('RequireSOReservations') ) THEN
      SELECT (COALESCE(pqty, coitem_qtyreserved) <= coitem_qtyreserved)
        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;
  END IF;

  IF (NOT _isqtyavail) THEN
    RETURN -4;
  END IF;

  RETURN 0;
END;
$_$;


ALTER FUNCTION public.sufficientinventorytoshipitem(text, integer, numeric) OWNER TO admin;

--
-- Name: sufficientinventorytoshiporder(text, integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION sufficientinventorytoshiporder(text, integer) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.sufficientinventorytoshiporder(text, integer) OWNER TO admin;

--
-- Name: summarizedbom(integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION summarizedbom(integer) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.summarizedbom(integer) OWNER TO admin;

--
-- Name: summarizedbom(integer, integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION summarizedbom(integer, integer) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.summarizedbom(integer, integer) OWNER TO admin;

--
-- Name: summarizedbom(integer, integer, integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION summarizedbom(integer, integer, integer) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.summarizedbom(integer, integer, integer) OWNER TO admin;

--
-- Name: summarizedbom(integer, integer, integer, integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION summarizedbom(integer, integer, integer, integer) RETURNS SETOF bomdata
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.summarizedbom(integer, integer, integer, integer) OWNER TO admin;

--
-- Name: summarizetransactions(integer, date, date); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION summarizetransactions(integer, date, date) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.summarizetransactions(integer, date, date) OWNER TO admin;

--
-- Name: summdemand(integer, integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION summdemand(integer, integer) RETURNS numeric
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.summdemand(integer, integer) OWNER TO admin;

--
-- Name: summdemand(integer, date, date); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION summdemand(integer, date, date) RETURNS numeric
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.summdemand(integer, date, date) OWNER TO admin;

--
-- Name: summprod(integer, integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION summprod(integer, integer) RETURNS numeric
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.summprod(integer, integer) OWNER TO admin;

--
-- Name: summprod(integer, date, date); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION summprod(integer, date, date) RETURNS numeric
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.summprod(integer, date, date) OWNER TO admin;

--
-- Name: summtransa(integer, integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION summtransa(integer, integer) RETURNS numeric
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.summtransa(integer, integer) OWNER TO admin;

--
-- Name: summtransa(integer, date, date); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION summtransa(integer, date, date) RETURNS numeric
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.summtransa(integer, date, date) OWNER TO admin;

--
-- Name: summtransc(integer, integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION summtransc(integer, integer) RETURNS numeric
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.summtransc(integer, integer) OWNER TO admin;

--
-- Name: summtransc(integer, date, date); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION summtransc(integer, date, date) RETURNS numeric
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.summtransc(integer, date, date) OWNER TO admin;

--
-- Name: summtransi(integer, integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION summtransi(integer, integer) RETURNS numeric
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.summtransi(integer, integer) OWNER TO admin;

--
-- Name: summtransi(integer, date, date); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION summtransi(integer, date, date) RETURNS numeric
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.summtransi(integer, date, date) OWNER TO admin;

--
-- Name: summtransr(integer, integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION summtransr(integer, integer) RETURNS numeric
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.summtransr(integer, integer) OWNER TO admin;

--
-- Name: summtransr(integer, date, date); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION summtransr(integer, date, date) RETURNS numeric
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.summtransr(integer, date, date) OWNER TO admin;

--
-- Name: summtranss(integer, integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION summtranss(pitemsiteid integer, pcalitemid integer) RETURNS numeric
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.summtranss(pitemsiteid integer, pcalitemid integer) OWNER TO admin;

--
-- Name: summtranss(integer, date, date); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION summtranss(pitemsiteid integer, pstartdate date, penddate date) RETURNS numeric
    LANGUAGE plpgsql
    AS $$
-- Copyright (c) 1999-2014 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;
$$;


ALTER FUNCTION public.summtranss(pitemsiteid integer, pstartdate date, penddate date) OWNER TO admin;

--
-- Name: summtranst(integer, integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION summtranst(integer, integer) RETURNS numeric
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.summtranst(integer, integer) OWNER TO admin;

--
-- Name: summtranst(integer, date, date); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION summtranst(integer, date, date) RETURNS numeric
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.summtranst(integer, date, date) OWNER TO admin;

--
-- Name: taxassignments(integer, integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION taxassignments(integer, integer) RETURNS SETOF taxassign
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.taxassignments(integer, integer) OWNER TO admin;

--
-- Name: thawaccountingperiod(integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION thawaccountingperiod(integer) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.thawaccountingperiod(integer) OWNER TO admin;

--
-- Name: thawitemsite(integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION thawitemsite(pitemsiteid integer) RETURNS integer
    LANGUAGE plpgsql
    AS $$
-- Copyright (c) 1999-2014 by OpenMFG LLC, d/b/a xTuple. 
-- See www.xtuple.com/CPAL for the full text of the software license.
DECLARE
  _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
      -- no inventory transactions to thaw, update itemsite and return
      UPDATE itemsite
      SET itemsite_freeze=FALSE
      WHERE (itemsite_id=pItemsiteid);

      RETURN 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
        INTO _itemloc
        FROM itemloc
        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;

        ELSE
          _itemlocid := _itemloc.itemloc_id;
          _qoh := _itemloc.itemloc_qty;
        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);

        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;

    UPDATE itemsite
       SET itemsite_qtyonhand = _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;
$$;


ALTER FUNCTION public.thawitemsite(pitemsiteid integer) OWNER TO admin;

--
-- Name: todoitem; Type: TABLE; Schema: public; Owner: admin; Tablespace: 
--

CREATE TABLE todoitem (
    todoitem_id integer NOT NULL,
    todoitem_name text NOT NULL,
    todoitem_description text,
    todoitem_incdt_id integer,
    todoitem_creator_username text DEFAULT geteffectivextuser() NOT NULL,
    todoitem_status character(1),
    todoitem_active boolean DEFAULT true NOT NULL,
    todoitem_start_date date,
    todoitem_due_date date,
    todoitem_assigned_date date,
    todoitem_completed_date date,
    todoitem_seq integer DEFAULT 0 NOT NULL,
    todoitem_notes text,
    todoitem_crmacct_id integer,
    todoitem_ophead_id integer,
    todoitem_owner_username text,
    todoitem_priority_id integer,
    todoitem_username text,
    todoitem_recurring_todoitem_id integer,
    todoitem_cntct_id integer
);


ALTER TABLE todoitem OWNER TO admin;

--
-- Name: TABLE todoitem; Type: COMMENT; Schema: public; Owner: admin
--

COMMENT ON TABLE todoitem IS 'To-Do List items.';


--
-- Name: COLUMN todoitem.todoitem_recurring_todoitem_id; Type: COMMENT; Schema: public; Owner: admin
--

COMMENT ON COLUMN todoitem.todoitem_recurring_todoitem_id IS 'The first todoitem record in the series if this is a recurring To-Do item. If the todoitem_recurring_todoitem_id is the same as the todoitem_id, this record is the first in the series.';


--
-- Name: todoitem(); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION todoitem() RETURNS SETOF todoitem
    LANGUAGE plpgsql
    AS $$
-- Copyright (c) 1999-2014 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;
$$;


ALTER FUNCTION public.todoitem() OWNER TO admin;

--
-- Name: FUNCTION todoitem(); Type: COMMENT; Schema: public; Owner: admin
--

COMMENT ON FUNCTION todoitem() IS 'A table function that returns To Do Items results according to privilege settings.';


--
-- Name: todoitemmove(integer, integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION todoitemmove(integer, integer) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.todoitemmove(integer, integer) OWNER TO admin;

--
-- Name: todoitemmovedown(integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION todoitemmovedown(integer) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.todoitemmovedown(integer) OWNER TO admin;

--
-- Name: todoitemmoveup(integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION todoitemmoveup(integer) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.todoitemmoveup(integer) OWNER TO admin;

--
-- Name: togglebankreccleared(integer, text, integer, numeric); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION togglebankreccleared(integer, text, integer, numeric) RETURNS boolean
    LANGUAGE plpgsql
    AS $_$
DECLARE
  pBankrecid ALIAS FOR $1;
  pSource    ALIAS FOR $2;
  pSourceid  ALIAS FOR $3;
  pCurrrate  ALIAS FOR $4;
  _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)
    VALUES
    (pBankrecid, pSource,
     pSourceid, _cleared,
     pCurrrate);
  ELSE
    _cleared := (NOT _r.bankrecitem_cleared);
    UPDATE bankrecitem
       SET bankrecitem_cleared=_cleared,
           bankrecitem_curr_rate=pCurrrate
     WHERE (bankrecitem_id=_r.bankrecitem_id);
  END IF;

  RETURN _cleared;
END;
$_$;


ALTER FUNCTION public.togglebankreccleared(integer, text, integer, numeric) OWNER TO admin;

--
-- Name: togglebankreccleared(integer, text, integer, numeric, numeric); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION togglebankreccleared(pbankrecid integer, psource text, psourceid integer, pcurrrate numeric, pbaseamount numeric) RETURNS boolean
    LANGUAGE plpgsql
    AS $$
-- Copyright (c) 1999-2014 by OpenMFG LLC, d/b/a xTuple. 
-- See www.xtuple.com/CPAL for the full text of the software license.
BEGIN
  RETURN toggleBankrecCleared(pBankrecid, pSource, pSourceid, pCurrrate, pBaseAmount, NULL);
END;
$$;


ALTER FUNCTION public.togglebankreccleared(pbankrecid integer, psource text, psourceid integer, pcurrrate numeric, pbaseamount numeric) OWNER TO admin;

--
-- Name: togglebankreccleared(integer, text, integer, numeric, numeric, date); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION togglebankreccleared(pbankrecid integer, psource text, psourceid integer, pcurrrate numeric, pbaseamount numeric, pdate date) RETURNS boolean
    LANGUAGE plpgsql
    AS $$
-- Copyright (c) 1999-2014 by OpenMFG LLC, d/b/a xTuple. 
-- See www.xtuple.com/CPAL for the full text of the software license.
DECLARE
  _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,
     bankrecitem_effdate)
    VALUES
    (pBankrecid, pSource,
     pSourceid, _cleared,
     CASE WHEN (fetchMetricValue('CurrencyExchangeSense') = 1) THEN ROUND(1.0 / pCurrrate, 8)
          ELSE ROUND(pCurrrate,8) END,
     CASE WHEN (fetchMetricValue('CurrencyExchangeSense') = 1) THEN ROUND(pBaseAmount * ROUND(1.0 / pCurrrate,8),2)
          ELSE ROUND(pBaseAmount * ROUND(pCurrrate,8),2) END,
     pDate);
  ELSE
    _cleared := FALSE;
    DELETE FROM bankrecitem 
    WHERE bankrecitem_id = _r.bankrecitem_id;
  END IF;

  RETURN _cleared;
END;
$$;


ALTER FUNCTION public.togglebankreccleared(pbankrecid integer, psource text, psourceid integer, pcurrrate numeric, pbaseamount numeric, pdate date) OWNER TO admin;

--
-- Name: togglebomitemcost(integer, boolean); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION togglebomitemcost(integer, boolean) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.togglebomitemcost(integer, boolean) OWNER TO admin;

--
-- Name: tonumeric(text, numeric); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION tonumeric(text, numeric) RETURNS numeric
    LANGUAGE plpgsql IMMUTABLE
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.tonumeric(text, numeric) OWNER TO admin;

--
-- Name: transitwhs(); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION transitwhs() RETURNS integer
    LANGUAGE plpgsql
    AS $$
-- Copyright (c) 1999-2014 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;
$$;


ALTER FUNCTION public.transitwhs() OWNER TO admin;

--
-- Name: transtype(text, integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION transtype(text, integer) RETURNS boolean
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.transtype(text, integer) OWNER TO admin;

--
-- Name: trylock(integer, integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION trylock(integer, integer) RETURNS boolean
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.trylock(integer, integer) OWNER TO admin;

--
-- Name: undomerge(text, text, integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION undomerge(text, text, integer) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.undomerge(text, text, integer) OWNER TO admin;

--
-- Name: unreleasepurchaseorder(integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION unreleasepurchaseorder(ppoheadid integer) RETURNS integer
    LANGUAGE plpgsql
    AS $$
-- Copyright (c) 1999-2014 by OpenMFG LLC, d/b/a xTuple. 
-- See www.xtuple.com/CPAL for the full text of the software license.
BEGIN

  IF ( ( SELECT (COUNT(*) > 0)
         FROM poitem LEFT OUTER JOIN recv ON (recv_order_type='PO' AND
                                              recv_orderitem_id=poitem_id)
         WHERE ( (poitem_pohead_id=pPoheadid)
           AND   ( (poitem_status='C') OR
                   (poitem_qty_received > 0.0) OR
                   (poitem_qty_returned > 0.0) OR
                   (poitem_qty_vouchered > 0.0) OR
                   (recv_id IS NOT NULL) ) ) ) ) THEN
    RETURN -1;
  END IF;

  IF ( ( SELECT (pohead_status='O')
         FROM pohead
         WHERE (pohead_id=pPoheadid) ) ) THEN

    --update status and erase the date that the order was released on
    UPDATE pohead
    SET pohead_status='U', pohead_released = NULL
    WHERE (pohead_id=pPoheadid);

  END IF;

  --update status and erase the duedates at release
  UPDATE poitem
  SET poitem_status='U', poitem_rlsd_duedate = NULL
  WHERE (poitem_pohead_id=pPoheadid);

  RETURN 1;

END;
$$;


ALTER FUNCTION public.unreleasepurchaseorder(ppoheadid integer) OWNER TO admin;

--
-- Name: uomusedforitem(integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION uomusedforitem(integer) RETURNS SETOF uom
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.uomusedforitem(integer) OWNER TO admin;

--
-- Name: updateabcclass(text, numeric, numeric, date, date); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION updateabcclass(text, numeric, numeric, date, date) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.updateabcclass(text, numeric, numeric, date, date) OWNER TO admin;

--
-- Name: updateabcclass(integer, numeric, numeric, date, date); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION updateabcclass(integer, numeric, numeric, date, date) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.updateabcclass(integer, numeric, numeric, date, date) OWNER TO admin;

--
-- Name: updateabcclass(text, integer, numeric, numeric, date, date); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION updateabcclass(text, integer, numeric, numeric, date, date) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.updateabcclass(text, integer, numeric, numeric, date, date) OWNER TO admin;

--
-- Name: updateabcclass(integer, integer, numeric, numeric, date, date); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION updateabcclass(integer, integer, numeric, numeric, date, date) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.updateabcclass(integer, integer, numeric, numeric, date, date) OWNER TO admin;

--
-- Name: updatecharassignment(text, integer, integer, text); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION updatecharassignment(text, integer, integer, text) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.updatecharassignment(text, integer, integer, text) OWNER TO admin;

--
-- Name: updatecharassignment(text, integer, integer, text, numeric); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION updatecharassignment(text, integer, integer, text, numeric) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.updatecharassignment(text, integer, integer, text, numeric) OWNER TO admin;

--
-- Name: updatecost(integer, numeric); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION updatecost(integer, numeric) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.updatecost(integer, numeric) OWNER TO admin;

--
-- Name: updatecost(integer, numeric, integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION updatecost(integer, numeric, integer) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.updatecost(integer, numeric, integer) OWNER TO admin;

--
-- Name: updatecost(integer, text, boolean, numeric); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION updatecost(integer, text, boolean, numeric) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.updatecost(integer, text, boolean, numeric) OWNER TO admin;

--
-- Name: updatecost(integer, integer, boolean, numeric); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION updatecost(integer, integer, boolean, numeric) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.updatecost(integer, integer, boolean, numeric) OWNER TO admin;

--
-- Name: updatecost(integer, text, boolean, numeric, integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION updatecost(integer, text, boolean, numeric, integer) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.updatecost(integer, text, boolean, numeric, integer) OWNER TO admin;

--
-- Name: updatecost(integer, integer, boolean, numeric, integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION updatecost(integer, integer, boolean, numeric, integer) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.updatecost(integer, integer, boolean, numeric, integer) OWNER TO admin;

--
-- Name: updatecreditmemoline(api.creditmemoline, api.creditmemoline); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION updatecreditmemoline(api.creditmemoline, api.creditmemoline) RETURNS boolean
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 by OpenMFG LLC, d/b/a xTuple.
-- See www.xtuple.com/CPAL for the full text of the software license.
DECLARE
  pNew ALIAS FOR $1;
  pOld ALIAS FOR $2;
  _check INTEGER;
  _r RECORD;

BEGIN
  SELECT cmitem_id INTO _check
  FROM cmitem
  WHERE ( (cmitem_cmhead_id=getCmheadId(pOld.memo_number, FALSE)) AND (cmitem_linenumber=pOld.line_number) );
  IF (NOT FOUND) THEN
    RAISE EXCEPTION 'Credit Memo # % Line Number # not found', pOld.memo_number, pOld.line_number;
  END IF;

  UPDATE cmitem
    SET cmitem_itemsite_id=COALESCE(itemsite_id, -1),
        cmitem_qtycredit=pNew.qty_to_credit,
        cmitem_qtyreturned=pNew.qty_returned,
        cmitem_unitprice=pNew.net_unit_price,
        cmitem_comments=pNew.notes,
        cmitem_rsncode_id=getRsnId(pNew.reason_code),
        cmitem_taxtype_id=taxtype_id,
        cmitem_qty_uom_id=COALESCE(getUomId(pNew.qty_uom), item_inv_uom_id),
        cmitem_qty_invuomratio=CASE WHEN item_id IS NOT NULL THEN itemuomtouomratio(item_id, COALESCE(getUomId(pNew.qty_uom),item_inv_uom_id),item_inv_uom_id)
                                    ELSE 1
                               END,
        cmitem_price_uom_id=COALESCE(getUomId(pNew.price_uom),item_price_uom_id),
        cmitem_price_invuomratio=CASE WHEN item_id IS NOT NULL THEN itemuomtouomratio(item_id, COALESCE(getUomId(pNew.price_uom),item_price_uom_id),item_price_uom_id)
                                      ELSE 1
                                 END
  FROM cmhead LEFT OUTER JOIN item ON (item_id=getItemId(pNew.item_number))
              LEFT OUTER JOIN itemsite ON (itemsite_item_id=item_id AND
                                           itemsite_warehous_id=getWarehousId(pNew.recv_site, 'ALL'))
              LEFT OUTER JOIN taxtype ON (taxtype_id=CASE WHEN pNew.tax_type IS NULL THEN getItemTaxType(item_id,cmhead_taxzone_id)
                                                          WHEN pNew.tax_type = 'None' THEN NULL
                                                          ELSE getTaxTypeId(pNew.tax_type)
                                                     END)
  WHERE cmitem_cmhead_id=cmhead_id
    AND cmhead_number=pOld.memo_number
    AND cmitem_linenumber=pOld.line_number
    AND cmhead_posted=FALSE;

  RETURN TRUE;
END;
$_$;


ALTER FUNCTION public.updatecreditmemoline(api.creditmemoline, api.creditmemoline) OWNER TO admin;

--
-- Name: updatecustomprivs(); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION updatecustomprivs() RETURNS boolean
    LANGUAGE plpgsql
    AS $$
-- Copyright (c) 1999-2014 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;
$$;


ALTER FUNCTION public.updatecustomprivs() OWNER TO admin;

--
-- Name: updateinvoicelineitem(api.invoiceline, api.invoiceline); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION updateinvoicelineitem(api.invoiceline, api.invoiceline) RETURNS boolean
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 by OpenMFG LLC, d/b/a xTuple.
-- See www.xtuple.com/CPAL for the full text of the software license.
DECLARE
	pNew ALIAS FOR $1;
	pOld ALIAS FOR $2;
	_r RECORD;
BEGIN
	UPDATE invcitem SET
		invcitem_linenumber=pNew.line_number,
		invcitem_item_id=COALESCE(item_id, -1),
		invcitem_custpn=pNew.customer_part_number,
		invcitem_number=(CASE WHEN item_id IS NULL THEN pNew.misc_item_number ELSE NULL END),
		invcitem_warehous_id=(CASE WHEN invcitem_warehous_id IS NULL THEN COALESCE(getwarehousid(pNew.site,'ALL'),-1) ELSE NULL END),
		invcitem_descrip=(CASE WHEN item_id IS NULL THEN pNew.misc_item_description ELSE NULL END),
		invcitem_ordered=pNew.qty_ordered,
		invcitem_billed=COALESCE(pNew.qty_billed, 0),
                invcitem_updateinv=COALESCE(pNew.update_inventory,FALSE),
		invcitem_price=COALESCE(pNew.net_unit_price,itemPrice(item_id,invchead_cust_id,
			invchead_shipto_id,pNew.qty_ordered,invchead_curr_id,invchead_orderdate)),
		invcitem_notes=COALESCE(pNew.notes,''),
		invcitem_salescat_id=CASE
			WHEN item_id IS NULL THEN
				(SELECT salescat_id FROM salescat WHERE salescat_name = pNew.sales_category)
			ELSE NULL
		END,
		invcitem_taxtype_id=taxtype_id,
		invcitem_qty_uom_id=CASE
			WHEN item_id IS NOT NULL THEN
				COALESCE((SELECT uom_id FROM uom WHERE (uom_name=pNew.qty_uom)), item_price_uom_id)
			ELSE NULL
		END,
		invcitem_qty_invuomratio=CASE
			WHEN item_id IS NOT NULL THEN
				itemuomtouomratio(item_id,
					COALESCE((SELECT uom_id FROM uom WHERE uom_name=pNew.qty_uom),item_price_uom_id),
					item_price_uom_id
				)
			ELSE 1
		END,
		invcitem_price_uom_id=CASE
			WHEN item_id IS NOT NULL THEN
				COALESCE((SELECT uom_id FROM uom WHERE uom_name=pNew.price_uom),item_price_uom_id)
			ELSE NULL
		END,
		invcitem_price_invuomratio=CASE
			WHEN item_id IS NOT NULL THEN
				itemuomtouomratio(item_id,
					COALESCE((SELECT uom_id FROM uom WHERE uom_name=pNew.price_uom),item_price_uom_id),
					item_price_uom_id
				)
			ELSE 1
		END,
                invcitem_rev_accnt_id=getGlAccntId(alternate_rev_account)
	FROM invchead
		LEFT OUTER JOIN item ON (item_id=getItemId(pNew.item_number))
		LEFT OUTER JOIN taxtype ON (taxtype_id=CASE
			WHEN pNew.tax_type IS NULL THEN getItemTaxType(item_id,invchead_taxzone_id)
			WHEN pNew.tax_type = 'None' THEN NULL
			ELSE (SELECT taxtype_id FROM taxtype WHERE taxtype_name=pNew.tax_type)
		END)
	WHERE invcitem_invchead_id=invchead_id
		AND invcitem_linenumber=pOld.line_number
		AND invchead_invcnumber=pOld.invoice_number
		AND invchead_posted=FALSE;
	RETURN TRUE;
END;
$_$;


ALTER FUNCTION public.updateinvoicelineitem(api.invoiceline, api.invoiceline) OWNER TO admin;

--
-- Name: updateitemcost(integer, integer, integer, numeric, boolean); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION updateitemcost(integer, integer, integer, numeric, boolean) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.updateitemcost(integer, integer, integer, numeric, boolean) OWNER TO admin;

--
-- Name: updateitemsiteleadtime(integer, integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION updateitemsiteleadtime(integer, integer) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.updateitemsiteleadtime(integer, integer) OWNER TO admin;

--
-- Name: updatelistprice(integer, numeric); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION updatelistprice(integer, numeric) RETURNS numeric
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.updatelistprice(integer, numeric) OWNER TO admin;

--
-- Name: updatelowerusercosts(integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION updatelowerusercosts(integer) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.updatelowerusercosts(integer) OWNER TO admin;

--
-- Name: updatelowerusercosts(integer, boolean); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION updatelowerusercosts(integer, boolean) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.updatelowerusercosts(integer, boolean) OWNER TO admin;

--
-- Name: updatelowlevel(integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION updatelowlevel(integer) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.updatelowlevel(integer) OWNER TO admin;

--
-- Name: updateoutlevel(integer, integer, integer[]); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION updateoutlevel(integer, integer, integer[]) RETURNS boolean
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.updateoutlevel(integer, integer, integer[]) OWNER TO admin;

--
-- Name: updateprice(integer, numeric); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION updateprice(integer, numeric) RETURNS numeric
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple.
-- See www.xtuple.com/CPAL for the full text of the software license.
DECLARE
  pIpsitemid ALIAS FOR $1;
  pUpdateBy ALIAS FOR $2;

BEGIN

  RETURN updatePrice(pIpsitemid, 'P', pUpdateBy);

END;

$_$;


ALTER FUNCTION public.updateprice(integer, numeric) OWNER TO admin;

--
-- Name: updateprice(integer, character, numeric); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION updateprice(integer, character, numeric) RETURNS numeric
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2012 by OpenMFG LLC, d/b/a xTuple.
-- See www.xtuple.com/CPAL for the full text of the software license.
DECLARE
  pIpsitemid ALIAS FOR $1;
  pUpdateType ALIAS FOR $2;
  pUpdateBy ALIAS FOR $3;

BEGIN

  IF (pUpdateType IN('V')) THEN
    UPDATE ipsitem
    SET ipsitem_price = (ipsitem_price + pUpdateBy)
    WHERE (ipsitem_id=pIpsitemid);
    RETURN 1;
  ELSE
    UPDATE ipsitem
    SET ipsitem_price = (ipsitem_price * pUpdateBy)
    WHERE (ipsitem_id=pIpsitemid);
    RETURN 1;
  END IF;

END;
$_$;


ALTER FUNCTION public.updateprice(integer, character, numeric) OWNER TO admin;

--
-- Name: updatepricesbypricingschedule(integer, character, numeric, boolean); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION updatepricesbypricingschedule(pipsheadid integer, pupdatetype character, pupdateby numeric, pupdatecharprices boolean) RETURNS numeric
    LANGUAGE plpgsql
    AS $$
DECLARE
  _percentMultiplier NUMERIC;
  _currencyDecimals INTEGER;
  _itemRows INTEGER :=0;
  _charRows INTEGER :=0;

BEGIN

	IF NOT (checkPrivilege('MaintainPricingSchedules')) THEN
		RAISE EXCEPTION 'You do not have privileges to maintain Price Schedules.';
	END IF;

	-- Get the current user's currency precision
	SELECT COALESCE(locale_curr_scale, 2) INTO _currencyDecimals
	FROM locale, usr
	WHERE usr_locale_id = locale_id AND usr_username = CURRENT_USER;

	IF (pUpdateType = 'V') THEN
		UPDATE ipsitem
		SET ipsitem_price = noNeg(ROUND( (ipsitem_price + pUpdateBy), _currencyDecimals))
		WHERE (ipsitem_ipshead_id = pIpsHeadId);

		GET DIAGNOSTICS _itemRows = ROW_COUNT;

	ELSE
		_percentMultiplier := (1.0 + (pUpdateBy / 100.0));

		UPDATE ipsitem
		SET ipsitem_price = noNeg(ROUND( (ipsitem_price * _percentMultiplier), _currencyDecimals))
		WHERE (ipsitem_ipshead_id = pIpsHeadId);

		GET DIAGNOSTICS _itemRows = ROW_COUNT;

		IF (pUpdateCharPrices) THEN
			UPDATE ipsitemchar
			SET ipsitemchar_price = noNeg(ROUND( (ipsitemchar_price * _percentMultiplier), _currencyDecimals))
			FROM ipsitem
			WHERE ipsitemchar_ipsitem_id = ipsitem_id AND ipsitem_ipshead_id = pIpsHeadId;

			GET DIAGNOSTICS _charRows = ROW_COUNT;
		END IF;

	END IF;

	RETURN _itemRows + _charRows;

END;
$$;


ALTER FUNCTION public.updatepricesbypricingschedule(pipsheadid integer, pupdatetype character, pupdateby numeric, pupdatecharprices boolean) OWNER TO admin;

--
-- Name: updatepricesbyproductcategory(integer, text, character, numeric, boolean); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION updatepricesbyproductcategory(pprodcatid integer, pprodcatpattern text, pupdatetype character, pupdateby numeric, pupdatecharprices boolean) RETURNS numeric
    LANGUAGE plpgsql
    AS $$
DECLARE
  _percentMultiplier NUMERIC;
  _currencyDecimals INTEGER;
  _itemRows INTEGER :=0;
  _charRows INTEGER :=0;

BEGIN

	IF NOT (checkPrivilege('MaintainPricingSchedules')) THEN
		RAISE EXCEPTION 'You do not have privileges to maintain Price Schedules.';
	END IF;

	-- Get the current user's currency precision
	SELECT COALESCE(locale_curr_scale, 2) INTO _currencyDecimals
	FROM locale, usr
	WHERE usr_locale_id = locale_id AND usr_username = CURRENT_USER;

	_percentMultiplier := (1.0 + (pUpdateBy / 100.0));

	IF (pUpdateType = 'V') THEN

		IF (pProdCatId IS NOT NULL) THEN
			-- Specified category id
			UPDATE ipsitem
			SET ipsitem_price = noNeg(ROUND( (ipsitem_price + pUpdateBy), _currencyDecimals ))
			FROM item
			WHERE
				ipsitem_item_id = item_id
				AND item_prodcat_id = pProdCatId;

		ELSIF (pProdCatPattern IS NOT NULL) THEN
			-- Pattern match category
			UPDATE ipsitem
			SET ipsitem_price = noNeg(ROUND( (ipsitem_price + pUpdateBy), _currencyDecimals ))
			FROM item
			WHERE
				ipsitem_item_id = item_id
				AND item_prodcat_id IN (
					SELECT prodcat_id FROM prodcat
					WHERE (prodcat_code ~ pProdCatPattern)
				);
		ELSE
			-- All categories
			UPDATE ipsitem
			SET ipsitem_price = noNeg(ROUND( (ipsitem_price + pUpdateBy), _currencyDecimals ));
		END IF;

		GET DIAGNOSTICS _itemRows = ROW_COUNT;

	ELSE

		IF (pProdCatId IS NOT NULL) THEN
			-- Specified category id
			UPDATE ipsitem
			SET ipsitem_price = noNeg(ROUND( (ipsitem_price * _percentMultiplier), _currencyDecimals ))
			FROM item
			WHERE
				ipsitem_item_id = item_id
				AND item_prodcat_id = pProdCatId;

			GET DIAGNOSTICS _itemRows = ROW_COUNT;

			IF(pUpdateCharPrices) THEN
				UPDATE ipsitemchar
				SET ipsitemchar_price = noNeg(ROUND( (ipsitemchar_price * _percentMultiplier), _currencyDecimals ))
				FROM ipsitem, item
				WHERE
					item_prodcat_id = pProdCatId
					AND ipsitem_item_id = item_id
					AND ipsitemchar_ipsitem_id = ipsitem_id;

				GET DIAGNOSTICS _charRows = ROW_COUNT;
			END IF;

		ELSIF (pProdCatPattern IS NOT NULL) THEN
			-- Pattern match category
			UPDATE ipsitem
			SET ipsitem_price = noNeg(ROUND( (ipsitem_price * _percentMultiplier), _currencyDecimals ))
			FROM item
			WHERE
				item_prodcat_id IN (
					SELECT prodcat_id FROM prodcat
					WHERE (prodcat_code ~ pProdCatPattern )
				)
				AND ipsitem_item_id = item_id;

			GET DIAGNOSTICS _itemRows = ROW_COUNT;

			IF(pUpdateCharPrices) THEN
				UPDATE ipsitemchar
				SET ipsitemchar_price = noNeg(ROUND( (ipsitemchar_price * _percentMultiplier), _currencyDecimals ))
				FROM ipsitem, item
				WHERE
					item_prodcat_id IN (
						SELECT prodcat_id FROM prodcat
						WHERE (prodcat_code ~ pProdCatPattern )
					)
					AND ipsitem_item_id = item_id
					AND ipsitemchar_ipsitem_id = ipsitem_id;

				GET DIAGNOSTICS _charRows = ROW_COUNT;
			END IF;

		ELSE
			-- All categories
			UPDATE ipsitem
			SET ipsitem_price = noNeg(ROUND( (ipsitem_price * _percentMultiplier), _currencyDecimals ));

			GET DIAGNOSTICS _itemRows = ROW_COUNT;

			IF(pUpdateCharPrices) THEN
				UPDATE ipsitemchar
				SET ipsitemchar_price = noNeg(ROUND( (ipsitemchar_price * _percentMultiplier), _currencyDecimals ));

				GET DIAGNOSTICS _charRows = ROW_COUNT;
			END IF;

		END IF;

	END IF;

	RETURN _itemRows + _charRows;

END;
$$;


ALTER FUNCTION public.updatepricesbyproductcategory(pprodcatid integer, pprodcatpattern text, pupdatetype character, pupdateby numeric, pupdatecharprices boolean) OWNER TO admin;

--
-- Name: updatereorderlevel(integer, integer, integer[]); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION updatereorderlevel(integer, integer, integer[]) RETURNS boolean
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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 INTEGER;
  _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;
$_$;


ALTER FUNCTION public.updatereorderlevel(integer, integer, integer[]) OWNER TO admin;

--
-- Name: updatereorderlevel(integer[], integer, boolean, integer[]); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION updatereorderlevel(integer[], integer, boolean, integer[]) RETURNS SETOF reordlvl
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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 	INTEGER := 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;
$_$;


ALTER FUNCTION public.updatereorderlevel(integer[], integer, boolean, integer[]) OWNER TO admin;

--
-- Name: updateretainedearnings(integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION updateretainedearnings(integer) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.updateretainedearnings(integer) OWNER TO admin;

--
-- Name: updatesoracost(integer, text, boolean, numeric, boolean); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION updatesoracost(integer, text, boolean, numeric, boolean) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.updatesoracost(integer, text, boolean, numeric, boolean) OWNER TO admin;

--
-- Name: updatestdcost(integer, text, boolean, numeric); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION updatestdcost(integer, text, boolean, numeric) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.updatestdcost(integer, text, boolean, numeric) OWNER TO admin;

--
-- Name: updatestdcost(integer, numeric, numeric, text, text); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION updatestdcost(pitemcostid integer, pnewcost numeric, poldcost numeric, pdocnumber text, pnotes text) RETURNS boolean
    LANGUAGE plpgsql
    AS $$
-- Copyright (c) 1999-2014 by OpenMFG LLC, d/b/a xTuple.
-- See www.xtuple.com/CPAL for the full text of the software license.
DECLARE
    _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 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 <> 0.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;
$$;


ALTER FUNCTION public.updatestdcost(pitemcostid integer, pnewcost numeric, poldcost numeric, pdocnumber text, pnotes text) OWNER TO admin;

--
-- Name: updatetodoitem(integer, text, text, text, integer, integer, integer, date, date, character, date, date, integer, text, boolean, text); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION updatetodoitem(integer, text, text, text, integer, integer, integer, date, date, character, date, date, integer, text, boolean, text) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.updatetodoitem(integer, text, text, text, integer, integer, integer, date, date, character, date, date, integer, text, boolean, text) OWNER TO admin;

--
-- Name: updatetodoitem(integer, text, text, text, integer, integer, integer, date, date, character, date, date, integer, text, boolean, text, integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION updatetodoitem(integer, text, text, text, integer, integer, integer, date, date, character, date, date, integer, text, boolean, text, integer) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.updatetodoitem(integer, text, text, text, integer, integer, integer, date, date, character, date, date, integer, text, boolean, text, integer) OWNER TO admin;

--
-- Name: usedefaultlocation(integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION usedefaultlocation(integer) RETURNS boolean
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.usedefaultlocation(integer) OWNER TO admin;

--
-- Name: usercancreateusers(text); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION usercancreateusers(text) RETURNS boolean
    LANGUAGE sql
    AS $_$
-- Copyright (c) 1999-2014 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);
$_$;


ALTER FUNCTION public.usercancreateusers(text) OWNER TO admin;

--
-- Name: usercanlogin(text); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION usercanlogin(pusername text) RETURNS boolean
    LANGUAGE plpgsql
    AS $$
-- Copyright (c) 1999-2014 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;
$$;


ALTER FUNCTION public.usercanlogin(pusername text) OWNER TO admin;

--
-- Name: userid(text); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION userid(text) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.userid(text) OWNER TO admin;

--
-- Name: validateorderqty(integer, numeric, boolean); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION validateorderqty(integer, numeric, boolean) RETURNS numeric
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.validateorderqty(integer, numeric, boolean) OWNER TO admin;

--
-- Name: validlocation(integer, integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION validlocation(integer, integer) RETURNS boolean
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.validlocation(integer, integer) OWNER TO admin;

--
-- Name: valueatshipping(integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION valueatshipping(plineitemid integer) RETURNS numeric
    LANGUAGE plpgsql
    AS $$
-- Copyright (c) 1999-2014 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;
$$;


ALTER FUNCTION public.valueatshipping(plineitemid integer) OWNER TO admin;

--
-- Name: valueatshipping(text, integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION valueatshipping(pordertype text, plineitemid integer) RETURNS numeric
    LANGUAGE plpgsql
    AS $$
-- Copyright (c) 1999-2014 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;
$$;


ALTER FUNCTION public.valueatshipping(pordertype text, plineitemid integer) OWNER TO admin;

--
-- Name: voidapcheck(integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION voidapcheck(integer) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 by OpenMFG LLC, d/b/a xTuple.
-- See www.xtuple.com/CPAL for the full text of the software license.
BEGIN
  RAISE WARNING 'voidAPCheck() is deprecated - use voidCheck() instead';
  RETURN voidCheck($1);
END;
$_$;


ALTER FUNCTION public.voidapcheck(integer) OWNER TO admin;

--
-- Name: voidapopenvoucher(integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION voidapopenvoucher(integer) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.voidapopenvoucher(integer) OWNER TO admin;

--
-- Name: voidapopenvoucher(integer, integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION voidapopenvoucher(integer, integer) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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 Id % as apopen not found [xtuple: voidAPOpenVoucher, -10, %]',
			pApopenid, 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 [xtuple: voidAPOpenVoucher, -20, %]',
			_n.apopen_docnumber, _n.apopen_docnumber;
  END IF;

--  Check for APApplications
  SELECT apapply_id INTO _test
  FROM apapply
  WHERE (apapply_target_apopen_id=_n.apopen_id)
  LIMIT 1;
  IF (FOUND) THEN
    RAISE EXCEPTION 'Cannot Void Voucher #% as applications exist [xtuple: voidAPOpenVoucher, -30, %]',
			_n.apopen_docnumber, _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 [xtuple: voidAPOpenVoucher, -40, %]',
			_p.vohead_number, _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 [xtuple: voidAPOpenVoucher, -50, %]',
			_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,
		     _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 [xtuple: voidAPOpenVoucher, -60, %]',
			_p.vohead_number, _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,
         _glDate, _glDate, _glDate, -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 % [xtuple: voidAPOpenVoucher, -70, %]',
			_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);

--  Delete any apselect approval records to prevent incorrect payment runs
  DELETE FROM apselect
   WHERE (apselect_apopen_id=_n.apopen_id);

--  Mark as voided
  UPDATE apopen
  SET apopen_void=TRUE
  WHERE (apopen_id=_n.apopen_id);

  RETURN pJournalNumber;

END;
$_$;


ALTER FUNCTION public.voidapopenvoucher(integer, integer) OWNER TO admin;

--
-- Name: voidcheck(integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION voidcheck(integer) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.voidcheck(integer) OWNER TO admin;

--
-- Name: voidcreditmemo(integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION voidcreditmemo(integer) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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;
$_$;


ALTER FUNCTION public.voidcreditmemo(integer) OWNER TO admin;

--
-- Name: voidinvoice(integer); Type: FUNCTION; Schema: public; Owner: admin
--

CREATE FUNCTION voidinvoice(integer) RETURNS integer
    LANGUAGE plpgsql
    AS $_$
-- Copyright (c) 1999-2014 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, cohist_unitcost,
         ( 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)
                JOIN cohist ON (cohist_doctype='I' AND cohist_invcnumber=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.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(_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, COALESCE(_r.invcitem_rev_accnt_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

--  Return billed stock to 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,
                         (_p.cohist_unitcost * _r.qty)) INTO _invhistid
    FROM itemsite JOIN costcat ON (itemsite_costcat_id=costcat_id)
    WHERE (itemsite_id=_r.itemsite_id);

  END LOOP;

-- Reopen Sales Order
  UPDATE coitem
  SET coitem_status='O'
  WHERE (coitem_id IN (SELECT cobill_coitem_id
                       FROM invcitem JOIN cobill ON (cobill_invcitem_id=invcitem_id)
                       WHERE (invcitem_invchead_id=_p.invchead_id)));

--  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;
$_$;


ALTER FUNCTION public.voidinvoice(integer) OWNER TO admin;

--
