DBA Data[Home] [Help]

PACKAGE BODY: APPS.AMS_LISTDEDUPE_PVT

Source


1 PACKAGE BODY AMS_ListDedupe_PVT as
2 /* $Header: amsvlddb.pls 120.2 2005/12/28 09:32:21 bmuthukr noship $ */
3 -- Start of Comments
4 --
5 -- NAME
6 --   AMS_ListDedupe_PVT
7 --
8 -- PURPOSE
9 --   This package is a Private API for managing List Deduplication information in
10 --   AMS.  It contains specification for pl/sql records and tables
11 --
12 -- Functions:
13 --  Exec_Sql_Stmt (see below for specification)
14 --  Filter_Word (see below for specification)
15 --  Dedupe_List (see below for specification)
16 --
17 --   Procedures:
18 --	Write_To_Act_Log (see below for specification)
19 --
20 --
21 -- NOTES
22 --
23 --
24 -- HISTORY
25 --
26 --  06/29/1999  khung       created
27 --  07/07/1999  khung       modify -> only one rule per list
28 --  07/22/1999  khung       changed package name and file name
29 --  08/02/1999  khung       add write_to_act_log funtion (8i)
30 --  09/30/1999  khung       add the capability to dedupe the entries
31 --                          from AMS_IMP_SOURCE_LINE table
32 --  11/11/1999  choang      Moved Generate_Key from AMS_PartyImport_PVT.
33 --  01/11/2000  khung       Add more debug code for LIST_RULE checking
34 --  09/27/2000  vbhandar    Made changes to dedupe_list and c_dedupe_keys cursor
35 --  10/19/2000  vbhandar    Made changes to dedupe_list to fix problem with dedup not working for more than 1 rank
36 --  11/07/2000  vbhandar    Made changes to dedupe_list to synchronize enabled flag with non deduped entries
37 --  11/15/2000  vbhandar    Made changes to filter word to do case insensitive comparison
38 --  26/06/2001  gjoby       changed the selection query  - For Hornet
39 --
40 -- End of Comments
41 
42 -- global constants
43 G_PKG_NAME           CONSTANT VARCHAR2(30) := 'AMS_ListDedupe_PVT';
44   g_original_text VARCHAR2(50) := 'z!"#$%&''()*+,-./:;<=>?@[\]^_`{|}~';
45   g_replace_text  VARCHAR2(50) := 'z';
46 
47   TYPE original_key     IS TABLE OF VARCHAR2(50) INDEX BY BINARY_INTEGER;
48   TYPE replacement_key  IS TABLE OF VARCHAR2(50) INDEX BY BINARY_INTEGER;
49 
50   g_original_key        original_key;
51   g_replacement_key     replacement_key;
52   g_special_enabled     VARCHAR2(1);
53 
54 
55 /*****************************************************************************************/
56 -- Start of Comments
57 --
58 --    Name        : Exec_Sql_Stmt
59 --    Type        : Private
60 --    Function    : This function takes a dunamic SQL stmt and executes it
61 --
62 --    Pre-reqs    : None
63 --    Paramaeters :
64 --  IN      :
65 --      p_stmt      VARCHAR2
66 --
67 -- End Of Comments
68 
69 AMS_DEBUG_HIGH_ON boolean := FND_MSG_PUB.CHECK_MSG_LEVEL(FND_MSG_PUB.G_MSG_LVL_DEBUG_HIGH);
70 AMS_DEBUG_LOW_ON boolean := FND_MSG_PUB.CHECK_MSG_LEVEL(FND_MSG_PUB.G_MSG_LVL_DEBUG_LOW);
71 AMS_DEBUG_MEDIUM_ON boolean := FND_MSG_PUB.CHECK_MSG_LEVEL(FND_MSG_PUB.G_MSG_LVL_DEBUG_MEDIUM);
72 
73 FUNCTION Exec_Sql_Stmt (p_stmt VARCHAR2)
74 RETURN NUMBER
75    IS
76 
77 --  l_sqlerrm           VARCHAR2(1000);
78 --  l_sqlcode           VARCHAR2(1000);
79     l_cursor_name       INTEGER;
80     l_rows_processed    INTEGER;
81 BEGIN
82 
83     --executing dynamic sql statement to populate the merge_key field in ams_list_entries.
84 
85     l_cursor_name := DBMS_SQL.open_cursor;
86     --insert into temp(text2) values('after open cursor');commit;
87 
88     DBMS_SQL.parse (l_cursor_name, p_stmt, DBMS_SQL.native);
89     --insert into temp(text2) values('after sql parse');commit;
90 
91     l_rows_processed := DBMS_SQL.execute (l_cursor_name);
92     --insert into temp(text2) values('after execute');commit;
93 
94     DBMS_SQL.close_cursor (l_cursor_name);
95     --insert into temp(text2) values('after close cursor');commit;
96 
97     RETURN  (l_rows_processed);
98 
99 END Exec_Sql_Stmt;
100 
101 /*****************************************************************************************/
102 -- Start of Comments
103 --
104 --    NAME
105 --	Write_To_Act_Log
106 
107 --    PURPOSE
108 --	Any log messages we write to the ams_act_logs table are commited even if
109 --	the whole process is ROLLED BACK because of a processing error.
110 --	(8i new feature -- AUTONOMOUS TRANSACTIONS)
111 --
112 --    Pre-reqs    : None
113 --    Paramaeters :
114 --    IN          :
115 --      p_list_header_id    NUMBER
116 --      p_msg_data          VARCHAR2
117 --
118 --    NOTES
119 --
120 --
121 --    HISTORY
122 --      08/02/1999  khung    created
123 
124 -- End Of Comments
125 
126 PROCEDURE Write_To_Act_Log
127  (p_list_header_id  IN NUMBER
128  ,p_msg_data        IN VARCHAR2,
129   p_level           in varchar2 default 'LOW'
130  ) IS
131 
132 --PRAGMA AUTONOMOUS_TRANSACTION;
133 
134 BEGIN
135 
136    if nvl(ams_listgeneration_pkg.g_log_level,'HIGH') = 'HIGH' and p_level = 'LOW' then
137       return;
138    end if;
139 
140    ams_listgeneration_pkg.write_to_act_log(p_msg_data,'LIST',p_list_header_id,p_level);
141 
142    /*
143    ams_listgeneration_pkg.g_message_table(ams_listgeneration_pkg.g_count) := p_msg_data;
144    ams_listgeneration_pkg.g_date(ams_listgeneration_pkg.g_count) := sysdate;
145    ams_listgeneration_pkg.g_count   := ams_listgeneration_pkg.g_count + 1;
146    */
147 
148    /* INSERT INTO ams_act_logs
149     ( activity_log_id
150      ,last_update_date
151      ,last_updated_by
152      ,creation_date
153      ,created_by
154      ,last_update_login
155      ,object_version_number
156      ,act_log_used_by_id
157      ,arc_act_log_used_by
158      ,log_transaction_id
159      ,log_message_text
160      ,log_message_level
161      ,log_message_type
162      ,description
163     )
164     VALUES
165     (
166      ams_act_logs_s.nextval,
167      sysdate,
168      fnd_global.user_id,
169      sysdate,
170      fnd_global.user_id,
171      fnd_global.conc_login_id,
172      1, -- object_version_number
173      p_list_header_id,
174      'LIST',
175      ams_act_logs_transaction_id_s.nextval,
176      p_msg_data,
177      null,
178      null,
179      null);
180 
181     COMMIT; */
182 
183 END Write_To_Act_Log;
184 
185 /*****************************************************************************************/
186 -- Start of Comments
187 --
188 --    Name        : Filter_Word
189 --    Type        : Private
190 --    Function    : Replaces all noise words for the relevant fields in AMS_LIST_ENTRIES
191 --
192 --    Pre-reqs    : None
193 --    Paramaeters :
194 --    IN    :
195 --      p_word          VARCHAR2
196 --      p_substr_len    AMS_LIST_RULE_FIELDS.SUBSTRING_LEN%TYPE
197 --      p_table_name    AMS_LIST_RULE_FIELDS.FIELD_TABLE_NAME%TYPE
198 --      p_column_name   AMS_LIST_RULE_FIELDS.FIELD_COLUMN_NAME%TYPE
199 --
200 -- End Of Comments
201 
202 FUNCTION Filter_Word
203  (p_word                VARCHAR2
204  ,p_substr_len          AMS_LIST_RULE_FIELDS.SUBSTRING_LENGTH%TYPE
205  ,p_field_table_name    AMS_LIST_RULE_FIELDS.FIELD_TABLE_NAME%TYPE
206  ,p_field_column_name   AMS_LIST_RULE_FIELDS.FIELD_COLUMN_NAME%TYPE
207  )
208 RETURN VARCHAR2
209 
210     IS
211     -- PL/SQL Block
212     -- this will select all noise words for the relevant field in list_entries
213     -- table and column name are the PK fields for this table.
214     CURSOR c_noise_words (my_field_table_name IN VARCHAR, my_field_column_name IN VARCHAR)
215     IS
216         SELECT w.original_word, w.replacement_word
217           FROM ams_list_word_fields w
218          WHERE w.field_table_name = my_field_table_name
219            AND w.field_column_name = my_field_column_name;
220 
221     l_original_word     ams_list_word_fields.original_word%TYPE;
222     l_replacement_word  ams_list_word_fields.replacement_word%TYPE;
223     l_word              VARCHAR2(500);
224 
225 BEGIN
226     --we always compare in uppercase.
227 
228     l_word := UPPER (p_word);
229 
230     OPEN c_noise_words (p_field_table_name, p_field_column_name);
231     LOOP
232         FETCH c_noise_words INTO l_original_word, l_replacement_word;
233         EXIT WHEN c_noise_words%notfound;
234 
235         --substituting the original with the replacement words.
236         --pv_word := replace(pv_word,pv_original_word,NVL(pv_replacement_word,'NULL'));
237         --09/28/2000 VB modified
238         --change case to UPPER before comparison because l_word was made UPPER case!!
239         IF  (l_word = UPPER(l_original_word))
240         THEN
241             l_word := NVL (UPPER(l_replacement_word), 'NULL');-- modified vb 11/15/2000
242         END IF;
243     END LOOP;
244     CLOSE c_noise_words;
245 
246     -- if the rule specifies to only take into account 1..p_substr_len characters
247     IF  (p_substr_len IS NULL)
248     THEN
249         RETURN  (l_word);
250     ELSE
251         RETURN  (SUBSTR (l_word, 1, p_substr_len));
252     END IF;
253 
254 END Filter_Word;
255 
256 /*****************************************************************************************/
257 -- Start of Comments
258 --
259 --    Name        : Dedupe_List
260 --    Type        : Private
261 --    Function    : Replaces all noise words for the relevant fields in AMS_LIST_ENTRIES
262 --
263 --    Pre-reqs    : None
264 --    Paramaeters :
265 --    IN		:
266 --      p_list_header_id                AMS_LIST_HEADERS_ALL.LIST_HEADER_ID%TYPE
267 --      p_enabled_wordreplacement_flag  AMS_LIST_HEADERS_ALL.ENABLED_WORDREPLACEMENT_FLAG%TYPE
268 --      p_send_to_log                   VARCHAR2 := 'N'
269 --      p_object_name                   VARCHAR2 := 'AMS_LIST_ENTRIES'
270 --
271 --    HISTORY
272 --      08/02/1999  khung   created
273 --      09/30/1999  khung   add the capability to dedupe the entries
274 --                          from AMS_IMP_SOURCE_LINE table
275 -- End Of Comments
276 
277 FUNCTION Dedupe_List
278  (p_list_header_id        AMS_LIST_HEADERS_ALL.LIST_HEADER_ID%TYPE
279  ,p_enable_word_replacement_flag
280                           AMS_LIST_HEADERS_ALL.ENABLE_WORD_REPLACEMENT_FLAG%TYPE
281  ,p_send_to_log           VARCHAR2 := 'N'
282  ,p_object_name           VARCHAR2 := 'AMS_LIST_ENTRIES'
283  )
284 RETURN NUMBER IS
285     -- the set of rules associated with a list.
286     CURSOR c_list_rules (my_list_header_id IN NUMBER)
287     IS SELECT list_rule_id
288        FROM ams_list_rule_usages
289        WHERE list_header_id = my_list_header_id
290        ORDER BY priority;
291 
292     -- the list of fields for the list rule which are used to generate the key.
293     CURSOR c_rule_fields
294            (my_list_rule_id IN
295             ams_list_rules_all.list_rule_id%TYPE)
296     IS
297         SELECT field_table_name,
298                field_column_name,
299                substring_length,
300                word_replacement_code
301         FROM ams_list_rule_fields
302         WHERE list_rule_id = my_list_rule_id;
303 
304     -- perform a check to see if this list has been deduped already.
305     CURSOR c_deduped_before (my_list_header_id IN NUMBER)
306     IS
307         SELECT last_deduped_by_user_id
308         FROM ams_list_headers_all
309         WHERE list_header_id = my_list_header_id;
310 
311     -- get a distinct list of merge keys for the list and a
312     -- count of the occurance of each key
313     -- we also exclude any records where the dedupe flag is already set.
314     CURSOR c_dedupe_keys (my_list_header_id IN NUMBER)
315     IS
316         SELECT DISTINCT dedupe_key, COUNT (dedupe_key)
317         FROM ams_list_entries
318          WHERE list_header_id = my_list_header_id
319   --    AND marked_as_duplicate_flag IS NULL  --commented by VB
320   -- 09/30/2000 because this is a not null column in database
321          GROUP BY dedupe_key;
322 
323 
324     l_sql_stmt1         VARCHAR2(10000);
325     l_sql_stmt2         VARCHAR2(10000);
326 
327     --l_sqlerrm         VARCHAR2(1000);
328     --l_sqlcode         VARCHAR2(1000);
329     l_fields            VARCHAR2(10000);
330     --l_temp_fields            VARCHAR2(10000);
331     l_no_of_masters     NUMBER := 0;
332     l_list_rule_id      ams_list_rules_all.list_rule_id%TYPE;
333     --l_list_entry_id   ams_list_entries.list_entry_id%TYPE;
334     l_last_dedupe_by    ams_list_headers_all.last_deduped_by_user_id%TYPE;
335     l_dedupe_key        ams_list_entries.dedupe_key%TYPE;
336     l_dedupe_key_count  NUMBER;
337     l_rank_count        NUMBER;
338     i                   BINARY_INTEGER := 1;
339 
340     TYPE rule_details
341     IS TABLE OF c_rule_fields%ROWTYPE
342     INDEX BY BINARY_INTEGER;
343 
344     list_rules          rule_details;
345     empty_list_rules    rule_details;
346     l_no_of_duplicates    number := 0;
347 BEGIN
348 
349     IF (p_object_name = 'AMS_LIST_ENTRIES') THEN
350         l_sql_stmt1 := 'update ams_list_entries set dedupe_key = ';
351     ELSIF (p_object_name = 'AMS_IMP_SOURCE_LINES') THEN
352         l_sql_stmt1 := 'update ams_imp_source_lines set dedupe_key = ';
353     ELSE
354         RETURN 0;
355     END IF;
356 
357     --performing check to see if this list has been deduped before.
358     OPEN c_deduped_before (p_list_header_id);
359     FETCH c_deduped_before INTO l_last_dedupe_by;
360     CLOSE c_deduped_before;
361 
362     IF  (p_send_to_log = 'Y') THEN
363         Write_To_Act_Log (p_list_header_id,
364                         'Executing dedupe_list procedure' ,'LOW' );
365         IF  (p_enable_word_replacement_flag = 'Y') THEN
366             Write_To_Act_Log (p_list_header_id,'Enable word replacement flag has been set','LOW' );
367         END IF;
368         COMMIT;
369 
370     END IF;
371 
372     -- we must ensure that this flag gets reset to NULL for the list to
373     -- ensure accurate results.
374     -- if a dedupe has never been perfomed then this field will contains
375     -- NULLS and there is no
376     -- need to perform this update
377     IF  (l_last_dedupe_by IS NOT NULL) THEN
378         UPDATE ams_list_entries
379            SET dedupe_key = NULL
380    --           ,marked_as_duplicate_flag = NULL
381     -- because column is not null in database
382          WHERE list_header_id = p_list_header_id;
383 
384         IF  (p_send_to_log = 'Y') THEN
385             Write_To_Act_Log (p_list_header_id,'Dedupe already done. Dedupe key reset to NULL.','LOW') ;
386         END IF;
387 
388     END IF;
389 
390     -- checking to see if there are any List Source Ranks associated
391     -- with the List.
392     SELECT COUNT (rank)
393       INTO l_rank_count
394       FROM ams_list_select_actions
395      WHERE action_used_by_id = p_list_header_id
396        and arc_action_used_by = 'LIST';
397 
398     IF  (p_send_to_log = 'Y')
399     THEN
400         Write_To_Act_Log (p_list_header_id,'No of Ranks for this list = ' ||TO_CHAR (l_rank_count), 'LOW' );
401     END IF;
402 
403     --getting the list rules for the list.
404     OPEN c_list_rules (p_list_header_id);
405     LOOP
406         FETCH c_list_rules INTO l_list_rule_id;
407 
408         IF  (p_send_to_log = 'Y') THEN
409             Write_To_Act_Log (p_list_header_id,
410                       'List rule id = ' ||TO_CHAR (l_list_rule_id),'LOW');
411         END IF;
412 
413         IF (c_list_rules%notfound) THEN
414           --  DBMS_OUTPUT.PUT_LINE('no list rule provided.');
415             IF  (p_send_to_log = 'Y')
416             THEN
417                 Write_To_Act_Log (p_list_header_id,'No more list rule associated with the list' ,'LOW' );
418             END IF;
419 
420             IF FND_MSG_PUB.check_msg_level(FND_MSG_PUB.g_msg_lvl_error)
421             THEN
422                  FND_MESSAGE.set_name('AMS', 'AMS_LIST_NO_LIST_RULE');
426             CLOSE c_list_rules;
423                  FND_MSG_PUB.add;
424             END IF;
425 
427             RETURN 0;
428         END IF;
429 
430 
431       IF  (c_list_rules%rowcount > 1) THEN
432           --we have more than one rule for this list
433           --we must ensure that the key gets reset to NULL for the list to
434           -- ensure accurate results.
435           -- removed khung 07/07/1999
436          IF (p_object_name = 'AMS_LIST_ENTRIES') THEN
437              UPDATE ams_list_entries
438              SET dedupe_key = NULL
439              WHERE list_header_id = p_list_header_id
440              AND marked_as_duplicate_flag IS NULL;
441              COMMIT;
442          END IF;
443 
444          IF  (p_send_to_log = 'Y') THEN
445              Write_To_Act_Log (p_list_header_id,'Only one rule can be associated with the list','LOW' );
446          END IF;
447 
448          IF FND_MSG_PUB.check_msg_level(FND_MSG_PUB.g_msg_lvl_error) THEN
449             FND_MESSAGE.set_name('AMS', 'AMS_LIST_ONLY_ONE_LIST_RULE');
450             FND_MSG_PUB.add;
451          END IF;
452 
453          CLOSE c_list_rules;
454          RETURN 0;
455       END IF; -- End of if for more than 1 rule count
456 
457       Write_To_Act_Log (p_list_header_id, 'starting  rule attributes','LOW' );
458         --fetch the rule entries associated with this list.
459         OPEN c_rule_fields (l_list_rule_id);
460         LOOP
461             FETCH c_rule_fields INTO
462                          list_rules (i).field_table_name,
463                          list_rules (i).field_column_name,
464                          list_rules (i).substring_length,
465                          list_rules (i).word_replacement_code;
466             EXIT WHEN c_rule_fields%notfound;
467 
468 
469             -- if the enable word replacement flag is set we construct the sql
470             -- to call the filter word function.
471             IF  (p_enable_word_replacement_flag = 'Y') THEN
472                 Write_To_Act_Log (p_list_header_id, 'Calling replace word procedure','LOW' );
473                 l_fields :=
474                   l_fields ||
475                       'AMS_ListDedupe_PVT.replace_word(' ||
476                       upper(list_rules (i).field_column_name) ||
477                       ',' ||
478                       '''' ||
479                       list_rules (i).word_replacement_code||
480                       '''' ||
481                       ')' ||
482                       '||' ||
483                       '''' ||
484                       '.' ||
485                       '''' ||
486                       '||';
487 
488             ELSE
489             --no substr specified for the rule field.
490                 IF  (list_rules (i).substring_length IS NULL)
491                 THEN
492                     l_fields :=
493                       l_fields ||
494                       'upper(' ||
495                       list_rules (i).field_column_name ||
496                       ')||' ||
497                       '''' ||
498                       '.' ||
499                       '''' ||
500                       '||';
501                 ELSE
502                     l_fields :=
503                       l_fields ||
504                       'upper(substr(' ||
505                       list_rules (i).field_column_name ||
506                       ',1,' ||
507                       TO_CHAR (list_rules (i).substring_length) ||
508                       '))||' ||
509                       '''' ||
510                       '.' ||
511                       '''' ||
512                       '||';
513                 END IF;
514             END IF;
515 
516             i := i + 1;
517 
518         END LOOP;   --c_rule_fields
519 
520         i := 1;   --reseting to one.
521         list_rules := empty_list_rules;   --re-initializing because we can have many rules.
522 	if c_rule_fields%rowcount = 0 then
523 	   write_to_act_log(p_list_header_id, 'No attribute defined for the dedupe rule attached to this list/tg. Aborting deduplication process','HIGH');
524 	   return -1;
525 	end if;
526 	CLOSE c_rule_fields;
527 
528 
529 	-- removing the last '.' from the string as this will cause an invalid syntax error
530         -- in the query.
531 
532         l_fields := SUBSTR (l_fields, 1, LENGTH (l_fields) - 7);
533 
534         --constucting the valid sql to be executed.
535         -- 08/02/99 khung, modified using Native Dynamic SQL
536 
537         -- removed by khung 09/30/1999 to support AMS_IMP_SOURCE_LINES
538         --l_sql_stmt1 := 'update ams_list_entries set dedupe_key =';
539         l_sql_stmt2 := l_sql_stmt1;
540         l_sql_stmt2 := l_sql_stmt2 ||
541                 l_fields ||
542                 ' where list_header_id =' ||
543                 TO_CHAR (p_list_header_id);
544 
545 
546         IF  (p_send_to_log = 'Y')
547         THEN
548             Write_To_Act_Log (p_list_header_id,'SQL to generate Merge Keys = ' ||l_sql_stmt2,'LOW');
549         END IF;
550 
551         -- executing the sql to generate the dedupe_key which has also been filtered.
552 
553         -- l_no_of_masters := exec_sql_stmt (l_sql_stmt2);
554 
555         -- 08/02/99 khung, modified using Native Dynamic SQL
556         --EXECUTE IMMEDIATE l_sql_stmt2
560 	--notice that l_no_of_masters is not really used, left it there to keep the signature of the function Dedupe_LIST unchanged.
557         --    INTO l_no_of_masters;
558 
559         --09/27/2000 vbhandar,modified Execute immediate , INTO should only be used for single row queries
561 	 EXECUTE IMMEDIATE l_sql_stmt2;
562 
563         COMMIT;
564 
565         -- getting a distinct set of merge keys for this list and the count of each occurance of the key.
566         -- if there is only one key then we know that there are no duplicates for this key as defined by
567         -- the rule.
568         -- if there is more than one occurance then we need to choose one of the candidates as the master
569         -- and flag the rest as duplicates, we use the RANK for the List Sources to choose a Master
570         -- Entries, if no Ranks Exist then we arbitrarily choose a Master.
571         OPEN c_dedupe_keys (p_list_header_id);
572         LOOP
573             FETCH c_dedupe_keys INTO l_dedupe_key, l_dedupe_key_count;
574             EXIT WHEN c_dedupe_keys%notfound;
575 
576             --there are duplicates, we must choose the master entry and flag the rest as duplicates.
577             IF  (l_dedupe_key_count > 1)
578             THEN
579 
580                 IF  (l_rank_count = 0)
581                 THEN   -- there are no ranks assoociated with the list source so we can choose at random.
582 
583                     UPDATE ams_list_entries
584                        SET marked_as_duplicate_flag = 'Y',
585                            enabled_flag='N'  --modified vb 11/7/2000
586                      WHERE list_header_id = p_list_header_id
587                        AND dedupe_key = l_dedupe_key
588                        AND list_entry_id <> ( SELECT MIN (c.list_entry_id)
589                                      FROM ams_list_entries c
590                                      WHERE c.list_header_id = p_list_header_id
591                                      AND c.dedupe_key = l_dedupe_key);
592                 ELSE   --there are ranks associated with at least one list source.
593                     --modified vb 10/19/2000 to fix bug where dedup not working with more than 1 rank
594                     UPDATE ams_list_entries
595                        SET marked_as_duplicate_flag = 'Y',enabled_flag='N'--modified vb 11/7/2000
596                      WHERE list_header_id = p_list_header_id
597                        AND dedupe_key = l_dedupe_key
598                        AND list_entry_id <> ( SELECT MIN (c.list_entry_id)
599                                               --selecting the master.
600                                               FROM ams_list_entries c,
601                                               ams_list_select_actions a
602                                               WHERE c.list_header_id =
603                                                              p_list_header_id
604                                                  AND c.dedupe_key = l_dedupe_key
605                         AND c.list_select_action_id = a.list_select_action_id);
606 /*
607 			AND a.rank =(SELECT min(b.rank)
608 			              FROM ams_list_select_actions b
609                                 WHERE b.action_used_by_id = p_list_header_id
610                                   and b.arc_action_used_by = 'LIST')
611                                 GROUP BY a.rank); */
612                                             --even if there are no ranks for this sql stmt we will still
613                                             --return one row.
614 
615                 END IF;
616             END IF;
617         END LOOP;   --c_dedupe_keys loop
618         CLOSE c_dedupe_keys;
619         --initializing as we may have more than one rule.
620 
621         l_fields := NULL;
622         l_sql_stmt2 := l_sql_stmt1;
623 
624         IF  (p_send_to_log = 'Y')      THEN
625            write_to_act_log(p_list_header_id, 'Duplicates identified and marked for this rule','HIGH');
626 	END IF;
627 
628     END LOOP;   --c_list_rules loop
629 
630 /*    IF  (p_send_to_log = 'Y')
631     THEN
632         Write_To_Act_Log (p_list_header_id,
633                   'All the rules are applied' ||
634                   TO_CHAR (SYSDATE, 'DD-MON-RRRR HH24:MM:SS') );
635     END IF;*/
636 
637     --recording who performed the deduplication and at what time.
638     UPDATE ams_list_headers_all
639        SET last_deduped_by_user_id = FND_GLOBAL.User_Id
640            ,last_dedupe_date = SYSDATE
641      WHERE list_header_id = p_list_header_id;
642 
643 /*    IF  (p_send_to_log = 'Y')
644     THEN
645         Write_To_Act_Log (p_list_header_id,
646                   'DEDUPE LIST: RECORDING WHO DEUPED THE LIST AND THE DATE' ||
647                   TO_CHAR (SYSDATE, 'DD-MON-RRRR HH24:MM:SS') );
648     END IF;*/
649 
650     --recording the number of duplicates found.
651     UPDATE ams_list_headers_all
652        SET no_of_rows_duplicates = (
653             SELECT COUNT (*)
654               FROM ams_list_entries
655              WHERE list_header_id = p_list_header_id
656                AND marked_as_duplicate_flag = 'Y')
657      WHERE list_header_id = p_list_header_id
658      returning no_of_rows_duplicates into l_no_of_duplicates;
659 
660     IF  (p_send_to_log = 'Y')
661     THEN
662         Write_To_Act_Log (p_list_header_id,'No of duplicates found for this list is ' ||l_no_of_duplicates,'HIGH');
663     END IF;
664 
665     COMMIT;
666 
670 
667     RETURN  (l_no_of_masters);
668 
669 END Dedupe_List;
671 
672 --------------------------------------------------------------------
673 -- PROCEDURE
674 --    Generate_Key
675 -- HISTORY
676 -- 10-Nov-1999 choang      Created.
677 -- 11-Nov-1999 choang      Moved to AMS_ListDedupe_PVT.
678 --------------------------------------------------------------------
679 PROCEDURE Generate_Key (
680    p_api_version        IN    NUMBER,
681    p_init_msg_list      IN    VARCHAR2  := FND_API.g_false,
682    p_validation_level   IN    NUMBER    := FND_API.g_valid_level_full,
683 
684    x_return_status      OUT NOCOPY   VARCHAR2,
685    x_msg_count          OUT NOCOPY   NUMBER,
686    x_msg_data           OUT NOCOPY   VARCHAR2,
687 
688    p_list_rule_id       IN    NUMBER,
689    p_sys_object_id      IN    NUMBER,
690    p_sys_object_id_field   IN    VARCHAR2,
691    p_word_replacement_flag IN VARCHAR2,
692    x_dedupe_key         OUT NOCOPY   VARCHAR2
693 )
694 IS
695    L_KEY_LENGTH         CONSTANT NUMBER := 500;
696    L_API_VERSION        CONSTANT NUMBER := 1.0;
697    L_API_NAME           CONSTANT VARCHAR2(30) := 'Generate_Key';
698    L_FULL_NAME          CONSTANT VARCHAR2(60) := g_pkg_name || '.' || l_api_name;
699 
700    l_dedupe_key         VARCHAR2(4000);
701    l_field_column_name  VARCHAR2(30);
702    l_field_table_name   VARCHAR2(30);
703    l_substring_length   NUMBER;
704    --
705    -- The following 3 variables used to construct
706    -- a SQL statement which has the format:
707    --    SELECT word1, word2, etc.
708    --    FROM   field_table_name
709    --    WHERE  id = p_sys_object_id
710    -- This statement is used to retrive the
711    -- dedupe key for the specified record.
712    l_select_sql         VARCHAR2(4000);
713    l_from_sql           VARCHAR2(4000);
714    l_where_sql          VARCHAR2(4000);
715 
716    l_return_status   VARCHAR2(1);
717 
718    --
719    -- LIST_RULE_FIELDS defines the tables and columns involved
720    -- in generating a key.  The key can also be a substring of
721    -- the specified column.
722    -- NOTE: Only substring is needed; we do not need to pad
723    -- the data if it is shorter than the specified substring
724    -- length.
725     CURSOR c_fields IS
726     SELECT field_table_name,
727            field_column_name,
728            substring_length
729       FROM ams_list_rule_fields
730      WHERE list_rule_id = p_list_rule_id;
731 BEGIN
732    --------------------- initialize -----------------------
733    IF (AMS_DEBUG_HIGH_ON) THEN
734 
735    AMS_Utility_PVT.debug_message (l_full_name || ': Start');
736    END IF;
737 
738    IF FND_API.to_boolean (p_init_msg_list) THEN
739       -- Clear out the message buffer.
740       FND_MSG_PUB.initialize;
741    END IF;
742 
743    IF NOT FND_API.compatible_api_call (
744          l_api_version,
745          p_api_version,
746          l_api_name,
747          g_pkg_name
748    ) THEN
749       RAISE FND_API.g_exc_unexpected_error;
750    END IF;
751 
752    x_return_status := FND_API.g_ret_sts_success;
753 
754    ----------------------- generate -----------------------
755    IF (AMS_DEBUG_HIGH_ON) THEN
756 
757    AMS_Utility_PVT.debug_message (l_full_name || ': Generate');
758    END IF;
759 
760    OPEN c_fields;
761    LOOP
762       FETCH c_fields INTO l_field_table_name, l_field_column_name, l_substring_length;
763       IF (c_fields%ROWCOUNT = 0) THEN
764          CLOSE c_fields;
765          IF FND_MSG_PUB.check_msg_level (FND_MSG_PUB.g_msg_lvl_error) THEN
766             FND_MESSAGE.set_name ('AMS', 'AMS_LIST_BAD_DEDUPE_RULE');
767             FND_MSG_PUB.add;
768          END IF;
769          RAISE FND_API.g_exc_error;
770       END IF;
771       EXIT WHEN c_fields%NOTFOUND;
772 
773       -- if the enable word replacement flag is set we construct the sql
774       -- to call the filter word function.
775       IF  (p_word_replacement_flag = 'Y') THEN
776          --
777          -- Construct select SQL clause in the format:
778          --    SELECT AMS_ListDedupe_PVT.filter_word ( the_word, the_length, the_table, the_column )
779          -- The final result of the query will look like: A.B.C.D
780          --
781          l_select_sql :=
782            l_select_sql ||
783                 'AMS_ListDedupe_PVT.filter_word (' ||
784                 l_field_column_name ||
785                 ',' ||
786                 NVL (TO_CHAR (l_substring_length), 'NULL') ||
787                 ',' ||
788                 '''' ||
789                 l_field_table_name ||
790                 '''' ||
791                 ',' ||
792                 '''' ||
793                 l_field_column_name ||
794                 '''' ||
795                 ')' ||
796                 '||' ||
797                 '''' ||
798                 '.' ||
799                 '''' ||
800                 '||';
801       ELSE
802       --no substr specified for the rule field.
803          IF  (NVL (l_substring_length, 0) = 0) THEN
804             l_select_sql :=
805               l_select_sql ||
806               'UPPER (' ||
807               l_field_column_name ||
808               ')||' ||
812               '||';
809               '''' ||
810               '.' ||
811               '''' ||
813          ELSE
814             l_select_sql :=
815               l_select_sql ||
816               'UPPER(SUBSTR(' ||
817               l_field_column_name ||
818               ',1,' ||
819               TO_CHAR (l_substring_length) ||
820               '))||' ||
821               '''' ||
822               '.' ||
823               '''' ||
824               '||';
825          END IF;
826       END IF;
827 
828 /***
829       IF p_word_replacement_flag = 'Y' THEN
830          l_select_sql :=  l_select_sql ||
831                           'AMS_ListDedupe_PVT.filter_word (' || l_field_column_name || ', '
832                                                              || l_substring_length || ', '
833                                                              || l_field_table_name || ', '
834                                                              || '''' || l_field_column_name || '''),'
835       ELSE
836       END IF;
837 ***/
838 
839     END LOOP;
840     CLOSE c_fields;
841 
842     -- removing the last '.' from the string as this will cause an invalid syntax error
843     -- in the query.
844     l_select_sql := 'SELECT ' || SUBSTR (l_select_sql, 1, LENGTH (l_select_sql) - 7) || ' ';
845 
846     l_from_sql := 'FROM ' || l_field_table_name || ' ';
847 
848     l_where_sql := 'WHERE ' || p_sys_object_id_field || ' = :p_sys_object_id';
849 
850     EXECUTE IMMEDIATE l_select_sql || l_from_sql || l_where_sql
851     INTO l_dedupe_key
852     USING p_sys_object_id
853     ;
854     -------------------- finish --------------------------
855     --
856     -- Set the out variable.
857     -- The returned key may be of greater length than
858     -- the allowable key length.
859     x_dedupe_key := SUBSTR (l_dedupe_key, 1, L_KEY_LENGTH);
860 
861     FND_MSG_PUB.count_and_get (
862          p_encoded => FND_API.g_false,
863          p_count   => x_msg_count,
864          p_data    => x_msg_data
865     );
866 
867     IF (AMS_DEBUG_HIGH_ON) THEN
868 
869 
870 
871     AMS_Utility_PVT.debug_message (l_full_name || ': End');
872 
873     END IF;
874 
875 EXCEPTION
876     WHEN FND_API.g_exc_error THEN
877       x_return_status := FND_API.g_ret_sts_error;
878       FND_MSG_PUB.count_and_get (
879             p_encoded => FND_API.g_false,
880             p_count   => x_msg_count,
881             p_data    => x_msg_data
882       );
883     WHEN FND_API.g_exc_unexpected_error THEN
884       x_return_status := FND_API.g_ret_sts_unexp_error ;
885       FND_MSG_PUB.count_and_get (
886             p_encoded => FND_API.g_false,
887             p_count   => x_msg_count,
888             p_data    => x_msg_data
889       );
890     WHEN OTHERS THEN
891       x_return_status := FND_API.g_ret_sts_unexp_error;
892       IF FND_MSG_PUB.check_msg_level (FND_MSG_PUB.g_msg_lvl_unexp_error) THEN
893          FND_MSG_PUB.add_exc_msg (g_pkg_name, l_api_name);
894       END IF;
895 
896       FND_MSG_PUB.count_and_get (
897             p_encoded => FND_API.g_false,
898             p_count   => x_msg_count,
899             p_data    => x_msg_data
900       );
901 END Generate_Key;
902 
903 FUNCTION Replace_Word(p_word              VARCHAR2,
904                         p_replacement_type  VARCHAR2)
905   RETURN VARCHAR2
906   IS
907     l_source_text        VARCHAR2(2000);
908     l_text_length        NUMBER;
909     l_current_word       VARCHAR2(2000);
910     l_key                VARCHAR2(2000);
911     l_old_word           VARCHAR2(2000);
912     l_count              NUMBER := 0;
913 
914     CURSOR C_Word_Rep (x_current_word VARCHAR2,
915                        x_replacement_type VARCHAR2) IS
916       SELECT upper(replacement_word)
917       FROM   hz_word_replacements
918       WHERE  upper(original_word) = x_current_word
919       AND    type = x_replacement_type;
920 
921     CURSOR c_key IS
922       SELECT ORIGINAL_WORD,
923              REPLACEMENT_WORD
924       FROM   HZ_WORD_REPLACEMENTS
925       WHERE  TYPE = 'KEY';
926 
927   BEGIN
928 
929     -- Steps mentioned here are in the context of complete fuzzy key
930     -- generation process. (Step 1 is in Generate_Key)
931     -- Step 2.
932     -- We need to remove 'S so that WILLIAM'S becomes WILLIAM and
933     -- it can become BILL if there is a replacement rule from
934     -- original word WILLIAM to replacement word BILL
935     l_source_text := replace(p_word, '''S ', ' ');
936 
937     -- Step 3.
938     -- We need to remove any punctuation characters etc.
939     -- For example, this will make 134/3, 134-3 etc mapped to 1343 in key for address.
940     l_source_text := ltrim(translate(l_source_text, g_original_text, g_replace_text));
941 
942     -- Step 3.5.
943     -- This step is for removal of special characters.
944     -- The special characters will only be replaced if user has
945     -- has set up Key Modifiers rules.
946     -- This will replace any number of characters to any number of characters mapping for
947     -- many european language. See bug 1868161 for detail.
948 
949     IF g_special_enabled IS NULL THEN
953             g_special_enabled := 'N';
950         OPEN c_key;
951         FETCH c_key INTO g_original_key(l_count), g_replacement_key(l_count);
952         IF c_key%NOTFOUND THEN
954         ELSE
955             g_special_enabled := 'Y';
956         END IF;
957 
958         WHILE c_key%FOUND LOOP
959             l_count := l_count + 1;
960             FETCH c_key INTO g_original_key(l_count), g_replacement_key(l_count);
961         END LOOP;
962         CLOSE c_key;
963     END IF;
964     IF g_special_enabled = 'Y' THEN
965         FOR i IN g_original_key.FIRST..g_original_key.LAST LOOP
966             l_source_text := REPLACE(l_source_text, g_original_key(i), g_replacement_key(i));
967         END LOOP;
968     END IF;
969 
970     -- Step 4.
971     -- We need to continue further processing on each word if a group
972     -- of words is the input parameter.
973     -- For example INTERNATIONAL BUSINESS MACHINES should have rules
974     -- applied to each word (INTERNATIONAL, BUSINESS, MACHINES) individually.
975     -- Append a blank space on the end of the text so that the loop can
976     -- always end with the last word.
977     l_source_text := l_source_text || ' ';
978     LOOP
979       l_text_length := NVL(length(l_source_text),0);
980       IF l_text_length = 0
981       THEN
982           EXIT;
983       END IF;
984       FOR i IN 1..l_text_length LOOP
985         IF substr(l_source_text,i,1) = ' '
986         THEN
987           l_current_word := substr(l_source_text,0,i-1);
988           l_old_word := l_current_word;
989           -- Fetch the replacement word for the current word.
990           -- If no replacement word is found, then use the original
991           -- word
992           --
993           -- Step 5.
994           -- Search a replacement word for the original word.
995           -- For example WILLIAM will be replaced by BILL if there is such rule.
996           -- If a replacement found, substitute the original word by it
997           OPEN C_Word_Rep(l_current_word, p_replacement_type);
998           FETCH C_Word_Rep INTO l_current_word;
999           IF (C_Word_Rep%NOTFOUND)
1000           THEN
1001             l_current_word := l_old_word;
1002           END IF;
1003           CLOSE C_Word_Rep;
1004 
1005           -- Step 7.
1006           -- If profile for cleansing is set, then cleanse the word.
1007           -- Cleanse converts double letters to single letter, removes
1008           -- vowels inside a word.
1009           -- For example : UNIVERSAL - UNVRSL, LITTLE - LTL etc.
1010           if fnd_profile.value('HZ_CLEANSE_FUZZY_KEY')  = 'Y' then
1011             l_current_word := hz_common_pub.cleanse(l_current_word);
1012           end if;
1013 
1014           -- Step 8.
1015           -- Build the key in a local variable
1016           -- This removes the white spaces
1017           l_key := l_key || l_current_word;
1018           l_source_text := substr(l_source_text,i);
1019           l_source_text := ltrim(l_source_text);
1020           EXIT;
1021         END IF;
1022       END LOOP;
1023     END LOOP;
1024     RETURN l_key;
1025   END Replace_Word;
1026 
1027 
1028 END AMS_ListDedupe_PVT;