Loading...
Searching...
No Matches
postdata.sas
Go to the documentation of this file.
1/**
2 @file postdata.sas
3 @brief Either returns the file diffs or actually loads the data to target
4 @details Before loading the target, a check is made against the time the
5 target was last updated (backend) and the time the DIFF was generated
6 (frontend). If the target was updated whilst the DIFF was on the screen,
7 then the provided diff may have been incorrect and so a new DIFF should be
8 generated and approved before load.
9
10 Only 100 rows (of each DIFF type) are displayed on the DIFF screen.
11
12 <h4> Service Inputs </h4>
13
14 <h5> SASCONTROLTABLE </h5>
15 |ACTION:$char10.|TABLE:$char32.|DIFFTIME:$char29.|
16 |---|---|---|
17 |SHOW_DIFFS|DC20220208T142124517_124703_1184|"Tue, 08 Feb 2022 14:23:05 GMT"|
18
19 <h4> SAS Macros </h4>
20 @li bitemporal_dataloader.sas
21 @li dc_assignlib.sas
22 @li mf_existds.sas
23 @li mf_existvar.sas
24 @li mf_getattrn.sas
25 @li mf_getengine.sas
26 @li mf_getquotedstr.sas
27 @li mf_getuniquelibref.sas
28 @li mf_getuser.sas
29 @li mf_getvarlist.sas
30 @li mf_nobs.sas
31 @li mf_verifymacvars.sas
32 @li mp_abort.sas
33 @li mp_cntlout.sas
34 @li mp_lockanytable.sas
35 @li mpe_accesscheck.sas
36 @li mpe_alerts.sas
37 @li mpe_runhook.sas
38 @li mpe_targetloader.sas
39 @li removecolsfromwork.sas
40
41
42 @version 9.2
43 @author 4GL Apps Ltd
44 @copyright 4GL Apps Ltd. This code may only be used within Data Controller
45 and may not be re-distributed or re-sold without the express permission of
46 4GL Apps Ltd.
47
48**/
49
50/* this could be a config setting if required */
51%let maxdiff=100;
52
53%mpeinit()
54
55/* load parameters */
56data _null_;
57 set work.sascontroltable;
58 call symputx('ACTION',ACTION);
59 call symputx('LOAD_REF',TABLE);
60 /* DIFFTIME is when the DIFF was generated on the frontend */
61 call symputx('DIFFTIME',DIFFTIME);
62 putlog (_all_)(=);
63run;
64
65%global action is_err err_msg msg;
66%let is_err=0;
67
68%let user=%mf_getuser();
69%let sastime=%sysfunc(datetime());
70data sastime;
71 dt_sastime=&sastime;
72run;
73
74PROC FORMAT;
75 picture yymmddhhmmss other='%0Y-%0m-%0d %0H:%0M:%0S' (datatype=datetime);
76 picture flatdate other='%0Y%0m%0d_%0H%0M%0S' (datatype=datetime);
77RUN;
78
79/* SHOW_DIFFS works by getting the temp tables from the bitemporal loader */
80/* so we share much of the logic from the actual load process */
81%let isfmtcat=0;
82data APPROVE1;
83 set &mpelib..mpe_submit;
84 where TABLE_ID="&LOAD_REF";
85 /* fetch mpe_submit data */
86 libds=cats(base_lib,'.',base_ds);
87 REVIEWED_ON=put(reviewed_on_dttm,datetime19.);
88 call symputx('REVIEW_STATUS_ID',submit_status_cd,'l');
89 call symputx('NUM_OF_APPROVALS_REQUIRED',NUM_OF_APPROVALS_REQUIRED);
90 call symputx('num_of_approvals_remaining',num_of_approvals_remaining);
91
92 /* other stuff that's useful to do in data step */
93 call symputx('orig_libds',libds);
94 call symputx('libds',libds);
95 if substr(cats(reverse(libds)),1,3)=:'CF-' then do;
96 libds=scan(libds,1,'-');
97 putlog "Format Catalog Captured";
98 call symputx('isfmtcat',1);
99 libds='work.fmtextract';
100 call symputx('libds',libds);
101 end;
102 putlog (_all_)(=);
103 /* convert provided string DIFFTIME back to a numeric SAS datetime */
104 if "&action" ne "SHOW_DIFFS" then do;
105 call symputx('DIFFTIME',input(symget('DIFFTIME'),anydtdtm18.));
106 end;
107 length difftime $32;
108 DIFFTIME=put(&sastime,datetime19.2);
109run;
110
111%mp_cntlout(
112 iftrue=(&isfmtcat=1)
113 ,libcat=&orig_libds
114 ,fmtlist=0
115 ,cntlout=work.fmtextract
116)
117
118%mp_abort(
119 iftrue=(%mf_verifymacvars(difftime orig_libds libds load_ref)=0)
120 ,mac=&_program
121 ,msg=%str(Missing: difftime orig_libds libds load_ref)
122)
123
124/* security checks */
125%mpe_accesscheck(&orig_libds,outds=authEDIT,user=&user,access_level=EDIT)
126%mpe_accesscheck(&orig_libds,outds=authAPP,user=&user,access_level=APPROVE)
127
128%mp_abort(iftrue= (&syscc ne 0)
129 ,mac=&_program
130 ,msg=%str(syscc=&syscc Before entering postdata macro)
131)
132
133%mp_abort(
134 iftrue=(
135 %mf_getattrn(work.authEDIT,NLOBS)=0 & %mf_getattrn(work.authAPP,NLOBS)=0
136 )
137 ,mac=&_program
138 ,msg=%str(&user not authorised to view approval screen for &orig_libds)
139)
140
141%macro quickmacro(inds,outds);
142 data &outds ;
143 %if %length(&VAR_BUSFROM)>0 %then %do;
144 format &VAR_BUSFROM &VAR_BUSTO yymmddhhmmss.;
145 %end;
146 if 0 then set &emptybasetable;
147 set &inds;
148 %if %mf_existvar(&libds,&var_txfrom) %then %do;
149 drop &var_txfrom &var_txto;
150 %end;
151 %if %mf_existvar(&inds,_____DELETE__THIS__RECORD_____) %then %do;
152 drop _____DELETE__THIS__RECORD_____;
153 %end;
154 %if %mf_existvar(&inds,&VAR_PROCESSED) %then %do;
155 drop &VAR_PROCESSED;
156 %end;
157 run;
158%mend quickmacro;
159
160%macro postdata();
161
162%if %quote(&REVIEW_STATUS_ID)=%quote(REJECTED)
163 or %quote(&REVIEW_STATUS_ID)=%quote(APPROVED) %then
164%do;
165 data params; set approve1; run;
166 %webout(OPEN)
167 %webout(OBJ,PARAMS)
168 %webout(CLOSE)
169 %return;
170%end;
171
172%mp_abort(iftrue= (&syscc ne 0)
173 ,mac=&_program..sas
174 ,msg=%str(syscc=&syscc)
175)
176
177%if &action=APPROVE_TABLE %then %do;
178
179 /* check user is authorised to approve table */
180 /* user could be an editor but not an approver */
181 %mp_abort(iftrue= (%mf_getattrn(work.authAPP,NLOBS)=0)
182 ,mac=&_program
183 ,msg=%str(&user may not APPROVE changes)
184 )
185
186 /* see if this user has already submitted an approval */
187 %let prev_upload_check=1;
188 proc sql;
189 select count(*) into: prev_upload_check from &mpelib..mpe_review
190 where TABLE_ID="&LOAD_REF" and REVIEWED_BY_NM="&user"
191 and REVIEW_STATUS_ID ne "SUBMITTED";
192 %let authcheck=%mf_getattrn(work.authAPP,NLOBS);
193 %if &authcheck=0 or &prev_upload_check=1 %then %do;
194 %put %str(WARN)ING: &=authcheck &=prev_upload_check;
195 data apPARAMS;
196 AUTHORISED=&authcheck;
197 PREV_UPLOAD_CHECK=&prev_upload_check;
198 run;
199 %webout(OPEN)
200 %webout(OBJ,apPARAMS);
201 %webout(CLOSE)
202 %return;
203 %end;
204
205 /* now check if table has been updated since DIFF screen shown */
206 %local fmt_tm usernm last_load etlsource;
207 %let last_load=0;
208 proc sql noprint;
209 select max(processed_dttm) format=16.2 into: last_load
210 from &mpelib..mpe_dataloads
211 where libref="%scan(&orig_libds,1,.)" and dsn="%scan(&orig_libds,2,.)";
212 select processed_dttm format=datetime19., user_nm, etlsource
213 into: fmt_tm, :usernm, :etlsource
214 from &mpelib..mpe_dataloads
215 where libref="%scan(&orig_libds,1,.)" and dsn="%scan(&orig_libds,2,.)"
216 and processed_dttm=&last_load;
217 %put TIMECHECK: &last_load>&difftime;
218 %if %sysevalf(&last_load>&difftime,boolean)=1 %then %do;
219 %let is_err=1;
220 %let err_msg=&orig_libds was updated in batch %trim(&etlsource
221 ) by %trim(&usernm) on &fmt_tm - please refresh the page!!;
222 %return;
223 %end;
224 %if &syscc ne 0 %then %do;
225 %let is_err=1;
226 %let err_msg=syscc=&syscc before logchange;
227 %return;
228 %end;
229
230 /* upload about to commence so ensure logs */
231 options notes mprint source2;
232 %local oldloc;
233 %if %symexist(SYSPRINTTOLOG) %then %let oldloc=&SYSPRINTTOLOG;
234 %else %let oldloc=%qsysfunc(getoption(LOG));
235 %if %length(&oldloc)>0 %then %do;
236 proc printto
237 log="&mpelocapprovals/&LOAD_REF/approval.log";
238 run;
239 data _null_;
240 if _n_=1 then do;
241 length oldloc $1000;
242 oldloc=symget('oldloc');
243 putlog "****** redirected:" oldloc " *****";
244 end;
245 infile &oldloc;
246 input; putlog _infile_;
247 run;
248 %end;
249 %else %do;
250 proc printto
251 log="&mpelocapprovals/&LOAD_REF/approval.log";
252 run;
253 %end;
254
255 %if &syscc ne 0 %then %do;
256 %let is_err=1;
257 %let err_msg=syscc=&syscc after logchange;
258 %return;
259 %end;
260%end;
261
262/**
263 * upload the actual table
264 */
265%local libref ds;
266%let libref=%scan(&orig_libds,1,.);
267%let ds=%scan(&orig_libds,2,.);
268
269proc sql noprint;
270select PRE_APPROVE_HOOK, POST_APPROVE_HOOK, LOADTYPE, var_txfrom, var_txto
271 ,BUSKEY, VAR_BUSFROM, VAR_BUSTO
272 ,AUDIT_LIBDS, NOTES, coalesce(NUM_OF_APPROVALS_REQUIRED,1)
273 ,VAR_PROCESSED
274 into: PRE_APPROVE_HOOK, :POST_APPROVE_HOOK, :LOADTYPE,:var_txfrom,:var_txto
275 ,:BUSKEY,:VAR_BUSFROM,:VAR_BUSTO
276 ,:AUDIT_LIBDS, :TABLE_DESC, :NUM_OF_APPROVALS_REQUIRED_TOT
277 ,:VAR_PROCESSED
278 from &mpelib..mpe_tables
279 where &dc_dttmtfmt. lt tx_to
280 and libref="&libref"
281 and dsn="&ds";
282
283%mp_abort(
284 iftrue=(%mf_verifymacvars(mpelocapprovals orig_libds)=0)
285 ,mac=&_program
286 ,msg=%str(Missing: mpelocapprovals orig_libds)
287)
288
289/* get dataset from approvals location (has same name as load_ref) */
290%let tmplib=%mf_getuniquelibref();
291libname &tmplib "&mpelocapprovals/&LOAD_REF";
292data STAGING_DS;
293 set &tmplib..&LOAD_REF;
294run;
295
296%mp_abort(iftrue= (&syscc ne 0)
297 ,mac=&_program..sas
298 ,msg=%str(syscc=&syscc before preapprove)
299)
300
301%dc_assignlib(WRITE,&libref)
302
303/* run pre-approve hook - occurs both BEFORE _and_ AFTER the diff */
304%mpe_runhook(PRE_APPROVE_HOOK)
305
306%mp_abort(iftrue= (&syscc ne 0)
307 ,mac=&_program..sas
308 ,msg=%str(syscc=&syscc after preapprove)
309)
310
311%if &num_of_approvals_remaining>1 and &action=APPROVE_TABLE %then %do;
312
313 /* append to mpe_review table */
314 %let apprno=%eval(&num_of_approvals_required-&num_of_approvals_remaining+1);
315 data work.append_review;
316 if 0 then set &mpelib..mpe_review;
317 TABLE_ID="&LOAD_REF";
318 BASE_TABLE="&orig_libds";
319 REVIEW_STATUS_ID="APPROVED";
320 REVIEWED_BY_NM="&user";
321 REVIEWED_ON_DTTM=&sastime;
322 REVIEW_REASON_TXT="APPROVAL &apprno of &num_of_approvals_required";
323 output;
324 stop;
325 run;
326 %mp_lockanytable(LOCK,
327 lib=&mpelib,ds=mpe_review,ref=%str(&LOAD_REF Approval),
328 ctl_ds=&mpelib..mpe_lockanytable
329 )
330 proc append base=&mpelib..mpe_review data=work.append_review;
331 run;
332 %mp_lockanytable(UNLOCK,
333 lib=&mpelib,ds=mpe_review,
334 ctl_ds=&mpelib..mpe_lockanytable
335 )
336
337 /* update mpe_submit table */
338 %mp_lockanytable(LOCK,
339 lib=&mpelib,ds=mpe_submit,ref=%str(&LOAD_REF Approval),
340 ctl_ds=&mpelib..mpe_lockanytable
341 )
342 proc sql;
343 update &mpelib..mpe_submit
344 set num_of_approvals_remaining=&num_of_approvals_remaining-1,
345 reviewed_by_nm="&user",
346 reviewed_on_dttm=&sastime
347 where table_id="&LOAD_REF";
348 %mp_lockanytable(UNLOCK,
349 lib=&mpelib,ds=mpe_submit,
350 ctl_ds=&mpelib..mpe_lockanytable
351 )
352
353 data apReqd;
354 AUTHORISED=1;
355 ALREADY_UPDATED=0;
356 ALREADY_UPDATED_DTTM=.;
357 set approve1; /* js will test for NUM_OF_APPROVALS_REQUIRED */
358 run;
359 %removecolsfromwork(___TMP___MD5)
360 %webout(OPEN)
361 %webout(OBJ,apReqd);
362 %webout(CLOSE)
363 %return;
364
365%end;
366
367%mp_abort(iftrue= (&syscc ne 0)
368 ,mac=&_program..sas
369 ,msg=%str(syscc=&syscc entering TARGETLOADER)
370)
371%mpe_targetloader(libds=&orig_libds
372 ,now= &sastime
373 ,etlsource=&LOAD_REF
374 ,STAGING_DS=STAGING_DS
375 ,dclib=&mpelib
376%if &action=APPROVE_TABLE %then %do;
377 ,LOADTARGET=YES
378%end;
379%else %do;
380 ,LOADTARGET=NO
381%end;
382 ,dc_dttmtfmt=&dc_dttmtfmt.
383)
384
385
386%if %mf_getattrn(STAGING_DS,NLOBS)=0 %then %do;
387 /* empty dataset! */
388 data out;
389 set STAGING_DS;
390 run;
391 %return;
392%end;
393
394%mp_abort(iftrue= (&syscc ne 0)
395 ,mac=&_program..sas
396 ,msg=%str(syscc=&syscc entering SHOWDIFFS)
397)
398%if &action=SHOW_DIFFS %then %do;
399
400 /**
401 * Now prepare the SHOW DIFFS (approve) screen
402 */
403
404 /*To create the CURRENT diffs, we compare with the ACTUAL data. But first
405 need to find out what version TIME to query it for.. */
406 proc sql noprint;
407 select max(processed_dttm)-1 format=datetime19. into: tstamp
408 from &mpelib..mpe_dataloads
409 where libref="&libref" and dsn="&ds" and ETLSOURCE="&LOAD_REF";
410 quit;
411 %if &tstamp=. %then %let tstamp=%sysfunc(datetime(),datetime19.);
412
413 /**
414 * now create the DIFFS dataset
415 * If using a database, then utilise pass through!
416 * Create a temporary table inside the database for joins..
417 */
418 options mprint;
419 %let engine_type=%mf_getEngine(%scan(&libds,1,.));
420 %put &libds engine type = &engine_type;
421 %local inner_table ;
422 %if &engine_type=OLEDB %then %do;
423 /* generate a unique ID for the temporary table */
424 data _null_;
425 call symputx('UNIQUE_REF'
426 ,cats(round(datetime(),1)
427 ,'_'
428 ,round(ranuni(0)*100000,1)
429 )
430 ,'l'
431 );
432 run;
433 %let inner_table=&libref.."##DIFF_&UNIQUE_REF"n;
434 proc sql;
435 create table &inner_table as
436 select * from work.outds_mod;
437 %end;
438 %else %let inner_table=work.outds_mod;
439 proc sql;
440 create view work.originals2 as
441 select b.*
442 from &inner_table a
443 inner join &libds
444 %if &loadtype=BITEMPORAL or &loadtype=TXTEMPORAL %then %do;
445 (where=("&tstamp"dt < &VAR_TXTO))
446 %end;
447 b
448 on 1
449 %do idx_pk=1 %to %sysfunc(countw(&buskey));
450 %let idx_val=%scan(&buskey,&idx_pk);
451 and a.&idx_val=b.&idx_val
452 %end;
453 order by %mf_getquotedstr(in_str=&buskey,dlm=%str(,),quote=)
454 ;
455
456 create view bitemp5c_updates2 as
457 select * from work.outds_mod
458 order by %mf_getquotedstr(in_str=&buskey,dlm=%str(,),quote=)
459 ;
460
461 data; set &libds;stop;run;
462 %let emptybasetable=&syslast;
463
464 options varlenchk=nowarn; /* for small numerics (<8) */
465 %quickmacro(work.outds_del,deleted)
466 %quickmacro(work.outds_add,new)
467 %quickmacro(bitemp5c_updates2,updates)
468 %quickmacro(originals2,originals)
469
470 %mp_abort(iftrue= (&syscc ne 0)
471 ,mac=&_program..sas
472 ,msg=%str(syscc=&syscc in quickmacro)
473 )
474
475 /* extract colnames for md5 creation / change tracking */
476 proc contents noprint data=work.updates
477 out=cols (keep=name type length varnum format);
478 run;
479 proc sort data=cols out=cols(drop=varnum); by varnum;run;
480 data cols; set cols; name=upcase(name);run;
481 %let tempDIFFS_CSV=tempDiffs_%trim(
482 %sysfunc(datetime(),flatdate.)).csv;
483
484 /**
485 * Store temp tables so we have a record of diffs
486 * do not change this libname or table name as it is used in some
487 * post approve hooks
488 */
489
490 data TEMPDIFFS (compress=no) /* for realistic file size */;
491 length _____status $10;
492 set work.deleted (in=_____del)
493 work.new(in=_____new)
494 work.updates (in=_____upd)
495 work.originals2 (in=_____orig);
496 if _____del then _____status='DELETED ';
497 else if _____new then _____status='NEW';
498 else if _____upd then _____status='UPDATED';
499 else if _____orig then _____status='ORIGINAL';
500 run;
501 proc export data=TEMPDIFFS dbms=csv replace
502 outfile="&mpelocapprovals/&LOAD_REF/&tempDIFFS_CSV" ;
503 run;
504 proc sql noprint;
505 select filesize format=sizekmg10.1, filesize as filesize_raw
506 into: filesize,:filesize_raw
507 from dictionary.tables
508 where libname='WORK' and memtype='DATA' and memname='TEMPDIFFS';
509
510 data params;
511 set approve1;
512 DIFFS_CSV="&tempDIFFS_CSV";
513 FILESIZE="&filesize";
514 FILESIZE_RAW=&filesize_raw;
515 if %mf_nobs(work.originals)>&maxdiff
516 or %mf_nobs(work.new)>&maxdiff
517 or %mf_nobs(work.deleted)>&maxdiff
518 or %mf_nobs(work.updates)>&maxdiff
519 then TRUNCATED="YES";
520 else TRUNCATED="NO";
521
522 NUM_ADDED=%mf_getattrn(work.new,NLOBS);
523 NUM_DELETED=%mf_getattrn(work.deleted,NLOBS);
524 NUM_UPDATED=%mf_getattrn(work.updates,NLOBS);
525 SUBMITTED_ON=put(submitted_on_dttm,datetime19.);
526 %if %mf_getattrn(work.authAPP,NLOBS)>0 %then %do;
527 ISAPPROVER='YES';
528 %end;
529 %else %do;
530 ISAPPROVER='NO';
531 %end;
532 run;
533
534 /*
535 * The PRE_APPROVE_HOOK may have applied custom formats to the staged table.
536 * To ensure consistency in the DIFF screen, we should apply the same formats
537 * to the base table. Limit rows at the same time.
538 */
539 data work.originals;
540 if 0 then set deleted new updates;
541 set work.originals;
542 if _n_>&maxdiff then stop;
543 run;
544
545 /* get additional submits against the same base table */
546 proc sort data=&mpelib..mpe_submit(where=(
547 submit_status_cd='SUBMITTED'
548 and cats(base_lib,'.',base_ds)="&orig_libds"
549 and table_id ne "&LOAD_REF"
550 )) out=submits;
551 by descending submitted_on_dttm;
552 run;
553
554 /* filter last 10 */
555 data submits;
556 set submits;
557 if _n_>10 then stop;
558 run;
559
560 %mp_abort(iftrue= (&syscc ne 0)
561 ,mac=&_program..sas
562 ,msg=%str(syscc=&syscc SHOWDIFFS prior to streamout)
563 )
564
565 %removecolsfromwork(___TMP___MD5)
566 %webout(OPEN)
567 %webout(OBJ,params)
568 %webout(OBJ,cols)
569 %webout(OBJ,submits)
570 %webout(OBJ,deleted,fmt=N,missing=STRING,maxobs=&maxdiff)
571 %webout(OBJ,new,fmt=N,missing=STRING,maxobs=&maxdiff)
572 %webout(OBJ,updates,fmt=N,missing=STRING,maxobs=&maxdiff)
573 %webout(OBJ,ORIGINALS,fmt=N,missing=STRING)
574 /* need same for formatted view */
575 %webout(OBJ,deleted,dslabel=fmt_deleted,fmt=Y,missing=STRING,maxobs=&maxdiff)
576 %webout(OBJ,new,dslabel=fmt_new,fmt=Y,missing=STRING,maxobs=&maxdiff)
577 %webout(OBJ,updates,dslabel=fmt_updates,fmt=Y,missing=STRING,maxobs=&maxdiff)
578 %webout(OBJ,originals,dslabel=fmt_ORIGINALS,fmt=Y,missing=STRING)
579 %webout(CLOSE)
580
581 %if &engine_type=OLEDB %then %do;
582 proc sql; /* needs to be dropped AFTER view execution */
583 drop table &inner_table;
584 %end;
585
586 %return;
587%end;
588
589
590%if &action=APPROVE_TABLE %then %do;
591 %approve:
592 /**
593 * store temp tables so we have a record of diffs
594 * do not change this libname or table name as it is used in some
595 * post approve hooks
596 * for REPLACE loads, temp tables not made, so make them
597 */
598 %if &LOADTYPE=REPLACE %then %do;
599 data work.outds_add; run;
600 data work.outds_mod; run;
601 data work.outds_del; run;
602 %end;
603 libname approve "&mpelocapprovals/&LOAD_REF";
604 data; set &libds;stop;run;
605 %let emptybasetable=&syslast;
606 data approve.ActualDiffs;
607 length _____STATUS_____ $10;
608 if 0 then set &emptybasetable;
609 set work.outds_del (in=_____del)
610 work.outds_add (in=_____new)
611 work.outds_mod (in=_____upd);
612 if _____del then _____STATUS_____='DELETED';
613 else if _____new then _____STATUS_____='NEW';
614 else if _____upd then _____STATUS_____='UPDATED';
615
616 %if %mf_existvar(&libds,&var_txfrom) %then %do;
617 drop &var_txfrom &var_txto;
618 %end;
619 %if %mf_existvar(&libds,&VAR_PROCESSED) %then %do;
620 drop &VAR_PROCESSED;
621 %end;
622 run;
623
624 proc export data=approve.ActualDiffs
625 outfile="&mpelocapprovals/&LOAD_REF/ActualDiffs.csv"
626 dbms=csv
627 replace;
628 run;
629
630 /* update the control table to show table as approved */
631 /* append to mpe_review table */
632 %let apprno=%eval(&num_of_approvals_required-&num_of_approvals_remaining+1);
633 data work.append_review;
634 if 0 then set &mpelib..mpe_review;
635 TABLE_ID="&LOAD_REF";
636 BASE_TABLE="&orig_libds";
637 REVIEW_STATUS_ID="APPROVED";
638 REVIEWED_BY_NM="&user";
639 REVIEWED_ON_DTTM=&sastime;
640 REVIEW_REASON_TXT="APPROVAL &apprno of &num_of_approvals_required";
641 output;
642 stop;
643 run;
644 %mp_lockanytable(LOCK,
645 lib=&mpelib,ds=mpe_review,ref=%str(&LOAD_REF Approval),
646 ctl_ds=&mpelib..mpe_lockanytable
647 )
648 proc append base=&mpelib..mpe_review data=work.append_review;
649 run;
650 %mp_lockanytable(UNLOCK,
651 lib=&mpelib,ds=mpe_review,
652 ctl_ds=&mpelib..mpe_lockanytable
653 )
654
655 /* update mpe_submit table */
656 %mp_lockanytable(LOCK,
657 lib=&mpelib,ds=mpe_submit,ref=%str(&LOAD_REF Approval in auditors/postdata),
658 ctl_ds=&mpelib..mpe_lockanytable
659 )
660 proc sql;
661 update &mpelib..mpe_submit
662 set submit_status_cd='APPROVED',
663 num_of_approvals_remaining=&num_of_approvals_remaining-1,
664 reviewed_by_nm="&user",
665 reviewed_on_dttm=&sastime
666 where table_id="&LOAD_REF";
667 %mp_lockanytable(UNLOCK,
668 lib=&mpelib,ds=mpe_submit,
669 ctl_ds=&mpelib..mpe_lockanytable
670 )
671
672 /* run post-approve hook */
673 %mpe_runhook(POST_APPROVE_HOOK)
674
675 data apPARAMS;
676 AUTHORISED=1;
677 ALREADY_UPDATED=0;
678 ALREADY_UPDATED_DTTM=.;
679 DIFFTIME="&difftime";
680 if &syscc=0 then RESPONSE='SUCCESS!';
681 else response="SYSCC=&syscc.";
682 run;
683
684 %mp_abort(iftrue= (&syscc ne 0)
685 ,mac=&_program 582
686 ,msg=%superq(msg)
687 )
688
689 %mpe_alerts(alert_event=APPROVED
690 , alert_lib=&libref
691 , alert_ds=&ds
692 , dsid=&LOAD_REF
693 )
694
695 %removecolsfromwork(___TMP___MD5)
696 %webout(OPEN)
697 %webout(OBJ,apPARAMS)
698 %webout(CLOSE)
699 %return;
700%end;
701%mend postdata;
702
703%postdata()
704
705%mp_abort(mode=INCLUDE)
706
707%mp_abort(iftrue= (&is_err=1)
708 ,mac=&_program
709 ,msg=%superq(err_msg)
710)
711
712%mpeterm()