1 PACKAGE BODY arpcurr AS
2 /* $Header: ARPLCURB.pls 120.9.12010000.2 2009/01/06 00:40:53 vpusulur ship $ */
3
4 TYPE CurrencyCodeType IS TABLE OF VARCHAR2(15) INDEX BY BINARY_INTEGER;
5 TYPE PrecisionType IS TABLE OF NUMBER(1) INDEX BY BINARY_INTEGER;
6 TYPE MauType IS TABLE OF NUMBER INDEX BY BINARY_INTEGER;
7 NextElement BINARY_INTEGER := 0;
8 CurrencyCode CurrencyCodeType;
9 Precision PrecisionType;
10 Mau MauType;
11 PG_DEBUG varchar2(1) := NVL(FND_PROFILE.value('AFLOG_ENABLED'), 'N');
12
13 /* 5468091 - cache sob_id for getfunccurr() function */
14 g_sob_id NUMBER := -2; -- init to known value
15 g_func_currency VARCHAR2(15);
16
17 CURSOR CurrencyCursor( cp_currency_code VARCHAR2 ) IS
18 SELECT precision,
19 minimum_accountable_unit
20 FROM fnd_currencies
21 WHERE currency_code = cp_currency_code;
22 --
23 PROCEDURE GetCurrencyDetails( p_currency_code IN VARCHAR2,
24 p_precision OUT NOCOPY NUMBER,
25 p_mau OUT NOCOPY NUMBER ) IS
26 i BINARY_INTEGER := 0;
27 BEGIN
28 WHILE i < NextElement
29 LOOP
30 EXIT WHEN CurrencyCode(i) = p_currency_code;
31 i := i + 1;
32 END LOOP;
33 --
34 IF i = NextElement
35 THEN
36 OPEN CurrencyCursor( p_currency_code );
37 DECLARE
38 l_Precision NUMBER;
39 l_Mau NUMBER;
40 BEGIN
41 FETCH CurrencyCursor
42 INTO l_Precision,
43 l_Mau;
44 IF CurrencyCursor%NOTFOUND THEN
45 RAISE NO_DATA_FOUND;
46 END IF;
47 Precision(i) := l_Precision;
48 Mau(i) := l_Mau;
49 END;
50 CLOSE CurrencyCursor;
51 CurrencyCode(i) := p_currency_code;
52 NextElement := i + 1;
53 END IF;
54 p_precision := Precision(i);
55 p_mau := Mau(i);
56 EXCEPTION
57 WHEN OTHERS THEN
58 -- bug 2191876
59 IF CurrencyCursor%ISOPEN THEN
60 CLOSE CurrencyCursor;
61 END IF;
62 RAISE;
63 END;
64 --
65 FUNCTION CurrRound( p_amount IN NUMBER, p_currency_code IN VARCHAR2 := FunctionalCurrency) RETURN NUMBER IS
66 l_precision NUMBER(1);
67 l_mau NUMBER;
68 BEGIN
69 GetCurrencyDetails( p_currency_code, l_precision, l_mau );
70 IF l_mau IS NOT NULL
71 THEN
72 RETURN( ROUND( p_amount / l_mau) * l_mau );
73 ELSE
74 RETURN( ROUND( p_amount, l_precision ));
75 END IF;
76 EXCEPTION
77 WHEN OTHERS THEN
78 RAISE;
79 END;
80 --
81 /*
82 -- This procedure is designed to help in calculating acctd equivalents,
83 -- and reconciling the sum of accounted equivalents
84 --
85 -- The basis of this routine is that the accounted equivalent of a 'child'
86 -- amount is the difference in the accounted amount produced by the
87 -- child on the 'parent' record
88 --
89 -- Additionally, at any point when the sum of the 'child' amounts is
90 -- equal to a reconcile amount on the parent, the sum of 'child'
91 -- accounted amounts should be equal to the accounted reconcile
92 -- amount of the 'parent'
93 --
94 -- The routine assumes that the amounts are being converted to the
95 -- Set of Books functional currency
96 --
97 -- Sample Implementation in calling context
98 --
99 -- The procedure takes an exchange rate and currency as parameters
100 -- It converts the entered values 1,2,3 to accounted, and reconciles
101 -- the accounted amount
102 --
103 -- PROCEDURE TestReconcile( p_ExchangeRate IN NUMBER ) IS
104 l_RunningTotalAmount NUMBER := 0;
105 l_RunningTotalAcctdAmount NUMBER := 0;
106 l_ReconcileAmount NUMBER;
107 l_ReconcileAcctdAmount NUMBER;
108 BEGIN
109 l_ReconcileAmount := 6; -- 1 + 2 + 3
110 l_ReconcileAcctdAmount := CurrRound( ReconcileAmount * p_ExchangeRate );
111 dbms_output.put_line( ReconcileAcctdAmount( p_ExchangeRate,
112 l_ReconcileAmount, l_ReconcileAcctdAmount,
113 1,
114 l_RunningTotalAmount, l_RunningTotalAcctdAmount ));
115 dbms_output.put_line( ReconcileAcctdAmount( p_ExchangeRate,
116 l_ReconcileAmount, l_ReconcileAcctdAmount,
117 2,
118 l_RunningTotalAmount, l_RunningTotalAcctdAmount ));
119 dbms_output.put_line( ReconcileAcctdAmount( p_ExchangeRate,
120 l_ReconcileAmount, l_ReconcileAcctdAmount,
121 3,
122 l_RunningTotalAmount, l_RunningTotalAcctdAmount ));
123 END;
124 */
125 FUNCTION ReconcileAcctdAmounts( p_ExchangeRate IN NUMBER,
126 p_ReconcileAmount IN NUMBER,
127 p_ReconcileAcctdAmount IN NUMBER,
128 p_ChildAmount IN NUMBER,
129 p_RunningTotalAmount IN OUT NOCOPY NUMBER,
130 p_RunningTotalAcctdAmount IN OUT NOCOPY NUMBER ) RETURN NUMBER IS
131 l_AcctdChildAmount NUMBER;
132 BEGIN
133 p_RunningTotalAmount := p_RunningTotalAmount + p_ChildAmount;
134 IF p_RunningTotalAmount = p_ReconcileAmount
135 THEN
136 l_AcctdChildAmount := p_ReconcileAcctdAmount - p_RunningTotalAcctdAmount;
137 ELSE
138 l_AcctdChildAmount := CurrRound( p_RunningTotalAmount * p_ExchangeRate ) - p_RunningTotalAcctdAmount;
139 END IF;
140 --
141 p_RunningTotalAcctdAmount := p_RunningTotalAcctdAmount + l_AcctdChildAmount;
142 RETURN l_AcctdChildAmount;
143 EXCEPTION
144 WHEN OTHERS THEN
145 arp_standard.debug( 'arpcurr.ReconcileAcctdAmounts: $Revision: 120.9.12010000.2 $' );
146 arp_standard.debug( 'l_AcctdChildAmount:'||l_AcctdChildAmount );
147 RAISE;
148 END;
149
150
151 /*----------------------------------------------------------------------------*
152 | PUBLIC FUNCTION |
153 | functional_amount |
154 | |
155 | DESCRIPTION |
156 | This function returns the functional amount for a given foreign amount. |
157 | THe functional amount is rounded to the correct precision. |
158 | |
159 | REQUIRES |
160 | Amount - the foreign amount |
161 | Exchange Rate - to use when converting to functional amount |
162 | one of: |
163 | Currency Code - of the functional amount |
164 | Precision - of the functional amount |
165 | minimum accountable unit - of the functional amount |
166 | |
167 | RETURNS |
168 | amount * exchange_rate to correct rounding for currency |
169 | |
170 | EXCEPTIONS RAISED |
171 | Oracle Error If Currency Code, Precision and minimum accountable |
172 | are all NULL |
173 | |
174 | Oracle Error If can not find information for Currency Code |
175 | supplied |
176 | |
177 | KNOWN BUGS |
178 | <none> |
179 | |
180 | NOTES |
181 | |
182 | HISTORY |
183 | 2/10/93 Martin Morris Created |
184 | 7/21/95 Martin Johnson Replaced fnd_message with |
185 | user-defined exception so that|
186 | pragma restrict_references |
187 | does not fail |
188 | |
189 *----------------------------------------------------------------------------*/
190
191
192 FUNCTION functional_amount(amount IN NUMBER,
193 currency_code IN VARCHAR2,
194 exchange_rate IN NUMBER,
195 precision IN NUMBER,
196 min_acc_unit IN NUMBER) RETURN NUMBER IS
197
198 /*----------------------------------------------------------------------------*
199 | PRIVATE CURSOR |
200 | curr_info |
201 | |
202 | DESCRIPTION |
203 | Gets the precision and the minimum accountable unit for the currency |
204 | Supplied |
205 | |
206 *----------------------------------------------------------------------------*/
207
208 CURSOR curr_info (cc FND_CURRENCIES.CURRENCY_CODE%TYPE) IS
209 SELECT PRECISION,
210 MINIMUM_ACCOUNTABLE_UNIT,
211 CURRENCY_CODE
212 FROM FND_CURRENCIES
213 WHERE CURRENCY_CODE = cc;
214
215 /*---------------------------------------------------------------------------*
216 | PRIVATE DATATYPES |
217 | |
218 *---------------------------------------------------------------------------*/
219
220
221 curr curr_info%ROWTYPE;
222 loc_amount NUMBER;
223 invalid_params EXCEPTION;
224
225 BEGIN
226
227 /*--------------------------------------------------------------------*
228 | Validate Parameters |
229 *--------------------------------------------------------------------*/
230
231 IF (((currency_code IS NULL) AND
232 (precision IS NULL) AND
233 (min_acc_unit IS NULL)) OR
234 (amount IS NULL) ) THEN
235 BEGIN
236
237 /* fnd_message('STD-FUNCT-AMT-INV-PAR'); */
238
239 RAISE invalid_params;
240
241 END;
242 END IF;
243
244 /*--------------------------------------------------------------------*
245 | Only get currency info from database if not supplied as parameters |
246 *--------------------------------------------------------------------*/
247
248
249 IF ((precision IS NULL) AND (min_acc_unit IS NULL)) THEN
250 BEGIN
251 OPEN curr_info(currency_code);
252 FETCH curr_info INTO curr;
253 CLOSE curr_info;
254
255 IF (curr.currency_code IS NULL) THEN
256
257 /* fnd_message('STD-FUNCT-AMT-CURR-NF',
258 'CURR',
259 currency_code); */
260
261 RAISE invalid_params;
262
263 END IF;
264
265 END;
266 ELSE
267 BEGIN
268 curr.precision := precision;
269 curr.minimum_accountable_unit := min_acc_unit;
270 END;
271 END IF;
272
273 loc_amount := amount * NVL(exchange_rate, 1);
274
275 /*-----------------*
276 | Round correctly |
277 *-----------------*/
278
279 IF (curr.minimum_accountable_unit IS NULL) THEN
280 RETURN( ROUND(loc_amount, curr.precision));
281 ELSE
282 RETURN( ROUND((loc_amount / curr.minimum_accountable_unit)) *
283 curr.minimum_accountable_unit);
284 END IF;
285
286 EXCEPTION
287 WHEN OTHERS THEN
288 -- Bug 2191876
289 IF curr_info%ISOPEN THEN
290 CLOSE curr_info;
291 END IF;
292
293 RAISE;
294
295 END functional_amount;
296
297 Function GetFunctCurr(p_set_of_books_id IN Number) Return Varchar2 IS
298
299 BEGIN
300
301 IF g_sob_id <> p_set_of_books_id
302 THEN
303
304 SELECT currency_code
305 INTO g_func_currency
306 FROM gl_sets_of_books
307 WHERE set_of_books_id = p_set_of_books_id;
308
309 END IF;
310
311 RETURN(g_func_currency);
312
313 EXCEPTION
314 WHEN OTHERS THEN
315 RAISE;
316 END GetFunctCurr;
317
318 Function GetConvType(p_conv_type IN varchar2) RETURN VARCHAR2 IS
319
320 l_user_conversion_type varchar2(30);
321
322 BEGIN
323
324 SELECT user_conversion_type
325 INTO l_user_conversion_type
326 FROM gl_daily_conversion_types
327 WHERE conversion_type = p_conv_type;
328
329 return(l_user_conversion_type);
330
331 EXCEPTION
332 WHEN OTHERS THEN
333 RAISE;
334 END GetConvType;
335
336
337 Function GetRate(p_from_curr_code IN varchar2,p_to_curr_code IN varchar2,p_conversion_date DATE,p_conversion_type IN varchar2) RETURN NUMBER IS
338 l_user_conversion_rate Number;
339 l_hash_value NUMBER;
340 p_concat_segments varchar2(100);
341 tab_indx BINARY_INTEGER := 0;
342 found BOOLEAN ;
343
344 BEGIN
345 /*----------------------------------------------------------------+
346 | Search the cache for the concantenated segments. |
347 | Return the conversion_rate if it is in the cache. |
348 | |
349 | If not found in cache, search the linear table (where |
350 | conversion_rate's |
351 | will go if collision on the hash table occurs). |
352 | |
353 | If not found above then get it from gl_currency_api.get_rate |
354 +----------------------------------------------------------------*/
355
356 IF PG_DEBUG in ('Y', 'C') THEN
357 arp_standard.debug('arpcurr.getrate');
358 arp_standard.debug('GetRate: ' || 'p_from_curr_code' || p_from_curr_code);
359 arp_standard.debug('GetRate: ' || 'p_to_curr_code' || p_to_curr_code);
360 arp_standard.debug('GetRate: ' || 'date ' || to_char(p_conversion_date));
361 arp_standard.debug('GetRate: ' || 'type = ' || p_conversion_type);
362 END IF;
363
364 IF (p_from_curr_code IS NOT NULL AND
365 p_to_curr_code IS NOT NULL AND
366 p_conversion_date IS NOT NULL ) THEN
367 -- Bug 2656787: EUR/EUR derived will always have null conversion type
368 -- p_conversion_type IS NOT NULL THEN
369
370 p_concat_segments := p_from_curr_code||'@*?'||p_to_curr_code||'@*?'||p_conversion_date||'@*?'||p_conversion_type;
371
372 l_hash_value := DBMS_UTILITY.get_hash_value(p_concat_segments,
373 1000,
374 25000);
375 found := FALSE;
376 IF pg_getrate_hash_seg_cache.exists(l_hash_value) THEN
377 IF pg_getrate_hash_seg_cache(l_hash_value) = p_concat_segments THEN
378 l_user_conversion_rate := pg_getrate_hash_id_cache(l_hash_value);
379 found := TRUE;
380
381 ELSE --- collision has occurred
382 tab_indx := 1; -- start at top of linear table and search for match
383
384 WHILE ((tab_indx < 25000) AND (not FOUND)) LOOP
385 IF pg_getrate_line_seg_cache(tab_indx) = p_concat_segments THEN
386 l_user_conversion_rate := pg_getrate_line_id_cache(tab_indx);
387 found := TRUE;
388 ELSE
389 tab_indx := tab_indx + 1;
390 END IF;
391 END LOOP;
392 END IF;
393 END IF;
394 IF found THEN
395 RETURN(l_user_conversion_rate);
396 ELSE
397 l_user_conversion_rate := gl_currency_api.get_rate(p_from_curr_code,p_to_curr_code,p_conversion_date,p_conversion_type);
398
399 IF PG_DEBUG in ('Y', 'C') THEN
400 arp_standard.debug('GetRate: ' || 'after call to gl_currency_api');
401 END IF;
402 IF pg_getrate_hash_seg_cache.exists(l_hash_value) then
403 tab_size := tab_size + 1;
404 pg_getrate_line_id_cache(tab_size) := l_user_conversion_rate;
405 pg_getrate_line_seg_cache(tab_size) := p_concat_segments;
406 ELSE
407 pg_getrate_hash_id_cache(l_hash_value) := l_user_conversion_rate;
408 pg_getrate_hash_seg_cache(l_hash_value) := p_concat_segments;
409 pg_getrate_line_id_cache(tab_size) := l_user_conversion_rate;
410 pg_getrate_line_seg_cache(tab_size) := p_concat_segments;
411 END IF;
412 RETURN(l_user_conversion_rate);
413 END IF;
414 END IF;
415
416 EXCEPTION
417 WHEN GL_CURRENCY_API.NO_RATE THEN
418 return -1;
419 WHEN OTHERS THEN
420 RAISE ;
421 END GetRate;
422
423 Function RateExists(p_set_of_books_id IN NUMBER,p_from_curr_code IN varchar2,p_conversion_date DATE,p_conversion_type IN varchar2) RETURN VARCHAR2 IS
424 --
425 l_user_conversion_rate Number;
426
427 BEGIN
428
429 IF (p_from_curr_code IS NULL)
430 THEN
431 return 'X';
432 END IF;
433 l_user_conversion_rate := gl_currency_api.get_rate(p_set_of_books_id,p_from_curr_code,p_conversion_date,p_conversion_type);
434
435 IF (l_user_conversion_rate IS NOT NULL)
436 THEN
437 return 'Y';
438 END IF;
439 EXCEPTION
440 WHEN GL_CURRENCY_API.NO_RATE THEN
441 arp_standard.debug('EXCEPTION: No Rate Exception In arpcurr.rateexists()');
442 return 'N';
443 WHEN OTHERS THEN
444 arp_standard.debug('EXCEPTION: Others Exception In arpcurr.rateexists()');
445 arp_standard.debug('EXCEPTION: '||SQLERRM);
446 RETURN 'N' ;
447 END RateExists;
448
449
450 Function IsFixedRate(p_rec_curr_code IN varchar2,
451 p_funct_curr_code IN varchar2,
452 p_rec_conversion_date DATE,
453 p_trx_curr_code IN varchar2 Default NULL,
454 p_trx_conversion_date DATE Default NULL) RETURN VARCHAR2 IS
455 --
456
457 l_rec_relation varchar2(1);
458 l_trx_relation varchar2(1);
459 l_relation varchar2(1);
460
461
462 BEGIN
463
464
465 IF (p_trx_curr_code IS NOT NULL) THEN
466
467 l_rec_relation := gl_currency_api.is_fixed_rate(p_rec_curr_code,p_funct_curr_code,p_rec_conversion_date);
468 l_trx_relation := gl_currency_api.is_fixed_rate(p_trx_curr_code,p_funct_curr_code,p_trx_conversion_date);
469
470 IF (l_rec_relation = 'Y' and l_trx_relation = 'Y') Then
471 l_relation := 'Y';
472 Else
473 l_relation := 'N';
474 End if;
475
476 ELSE
477
478 l_relation := gl_currency_api.is_fixed_rate(p_rec_curr_code,p_funct_curr_code,p_rec_conversion_date);
479
480 END IF;
481
482 return(l_relation);
483
484 EXCEPTION
485 WHEN GL_CURRENCY_API.INVALID_CURRENCY THEN
486 RETURN 'N';
487 WHEN OTHERS THEN
488 RAISE;
489
490 END IsFixedRate;
491 --
492 --
493 /* Bug 3810649 */
494 PROCEDURE flush_cached_rates IS
495 BEGIN
496 pg_getrate_hash_seg_cache := pg_init_seg_cache;
497 END flush_cached_rates;
498
499 --
500 -- constructor section
501 --
502 PROCEDURE init IS
503 BEGIN
504 SELECT sob.currency_code
505 INTO FunctionalCurrency
506 FROM ar_system_parameters sp,
507 gl_sets_of_books sob
508 WHERE sob.set_of_books_id = sp.set_of_books_id;
509 EXCEPTION
510 WHEN OTHERS THEN
511 arp_standard.debug( 'arpcurr.constructor' );
512 RAISE;
513 END init;
514
515 /* 5885313 - call init in constructor code */
516 BEGIN
517 init;
518 --
519 END arpcurr;