DBA Data[Home] [Help]

PACKAGE BODY: APPS.AMS_LISTDEDUPE_PVT

Source


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