DBA Data[Home] [Help]

PACKAGE BODY: APPS.FND_CURRENCY

Source


1 PACKAGE BODY FND_CURRENCY AS
2 /* $Header: AFMLCURB.pls 120.2 2006/09/18 15:06:49 pdeluna ship $ */
3 
4 
5 /* GET_FORMAT_MASK- get the format mask for a particular currency.
6 **
7 ** Creates a currency format mask to be used with the forms
8 ** SET_ITEM_PROPERTY(item_name, FORMAT_MASK, new_format_mask)
9 ** built-in call, or the PLSQL to_char() routine,
10 ** based on the currency code passed in.
11 **
12 */
13 function GET_FORMAT_MASK(
14    currency_code   IN VARCHAR2,
15    field_length    IN NUMBER
16 )
17    return VARCHAR2
18 is
19    return_mask    VARCHAR2(100);
20    precision      NUMBER; /* number of digits to right of decimal*/
21    ext_precision  NUMBER; /* precision where more precision is needed*/
22    min_acct_unit  NUMBER; /* minimum value by which amt can vary */
23 
24 begin
25 
26    return_mask := NULL;   /* initialize return_mask */
27 
28    /* Check whether field_length exceeds maximum length of return_mask
29       or if currency_code is NULL. */
30    if(field_length > 100) OR (currency_code is NULL) then
31       return return_mask;
32    end if;
33 
34    /* Get the precision information for a currency code */
35    GET_INFO(currency_code, precision, ext_precision, min_acct_unit);
36 
37    /* Create the format mask for the given currency value */
38    BUILD_FORMAT_MASK(return_mask, field_length, precision, min_acct_unit);
39 
40    return return_mask;
41 
42 end;
43 
44 /* SAFE_GET_FORMAT_MASK -  slower version of GET_FORMAT_MASK
45 **                         without WNPS pragma restrictions.
46 **
47 ** This version of GET_FORMAT_MASK uses slower,
48 ** non-caching profiles functions to do its defaulting.  It runs
49 ** about half the speed of GET_FORMAT_MASK, but it can
50 ** be used in situations, like where clauses, in views, that
51 ** GET_FORMAT_MASK cannot be used due to pragma restrictions.
52 */
53 function SAFE_GET_FORMAT_MASK(
54    currency_code   IN VARCHAR2,
55    field_length    IN NUMBER
56 )
57    return VARCHAR2
58 is
59    return_mask    VARCHAR2(100);
60    precision      NUMBER; /* number of digits to right of decimal*/
61    ext_precision  NUMBER; /* precision where more precision is needed*/
62    min_acct_unit  NUMBER; /* minimum value by which amt can vary */
63 
64 begin
65 
66    return_mask := NULL;   /* initialize return_mask */
67 
68    /* Check whether field_length exceeds maximum length of return_mask
69       or if currency_code is NULL. */
70    if (field_length > 100) OR (currency_code is NULL) then
71       return return_mask;
72    end if;
73 
74    /* Get the precision information for a currency code */
75    GET_INFO(currency_code, precision, ext_precision, min_acct_unit);
76 
77    /* Create the format mask for the given currency value */
78    SAFE_BUILD_FORMAT_MASK(return_mask, field_length, precision, min_acct_unit);
79 
80    return return_mask;
81 
82 end;
83 
84 /*
85 ** GET_INFO- get the precision information for a currency code
86 **
87 ** returns information about a currency code, based on the
88 ** cache of currency information from the FND db table.
89 **
90 */
91 procedure GET_INFO(
92    currency_code  IN  VARCHAR2,       /* currency code */
93    precision      OUT NOCOPY NUMBER,  /* number digits to right of decimal */
94    ext_precision  OUT NOCOPY NUMBER,  /* precision if more precision needed */
95    min_acct_unit  OUT NOCOPY NUMBER   /* min value by which amt can vary */
96 )
97 is
98    x_currency_code VARCHAR2(15);
99    z_precision NUMBER;
100    z_ext_precision NUMBER;
101 
102 begin
103 
104    x_currency_code := currency_code;
105 
106    select PRECISION, EXTENDED_PRECISION, MINIMUM_ACCOUNTABLE_UNIT
107    into z_precision, z_ext_precision, min_acct_unit
108    from FND_CURRENCIES c
109    where x_currency_code = c.CURRENCY_CODE;
110 
111    /* Precision should never be NULL; this is just so it works w/ bad data */
112    if (z_precision is NULL) then /* Default precision to two if necessary. */
113       precision := 2;
114    else
115       precision := z_precision;
116    end if;
117 
118    /* Ext Precision should never be NULL; this is so it works w/ bad data*/
119    if (z_ext_precision is NULL) then /* Default ext_precision if necc. */
120       ext_precision := 5;
121    else
122       ext_precision := z_ext_precision;
123    end if;
124 
125    exception
126       when NO_DATA_FOUND then
127          precision := 0;
128          ext_precision := 0;
129          min_acct_unit := 0;
130 
131 end;
132 
133 
134 /* BUILD_FORMAT_MASK- create a format mask for a currency value
135 **
136 ** Creates a currency format mask to be used with forms
137 ** SET_ITEM_PROPERTY(item_name, FORMAT_MASK, new_format_mask)
138 ** built-in call, or the PLSQL to_char() routine,
139 ** based on the currency parameters passed in.
140 **
141 ** Note that if neg_format is '-XXX', then pos_format must
142 ** be '+XXX' or 'XXX'.
143 **
144 ** If the last three parameters are left off, their values will
145 ** default from the profile value system.
146 */
147 procedure BUILD_FORMAT_MASK(
148    format_mask    OUT NOCOPY VARCHAR2,
149    field_length   IN  NUMBER,  /* maximum number of char in dest field */
150    precision      IN  NUMBER,  /* number of digits to right of decimal*/
151    min_acct_unit  IN  NUMBER,  /* minimum value by which amt can vary */
152    disp_grp_sep   IN  BOOLEAN default NULL,
153       /* NULL=from profile CURRENCY:THOUSANDS_SEPARATOR */
154    neg_format     IN  VARCHAR2 default NULL,
155       /* '-XXX', 'XXX-', '<XXX>', '(XXX)' */
156       /* NULL=from profile CURRENCY:NEGATVE_FORMAT */
157    pos_format     IN  VARCHAR2 default NULL
158       /* 'XXX', '+XXX', 'XXX-', */
159       /* NULL=from profile CURRENCY:POSITIVE_FORMAT*/
160 )
161 is
162 
163    mask            VARCHAR2(100);
164    whole_width     NUMBER;  /* number of characters to left of decimal */
165    decimal_width   NUMBER;  /* width of decimal and numbers rt of dec */
166    sign_width      NUMBER;  /* width of pos/neg sign */
167    profl_val       VARCHAR2(80);
168    x_disp_grp_sep  BOOLEAN;
169    x_pos_format    VARCHAR2(30);
170    x_neg_format    VARCHAR2(30);
171 
172 begin
173 
174    /* process the arguments, defaulting in profile values if necessary*/
175 
176    if(disp_grp_sep is NULL) then
177       profl_val:= fnd_profile.value('CURRENCY:THOUSANDS_SEPARATOR');
178       if (profl_val = 'Y') then
179          x_disp_grp_sep := TRUE;
180       else
181          x_disp_grp_sep := FALSE;
182       end if;
183    else
184       x_disp_grp_sep := disp_grp_sep;
185    end if;
186 
187    /* Bug 5529158: FND_CURRENCY.BUILD_FORMAT_MASK MISMATCH IN FNDSQF.PLD AND
188     * AFMLCURB.PLS
189     *
190     * FNDSQF.pld 115.1 was changed to enable support of the (XXX) Core number
191     * formatting ability in Core 3 (4.5.7). This change was not made in the
192     * PL/SQL package AFMLCURB.pls.
193     */
194 
195    -- if(neg_format is NULL) then
196    --    profl_val := fnd_profile.value('CURRENCY:NEGATIVE_FORMAT');
197    --    if(profl_val = '0' or profl_val = '1' or profl_val = '2') then
198    --       x_neg_format := '<XXX>';
199    --    elsif (profl_val = '4') then  /* '4' gives trailing sign */
200    --       x_neg_format := 'XXX-';
201    --
202    --    /* Found out that the default value being set is 'XXX-', not '-XXX',
203    --     * which is documented to be the default value.
204    --     */
205    --
206    --    else                          /* '3' or default gives leading sign */
207    --       x_neg_format := '-XXX';
208    --    end if;
209    -- else
210    --    x_neg_format := neg_format;
211    -- end if;
212 
213    if(neg_format is NULL) then
214       profl_val := fnd_profile.value('CURRENCY:NEGATIVE_FORMAT');
215       if(   profl_val = '0'         /* (XXX) */
216          or profl_val = '1') then   /* [XXX] */
217          x_neg_format := '(XXX)';   -- Bug 5529158
218       elsif( profl_val = '2') then  /* <XXX> */
219          x_neg_format := '<XXX>';
220       elsif( profl_val = '4') then  /* '4' gives trailing sign*/
221          x_neg_format := 'XXX-';
222 
223       /* Found out that the default value being set is 'XXX-', not '-XXX',
224        * which is documented to be the default value.
225        */
226 
227       else                          /* '3' or default gives leading sign */
228          x_neg_format := '-XXX';
229       end if;
230    else
231       x_neg_format := neg_format;
232    end if;
233 
234 
235    if(pos_format is NULL) then
236       profl_val := fnd_profile.value('CURRENCY:POSITIVE_FORMAT');
237       if(profl_val = '1') then
238          x_pos_format := '+XXX';
239       elsif (profl_val = '2') then
240          x_pos_format := 'XXX+';
241       else                          /* '0' or default gives no pos. */
242          x_pos_format := 'XXX';
243       end if;
244    else
245       x_pos_format := pos_format;
246    end if;
247 
248    /* Build the format mask */
249    SAFE_BUILD_FORMAT_MASK(mask, field_length, precision, min_acct_unit,
250                            x_disp_grp_sep, x_neg_format, x_pos_format);
251 
252    format_mask := mask;
253 
254 end;
255 
256 
257 /* SAFE_BUILD_FORMAT_MASK- slower version of BUILD_FORMAT_MASK
258 **                         without WNPS pragma restrictions.
259 **
260 ** This version of BUILD_FORMAT_MASK uses slower,
261 ** non-caching profiles functions to do its defaulting.  It runs
262 ** about half the speed of BUILD_FORMAT_MASK, but it can
263 ** be used in situations, like views, that BUILD_FORMAT_MASK
264 ** cannot be used due to pragma restrictions.
265 ** Note, however, that if you pass values for the
266 ** disp_grp_sep, neg_format, and pos_format parameters instead
267 ** of letting them default to NULL, then this routine will
268 ** not be any slower than BUILD_FORMAT_MASK
269 */
270 procedure SAFE_BUILD_FORMAT_MASK(
271    format_mask    OUT NOCOPY VARCHAR2,
272    field_length   IN  NUMBER,  /* maximum number of char in dest field */
273    precision      IN  NUMBER,  /* number of digits to right of decimal*/
274    min_acct_unit  IN  NUMBER,  /* minimum value by which amt can vary */
275    disp_grp_sep   IN  BOOLEAN default NULL,
276       /* NULL=from profile CURRENCY:THOUSANDS_SEPARATOR */
277    neg_format     IN  VARCHAR2 default NULL,
278       /* '-XXX', 'XXX-', '<XXX>', '(XXX)' */
279       /* NULL=from profile CURRENCY:NEGATVE_FORMAT */
280    pos_format     IN  VARCHAR2 default NULL
281       /* 'XXX', '+XXX', 'XXX-', */
282       /* NULL=from profile CURRENCY:POSITIVE_FORMAT*/
283 )
284 is
285 
286    mask            VARCHAR2(100);
287    whole_width     NUMBER;  /* number of characters to left of decimal */
288    decimal_width   NUMBER;  /* width of decimal and numbers rt of dec */
289    sign_width      NUMBER;  /* width of pos/neg sign */
290    profl_val       VARCHAR2(80);
291    x_disp_grp_sep  BOOLEAN;
292    x_pos_format    VARCHAR2(30);
293    x_neg_format    VARCHAR2(30);
294 
295 begin
296 
297    /* process the arguments, defaulting in profile values if necessary*/
298 
299    if(disp_grp_sep is NULL) then
300    profl_val:= fnd_profile.value_specific('CURRENCY:THOUSANDS_SEPARATOR');
301       if (profl_val = 'Y') then
302          x_disp_grp_sep := TRUE;
303       else
304          x_disp_grp_sep := FALSE;
305       end if;
306    else
307       x_disp_grp_sep := disp_grp_sep;
308    end if;
309 
310    /* Bug 5529158: FND_CURRENCY.BUILD_FORMAT_MASK MISMATCH IN FNDSQF.PLD AND
311     * AFMLCURB.PLS
312     *
313     * FNDSQF.pld 115.1 was changed to enable support of the (XXX) Core number
314     * formatting ability in Core 3 (4.5.7). This change was not made in the
315     * PL/SQL package AFMLCURB.pls. Hence, there is an inconsistency between
316     * FNDSQF.pld and AFMLCURB.pls with regards to handling (XXX).
317     */
318 
319    -- if(neg_format is NULL) then
320    --   profl_val := fnd_profile.value_specific('CURRENCY:NEGATIVE_FORMAT');
321    --   if(profl_val = '0' or profl_val = '1' or profl_val = '2') then
322    --      x_neg_format := '<XXX>';
323    --   elsif (profl_val = '4') then  /* '4' gives trailing sign */
324    --      x_neg_format := 'XXX-';
325    --
326    --   /* Found out that the default value being set is 'XXX-', not '-XXX',
327    --    * which is documented to be the default value.
328    --    */
329    --
330    --   else                          /* '3' or default gives leading sign */
331    --      x_neg_format := '-XXX';
332    --   end if;
333    -- else
334    --   x_neg_format := neg_format;
335    -- end if;
336 
337    if(neg_format is NULL) then
338       profl_val := fnd_profile.value_specific('CURRENCY:NEGATIVE_FORMAT');
339       if(   profl_val = '0'         /* (XXX) */
340          or profl_val = '1') then   /* [XXX] */
341          x_neg_format := '(XXX)';   -- Bug 5529158
342       elsif( profl_val = '2') then  /* <XXX> */
343          x_neg_format := '<XXX>';
347       /* Found out that the default value being set is 'XXX-', not '-XXX',
344       elsif( profl_val = '4') then  /* '4' gives trailing sign*/
345          x_neg_format := 'XXX-';
346 
348        * which is documented to be the default value.
349        */
350 
351       else                          /* '3' or default gives leading sign */
352          x_neg_format := '-XXX';
353       end if;
354    else
355       x_neg_format := neg_format;
356    end if;
357 
358    if(pos_format is NULL) then
359       profl_val := fnd_profile.value_specific('CURRENCY:POSITIVE_FORMAT');
360       if(profl_val = '1') then
361          x_pos_format := '+XXX';
362       elsif (profl_val = '2') then
363          x_pos_format := 'XXX+';
364       else                          /* '0' or default gives no pos. */
365          x_pos_format := 'XXX';
366       end if;
367    else
368       x_pos_format := pos_format;
369    end if;
370 
371    /* NULL precision can mean that GET_INFO failed to find info for currency*/
372    if (precision is NULL) then
373       format_mask := '';
374       return;
375    end if;
376 
377    if (precision > 0) then /* If there is a decimal portion */
378       decimal_width := 1 + precision;
379    else
380       decimal_width := 0;
381    end if;
382 
383    /* Bug 2993411: FND_CURRENCY.GET_FORMAT_MASK:PL/SQL:NUMERIC OR VALUE
384     * ERROR:STRING BUFFER
385     *
386     * When the profile option 'Currency: Negative Format' is set to 'XXX-', the    * string 'MI'is appended to the end of the format mask.  This addition
387     * causes the string to be longer than the field_length.  So, along with
388     * '<XXX>', 'XXX-' is also adjusted for proper sign_width to ensure that the    * resulting format mask does not exceed the desired field_length.
389     */
390    if (x_neg_format = '<XXX>'
391        or x_neg_format = '(XXX)' -- Bug 5529158
392        or x_neg_format = 'XXX-') then
393       sign_width := 2;
394    else
395       sign_width := 1;
396    end if;
397 
398    /* Determine the length of the portion to the left of decimal.
399     * This value has been adjusted by subtracting 1 to account for
400     * the addition of the string 'FM' which prevents leading spaces.
401     * Without the adjustment, the resulting format mask can be larger
402     * than the allotted maximum length for format_mask.  This would
403     * result in ORA-6502 PL/SQL: numeric or value error: character string
404     * buffer too small.  See bug 1580374.
405     */
406    whole_width := field_length - decimal_width - sign_width - 1;
407 
408    if (whole_width < 0) then
409       format_mask := '';
410       return;
411    end if;
412 
413    /* build up the portion to the left of decimal, e.g. 99G999G990 */
414 
415    mask := '0' || mask;  /* Start the format with 0 */
416 
417    if (whole_width > 1) then
418 
419       for i in 2..whole_width loop
420 
421          /* If there is a thousands separator, need to mark it. */
422          if (x_disp_grp_sep) AND (mod(i, 4) = 0) then
423             if (i < whole_width - 1) then     /* don't start with */
424                mask := 'G' || mask;           /* group separator */
425             end if;
426          /* Else, add 9 to the format as long as we have not reached
427           * the maximum length of whole numbers.  This was added due
428           * to bug 1580374 to ensure that ORA-6502 is not obtained.
429           */
430          elsif (i <> whole_width) then
431             mask := '9' || mask;
432          end if;
433 
434       end loop;
435 
436    end if;
437 
438    /* build up the portion to the right of the decimal e.g. .0000 */
439    if (precision > 0) then
440       mask := mask || 'D';
441       for i in 1..precision loop
442          mask := mask || '0';
443       end loop;
444    end if;
445 
446    /* Add the FM mask element to keep from getting leading spaces */
447    mask := 'FM' || mask;
448 
449 
450    /* Add the appropriate sign */
451 
452    /*
453    Per bug 2708367, according to SQL Reference Manual. Chapter 2:"Basic
454    Elements of Oracle SQL", in the table of "Number Format Elements", MI means
455    "returns negative value with a trailing minus sign (-) and returns positive
456    value with a trailing blank".  Therefore, the returned format mask is
457    incorrect if the profile options CURRENCY:NEGATIVE_FORMAT is set to XXX- and
458    CURRENCY:POSITIVE_FORMAT is set to XXX+.
459    */
460 
461    if (x_neg_format = 'XXX-' and x_pos_format = 'XXX+' ) then
462       mask := mask || 'S';
463    elsif (x_neg_format = 'XXX-' and x_pos_format <> 'XXX+') then
464       mask := mask || 'MI';
465    elsif (x_neg_format = '<XXX>') then
466       mask := mask || 'PR';
467    elsif (x_neg_format = '(XXX)') then -- Bug 5529158: This is being made
468       mask := mask || 'PT';            -- consistent with FNDSQF.pld
469    elsif (x_pos_format = '+XXX') then
470       mask := 'S' || mask;
471    end if;
472 
473    format_mask := mask;
474 
475 end;
476 
477 END FND_CURRENCY;
478