DBA Data[Home] [Help]

PACKAGE BODY: APPS.MTL_SAFETY_STOCKS_PKG

Source


1 PACKAGE BODY MTL_SAFETY_STOCKS_PKG as
2 /* $Header: INVDDFSB.pls 120.6.12020000.2 2012/07/09 08:05:15 asugandh ship $ */
3 
4 
5 /****************************************************************
6  *  Enhancement Bug #2231655     Added Function.                *
7  *  The function check_project_task checks if there exists an   *
8  *  entry for project and task for the combination of item      *
9  *  Org and effectivity date >=start date. If exists then it    *
10  *  returns TRUE else returns FALSE.                            *
11  ****************************************************************/
12 
13 FUNCTION check_project_task(p_inventory_item_id IN NUMBER
14 			  ,p_organization_id    IN NUMBER
15 			  ,p_effect_date        IN DATE
16 			  ) RETURN BOOLEAN IS
17 l_num   NUMBER;
18 l_return_sts BOOLEAN := FALSE;
19 BEGIN
20   SELECT 1
21   INTO l_num
22   FROM dual
23   WHERE EXISTS (SELECT 1
24 		FROM mtl_safety_stocks
25 		WHERE organization_id = p_organization_id
26 		AND inventory_item_id = p_inventory_item_id
27 		AND effectivity_date >= p_effect_date
28 		AND (project_id IS NOT NULL OR task_id IS NOT NULL)
29 		);
30   l_return_sts := TRUE;
31   RETURN l_return_sts;
32 EXCEPTION
33   WHEN NO_DATA_FOUND THEN
34   l_return_sts := FALSE;
35   RETURN l_return_sts;
36 END check_project_task;
37 
38 /****************************************************************
39  *  Enhancement Bug#2231655    Added local Procedure println    *
40  *  Procedure println writes a debug message in the log file.   *
41  ****************************************************************/
42 
43 PROCEDURE println(msg IN VARCHAR2,
44 		  g_debug_level IN NUMBER default NULL
45 		  ) IS
46 BEGIN
47   IF(g_debug_level IS NULL) THEN
48     fnd_file.put_line(FND_FILE.LOG,substr(msg,1,255));
49   ELSIF(g_debug_level in (2,3)) THEN
50     fnd_file.put_line(FND_FILE.LOG,substr(msg,1,255));
51   END IF;
52 END println;
53 
54 
55 procedure SafetyStock(X_ORGANIZATION_ID NUMBER,
56                       X_SELECTION NUMBER,
57                       X_INVENTORY_ITEM_ID NUMBER,
58 		      X_SAFETY_STOCK_CODE NUMBER,
59                       X_FORECAST_NAME VARCHAR2,
60                       X_CATEGORY_SET_ID NUMBER,
61                       X_CATEGORY_ID NUMBER,
62                       X_PERCENT NUMBER,
63                       X_SERVICE_LEVEL NUMBER,
64                       X_START_DATE DATE,
65 		      login_id NUMBER,
66 		      user_id NUMBER) IS
67 
68     cursor ITEM1_cur IS
69    --Bug#2713829, also selected MRP_SAFETY_STOCK_PERCENT from MSI.
70 	SELECT DISTINCT F.INVENTORY_ITEM_ID, MSI.MRP_SAFETY_STOCK_PERCENT
71 	FROM MRP_FORECAST_DESIGNATORS D, MRP_FORECAST_DATES F, MTL_SYSTEM_ITEMS MSI
72 	WHERE D.ORGANIZATION_ID = X_ORGANIZATION_ID AND
73 	      D.FORECAST_DESIGNATOR = X_FORECAST_NAME AND
74 	      D.ORGANIZATION_ID = F.ORGANIZATION_ID AND
75 	      D.FORECAST_DESIGNATOR = F.FORECAST_DESIGNATOR AND
76          MSI.ORGANIZATION_ID = F.ORGANIZATION_ID AND
77          MSI.INVENTORY_ITEM_ID = F.INVENTORY_ITEM_ID;
78 
79     cursor ITEM2_cur IS
80 	SELECT DISTINCT F.INVENTORY_ITEM_ID
81 	FROM MRP_FORECAST_DESIGNATORS D, MRP_FORECAST_DATES F,
82              MTL_ITEM_CATEGORIES C
83 	WHERE D.ORGANIZATION_ID = X_ORGANIZATION_ID AND
84 	      D.FORECAST_DESIGNATOR = X_FORECAST_NAME AND
85 	      D.ORGANIZATION_ID = F.ORGANIZATION_ID AND
86 	      D.FORECAST_DESIGNATOR = F.FORECAST_DESIGNATOR AND
87               C.CATEGORY_SET_ID = X_CATEGORY_SET_ID AND
88               C.CATEGORY_ID = X_CATEGORY_ID AND
89               C.ORGANIZATION_ID = X_ORGANIZATION_ID AND
90               C.INVENTORY_ITEM_ID = F.INVENTORY_ITEM_ID;
91 
92     cursor FORECAST_cur IS
93         SELECT 'X' FROM MRP_FORECAST_DESIGNATORS
94         WHERE FORECAST_DESIGNATOR = X_FORECAST_NAME AND
95               FORECAST_DESIGNATOR
96               IN (SELECT d.FORECAST_DESIGNATOR
97                   FROM MRP_FORECAST_DESIGNATORS d
98                   WHERE d.ORGANIZATION_ID = X_ORGANIZATION_ID AND
99                         EXISTS (SELECT 'X' FROM MRP_FORECAST_DATES f
100                                 WHERE f.INVENTORY_ITEM_ID = X_INVENTORY_ITEM_ID AND
101                                       d.FORECAST_DESIGNATOR = f.FORECAST_DESIGNATOR AND
102                                       ORGANIZATION_ID = X_ORGANIZATION_ID));
103 
104     ss_prcnt    NUMBER;
105     srv_lvl     NUMBER;
106     srv_factor 	NUMBER;
107     except_id	NUMBER;
108     cal_code	VARCHAR2(10);
109     forecast    VARCHAR2(10);
110     item_tmp	NUMBER;
111     ss_prcnt_temp NUMBER;      --Bug#2713829.
112     l_return_sts BOOLEAN;
113     l_prcnt_level NUMBER := 1; --Bug#2713829. 1 = get % from Concurrent Program
114 
115     BEGIN
116       IF X_SAFETY_STOCK_CODE = 3 THEN
117          ss_prcnt := NULL;
118          srv_lvl := X_SERVICE_LEVEL;
119       END IF;
120 
121       IF X_SAFETY_STOCK_CODE = 2 THEN
122          ss_prcnt := X_PERCENT;
123          srv_lvl := NULL;
124       END IF;
125 
126       IF ss_prcnt = 0 THEN
127          l_prcnt_level := 2; --2 = get % from item attributes
128       END IF;
129 
130       Init(X_ORGANIZATION_ID, srv_lvl , srv_factor, cal_code, except_id);
131 
132       -- Bug 5041094 Deleting the safety stock entries existing for forecast specified
133       --             when those items no longer exist on forecast but are selected by
134       --             item filter , but skipping items for which project/task level entries exist
135       IF (X_SELECTION = 1) THEN
136         DELETE FROM MTL_SAFETY_STOCKS M
137         WHERE ORGANIZATION_ID = X_ORGANIZATION_ID
138         AND FORECAST_DESIGNATOR = X_FORECAST_NAME
139         AND INVENTORY_ITEM_ID NOT IN ( SELECT DISTINCT F.INVENTORY_ITEM_ID
140                                        FROM MRP_FORECAST_DESIGNATORS D, MRP_FORECAST_DATES F
141                                        WHERE D.ORGANIZATION_ID = X_ORGANIZATION_ID
142                                        AND D.FORECAST_DESIGNATOR = X_FORECAST_NAME
143                                        AND D.ORGANIZATION_ID = F.ORGANIZATION_ID
144                                        AND D.FORECAST_DESIGNATOR = F.FORECAST_DESIGNATOR )
145         AND NOT EXISTS ( SELECT 1 FROM MTL_SAFETY_STOCKS
146                       	 WHERE ORGANIZATION_ID = M.ORGANIZATION_ID
147                          AND INVENTORY_ITEM_ID = M.INVENTORY_ITEM_ID
148                          AND EFFECTIVITY_DATE >= X_START_DATE
149 		         AND (PROJECT_ID IS NOT NULL OR TASK_ID IS NOT NULL))
150         AND EFFECTIVITY_DATE >= X_START_DATE ;
151      ELSIF ( X_SELECTION = 2 ) THEN
152         DELETE FROM MTL_SAFETY_STOCKS M
153         WHERE ORGANIZATION_ID = X_ORGANIZATION_ID
154         AND FORECAST_DESIGNATOR = X_FORECAST_NAME
155         AND INVENTORY_ITEM_ID = X_INVENTORY_ITEM_ID
156         AND NOT EXISTS ( SELECT 1 FROM MRP_FORECAST_DESIGNATORS D, MRP_FORECAST_DATES F
157                          WHERE D.ORGANIZATION_ID = X_ORGANIZATION_ID
158                          AND D.FORECAST_DESIGNATOR = X_FORECAST_NAME
159                          AND D.ORGANIZATION_ID = F.ORGANIZATION_ID
160                          AND D.FORECAST_DESIGNATOR = F.FORECAST_DESIGNATOR
161                          AND F.INVENTORY_ITEM_ID = X_INVENTORY_ITEM_ID )
162         AND NOT EXISTS ( SELECT 1 FROM MTL_SAFETY_STOCKS
163                       	 WHERE ORGANIZATION_ID = M.ORGANIZATION_ID
164                          AND INVENTORY_ITEM_ID = M.INVENTORY_ITEM_ID
165                          AND EFFECTIVITY_DATE >= X_START_DATE
166 		         AND (PROJECT_ID IS NOT NULL OR TASK_ID IS NOT NULL))
167         AND EFFECTIVITY_DATE >= X_START_DATE ;
168      ELSE
169         DELETE FROM MTL_SAFETY_STOCKS M
170         WHERE ORGANIZATION_ID = X_ORGANIZATION_ID
171         AND FORECAST_DESIGNATOR = X_FORECAST_NAME
172 	AND EXISTS  ( SELECT 1 FROM  MTL_ITEM_CATEGORIES C
173                       WHERE C.ORGANIZATION_ID = M.ORGANIZATION_ID
174                       AND C.INVENTORY_ITEM_ID = M.INVENTORY_ITEM_ID
175                       AND C.CATEGORY_SET_ID = X_CATEGORY_SET_ID
176                       AND C.CATEGORY_ID = X_CATEGORY_ID
177                       AND C.ORGANIZATION_ID = X_ORGANIZATION_ID )
178         AND INVENTORY_ITEM_ID NOT IN ( SELECT DISTINCT F.INVENTORY_ITEM_ID
179                                        FROM MRP_FORECAST_DESIGNATORS D, MRP_FORECAST_DATES F,
180                                             MTL_ITEM_CATEGORIES C
181                                        WHERE D.ORGANIZATION_ID = X_ORGANIZATION_ID
182                                        AND D.FORECAST_DESIGNATOR = X_FORECAST_NAME
183                                        AND D.ORGANIZATION_ID = F.ORGANIZATION_ID
184                                        AND D.FORECAST_DESIGNATOR = F.FORECAST_DESIGNATOR
185                                        AND C.CATEGORY_SET_ID = X_CATEGORY_SET_ID
186                                        AND C.CATEGORY_ID = X_CATEGORY_ID
187                                        AND C.ORGANIZATION_ID = X_ORGANIZATION_ID
188                                        AND C.INVENTORY_ITEM_ID = F.INVENTORY_ITEM_ID )
189         AND NOT EXISTS ( SELECT 1 FROM MTL_SAFETY_STOCKS
190                       	 WHERE ORGANIZATION_ID = M.ORGANIZATION_ID
191                          AND INVENTORY_ITEM_ID = M.INVENTORY_ITEM_ID
192                          AND EFFECTIVITY_DATE >= X_START_DATE
193 		         AND (PROJECT_ID IS NOT NULL OR TASK_ID IS NOT NULL))
194         AND EFFECTIVITY_DATE >= X_START_DATE ;
195       END IF ;
196       COMMIT;
197      -- Bug 5041094 Ends
198 
199       IF X_SELECTION = 1 THEN
200          OPEN ITEM1_cur;
201          FETCH ITEM1_cur INTO item_tmp,ss_prcnt_temp;
202          IF ITEM1_cur%ROWCOUNT = 0 THEN
203             CLOSE ITEM1_cur;
204             RAISE NO_DATA_FOUND;
205          END IF;
206          CLOSE ITEM1_cur;
207          FOR item IN ITEM1_cur LOOP
208 
209       /******************************************************************/
210       /* Enhancement Bug #2231655  . Changed the logic to check for     */
211       /* project and task entry for the combination of item,Organization*/
212       /* and effectivity date. If an entry exists for a project and task*/
213       /* then the processing of that item is skipped and a log message  */
214       /* is written.                                                    */
215       /******************************************************************/
216 
217       l_return_sts := check_project_task(p_inventory_item_id => item.inventory_item_id
218 					 ,p_organization_id => x_organization_id
219 					 ,p_effect_date => x_start_date
220 					 );
221      IF l_return_sts THEN
222        println('Item skipped as Entry for project found');
223 	    println('X_SELECTION = 1 ');
224 	    println('item id ='||item.inventory_item_id);
225 	    println('Org id = '||x_organization_id);
226 	    println('effectivity date ='||x_start_date);
227 	  ELSE
228         --Bug#2713829.If ss_prcnt is not defined at the concurrent program, then obtain it
229         --from mtl_system_items.
230         IF l_prcnt_level = 2 THEN
231            ss_prcnt := nvl(item.MRP_SAFETY_STOCK_PERCENT,0);
232         END IF;
233              Main(X_ORGANIZATION_ID, item.INVENTORY_ITEM_ID,
234                   X_SAFETY_STOCK_CODE, X_FORECAST_NAME,
235                   ss_prcnt, srv_lvl, X_START_DATE,
236                   srv_factor, cal_code, except_id, login_id, user_id);
237 	  END IF;
238 	/* End  Bug#        */
239          END LOOP;
240       ELSIF X_SELECTION = 2 THEN
241       /******************************************************************/
242       /* Enhancement Bug #2231655  . Changed the logic to check for     */
243       /* project and task entry for the combination of item,Organization*/
244       /* and effectivity date. If an entry exists for a project and task*/
245       /* then the processing of that item is skipped and a log message  */
246       /* is written.                                                    */
247       /******************************************************************/
248 
249       l_return_sts := check_project_task(p_inventory_item_id => x_inventory_item_id
250 					 ,p_organization_id => x_organization_id
251 					 ,p_effect_date => x_start_date
252 					 );
253 	  IF l_return_sts THEN
254 	    println('Item skipped as Entry for project found');
255 	    println('X_SELECTION = 2 ');
256 	    println('item id ='||x_inventory_item_id);
257 	    println('Org id = '||x_organization_id);
258 	    println('effectivity date ='||x_start_date);
259 	  ELSE
260 	    OPEN FORECAST_cur;
261             FETCH FORECAST_cur INTO forecast;
262             IF FORECAST_cur%NOTFOUND THEN
263               CLOSE FORECAST_cur;
264               RAISE NO_DATA_FOUND;
265             END IF;
266             CLOSE FORECAST_cur;
267             Main(X_ORGANIZATION_ID, X_INVENTORY_ITEM_ID,
268               X_SAFETY_STOCK_CODE, X_FORECAST_NAME,
269               ss_prcnt, srv_lvl, X_START_DATE,
270               srv_factor, cal_code, except_id, login_id, user_id);
271 	  END IF;
272 	  /* End  Bug#        */
273       ELSE
274             OPEN ITEM2_cur;
275             FETCH ITEM2_cur INTO item_tmp;
276             IF ITEM2_cur%ROWCOUNT = 0 THEN
277                CLOSE ITEM2_cur;
278                RAISE NO_DATA_FOUND;
279             END IF;
280             CLOSE ITEM2_cur;
281             FOR item IN ITEM2_cur LOOP
282       /******************************************************************/
283       /* Enhancement Bug #2231655  . Changed the logic to check for     */
284       /* project and task entry for the combination of item,Organization*/
285       /* and effectivity date. If an entry exists for a project and task*/
286       /* then the processing of that item is skipped and a log message  */
287       /* is written.                                                    */
288       /******************************************************************/
289 
290       l_return_sts := check_project_task(p_inventory_item_id => item.inventory_item_id
291 					 ,p_organization_id => x_organization_id
292 					 ,p_effect_date => x_start_date
293 					 );
294              IF l_return_sts THEN
295 	       println('Item skipped as Entry for project found');
296 	       println('X_SELECTION <> 1 or 2 ');
297 	       println('item id ='||item.inventory_item_id);
298 	       println('Org id = '||x_organization_id);
299 	       println('effectivity date ='||x_start_date);
300 	     ELSE
301 		Main(X_ORGANIZATION_ID, item.INVENTORY_ITEM_ID,
302                      X_SAFETY_STOCK_CODE, X_FORECAST_NAME,
303                      ss_prcnt, srv_lvl, X_START_DATE,
304                      srv_factor, cal_code, except_id, login_id, user_id);
305 	     END IF;
306 	    /* End  Bug#        */
307             END LOOP;
308       END IF;
309       commit;
310 
311     END SafetyStock;
312 
313 procedure Init(org_id IN NUMBER,
314                srv_level IN NUMBER,
315                srv_factor OUT NOCOPY NUMBER,
316                cal_code OUT NOCOPY VARCHAR2,
317                except_id OUT NOCOPY NUMBER) IS
318 
319     BEGIN
320        srv_factor := CalSF(srv_level)* 1.25;
321 
325     /* with MTL_PARAMETERS table.                           */
322     /********************************************************/
323     /* Select calendar Code and Exception Set Id for future */
324     /* use so that the SQL statements do not have to join   */
326     /********************************************************/
327 
328        SELECT CALENDAR_CODE, CALENDAR_EXCEPTION_SET_ID
329        INTO   cal_code, except_id
330        FROM   MTL_PARAMETERS
331        WHERE  ORGANIZATION_ID = org_id;
332 
333     END Init;
334 
335 
336 procedure Main(org_id NUMBER,
337                item_id NUMBER,
338 	       ss_code NUMBER,
339                forc_name VARCHAR2,
340                ss_percent NUMBER,
341                srv_level NUMBER,
342                effect_date DATE,
343                srv_factor NUMBER,
344                cal_code VARCHAR2,
345                except_id NUMBER,
346 	       login_id NUMBER,
347                user_id NUMBER) IS
348 
349     /********************************************************/
350     /* Select the the forecast quantity for each date.      */
351     /********************************************************/
352 
353     /* Bug# 5855934
354      * Modified the below cursor to prevent checks on Forecast Set as
355      * this value will never be passed through the 'forc_name' parameter.
356      * Removed the Nvl function as FORECAST_SET will never be 'NULL'.
357      * Removed the Decode function as FORECAST_DESIGNATOR will always refer
358      * to the Forecast Name and not the Forecast Set.
359      */
360     CURSOR SelForecast_mad IS							-- 6797274  changes
361        SELECT 1,
362               TO_NUMBER(TO_CHAR(C1.CALENDAR_DATE,'J')),
363               ROUND(SUM(NVL(F.FORECAST_MAD, 0)) * srv_factor, 5)		-- 6797274  changes
364        FROM
365               BOM_CALENDAR_DATES        C1,
366               MRP_FORECAST_DESIGNATORS  D1,
367               MRP_FORECAST_DATES        F
368        WHERE  D1.ORGANIZATION_ID = org_id
369        AND    D1.FORECAST_DESIGNATOR = forc_name
370        AND    F.ORGANIZATION_ID = org_id
371        AND    F.INVENTORY_ITEM_ID = item_id
372        AND    F.FORECAST_DESIGNATOR = D1.FORECAST_DESIGNATOR
373        AND    F.BUCKET_TYPE = 1
374        AND    NVL(F.ORIGINATION_TYPE, -1) = 5					-- 6797274  changes
375        AND    C1.CALENDAR_CODE = cal_code
376        AND    C1.EXCEPTION_SET_ID = except_id
377        AND    (C1.CALENDAR_DATE >= F.FORECAST_DATE
378        AND    C1.CALENDAR_DATE >= effect_date
379        AND    C1.CALENDAR_DATE <= NVL(F.RATE_END_DATE, F.FORECAST_DATE))
380        GROUP BY C1.CALENDAR_DATE
381        UNION
382        SELECT 2,
383               TO_NUMBER(TO_CHAR(C3.CALENDAR_DATE,'J')),
384               ROUND(SUM(NVL(F.FORECAST_MAD, 0)/(C2.NEXT_SEQ_NUM -		-- 6797274  changes
385                               C3.NEXT_SEQ_NUM)) * srv_factor, 5)
386        FROM   BOM_CALENDAR_DATES C1, BOM_CALENDAR_DATES C2,
387               BOM_CALENDAR_DATES C3,
388               BOM_CAL_WEEK_START_DATES W1, MRP_FORECAST_DATES F,
389               MRP_FORECAST_DESIGNATORS D1
390        WHERE  D1.ORGANIZATION_ID = org_id
391        AND    D1.FORECAST_DESIGNATOR = forc_name
392        AND    F.ORGANIZATION_ID = org_id
393        AND    F.INVENTORY_ITEM_ID = item_id
394        AND    F.FORECAST_DESIGNATOR = D1.FORECAST_DESIGNATOR
395        AND    F.BUCKET_TYPE = 2
396        AND    NVL(F.ORIGINATION_TYPE, -1) = 5					-- 6797274  changes
397        AND    W1.CALENDAR_CODE = cal_code
398        AND    W1.EXCEPTION_SET_ID = except_id
399        AND    (W1.WEEK_START_DATE >= F.FORECAST_DATE
400        AND    W1.WEEK_START_DATE <= NVL(F.RATE_END_DATE, F.FORECAST_DATE))
401        AND    W1.NEXT_DATE > effect_date
402        AND    C1.CALENDAR_CODE = cal_code
403        AND    C2.CALENDAR_CODE = cal_code
404        AND    C3.CALENDAR_CODE = cal_code
405        AND    C1.EXCEPTION_SET_ID = except_id
406        AND    C2.EXCEPTION_SET_ID = except_id
407        AND    C3.EXCEPTION_SET_ID = except_id
408        AND    C3.CALENDAR_DATE= W1.WEEK_START_DATE
409        AND    C2.CALENDAR_DATE = W1.NEXT_DATE
410        AND    (C1.CALENDAR_DATE >= C3.CALENDAR_DATE
411        AND    C1.CALENDAR_DATE >= effect_date
412        AND    C1.CALENDAR_DATE < C2.CALENDAR_DATE)
413        GROUP BY C3.CALENDAR_DATE
414        UNION
415        SELECT 3,
416               TO_NUMBER(TO_CHAR(C3.CALENDAR_DATE,'J')),
417               ROUND(SUM(NVL(F.FORECAST_MAD, 0)/(C2.NEXT_SEQ_NUM -		-- 6797274  changes
418                               C3.NEXT_SEQ_NUM)) * srv_factor, 5)
419        FROM   BOM_CALENDAR_DATES C1, BOM_CALENDAR_DATES C2,
420               BOM_CALENDAR_DATES C3,
421               BOM_PERIOD_START_DATES W1, MRP_FORECAST_DATES F,
422               MRP_FORECAST_DESIGNATORS D1
423        WHERE  D1.ORGANIZATION_ID = org_id
424        AND    D1.FORECAST_DESIGNATOR = forc_name
425        AND    F.ORGANIZATION_ID = org_id
426        AND    F.INVENTORY_ITEM_ID = item_id
427        AND    F.FORECAST_DESIGNATOR = D1.FORECAST_DESIGNATOR
428        AND    F.BUCKET_TYPE = 3
429        AND    NVL(F.ORIGINATION_TYPE, -1) = 5					-- 6797274  changes
430        AND    W1.CALENDAR_CODE = cal_code
431        AND    W1.EXCEPTION_SET_ID = except_id
432        AND    (W1.PERIOD_START_DATE >= F.FORECAST_DATE
433        AND    W1.PERIOD_START_DATE <= NVL(F.RATE_END_DATE, F.FORECAST_DATE))
434        AND    W1.NEXT_DATE > effect_date
435        AND    C1.CALENDAR_CODE = cal_code
436        AND    C2.CALENDAR_CODE = cal_code
437        AND    C3.CALENDAR_CODE = cal_code
438        AND    C1.EXCEPTION_SET_ID = except_id
439        AND    C2.EXCEPTION_SET_ID = except_id
440        AND    C3.EXCEPTION_SET_ID = except_id
441        AND    C3.CALENDAR_DATE= W1.PERIOD_START_DATE
442        AND    C2.CALENDAR_DATE = W1.NEXT_DATE
443        AND    (C1.CALENDAR_DATE >= C3.CALENDAR_DATE
447        ORDER BY 2;
444        AND    C1.CALENDAR_DATE >= effect_date
445        AND    C1.CALENDAR_DATE < C2.CALENDAR_DATE)
446        GROUP BY C3.CALENDAR_DATE
448 
449     CURSOR SelForecast_userper IS						-- 6797274  Changes Start
450        SELECT 1,
451               TO_NUMBER(TO_CHAR(C1.CALENDAR_DATE,'J')),
452               ROUND(SUM(F.ORIGINAL_FORECAST_QUANTITY)* ss_percent/100 , 5)
453        FROM
454               BOM_CALENDAR_DATES        C1,
455               MRP_FORECAST_DESIGNATORS  D1,
456               MRP_FORECAST_DATES        F
457        WHERE  D1.ORGANIZATION_ID = org_id
458        AND    D1.FORECAST_DESIGNATOR = forc_name
459        AND    F.ORGANIZATION_ID = org_id
460        AND    F.INVENTORY_ITEM_ID = item_id
461        AND    F.FORECAST_DESIGNATOR = D1.FORECAST_DESIGNATOR
462        AND    F.BUCKET_TYPE = 1
463        AND    C1.CALENDAR_CODE = cal_code
464        AND    C1.EXCEPTION_SET_ID = except_id
465        AND    (C1.CALENDAR_DATE >= F.FORECAST_DATE
466        AND    C1.CALENDAR_DATE >= effect_date
467        AND    C1.CALENDAR_DATE <= NVL(F.RATE_END_DATE, F.FORECAST_DATE))
468        GROUP BY C1.CALENDAR_DATE
469        UNION
470        SELECT 2,
471               TO_NUMBER(TO_CHAR(C1.CALENDAR_DATE,'J')),
472               ROUND(SUM(ORIGINAL_FORECAST_QUANTITY/
473                      (C2.NEXT_SEQ_NUM - C3.NEXT_SEQ_NUM))* ss_percent/100 ,5)
474        FROM   BOM_CALENDAR_DATES C1, BOM_CALENDAR_DATES C2,
475               BOM_CALENDAR_DATES C3,
476               BOM_CAL_WEEK_START_DATES W1, MRP_FORECAST_DATES F,
477               MRP_FORECAST_DESIGNATORS D1
478        WHERE  D1.ORGANIZATION_ID = org_id
479        AND    D1.FORECAST_DESIGNATOR = forc_name
480        AND    F.ORGANIZATION_ID = org_id
481        AND    F.INVENTORY_ITEM_ID = item_id
482        AND    F.FORECAST_DESIGNATOR = D1.FORECAST_DESIGNATOR
483        AND    F.BUCKET_TYPE = 2
484        AND    W1.CALENDAR_CODE = cal_code
485        AND    W1.EXCEPTION_SET_ID = except_id
486        AND    (W1.WEEK_START_DATE >= F.FORECAST_DATE
487        AND    W1.WEEK_START_DATE <= NVL(F.RATE_END_DATE, F.FORECAST_DATE))
488        AND    W1.NEXT_DATE > effect_date
489        AND    C1.CALENDAR_CODE = cal_code
490        AND    C2.CALENDAR_CODE = cal_code
491        AND    C3.CALENDAR_CODE = cal_code
492        AND    C1.EXCEPTION_SET_ID = except_id
493        AND    C2.EXCEPTION_SET_ID = except_id
494        AND    C3.EXCEPTION_SET_ID = except_id
495        AND    C3.CALENDAR_DATE= W1.WEEK_START_DATE
496        AND    C2.CALENDAR_DATE = W1.NEXT_DATE
497        AND    (C1.CALENDAR_DATE >= C3.CALENDAR_DATE
498        AND    C1.CALENDAR_DATE >= effect_date
499        AND    C1.CALENDAR_DATE < C2.CALENDAR_DATE)
500        GROUP BY C1.CALENDAR_DATE
501        UNION
502        SELECT 3,
503               TO_NUMBER(TO_CHAR(C1.CALENDAR_DATE,'J')),
504               ROUND(SUM(ORIGINAL_FORECAST_QUANTITY/
505                      (C2.NEXT_SEQ_NUM - C3.NEXT_SEQ_NUM))* ss_percent/100 ,5)
506        FROM   BOM_CALENDAR_DATES C1, BOM_CALENDAR_DATES C2,
507               BOM_CALENDAR_DATES C3,
508               BOM_PERIOD_START_DATES W1, MRP_FORECAST_DATES F,
509               MRP_FORECAST_DESIGNATORS D1
510        WHERE  D1.ORGANIZATION_ID = org_id
511        AND    D1.FORECAST_DESIGNATOR = forc_name
512        AND    F.ORGANIZATION_ID = org_id
513        AND    F.INVENTORY_ITEM_ID = item_id
514        AND    F.FORECAST_DESIGNATOR = D1.FORECAST_DESIGNATOR
515        AND    F.BUCKET_TYPE = 3
516        AND    W1.CALENDAR_CODE = cal_code
517        AND    W1.EXCEPTION_SET_ID = except_id
518        AND    (W1.PERIOD_START_DATE >= F.FORECAST_DATE
519        AND    W1.PERIOD_START_DATE <= NVL(F.RATE_END_DATE, F.FORECAST_DATE))
520        AND    W1.NEXT_DATE > effect_date
521        AND    C1.CALENDAR_CODE = cal_code
522        AND    C2.CALENDAR_CODE = cal_code
523        AND    C3.CALENDAR_CODE = cal_code
524        AND    C1.EXCEPTION_SET_ID = except_id
525        AND    C2.EXCEPTION_SET_ID = except_id
526        AND    C3.EXCEPTION_SET_ID = except_id
527        AND    C3.CALENDAR_DATE= W1.PERIOD_START_DATE
528        AND    C2.CALENDAR_DATE = W1.NEXT_DATE
529        AND    (C1.CALENDAR_DATE >= C3.CALENDAR_DATE
530        AND    C1.CALENDAR_DATE >= effect_date
531        AND    C1.CALENDAR_DATE < C2.CALENDAR_DATE)
532        GROUP BY C1.CALENDAR_DATE
533        ORDER BY 2;										-- 6797274  Changes End
534 
535         --Added as a part of bug # 5718937
536        CURSOR c_bucket_type (cp_forecast IN VARCHAR2) IS
537          SELECT BUCKET_TYPE
538          FROM   MRP_FORECAST_DESIGNATORS
539          WHERE  FORECAST_DESIGNATOR = cp_forecast;
540 
541        CURSOR QTY_cur IS
542 		SELECT SAFETY_STOCK_QUANTITY
543 	       	FROM   MTL_SAFETY_STOCKS
544        		WHERE  ORGANIZATION_ID = org_id
545        		AND    INVENTORY_ITEM_ID = item_id
546        		AND    EFFECTIVITY_DATE = (
547        		SELECT MAX(EFFECTIVITY_DATE)
548        		FROM   MTL_SAFETY_STOCKS
549        		WHERE  ORGANIZATION_ID = org_id
550        		AND    INVENTORY_ITEM_ID = item_id
551        		AND    EFFECTIVITY_DATE < effect_date);
552 
553        pro_fdate	NUMBER;
554        pro_sdate	NUMBER;
555        pro_fqty		NUMBER;
556        j_effect_date	NUMBER;
557        forc_type	NUMBER;
558        ss_date		NUMBER;
559        forc_date	NUMBER;
560        ss_qty		NUMBER;
561        forc_qty		NUMBER;
562        pro_sqty		NUMBER;
563        bucket_type      MRP_FORECAST_DESIGNATORS.BUCKET_TYPE%TYPE; -- Bug # 5718937
564        l_mad_calc       BOOLEAN;                                   -- Bug # 5718937
565 
566        next_date        DATE;
567 
568     BEGIN
569 
570     /********************************************************/
571     /* Select calendar Code and Exception Set Id for future */
575 
572     /* use so that the SQL statements do not have to join   */
573     /* with MTL_PARAMETERS table.                           */
574     /********************************************************/
576        SELECT TO_NUMBER(TO_CHAR(effect_date, 'J'))
577        INTO   j_effect_date
578        FROM   DUAL;
579 
580     /********************************************************/
581     /* pro_sqty: Processing Safety Stock Qty.               */
582     /* pro_sdate: Processing Safety Stock Effectivity Date. */
583     /* This means the last stored safety stock date and qty */
584     /* Initialize the last stored safety stock date and qty */
585     /* pro_sqty is set to the last safety stock qty, while  */
586     /* pro_sdate is set to the date before the effect_date. */
587     /* If there is no safety stock record, pro_sqty is set  */
588     /* to 0.                                                */
589     /********************************************************/
590 
591        pro_sdate := j_effect_date;
592 
593        OPEN QTY_cur;
594        FETCH QTY_cur INTO pro_sqty;
595        IF QTY_cur%NOTFOUND THEN
596           pro_sqty := 0;
597        END IF;
598        CLOSE QTY_cur;
599 
600     /********************************************************/
601     /* Delete all the records since the effect_date, and    */
602     /* prepare to reload them.                              */
603     /********************************************************/
604 
605        DELETE FROM MTL_SAFETY_STOCKS
606        WHERE  ORGANIZATION_ID = org_id
607        AND    INVENTORY_ITEM_ID = item_id
608        AND    EFFECTIVITY_DATE >= effect_date;
609 
610 
611     /********************************************************/
612     /* The following opens the cursor and select the        */
613     /* forecast results into the variables.                 */
614     /* If there is no row selected, it implies that there   */
615     /* is no forecast qty.  In this case, the user          */
616     /* exit will insert one row into the MTL_SAFETY_STOCKS  */
617     /* table (ie, qty = 0 and effectivety_date).            */
618     /********************************************************/
619     /********************************************************/
620     /* In contrast to the pro_sdate and pro_sqty, pro_fdate */
621     /* and pro_fqty are defined as the processing forecast  */
622     /* qty and date respetively.  The forecast qty and date */
623     /* are selected from the MRP_FORECAST_DATES.            */
624     /* Initialize the pro_fdate and pro_fqty.               */
625     /* pro_fdate <- effect_date                             */
626     /* pro_fqty <- 0                                        */
627     /*                                                      */
628     /* The following variables are notable.                 */
629     /* ss_date: safety stock dates.                         */
630     /* ss_qty:  safety stock qty.                           */
631     /********************************************************/
632 
633      -- start Bug # 5718937
634        l_mad_calc := FALSE;
635        OPEN c_bucket_type (forc_name);
636        FETCH c_bucket_type INTO bucket_type;
637        IF c_bucket_type%FOUND AND bucket_type <> 1 THEN
638           l_mad_calc := TRUE;
639        END IF;
640        -- end Bug # 5718937
641 
642        if ss_code = 3 then			-- 6797274  changes start
643          OPEN SelForecast_mad;
644        else
645 	 OPEN SelForecast_userper;
646        end if;					-- 6797274  changes end
647 
648        pro_fdate := j_effect_date;
649        pro_fqty := 0;
650        loop
651          if ss_code = 3 then			-- 6797274  changes
652     	   FETCH SelForecast_mad INTO forc_type, forc_date, forc_qty;
653            exit when (SelForecast_mad%NOTFOUND);
654          else
655     	   FETCH SelForecast_userper INTO forc_type, forc_date, forc_qty;
656            exit when (SelForecast_userper%NOTFOUND);
657          end if;				-- 6797274  changes end
658 
659 	 --Added IF for bug # 5718937
660          IF ss_code = 3 and l_mad_calc THEN	-- 6797274  changes
661 
662 	/*  Added the below query for bug # 10387262 and prepare to reload them.*/
663 
664 		DELETE FROM MTL_SAFETY_STOCKS
665                  WHERE  ORGANIZATION_ID = org_id
666                    AND    INVENTORY_ITEM_ID = item_id
667                    AND    EFFECTIVITY_DATE >=  to_date(forc_date,'J');
668 
669 
670             Insert_Safety_Stocks(org_id, item_id, ss_code, forc_name,
671                                  ss_percent, srv_level, to_date(forc_date,'J'), forc_qty,
672                                  login_id, user_id);
673             pro_sdate := forc_date + 1;
674 
675 	 ELSE
676 
677          if pro_fdate = forc_date then
678             pro_fqty := pro_fqty + forc_qty;
679          elsif (pro_fdate <> pro_sdate or
680                  pro_fqty <> pro_sqty) then
681             if pro_fdate <> pro_sdate then
682                ss_date := pro_sdate;
683                ss_qty := 0;
684                -- Changes for Bug 3146158
685                next_date := MRP_CALENDAR.NEXT_WORK_DAY(org_id, 1 , to_date(ss_date,'J'));
686                if (next_date = to_date(ss_date,'J')) then
687                   Insert_Safety_Stocks(org_id, item_id, ss_code, forc_name,
688                        		       ss_percent, srv_level, to_date(ss_date,'J'), ss_qty,
689                                        login_id, user_id);
690                end if;
691             end if;
692             pro_sdate := pro_fdate +1 ;
693             pro_sqty := pro_fqty;
694             ss_date := pro_fdate;
695             ss_qty := pro_fqty;
696             pro_fdate := forc_date;
697             pro_fqty := forc_qty;
698             Insert_Safety_Stocks(org_id, item_id, ss_code, forc_name,
702              pro_fdate := forc_date;
699                     		 ss_percent, srv_level, to_date(ss_date,'J'), ss_qty,
700                                  login_id, user_id);
701          else
703              pro_fqty := forc_qty;
704              pro_sdate := pro_sdate + 1;
705          end if;
706       END IF;
707      end loop;
708 
709     /********************************************************/
710     /* Test and insert the last one which has the forecast  */
711     /* qty and date, if necessary.                          */
712     /********************************************************/
713 
714 				-- 6797274  changes for below if condition
715        if (ss_code <> 3 and SelForecast_userper%ROWCOUNT > 0) or (ss_code = 3 and SelForecast_mad%ROWCOUNT > 0 AND (NOT l_mad_calc )) then /* at least one row selected */ -- Modified for Bug # 5718937
716          if(pro_fdate <> pro_sdate or
717             pro_fqty <> pro_sqty) then
718            if pro_fdate <> pro_sdate then
719               ss_date := pro_sdate;
720               ss_qty := 0;
721               -- Changes for Bug 3146158
722               next_date := MRP_CALENDAR.NEXT_WORK_DAY(org_id, 1 , to_date(ss_date,'J'));
723               if (next_date = to_date(ss_date,'J')) then
724                  Insert_Safety_Stocks(org_id, item_id, ss_code, forc_name,
725                   		      ss_percent, srv_level, to_date(ss_date,'J'), ss_qty,
726                                       login_id, user_id);
727               end if;
728            end if;
729            pro_sdate := pro_fdate +1 ;
730            pro_sqty := pro_fqty;
731            ss_date := pro_fdate;
732            ss_qty := pro_fqty;
733            Insert_Safety_Stocks(org_id, item_id, ss_code, forc_name,
734                   		ss_percent, srv_level, to_date(ss_date,'J'), ss_qty,
735                                 login_id, user_id);
736         else
737            pro_sdate := pro_sdate + 1;
738         end if;
739       END IF;
740       if ss_code = 3 then		-- 6797274  changes
741         CLOSE SelForecast_mad;
742       else
743         CLOSE SelForecast_userper;
744       end if ;				-- 6797274  changes End
745 
746     /********************************************************/
747     /* Insert the very last one whose qty is 0 and date is  */
748     /* the next day of the last day which has forecast qty. */
749     /********************************************************/
750        ss_date := pro_sdate;
751        ss_qty := 0;
752        -- Changes for Bug 3146158
753        next_date := MRP_CALENDAR.NEXT_WORK_DAY(org_id, 1 , to_date(ss_date,'J'));
754        if (next_date <> to_date(ss_date,'J')) then
755            ss_date := TO_NUMBER(TO_CHAR(next_date, 'J'));
756        end if;
757        Insert_Safety_Stocks(org_id, item_id, ss_code, forc_name,
758                             ss_percent, srv_level, to_date(ss_date,'J'), ss_qty,
759                             login_id, user_id);
760 
761    END Main;
762 
763 
764     /********************************************************/
765     /* Build a dynamic SQL to insert into the MTL_SAFETY_   */
766     /* STOCKS table.                                        */
767     /* ss_code = 1 :User-defined quantity                   */
768     /*         = 2 :User-defined percentage                 */
769     /*         = 3 :Mean absolute deviation (MAD)           */
770     /********************************************************/
771 
772     procedure Insert_Safety_Stocks (org_id NUMBER,
773 				    item_id NUMBER,
774 				    ss_code NUMBER,
775 				    forc_name VARCHAR2,
776                       		    ss_percent NUMBER,
777                       		    srv_level NUMBER,
778                       		    ss_date DATE,
779                                     ss_qty NUMBER,
780 				    login_id NUMBER,
781 				    user_id NUMBER) IS
782 
783 
784 
785     BEGIN
786       INSERT INTO MTL_SAFETY_STOCKS(
787        			EFFECTIVITY_DATE,SAFETY_STOCK_QUANTITY,
788        			SAFETY_STOCK_PERCENT, LAST_UPDATE_DATE,
789        			SERVICE_LEVEL, CREATION_DATE, LAST_UPDATED_BY,
790       			CREATED_BY, LAST_UPDATE_LOGIN, ORGANIZATION_ID,
791       			INVENTORY_ITEM_ID, SAFETY_STOCK_CODE,
792        			FORECAST_DESIGNATOR)
793       VALUES(
794        		ss_date, ss_qty,
795                 ss_percent, SYSDATE,
796 		srv_level, SYSDATE, user_id,
797 		user_id, login_id,
798   		org_id, item_id, ss_code, forc_name);
799    END Insert_Safety_Stocks;
800 
801 
802 
803 	/******* CALCULATE SAFETY FACTOR FOR SAFETY STOCK *******/
804 	/*							*/
805 	/* The safety factor calculation is based on piecewise	*/
806 	/* linear interpolation.				*/
807 	/* Interpolation:					*/
808 	/* y = (y2 - y1)/(x2 - x1) * (x - x1) + y1		*/
809 	/*							*/
810 	/********************************************************/
811 
812      FUNCTION CalSF(service_level NUMBER)
813        RETURN NUMBER IS
814 
815 	safety_factor	NUMBER;
816 
817       BEGIN
818 	IF 50 <= service_level THEN
819   	   safety_factor := 0;
820         end if;
821 	IF 60 <= service_level THEN
822   	   safety_factor := 0.253*(service_level-50)/10;
823         end if;
824 	IF 70 <= service_level THEN
825   	   safety_factor := (0.525-0.253)*(service_level-60)/10+0.253;
826         end if;
827 	IF 80 <= service_level THEN
828   	   safety_factor := (0.84-0.525)*(service_level-70)/10+0.525;
829         end if;
830 	IF 86 <= service_level THEN
831   	   safety_factor := (1.08-0.84)*(service_level-80)/6+0.84;
832         end if;
833 	IF 90 <= service_level THEN
834   	   safety_factor := (1.28-1.08)*(service_level-86)/4+1.08;
835         end if;
836 	IF 94 <= service_level THEN
840   	   safety_factor := (1.88-1.555)*(service_level-94)/3+1.555;
837   	   safety_factor := (1.555-1.28)*(service_level-90)/4+1.28;
838         end if;
839 	IF 97 <= service_level THEN
841         end if;
842 	IF 98 <= service_level THEN
843   	   safety_factor := (2.055-1.88)*(service_level-97)+1.88;
844         end if;
845 	IF 99 <= service_level THEN
846   	   safety_factor := (2.33-2.055)*(service_level-98)+2.055;
847         end if;
848 	IF 99.36 <= service_level THEN
849   	   safety_factor := (2.49-2.33)*(service_level-99)/0.36+2.33;
850         end if;
851 	IF 99.56 <= service_level THEN
852   	   safety_factor := (2.62-2.49)*(service_level-99.36)/0.2+2.49;
853         end if;
854 	IF 99.66 <= service_level THEN
855   	   safety_factor := (2.706-2.62)*(service_level-99.56)/0.1+2.62;
856         end if;
857 	IF 99.76 <= service_level THEN
858   	   safety_factor := (2.82-2.706)*(service_level-99.66)/0.1+2.706;
859         end if;
860 	IF 99.83 <= service_level THEN
861   	   safety_factor := (2.93-2.82)*(service_level-99.76)/0.07+2.82;
862         end if;
863 	IF 99.83 <= service_level THEN
864   	   safety_factor := (2.93-2.82)*(service_level-99.76)/0.07+2.82;
865         end if;
866 	IF 99.875 <= service_level THEN
867   	   safety_factor := (3-2.93)*(service_level-99.83)/0.045+2.93;
868         end if;
869 	IF 99.9032 <= service_level THEN
870   	   safety_factor := (3.1-3)*(service_level-99.875)/(99.9032-99.875)+3;
871         end if;
872         IF 99.9313 <= service_level THEN
873   	   safety_factor := (3.2-3.1)*(service_level-99.9032)/(99.9313-99.9032)+3.1;
874         end if;
875         IF 99.9517 <= service_level THEN
876   	   safety_factor := (3.3-3.2)*(service_level-99.9313)/(99.9517-99.9313)+3.2;
877         end if;
878         IF 99.9663 <= service_level THEN
879   	   safety_factor := (3.4-3.3)*(service_level-99.9517)/(99.9663-99.9517)+3.3;
880         end if;
881         IF 99.9767 <= service_level THEN
882   	   safety_factor := (3.5-3.4)*(service_level-99.9663)/(99.9767-99.9663)+3.4;
883         end if;
884         IF 99.9841 <= service_level THEN
885   	   safety_factor := (3.6-3.5)*(service_level-99.9767)/(99.9841-99.9767)+3.5;
886         end if;
887         IF 99.992 <= service_level THEN
888   	   safety_factor := (3.7-3.6)*(service_level-99.9841)/(99.992-99.9841)+3.6;
889         end if;
890         IF 100 <= service_level THEN
891   	   safety_factor := (4-3.7)*(service_level-99.992)/(100-99.992)+3.7;
892         end if;
893 	return(safety_factor);
894       END CalSF;
895 
896 
897 END MTL_SAFETY_STOCKS_PKG;