1 CREATE OR REPLACE FUNCTION reverseCashReceipt(INTEGER, 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 pCashrcptid ALIAS FOR $1;
6 pJournalNumber ALIAS FOR $2;
11 _posted_base NUMERIC := 0;
18 _debitAccntid INTEGER;
28 SELECT fetchGLSequence() INTO _sequence;
30 SELECT accnt_id INTO _arAccntid
31 FROM cashrcpt, accnt, salescat
32 WHERE ((cashrcpt_salescat_id=salescat_id)
33 AND (salescat_ar_accnt_id=accnt_id)
34 AND (cashrcpt_id=pCashrcptid));
36 SELECT accnt_id INTO _arAccntid
38 WHERE ( (findARAccount(cashrcpt_cust_id)=accnt_id)
39 AND (cashrcpt_id=pCashrcptid) );
45 SELECT cashrcpt_cust_id, ('Reverse Cash Receipt posting for ' || cust_number||'-'||cust_name) AS custnote,
46 cashrcpt_fundstype, cashrcpt_number, cashrcpt_docnumber,
47 cashrcpt_distdate, cashrcpt_amount, cashrcpt_discount,
48 (cashrcpt_amount / cashrcpt_curr_rate) AS cashrcpt_amount_base,
49 (cashrcpt_discount / cashrcpt_curr_rate) AS cashrcpt_discount_base,
51 cashrcpt_bankaccnt_id AS bankaccnt_id,
52 accnt_id AS prepaid_accnt_id,
53 cashrcpt_usecustdeposit,
54 cashrcpt_curr_id, cashrcpt_curr_rate INTO _p
55 FROM accnt, cashrcpt LEFT OUTER JOIN custinfo ON (cashrcpt_cust_id=cust_id)
56 WHERE ( (findPrepaidAccount(cashrcpt_cust_id)=accnt_id)
57 AND (cashrcpt_id=pCashrcptid) );
62 IF (_p.cashrcpt_fundstype IN ('A', 'D', 'M', 'V')) THEN
63 SELECT ccpay_id, ccpay_type INTO _ccpayid, _cctype
65 WHERE ((ccpay_r_ordernum IN (CAST(pCashrcptid AS TEXT), _p.cashrcpt_docnumber))
66 AND (ccpay_status IN ('C', 'A')));
69 -- the following select seems to work except for xikar - bug 8848. why?
70 -- raise warning so there is some visibility if people fall into this path.
71 SELECT ccpay_id, ccpay_type INTO _ccpayid, _cctype
73 WHERE ((ccpay_order_number IN (CAST(pCashrcptid AS TEXT), _p.cashrcpt_docnumber))
74 AND (ccpay_status IN ('C', 'A')));
78 RAISE NOTICE 'PostCashReceipt() found ccpay_id % for order number %/% (ref 8848).',
79 _ccpayid, pCashrcptid, _p.cashrcpt_docnumber;
83 -- If there is a ccpay entry and the card was charged directly, use the prepaid account
84 IF (_cctype = 'C' ) THEN
85 _debitAccntid := findPrepaidAccount(_p.cashrcpt_cust_id);
86 -- If there is a ccpay entry and the card was preauthed and then charged, use the Bank account
88 SELECT accnt_id INTO _debitAccntid
89 FROM cashrcpt, bankaccnt, accnt
90 WHERE ( (cashrcpt_bankaccnt_id=bankaccnt_id)
91 AND (bankaccnt_accnt_id=accnt_id)
92 AND (cashrcpt_id=pCashrcptid) );
98 SELECT accnt_id INTO _debitAccntid
99 FROM cashrcpt, bankaccnt, accnt
100 WHERE ( (cashrcpt_bankaccnt_id=bankaccnt_id)
101 AND (bankaccnt_accnt_id=accnt_id)
102 AND (cashrcpt_id=pCashrcptid) );
108 -- Determine the amount to post to A/R Open Items
109 SELECT COALESCE(SUM(cashrcptitem_amount),0) INTO _postToAR
110 FROM cashrcptitem JOIN aropen ON (aropen_id=cashrcptitem_aropen_id)
111 WHERE ((cashrcptitem_cashrcpt_id=pCashrcptid)
112 AND (cashrcptitem_applied));
117 -- Determine the amount to post to Misc. Distributions
118 SELECT COALESCE(SUM(cashrcptmisc_amount),0) INTO _postToMisc
120 WHERE (cashrcptmisc_cashrcpt_id=pCashrcptid);
125 -- Check to see if the C/R is over applied
126 IF ((_postToAR + _postToMisc) > _p.cashrcpt_amount) THEN
130 -- Check to see if the C/R is positive amount
131 IF (_p.cashrcpt_amount <= 0) THEN
135 -- Distribute A/R Applications
136 FOR _r IN SELECT aropen_id, aropen_doctype, aropen_docnumber, aropen_docdate,
137 aropen_duedate, aropen_curr_id, aropen_curr_rate,
138 round(aropen_amount - aropen_paid, 2) <=
140 currToCurr(_p.cashrcpt_curr_id, aropen_curr_id,abs(cashrcptitem_amount + cashrcptitem_discount),_p.cashrcpt_distdate),2)
142 cashrcptitem_id, cashrcptitem_amount, cashrcptitem_discount,
143 (cashrcptitem_amount / _p.cashrcpt_curr_rate) AS cashrcptitem_amount_base,
144 (cashrcptitem_discount / _p.cashrcpt_curr_rate) AS cashrcptitem_discount_base,
146 currToCurr(_p.cashrcpt_curr_id, aropen_curr_id,abs(cashrcptitem_amount),_p.cashrcpt_distdate),2) AS new_paid,
147 round(currToCurr(_p.cashrcpt_curr_id, aropen_curr_id,abs(cashrcptitem_discount),_p.cashrcpt_distdate),2) AS new_discount
148 FROM cashrcptitem JOIN aropen ON (cashrcptitem_aropen_id=aropen_id)
149 WHERE ((cashrcptitem_cashrcpt_id=pCashrcptid)
150 AND (cashrcptitem_applied)) LOOP
153 IF (_r.cashrcptitem_discount_base > 0) THEN
154 PERFORM reverseCashReceiptDisc(_r.cashrcptitem_id, pJournalNumber);
157 -- Update the aropen item to post the paid amount
159 SET aropen_paid = _r.new_paid - _r.new_discount,
161 aropen_closedate = NULL
162 WHERE (aropen_id=_r.aropen_id);
164 -- Cache the running amount posted
165 _posted_base := _posted_base + _r.cashrcptitem_amount_base;
166 _posted := _posted + _r.cashrcptitem_amount;
168 -- Record the cashrcpt application
169 IF (_r.aropen_doctype IN ('I','D')) THEN
172 arapply_source_aropen_id, arapply_source_doctype, arapply_source_docnumber,
173 arapply_target_aropen_id, arapply_target_doctype, arapply_target_docnumber,
174 arapply_fundstype, arapply_refnumber, arapply_reftype, arapply_ref_id,
175 arapply_applied, arapply_closed,
176 arapply_postdate, arapply_distdate, arapply_journalnumber, arapply_username,
180 ( _p.cashrcpt_cust_id,
181 -1, 'K', _p.cashrcpt_number,
182 _r.aropen_id, _r.aropen_doctype, _r.aropen_docnumber,
183 _p.cashrcpt_fundstype, _p.cashrcpt_docnumber, 'CRA', _r.cashrcptitem_id,
184 (round(_r.cashrcptitem_amount, 2) * -1.0), _r.closed,
185 CURRENT_DATE, _p.cashrcpt_distdate, pJournalNumber, getEffectiveXtUser(), _p.cashrcpt_curr_id );
189 arapply_source_aropen_id, arapply_source_doctype, arapply_source_docnumber,
190 arapply_target_aropen_id, arapply_target_doctype, arapply_target_docnumber,
191 arapply_fundstype, arapply_refnumber, arapply_reftype, arapply_ref_id,
192 arapply_applied, arapply_closed, arapply_postdate, arapply_distdate,
193 arapply_journalnumber, arapply_username, arapply_curr_id )
195 ( _p.cashrcpt_cust_id,
196 _r.aropen_id, _r.aropen_doctype, _r.aropen_docnumber,
197 -1, 'R', _p.cashrcpt_number,
198 '', '', 'CRA', _r.cashrcptitem_id,
199 (round(abs(_r.cashrcptitem_amount), 2) * -1.0), _r.closed,
200 CURRENT_DATE, _p.cashrcpt_distdate, pJournalNumber, getEffectiveXtUser(), _p.cashrcpt_curr_id );
203 _exchGain := arCurrGain(_r.aropen_id,_p.cashrcpt_curr_id, abs(_r.cashrcptitem_amount),
204 _p.cashrcpt_distdate);
206 PERFORM insertIntoGLSeries( _sequence, 'A/R', 'CR',
207 (_r.aropen_doctype || '-' || _r.aropen_docnumber),
208 CASE WHEN _r.aropen_doctype != 'R' THEN _arAccntid
209 ELSE findDeferredAccount(_p.cashrcpt_cust_id) END,
210 (round(_r.cashrcptitem_amount_base + _exchGain, 2) * -1.0),
211 _p.cashrcpt_distdate, _p.custnote );
213 IF (_exchGain <> 0) THEN
214 PERFORM insertIntoGLSeries(_sequence, 'A/R', 'CR',
215 _r.aropen_doctype || '-' || _r.aropen_docnumber,
217 CASE WHEN _r.aropen_doctype != 'R' THEN _arAccntid
218 ELSE findDeferredAccount(_p.cashrcpt_cust_id) END
219 ), round(_exchGain, 2),
220 _p.cashrcpt_distdate, _p.custnote);
226 -- Distribute Misc. Applications
227 FOR _r IN SELECT cashrcptmisc_id, cashrcptmisc_accnt_id, cashrcptmisc_amount,
228 (cashrcptmisc_amount / _p.cashrcpt_curr_rate) AS cashrcptmisc_amount_base,
231 WHERE (cashrcptmisc_cashrcpt_id=pCashrcptid) LOOP
233 -- Cache the running amount posted
234 _posted_base := (_posted_base + _r.cashrcptmisc_amount_base);
235 _posted := (_posted + _r.cashrcptmisc_amount);
237 -- Record the cashrcpt application
240 arapply_source_aropen_id, arapply_source_doctype, arapply_source_docnumber,
241 arapply_target_aropen_id, arapply_target_doctype, arapply_target_docnumber,
242 arapply_fundstype, arapply_refnumber,
243 arapply_applied, arapply_closed,
244 arapply_postdate, arapply_distdate, arapply_journalnumber, arapply_username,
245 arapply_curr_id, arapply_reftype, arapply_ref_id )
247 ( _p.cashrcpt_cust_id,
250 _p.cashrcpt_fundstype, _p.cashrcpt_docnumber,
251 (round(_r.cashrcptmisc_amount, 2) * -1.0), TRUE,
252 CURRENT_DATE, _p.cashrcpt_distdate, pJournalNumber, getEffectiveXtUser(),
253 _p.cashrcpt_curr_id, 'CRD', _r.cashrcptmisc_id );
255 PERFORM insertIntoGLSeries( _sequence, 'A/R', 'CR', _r.cashrcptmisc_notes,
256 _r.cashrcptmisc_accnt_id,
257 (round(_r.cashrcptmisc_amount_base, 2) * -1.0),
258 _p.cashrcpt_distdate, _p.custnote );
262 -- Post any remaining Cash to an A/R Debit Memo
263 -- this credit memo may absorb an occasional currency exchange rounding error
264 IF (round(_posted_base, 2) < round(_p.cashrcpt_amount_base, 2)) THEN
265 _comment := ('Unapplied from ' || _p.cashrcpt_fundstype || '-' || _p.cashrcpt_docnumber);
266 PERFORM insertIntoGLSeries( _sequence, 'A/R', 'CR',
269 ((round(_p.cashrcpt_amount_base, 2) - round(_posted_base, 2)) * -1.0),
270 _p.cashrcpt_distdate, _p.custnote );
271 SELECT fetchArMemoNumber() INTO _arMemoNumber;
272 -- Post A/R Debit Memo
273 SELECT createARDebitMemo(NULL, _p.cashrcpt_cust_id, pJournalNumber, _arMemoNumber, '',
274 _p.cashrcpt_distdate, (_p.cashrcpt_amount - _posted),
275 _comment, -1, -1, -1, _p.cashrcpt_distdate, -1, NULL, 0,
276 _p.cashrcpt_curr_id) INTO _aropenid;
277 -- Create Cash Receipt Item to capture posting
278 INSERT INTO cashrcptitem
279 ( cashrcptitem_cashrcpt_id, cashrcptitem_aropen_id, cashrcptitem_amount )
281 ( pCashrcptid, _aropenid, ((_p.cashrcpt_amount - _posted) * 1.0) );
283 ELSIF (round(_posted_base, 2) > round(_p.cashrcpt_amount_base, 2)) THEN
284 PERFORM insertIntoGLSeries(_sequence, 'A/R', 'CR',
285 'Currency Exchange Rounding - ' || _p.cashrcpt_docnumber,
286 getGainLossAccntId(_debitAccntid),
287 ((round(_posted_base, 2) - round((_p.cashrcpt_amount_base + _p.cashrcpt_discount_base), 2)) * 1.0),
288 _p.cashrcpt_distdate, _p.custnote);
292 PERFORM insertIntoGLSeries( _sequence, 'A/R', 'CR',
293 (_p.cashrcpt_fundstype || '-' || _p.cashrcpt_docnumber),
294 _debitAccntid, round(_p.cashrcpt_amount_base, 2),
295 _p.cashrcpt_distdate,
298 PERFORM postGLSeries(_sequence, pJournalNumber);
300 -- Update and void the posted cashrcpt
301 UPDATE cashrcpt SET cashrcpt_posted=FALSE,
302 cashrcpt_posteddate=NULL,
303 cashrcpt_postedby=NULL,
305 WHERE (cashrcpt_id=pCashrcptid);
310 $$ LANGUAGE 'plpgsql';