1 CREATE OR REPLACE FUNCTION voidApopenVoucher(INTEGER) RETURNS INTEGER AS $$
2 -- Copyright (c) 1999-2014 by OpenMFG LLC, d/b/a xTuple.
3 -- See www.xtuple.com/CPAL for the full text of the software license.
5 pApopenid ALIAS FOR $1;
7 RETURN voidApopenVoucher(pApopenid, fetchJournalNumber('AP-VO'));
11 CREATE OR REPLACE FUNCTION voidApopenVoucher(INTEGER, INTEGER) RETURNS INTEGER AS $$
12 -- Copyright (c) 1999-2014 by OpenMFG LLC, d/b/a xTuple.
13 -- See www.xtuple.com/CPAL for the full text of the software license.
15 pApopenid ALIAS FOR $1;
16 pJournalNumber ALIAS FOR $2;
18 _apcreditapplyid INTEGER;
22 _totalAmount_base NUMERIC;
24 _itemAmount_base NUMERIC;
36 _exchGainFreight NUMERIC;
37 _firstExchDateFreight DATE;
43 _totalAmount_base := 0;
45 SELECT fetchGLSequence() INTO _sequence;
47 -- Cache APOpen Information
48 SELECT apopen.* INTO _n
50 WHERE ( (apopen_doctype='V')
51 AND (apopen_id=pApopenid) );
53 RAISE EXCEPTION 'Cannot Void Voucher Id % as apopen not found [xtuple: voidAPOpenVoucher, -10, %]',
57 -- Cache Voucher Infomation
59 vend_number || '-' || vend_name || ' ' || vohead_reference
61 COALESCE(pohead_orderdate, vohead_docdate) AS pohead_orderdate,
62 COALESCE(pohead_curr_id, vohead_curr_id) AS pohead_curr_id INTO _p
63 FROM vohead JOIN vendinfo ON (vend_id=vohead_vend_id)
64 LEFT OUTER JOIN pohead ON (vohead_pohead_id = pohead_id)
65 WHERE (vohead_number=_n.apopen_docnumber);
67 RAISE EXCEPTION 'Cannot Void Voucher #% as vohead not found [xtuple: voidAPOpenVoucher, -20, %]',
68 _n.apopen_docnumber, _n.apopen_docnumber;
71 -- Check for APApplications
72 SELECT apapply_id INTO _test
74 WHERE (apapply_target_apopen_id=_n.apopen_id)
77 RAISE EXCEPTION 'Cannot Void Voucher #% as applications exist [xtuple: voidAPOpenVoucher, -30, %]',
78 _n.apopen_docnumber, _n.apopen_docnumber;
81 _glDate := COALESCE(_p.vohead_gldistdate, _p.vohead_distdate);
83 -- there is no currency gain/loss on items, see issue 3892,
84 -- but there might be on freight, which is first encountered at p/o receipt
85 SELECT recv_date::DATE INTO _firstExchDateFreight
87 WHERE (recv_vohead_id = _p.vohead_id);
89 -- Start by handling taxes
90 FOR _r IN SELECT tax_sales_accnt_id,
91 round(sum(taxdetail_tax),2) AS tax,
92 currToBase(_p.vohead_curr_id, round(sum(taxdetail_tax),2), _p.vohead_docdate) AS taxbasevalue
94 JOIN calculateTaxDetailSummary('VO', _p.vohead_id, 'T') ON (taxdetail_tax_id=tax_id)
95 GROUP BY tax_id, tax_sales_accnt_id LOOP
97 PERFORM insertIntoGLSeries( _sequence, 'A/P', 'VO', _p.vohead_number,
98 _r.tax_sales_accnt_id,
99 (_r.taxbasevalue * -1),
100 _glDate, _p.glnotes );
102 _totalAmount_base := (_totalAmount_base - _r.taxbasevalue);
103 _totalAmount := (_totalAmount - _r.tax);
107 -- Loop through the vodist records for the passed vohead that
108 -- are posted against a P/O Item
109 FOR _g IN SELECT DISTINCT poitem_id, voitem_qty, poitem_expcat_id,
110 poitem_invvenduomratio,
111 COALESCE(itemsite_id, -1) AS itemsiteid,
112 COALESCE(itemsite_costcat_id, -1) AS costcatid,
113 COALESCE(itemsite_item_id, -1) AS itemsite_item_id,
116 SELECT SUM(recv_value) AS value
118 WHERE (recv_voitem_id=voitem_id)
120 SELECT SUM(poreject_value)*-1 AS value
122 WHERE (poreject_voitem_id=voitem_id)) as data)
124 (poitem_freight_vouchered / poitem_qty_vouchered) * voitem_qty AS vouchered_freight,
125 currToBase(_p.pohead_curr_id, (poitem_freight_vouchered / poitem_qty_vouchered) * voitem_qty, _firstExchDateFreight ) AS vouchered_freight_base,
127 currToBase(_p.vohead_curr_id, voitem_freight,
128 _p.vohead_distdate) AS voitem_freight_base
130 poitem LEFT OUTER JOIN itemsite ON (poitem_itemsite_id=itemsite_id)
131 WHERE ( (vodist_poitem_id=poitem_id)
132 AND (voitem_poitem_id=poitem_id)
133 AND (voitem_vohead_id=vodist_vohead_id)
134 AND (vodist_vohead_id=_p.vohead_id)) LOOP
136 -- Grab the G/L Accounts
137 IF (_g.costcatid = -1) THEN
138 SELECT pp.accnt_id AS pp_accnt_id,
139 lb.accnt_id AS lb_accnt_id INTO _a
140 FROM expcat, accnt AS pp, accnt AS lb
141 WHERE ( (expcat_purchprice_accnt_id=pp.accnt_id)
142 AND (expcat_liability_accnt_id=lb.accnt_id)
143 AND (expcat_id=_g.poitem_expcat_id) );
145 RAISE EXCEPTION 'Cannot Void Voucher #% due to unassigned G/L Accounts [xtuple: voidAPOpenVoucher, -40, %]',
146 _p.vohead_number, _p.vohead_number;
149 SELECT pp.accnt_id AS pp_accnt_id,
150 lb.accnt_id AS lb_accnt_id INTO _a
151 FROM costcat, accnt AS pp, accnt AS lb
152 WHERE ( (costcat_purchprice_accnt_id=pp.accnt_id)
153 AND (costcat_liability_accnt_id=lb.accnt_id)
154 AND (costcat_id=_g.costcatid) );
156 RAISE EXCEPTION 'Cannot Void Voucher #% due to unassigned G/L Accounts [xtuple: voidAPOpenVoucher, -50, %]',
157 _p.vohead_number, _p.vohead_number;
161 -- Clear the Item Amount accumulator
162 _itemAmount_base := 0;
165 -- Figure out the total posted value for this line item
166 FOR _d IN SELECT vodist_id, vodist_amount,
167 _p.vohead_curr_id, vodist_costelem_id,
168 currToBase(_p.vohead_curr_id, vodist_amount,
169 _p.vohead_distdate) AS vodist_amount_base
171 WHERE ( (vodist_vohead_id=_p.vohead_id)
172 AND (vodist_poitem_id=_g.poitem_id) ) LOOP
177 WHERE ( (itemcost_item_id = _g.itemsite_item_id)
178 AND (itemcost_costelem_id = _d.vodist_costelem_id) );
181 _pExplain := _costx.itemcost_lowlevel;
184 -- Add the Distribution Amount to the Item Amount
185 _itemAmount_base := _itemAmount_base + ROUND(_d.vodist_amount_base, 2);
186 _itemAmount := _itemAmount + _d.vodist_amount;
190 -- Distribute from the clearing account
191 PERFORM insertIntoGLSeries( _sequence, 'A/P', 'VO', text(_p.vohead_number),
193 round(_g.value_base + _g.vouchered_freight_base, 2),
194 _glDate, _p.glnotes );
196 -- Attribute the correct portion to currency gain/loss
197 _exchGainFreight := 0;
198 SELECT currGain(_p.pohead_curr_id, _g.vouchered_freight,
199 _firstExchDateFreight, _p.vohead_distdate )
200 INTO _exchGainFreight;
201 IF (round(_exchGainFreight, 2) <> 0) THEN
202 PERFORM insertIntoGLSeries(_sequence, 'A/P', 'VO',
203 text(_p.vohead_number),
204 getGainLossAccntId(_a.lb_accnt_id), round(_exchGainFreight, 2) * -1,
205 _glDate, _p.glnotes);
208 -- Distribute the remaining variance to the Purchase Price Variance account
209 IF (round(_itemAmount_base, 2) <> round(_g.value_base, 2)) THEN
210 _tmpTotal := round(_itemAmount_base, 2) - round(_g.value_base, 2);
211 PERFORM insertIntoGLSeries( _sequence, 'A/P', 'VO', text(_p.vohead_number),
214 _glDate, _p.glnotes );
217 -- Distribute the remaining freight variance to the Purchase Price Variance account
218 IF (round(_g.voitem_freight_base + _exchGainFreight, 2) <> round(_g.vouchered_freight_base, 2)) THEN
219 _tmpTotal := round(_g.voitem_freight_base + _exchGainFreight, 2) - round(_g.vouchered_freight_base, 2);
220 PERFORM insertIntoGLSeries( _sequence, 'A/P', 'VO', text(_p.vohead_number),
223 _glDate, _p.glnotes );
226 -- Add the distribution amount to the total amount to distribute
227 _totalAmount_base := (_totalAmount_base + _itemAmount_base + _g.voitem_freight_base);
228 _totalAmount := (_totalAmount + _itemAmount + _g.voitem_freight);
230 -- Reverse the posting for all the Tagged Receivings for this P/O Item
232 SET recv_invoiced=FALSE,
233 recv_recvcost_curr_id=basecurrid(),
238 WHERE ( (recv_orderitem_id=poitem_id)
239 AND (recv_order_type='PO')
240 AND (recv_orderitem_id=_g.poitem_id)
241 AND (recv_vohead_id=_p.vohead_id) );
243 -- Reverse the posting for all the Tagged Rejections for this P/O Item
245 SET poreject_invoiced=FALSE,
246 poreject_vohead_id=NULL,
247 poreject_voitem_id=NULL
248 WHERE ( (poreject_poitem_id=_g.poitem_id)
249 AND (poreject_vohead_id=_p.vohead_id) );
251 -- Update the qty and freight vouchered fields
253 SET poitem_qty_vouchered = (poitem_qty_vouchered - _g.voitem_qty),
254 poitem_freight_vouchered = (poitem_freight_vouchered - _g.voitem_freight)
255 WHERE (poitem_id=_g.poitem_id);
259 -- Loop through the vodist records for the passed vohead that
260 -- are not posted against a P/O Item
261 -- Skip the tax distributions
262 FOR _d IN SELECT vodist_id,
263 currToBase(_p.vohead_curr_id, vodist_amount,
264 _p.vohead_distdate) AS vodist_amount_base,
266 vodist_accnt_id, vodist_expcat_id
268 WHERE ( (vodist_vohead_id=_p.vohead_id)
269 AND (vodist_poitem_id=-1)
270 AND (vodist_tax_id=-1) ) LOOP
272 -- Distribute from the misc. account
273 IF (_d.vodist_accnt_id = -1) THEN
274 PERFORM insertIntoGLSeries( _sequence, 'A/P', 'VO', text(_p.vohead_number),
276 round(_d.vodist_amount_base, 2),
277 _glDate, _p.glnotes )
279 WHERE (expcat_id=_d.vodist_expcat_id);
281 PERFORM insertIntoGLSeries( _sequence, 'A/P', 'VO', text(_p.vohead_number),
283 round(_d.vodist_amount_base, 2),
284 _glDate, _p.glnotes );
287 -- Add the Distribution Amount to the Total Amount
288 _totalAmount_base := _totalAmount_base + ROUND(_d.vodist_amount_base, 2);
289 _totalAmount := _totalAmount + _d.vodist_amount;
293 SELECT insertIntoGLSeries( _sequence, 'A/P', 'VO', text(vohead_number),
294 accnt_id, round(_totalAmount_base, 2) * -1,
295 _glDate, _p.glnotes ) INTO _test
296 FROM vohead LEFT OUTER JOIN accnt ON (accnt_id=findAPAccount(vohead_vend_id))
297 WHERE ( (findAPAccount(vohead_vend_id)=0 OR accnt_id > 0) -- G/L interface might be disabled
298 AND (vohead_id=_p.vohead_id) );
300 RAISE EXCEPTION 'Cannot Void Voucher #% due to an unassigned A/P Account [xtuple: voidAPOpenVoucher, -60, %]',
301 _p.vohead_number, _p.vohead_number;
304 PERFORM postGLSeries(_sequence, pJournalNumber);
306 -- Create the A/P Open Item
307 SELECT NEXTVAL('apopen_apopen_id_seq') INTO _apopenid;
308 _reference := ('Void Voucher #' || _n.apopen_docnumber);
310 ( apopen_id, apopen_username, apopen_journalnumber,
311 apopen_vend_id, apopen_docnumber, apopen_doctype, apopen_ponumber,
312 apopen_docdate, apopen_duedate, apopen_distdate, apopen_terms_id, apopen_curr_id,
313 apopen_amount, apopen_paid, apopen_open, apopen_notes, apopen_discount, apopen_curr_rate )
314 SELECT _apopenid, getEffectiveXtUser(), pJournalnumber,
315 apopen_vend_id, apopen_docnumber, 'C', apopen_ponumber,
316 _glDate, _glDate, _glDate, -1, apopen_curr_id,
317 apopen_amount - apopen_paid, 0, TRUE, _reference, TRUE, apopen_curr_rate
319 WHERE (apopen_id=_n.apopen_id);
321 SELECT apcreditapply_id INTO _apcreditapplyid
323 WHERE ( (apcreditapply_source_apopen_id=_apopenid)
324 AND (apcreditapply_target_apopen_id=_n.apopen_id) );
327 SET apcreditapply_amount=_n.apopen_amount-_n.apopen_paid
328 WHERE (apcreditapply_id=_apcreditapplyid);
330 SELECT nextval('apcreditapply_apcreditapply_id_seq') INTO _apcreditapplyid;
331 INSERT INTO apcreditapply
332 ( apcreditapply_id, apcreditapply_source_apopen_id,
333 apcreditapply_target_apopen_id, apcreditapply_amount,
334 apcreditapply_curr_id )
335 VALUES ( _apcreditapplyid, _apopenid, _n.apopen_id, _n.apopen_amount-_n.apopen_paid, _n.apopen_curr_id );
338 SELECT postAPCreditMemoApplication(_apopenid) INTO _result;
340 IF (_result < 0) THEN
341 RAISE EXCEPTION 'Credit application failed with result % [xtuple: voidAPOpenVoucher, -70, %]',
345 -- Reopen all of the P/O Items that were closed by this Voucher
347 SET poitem_status='O'
349 WHERE ( (voitem_poitem_id=poitem_id)
351 AND (voitem_vohead_id=_p.vohead_id) );
355 SET pohead_status='O'
356 WHERE (pohead_id=_p.vohead_pohead_id);
361 WHERE (apopen_id=_n.apopen_id);
363 RETURN pJournalNumber;
366 $$ LANGUAGE 'plpgsql';