1 PACKAGE BODY MTL_SAFETY_STOCKS_PKG as
2 /* $Header: INVDDFSB.pls 120.6 2008/02/18 09:12:05 athammin noship $ */
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
322 /********************************************************/
326 /********************************************************/
323 /* Select calendar Code and Exception Set Id for future */
324 /* use so that the SQL statements do not have to join */
325 /* with MTL_PARAMETERS table. */
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
440 AND C3.EXCEPTION_SET_ID = except_id
437 AND C3.CALENDAR_CODE = cal_code
438 AND C1.EXCEPTION_SET_ID = except_id
439 AND C2.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
444 AND C1.CALENDAR_DATE >= effect_date
445 AND C1.CALENDAR_DATE < C2.CALENDAR_DATE)
446 GROUP BY C3.CALENDAR_DATE
447 ORDER BY 2;
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
556 j_effect_date NUMBER;
553 pro_fdate NUMBER;
554 pro_sdate NUMBER;
555 pro_fqty 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 */
572 /* use so that the SQL statements do not have to join */
573 /* with MTL_PARAMETERS table. */
574 /********************************************************/
575
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 Insert_Safety_Stocks(org_id, item_id, ss_code, forc_name,
662 ss_percent, srv_level, to_date(forc_date,'J'), forc_qty,
663 login_id, user_id);
664 pro_sdate := forc_date + 1;
665
666 ELSE
667
668 if pro_fdate = forc_date then
669 pro_fqty := pro_fqty + forc_qty;
670 elsif (pro_fdate <> pro_sdate or
671 pro_fqty <> pro_sqty) then
672 if pro_fdate <> pro_sdate then
673 ss_date := pro_sdate;
674 ss_qty := 0;
675 -- Changes for Bug 3146158
679 ss_percent, srv_level, to_date(ss_date,'J'), ss_qty,
676 next_date := MRP_CALENDAR.NEXT_WORK_DAY(org_id, 1 , to_date(ss_date,'J'));
677 if (next_date = to_date(ss_date,'J')) then
678 Insert_Safety_Stocks(org_id, item_id, ss_code, forc_name,
680 login_id, user_id);
681 end if;
682 end if;
683 pro_sdate := pro_fdate +1 ;
684 pro_sqty := pro_fqty;
685 ss_date := pro_fdate;
686 ss_qty := pro_fqty;
687 pro_fdate := forc_date;
688 pro_fqty := forc_qty;
689 Insert_Safety_Stocks(org_id, item_id, ss_code, forc_name,
690 ss_percent, srv_level, to_date(ss_date,'J'), ss_qty,
691 login_id, user_id);
692 else
693 pro_fdate := forc_date;
694 pro_fqty := forc_qty;
695 pro_sdate := pro_sdate + 1;
696 end if;
697 END IF;
698 end loop;
699
700 /********************************************************/
701 /* Test and insert the last one which has the forecast */
702 /* qty and date, if necessary. */
703 /********************************************************/
704
705 -- 6797274 changes for below if condition
706 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
707 if(pro_fdate <> pro_sdate or
708 pro_fqty <> pro_sqty) then
709 if pro_fdate <> pro_sdate then
710 ss_date := pro_sdate;
711 ss_qty := 0;
712 -- Changes for Bug 3146158
713 next_date := MRP_CALENDAR.NEXT_WORK_DAY(org_id, 1 , to_date(ss_date,'J'));
714 if (next_date = to_date(ss_date,'J')) then
715 Insert_Safety_Stocks(org_id, item_id, ss_code, forc_name,
716 ss_percent, srv_level, to_date(ss_date,'J'), ss_qty,
717 login_id, user_id);
718 end if;
719 end if;
720 pro_sdate := pro_fdate +1 ;
721 pro_sqty := pro_fqty;
722 ss_date := pro_fdate;
723 ss_qty := pro_fqty;
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 else
728 pro_sdate := pro_sdate + 1;
729 end if;
730 END IF;
731 if ss_code = 3 then -- 6797274 changes
732 CLOSE SelForecast_mad;
733 else
734 CLOSE SelForecast_userper;
735 end if ; -- 6797274 changes End
736
737 /********************************************************/
738 /* Insert the very last one whose qty is 0 and date is */
739 /* the next day of the last day which has forecast qty. */
740 /********************************************************/
741 ss_date := pro_sdate;
742 ss_qty := 0;
743 -- Changes for Bug 3146158
744 next_date := MRP_CALENDAR.NEXT_WORK_DAY(org_id, 1 , to_date(ss_date,'J'));
745 if (next_date <> to_date(ss_date,'J')) then
746 ss_date := TO_NUMBER(TO_CHAR(next_date, 'J'));
747 end if;
748 Insert_Safety_Stocks(org_id, item_id, ss_code, forc_name,
749 ss_percent, srv_level, to_date(ss_date,'J'), ss_qty,
750 login_id, user_id);
751
752 END Main;
753
754
755 /********************************************************/
756 /* Build a dynamic SQL to insert into the MTL_SAFETY_ */
757 /* STOCKS table. */
758 /* ss_code = 1 :User-defined quantity */
759 /* = 2 :User-defined percentage */
760 /* = 3 :Mean absolute deviation (MAD) */
761 /********************************************************/
762
763 procedure Insert_Safety_Stocks (org_id NUMBER,
764 item_id NUMBER,
765 ss_code NUMBER,
766 forc_name VARCHAR2,
770 ss_qty NUMBER,
767 ss_percent NUMBER,
768 srv_level NUMBER,
769 ss_date DATE,
771 login_id NUMBER,
772 user_id NUMBER) IS
773
774
775
776 BEGIN
777 INSERT INTO MTL_SAFETY_STOCKS(
778 EFFECTIVITY_DATE,SAFETY_STOCK_QUANTITY,
779 SAFETY_STOCK_PERCENT, LAST_UPDATE_DATE,
780 SERVICE_LEVEL, CREATION_DATE, LAST_UPDATED_BY,
781 CREATED_BY, LAST_UPDATE_LOGIN, ORGANIZATION_ID,
782 INVENTORY_ITEM_ID, SAFETY_STOCK_CODE,
783 FORECAST_DESIGNATOR)
784 VALUES(
785 ss_date, ss_qty,
786 ss_percent, SYSDATE,
787 srv_level, SYSDATE, user_id,
788 user_id, login_id,
789 org_id, item_id, ss_code, forc_name);
790 END Insert_Safety_Stocks;
791
792
793
794 /******* CALCULATE SAFETY FACTOR FOR SAFETY STOCK *******/
795 /* */
796 /* The safety factor calculation is based on piecewise */
797 /* linear interpolation. */
798 /* Interpolation: */
799 /* y = (y2 - y1)/(x2 - x1) * (x - x1) + y1 */
800 /* */
801 /********************************************************/
802
803 FUNCTION CalSF(service_level NUMBER)
804 RETURN NUMBER IS
805
806 safety_factor NUMBER;
807
808 BEGIN
809 IF 50 <= service_level THEN
810 safety_factor := 0;
811 end if;
812 IF 60 <= service_level THEN
813 safety_factor := 0.253*(service_level-50)/10;
814 end if;
818 IF 80 <= service_level THEN
815 IF 70 <= service_level THEN
816 safety_factor := (0.525-0.253)*(service_level-60)/10+0.253;
817 end if;
819 safety_factor := (0.84-0.525)*(service_level-70)/10+0.525;
820 end if;
821 IF 86 <= service_level THEN
822 safety_factor := (1.08-0.84)*(service_level-80)/6+0.84;
823 end if;
824 IF 90 <= service_level THEN
825 safety_factor := (1.28-1.08)*(service_level-86)/4+1.08;
826 end if;
827 IF 94 <= service_level THEN
828 safety_factor := (1.555-1.28)*(service_level-90)/4+1.28;
829 end if;
830 IF 97 <= service_level THEN
831 safety_factor := (1.88-1.555)*(service_level-94)/3+1.555;
832 end if;
833 IF 98 <= service_level THEN
834 safety_factor := (2.055-1.88)*(service_level-97)+1.88;
835 end if;
836 IF 99 <= service_level THEN
837 safety_factor := (2.33-2.055)*(service_level-98)+2.055;
838 end if;
839 IF 99.36 <= service_level THEN
840 safety_factor := (2.49-2.33)*(service_level-99)/0.36+2.33;
841 end if;
842 IF 99.56 <= service_level THEN
843 safety_factor := (2.62-2.49)*(service_level-99.36)/0.2+2.49;
844 end if;
845 IF 99.66 <= service_level THEN
846 safety_factor := (2.706-2.62)*(service_level-99.56)/0.1+2.62;
847 end if;
848 IF 99.76 <= service_level THEN
849 safety_factor := (2.82-2.706)*(service_level-99.66)/0.1+2.706;
850 end if;
851 IF 99.83 <= service_level THEN
852 safety_factor := (2.93-2.82)*(service_level-99.76)/0.07+2.82;
853 end if;
854 IF 99.83 <= service_level THEN
855 safety_factor := (2.93-2.82)*(service_level-99.76)/0.07+2.82;
856 end if;
857 IF 99.875 <= service_level THEN
858 safety_factor := (3-2.93)*(service_level-99.83)/0.045+2.93;
859 end if;
860 IF 99.9032 <= service_level THEN
861 safety_factor := (3.1-3)*(service_level-99.875)/(99.9032-99.875)+3;
862 end if;
863 IF 99.9313 <= service_level THEN
864 safety_factor := (3.2-3.1)*(service_level-99.9032)/(99.9313-99.9032)+3.1;
865 end if;
866 IF 99.9517 <= service_level THEN
867 safety_factor := (3.3-3.2)*(service_level-99.9313)/(99.9517-99.9313)+3.2;
868 end if;
869 IF 99.9663 <= service_level THEN
870 safety_factor := (3.4-3.3)*(service_level-99.9517)/(99.9663-99.9517)+3.3;
871 end if;
872 IF 99.9767 <= service_level THEN
873 safety_factor := (3.5-3.4)*(service_level-99.9663)/(99.9767-99.9663)+3.4;
874 end if;
875 IF 99.9841 <= service_level THEN
876 safety_factor := (3.6-3.5)*(service_level-99.9767)/(99.9841-99.9767)+3.5;
877 end if;
878 IF 99.992 <= service_level THEN
879 safety_factor := (3.7-3.6)*(service_level-99.9841)/(99.992-99.9841)+3.6;
880 end if;
881 IF 100 <= service_level THEN
882 safety_factor := (4-3.7)*(service_level-99.992)/(100-99.992)+3.7;
883 end if;
884 return(safety_factor);
885 END CalSF;
886
887
888 END MTL_SAFETY_STOCKS_PKG;