1 CREATE OR REPLACE FUNCTION postCashReceipt(pCashrcptid INTEGER,
2 pJournalNumber INTEGER) RETURNS INTEGER AS $$
3 -- Copyright (c) 1999-2014 by OpenMFG LLC, d/b/a xTuple.
4 -- See www.xtuple.com/CPAL for the full text of the software license.
15 _posted_base NUMERIC := 0;
22 _debitAccntid INTEGER;
32 SELECT fetchGLSequence() INTO _sequence;
34 SELECT accnt_id INTO _arAccntid
35 FROM cashrcpt, accnt, salescat
36 WHERE ((cashrcpt_salescat_id=salescat_id)
37 AND (salescat_ar_accnt_id=accnt_id)
38 AND (cashrcpt_id=pCashrcptid));
40 SELECT accnt_id INTO _arAccntid
41 FROM cashrcpt LEFT OUTER JOIN accnt ON (accnt_id=findARAccount(cashrcpt_cust_id))
42 WHERE ( (findARAccount(cashrcpt_cust_id)=0 OR accnt_id > 0) -- G/L interface might be disabled
43 AND (cashrcpt_id=pCashrcptid) );
49 SELECT cashrcpt_cust_id, (cust_number||'-'||cust_name) AS custnote,
50 cashrcpt_fundstype, cashrcpt_number, cashrcpt_docnumber,
51 cashrcpt_distdate, cashrcpt_amount, cashrcpt_discount,
52 (cashrcpt_amount / cashrcpt_curr_rate) AS cashrcpt_amount_base,
53 (cashrcpt_discount / cashrcpt_curr_rate) AS cashrcpt_discount_base,
55 cashrcpt_bankaccnt_id AS bankaccnt_id,
56 accnt_id AS prepaid_accnt_id,
57 cashrcpt_usecustdeposit,
58 COALESCE(cashrcpt_applydate, cashrcpt_distdate) AS applydate,
59 cashrcpt_curr_id, cashrcpt_curr_rate, cashrcpt_posted, cashrcpt_void INTO _p
60 FROM cashrcpt LEFT OUTER JOIN custinfo ON (cashrcpt_cust_id=cust_id)
61 LEFT OUTER JOIN accnt ON (accnt_id=findPrepaidAccount(cashrcpt_cust_id))
62 WHERE ( (findPrepaidAccount(cashrcpt_cust_id)=0 OR accnt_id > 0) -- G/L interface might be disabled
63 AND (cashrcpt_id=pCashrcptid) );
68 IF (COALESCE(_p.cashrcpt_distdate > _p.applydate, false)) THEN
69 RAISE EXCEPTION 'Cannot post cashrcpt % because application date is before distribution date.', _p.cashrcpt_docnumber;
72 IF (COALESCE(_p.cashrcpt_posted, false)) THEN
73 RAISE EXCEPTION 'Cannot post cashrcpt % because the document has already been posted.', _p.cashrcpt_docnumber;
76 IF (COALESCE(_p.cashrcpt_void, false)) THEN
77 RAISE EXCEPTION 'Cannot post cashrcpt % because the document has been voided.', _p.cashrcpt_docnumber;
80 _predist := COALESCE(_p.cashrcpt_distdate < _p.applydate, false);
82 IF (_p.cashrcpt_fundstype IN ('A', 'D', 'M', 'V')) THEN
83 SELECT ccpay_id, ccpay_type INTO _ccpayid, _cctype
85 WHERE ((ccpay_r_ordernum IN (CAST(pCashrcptid AS TEXT), _p.cashrcpt_docnumber))
86 AND (ccpay_status IN ('C', 'A')));
89 -- the following select seems to work except for xikar - bug 8848. why?
90 -- raise warning so there is some visibility if people fall into this path.
91 SELECT ccpay_id, ccpay_type INTO _ccpayid, _cctype
93 WHERE ((ccpay_order_number IN (CAST(pCashrcptid AS TEXT), _p.cashrcpt_docnumber))
94 AND (ccpay_status IN ('C', 'A')));
98 RAISE NOTICE 'PostCashReceipt() found ccpay_id % for order number %/% (ref 8848).',
99 _ccpayid, pCashrcptid, _p.cashrcpt_docnumber;
103 -- If there is a ccpay entry and the card was charged directly, use the prepaid account
104 IF (_cctype = 'C' ) THEN
105 _debitAccntid := findPrepaidAccount(_p.cashrcpt_cust_id);
106 -- If there is a ccpay entry and the card was preauthed and then charged, use the Bank account
108 SELECT accnt_id INTO _debitAccntid
109 FROM cashrcpt, bankaccnt, accnt
110 WHERE ( (cashrcpt_bankaccnt_id=bankaccnt_id)
111 AND (bankaccnt_accnt_id=accnt_id)
112 AND (cashrcpt_id=pCashrcptid) );
118 SELECT accnt_id INTO _debitAccntid
119 FROM cashrcpt, bankaccnt, accnt
120 WHERE ( (cashrcpt_bankaccnt_id=bankaccnt_id)
121 AND (bankaccnt_accnt_id=accnt_id)
122 AND (cashrcpt_id=pCashrcptid) );
128 -- Determine the amount to post to A/R Open Items
129 SELECT COALESCE(SUM(cashrcptitem_amount), 0) INTO _postToAR
130 FROM cashrcptitem JOIN aropen ON (aropen_id=cashrcptitem_aropen_id)
131 WHERE (cashrcptitem_cashrcpt_id=pCashrcptid);
136 -- Determine the amount to post to Misc. Distributions
137 SELECT COALESCE(SUM(cashrcptmisc_amount), 0) INTO _postToMisc
139 WHERE (cashrcptmisc_cashrcpt_id=pCashrcptid);
144 -- Determine the amount to post to Discount Credit Memo
145 SELECT COALESCE(SUM(cashrcptitem_discount), 0) INTO _postToCM
146 FROM cashrcptitem JOIN aropen ON ( (aropen_id=cashrcptitem_aropen_id) AND (aropen_doctype IN ('I', 'D')) )
147 WHERE (cashrcptitem_cashrcpt_id=pCashrcptid);
152 -- Check to see if the C/R is over applied
153 IF ((_postToAR + _postToMisc) > _p.cashrcpt_amount) THEN
157 -- Check to see if the C/R is positive amount
158 IF (_p.cashrcpt_amount <= 0) THEN
162 -- Distribute A/R Applications
163 FOR _r IN SELECT aropen_id, aropen_doctype, aropen_docnumber, aropen_docdate,
164 aropen_duedate, aropen_curr_id, aropen_curr_rate, aropen_amount,
165 round(aropen_amount - aropen_paid, 2) <=
166 round(currToCurr(_p.cashrcpt_curr_id, aropen_curr_id,abs(cashrcptitem_amount + cashrcptitem_discount),_p.cashrcpt_distdate),2)
168 cashrcptitem_id, cashrcptitem_amount, cashrcptitem_discount,
169 (cashrcptitem_amount / _p.cashrcpt_curr_rate) AS cashrcptitem_amount_base,
170 (cashrcptitem_discount / _p.cashrcpt_curr_rate) AS cashrcptitem_discount_base,
172 currToCurr(_p.cashrcpt_curr_id, aropen_curr_id,abs(cashrcptitem_amount),_p.cashrcpt_distdate),2) AS new_paid,
173 round(currToCurr(_p.cashrcpt_curr_id, aropen_curr_id,cashrcptitem_discount,_p.cashrcpt_distdate),2) AS new_discount
174 FROM cashrcptitem JOIN aropen ON (aropen_id=cashrcptitem_aropen_id)
175 WHERE ((cashrcptitem_cashrcpt_id=pCashrcptid)
176 AND (NOT _predist OR aropen_doctype IN ('C','R'))) LOOP
179 IF (_r.cashrcptitem_discount_base > 0) THEN
180 PERFORM postCashReceiptDisc(_r.cashrcptitem_id, pJournalNumber);
183 -- Update the aropen item to post the paid amount
185 SET aropen_paid = _r.new_paid + _r.new_discount,
186 aropen_open = (NOT _r.closed),
187 aropen_closedate = CASE WHEN _r.closed THEN _p.cashrcpt_distdate END
188 WHERE (aropen_id=_r.aropen_id);
190 -- Cache the running amount posted
191 _posted_base := _posted_base + _r.cashrcptitem_amount_base;
192 _posted := _posted + _r.cashrcptitem_amount;
194 -- Record the cashrcpt application
195 IF (_r.aropen_doctype IN ('I','D')) THEN
198 arapply_source_aropen_id, arapply_source_doctype, arapply_source_docnumber,
199 arapply_target_aropen_id, arapply_target_doctype, arapply_target_docnumber,
200 arapply_fundstype, arapply_refnumber, arapply_reftype, arapply_ref_id,
201 arapply_applied, arapply_closed,
202 arapply_postdate, arapply_distdate, arapply_journalnumber, arapply_username,
205 ( _p.cashrcpt_cust_id,
206 -1, 'K', _p.cashrcpt_number,
207 _r.aropen_id, _r.aropen_doctype, _r.aropen_docnumber,
208 _p.cashrcpt_fundstype, _p.cashrcpt_docnumber, 'CRA', _r.cashrcptitem_id,
209 round(_r.cashrcptitem_amount, 2), _r.closed,
210 _p.applydate, _p.cashrcpt_distdate, pJournalNumber, getEffectiveXtUser(), _p.cashrcpt_curr_id);
214 arapply_source_aropen_id, arapply_source_doctype, arapply_source_docnumber,
215 arapply_target_aropen_id, arapply_target_doctype, arapply_target_docnumber,
216 arapply_fundstype, arapply_refnumber, arapply_reftype, arapply_ref_id,
217 arapply_applied, arapply_closed, arapply_postdate, arapply_distdate,
218 arapply_journalnumber, arapply_username, arapply_curr_id )
220 ( _p.cashrcpt_cust_id,
221 _r.aropen_id, _r.aropen_doctype, _r.aropen_docnumber,
222 -1, 'R', _p.cashrcpt_number,
223 '', '', 'CRA', _r.cashrcptitem_id,
224 round(abs(_r.cashrcptitem_amount), 2), _r.closed,
225 _p.applydate, _p.cashrcpt_distdate, pJournalNumber, getEffectiveXtUser(), _p.cashrcpt_curr_id );
228 _exchGain := arCurrGain(_r.aropen_id,_p.cashrcpt_curr_id, abs(_r.cashrcptitem_amount),
229 _p.cashrcpt_distdate);
231 PERFORM insertIntoGLSeries( _sequence, 'A/R', 'CR',
232 (_r.aropen_doctype || '-' || _r.aropen_docnumber),
233 CASE WHEN _r.aropen_doctype != 'R' THEN _arAccntid
234 ELSE findDeferredAccount(_p.cashrcpt_cust_id) END,
235 round(_r.cashrcptitem_amount_base + _exchGain, 2),
236 _p.cashrcpt_distdate, _p.custnote, pCashrcptid );
238 IF (_exchGain <> 0) THEN
239 PERFORM insertIntoGLSeries(_sequence, 'A/R', 'CR',
240 _r.aropen_doctype || '-' || _r.aropen_docnumber,
242 CASE WHEN _r.aropen_doctype != 'R' THEN _arAccntid
243 ELSE findDeferredAccount(_p.cashrcpt_cust_id) END
244 ), round(_exchGain, 2) * -1,
245 _p.cashrcpt_distdate, _p.custnote, pCashrcptid);
250 -- Distribute Misc. Applications
251 FOR _r IN SELECT cashrcptmisc_id, cashrcptmisc_accnt_id, cashrcptmisc_amount,
252 (cashrcptmisc_amount / cashrcpt_curr_rate) AS cashrcptmisc_amount_base,
253 cashrcptmisc_notes, cashrcpt_curr_id
254 FROM cashrcptmisc JOIN
255 cashrcpt ON (cashrcptmisc_cashrcpt_id = cashrcpt_id)
256 WHERE (cashrcptmisc_cashrcpt_id=pCashrcptid) LOOP
258 -- Cache the running amount posted
259 _posted_base := (_posted_base + _r.cashrcptmisc_amount_base);
260 _posted := (_posted + _r.cashrcptmisc_amount);
262 -- Record the cashrcpt application
265 arapply_source_aropen_id, arapply_source_doctype, arapply_source_docnumber,
266 arapply_target_aropen_id, arapply_target_doctype, arapply_target_docnumber,
267 arapply_fundstype, arapply_refnumber,
268 arapply_applied, arapply_closed,
269 arapply_postdate, arapply_distdate, arapply_journalnumber, arapply_username,
270 arapply_curr_id, arapply_reftype, arapply_ref_id )
272 ( _p.cashrcpt_cust_id,
275 _p.cashrcpt_fundstype, _p.cashrcpt_docnumber,
276 round(_r.cashrcptmisc_amount, 2), TRUE,
277 _p.applydate, _p.cashrcpt_distdate, pJournalNumber, getEffectiveXtUser(),
278 _r.cashrcpt_curr_id, 'CRD', _r.cashrcptmisc_id );
279 PERFORM insertIntoGLSeries( _sequence, 'A/R', 'CR', _r.cashrcptmisc_notes,
280 _r.cashrcptmisc_accnt_id,
281 round(_r.cashrcptmisc_amount_base, 2),
282 _p.cashrcpt_distdate, _p.custnote, pCashrcptid );
286 -- Post any remaining Cash to an A/R Cash Despoit (Credit Memo)
287 -- this credit memo may absorb an occasional currency exchange rounding error
288 IF (round(_posted_base, 2) < round(_p.cashrcpt_amount_base, 2)) THEN
289 _comment := ('Unapplied from ' || _p.cashrcpt_fundstype || '-' || _p.cashrcpt_docnumber);
290 PERFORM insertIntoGLSeries( _sequence, 'A/R', 'CR',
293 round(_p.cashrcpt_amount_base, 2) -
294 round(_posted_base, 2),
295 _p.cashrcpt_distdate, _p.custnote, pCashrcptid );
296 SELECT fetchArMemoNumber() INTO _arMemoNumber;
297 IF(_p.cashrcpt_usecustdeposit) THEN
298 -- Post Customer Deposit
299 SELECT createARCashDeposit(_p.cashrcpt_cust_id, _arMemoNumber, '',
300 _p.cashrcpt_distdate, (_p.cashrcpt_amount - _posted),
301 _comment, pJournalNumber, _p.cashrcpt_curr_id) INTO _aropenid;
303 -- Post A/R Credit Memo
304 _aropenid := createARCreditMemo(NULL, _p.cashrcpt_cust_id, _arMemoNumber, '',
305 _p.cashrcpt_distdate, (_p.cashrcpt_amount - _posted),
306 _comment, -1, -1, -1, _p.cashrcpt_distdate, -1, NULL, 0,
307 pJournalNumber, _p.cashrcpt_curr_id, _arAccntid);
310 IF (_ccpayid IS NOT NULL) THEN
311 INSERT INTO payaropen (payaropen_ccpay_id, payaropen_aropen_id,
312 payaropen_amount, payaropen_curr_id
313 ) VALUES (_ccpayid, _aropenid,
314 _p.cashrcpt_amount, _p.cashrcpt_curr_id);
317 -- Create Cash Receipt Item to capture posting
318 IF (_predist=false) THEN
319 INSERT INTO cashrcptitem
320 ( cashrcptitem_cashrcpt_id, cashrcptitem_aropen_id, cashrcptitem_amount, cashrcptitem_applied )
322 ( pCashrcptid, _aropenid, (_p.cashrcpt_amount - _posted), false );
325 ELSIF (round(_posted_base, 2) > round((_p.cashrcpt_amount_base), 2)) THEN
326 PERFORM insertIntoGLSeries(_sequence, 'A/R', 'CR',
327 'Currency Exchange Rounding - ' || _p.cashrcpt_docnumber,
328 getGainLossAccntId(_debitAccntid),
329 round(_posted_base, 2) - round((_p.cashrcpt_amount_base + _p.cashrcpt_discount_base), 2),
330 _p.cashrcpt_distdate, _p.custnote, pCashrcptid);
334 PERFORM insertIntoGLSeries( _sequence, 'A/R', 'CR',
335 (_p.cashrcpt_fundstype || '-' || _p.cashrcpt_docnumber),
336 _debitAccntid, round(_p.cashrcpt_amount_base, 2) * -1,
337 _p.cashrcpt_distdate,
338 _p.custnote, pCashrcptid );
340 PERFORM postGLSeries(_sequence, pJournalNumber);
342 -- convert the cashrcptitem records to applications against the cm/cd if we are _predist
343 IF(_predist=true) THEN
346 WHERE ((cashrcptitem_cashrcpt_id=pCashrcptid)
347 AND (cashrcptitem_amount > 0)) LOOP
349 -- Handle discount if applicable
350 IF (_r.cashrcptitem_discount > 0) THEN
351 PERFORM postCashReceiptDisc(_r.cashrcptitem_id, pJournalNumber);
354 INSERT INTO arcreditapply (arcreditapply_source_aropen_id, arcreditapply_target_aropen_id,
355 arcreditapply_amount, arcreditapply_curr_id)
356 VALUES(_aropenid, _r.cashrcptitem_aropen_id,
357 _r.cashrcptitem_amount, _p.cashrcpt_curr_id);
358 _posted := (_posted + _r.cashrcptitem_amount);
362 PERFORM postArCreditMemoApplication(_aropenid, _p.applydate);
364 -- If there is any left over go ahead and create an additional cashrcptitem record for it with the amount
365 IF (round(_posted, 2) < round(_p.cashrcpt_amount, 2)) THEN
366 INSERT INTO cashrcptitem
367 ( cashrcptitem_cashrcpt_id, cashrcptitem_aropen_id, cashrcptitem_amount, cashrcptitem_applied )
369 ( pCashrcptid, _aropenid, (_p.cashrcpt_amount - _posted), false );
373 -- Update the posted cashrcpt
374 UPDATE cashrcpt SET cashrcpt_posted=TRUE,
375 cashrcpt_posteddate=CURRENT_DATE,
376 cashrcpt_postedby=getEffectiveXtUser()
377 WHERE (cashrcpt_id=pCashrcptid);
382 $$ LANGUAGE 'plpgsql';