I wrote a code where user filters a source number from the table, the code get the source number and open an Excel file with the same number in a folder, write other data into the file and then save to temp folder. The program worked for some of the Excel files in the folder, but it won't work for the majority of the files. When it finishes running it'll say Exception from HRESULT: 0x800A01A8, the file gets saved into temp folder but the data is not written into it.
https://i.stack.imgur.com/kHtbC.png
I have searched all over Google but it seems like no one had encountered the same issue as I am and I have no idea what this exception means. Below is the code.
ProductionOrder - OnAfterGetRecord()
CLEAR(xlApplication);
CLEAR(xlWorkbooks);
CLEAR(xlWorksheet);
CLEAR(xlshape);
// Open excel
IF CREATE(xlApplication, FALSE, TRUE) THEN BEGIN
xlApplication.SheetsInNewWorkbook := 1;
xlApplication.ScreenUpdating(TRUE);
xlWorkbooks := xlApplication.Workbooks;
END ELSE ERROR('Could not start Excel.');
xlWorkbooks.Open('C:\PROCESS CHECKSHEET\' + ProductionOrder."Source No." + '.xlsx');
xlWorkbook := xlApplication.ActiveWorkbook;
xlSheets := xlWorkbook.Worksheets;
FOR i := 1 TO xlSheets.Count DO BEGIN
xlWorksheet := xlSheets.Item(i);
xlWorksheet.Activate;
xlRange := xlWorksheet.Range(xlsCell(14,7));
xlRange.Value := ProductionOrder."No.";
xlRange := xlWorksheet.Range(xlsCell(14,8));
xlRange.Value := FORMAT(ProductionOrder.Quantity);
xlWorkbook._SaveAs('C:\temp\' + ProductionOrder."Source No.");
xlWorkbook.Close(TRUE);
xlApplication.Quit;
END;
CurrReport.QUIT;
LOCAL xlsCol(col : Integer) : Text
IF col > 26 THEN BEGIN
ColFirst := col DIV 26;
col := col MOD 26;
END
ELSE
ColFirst := 0;
Letters := 'ABCDEFGHIJKLMNOPQRSTUVYWXYZ';
IF ColFirst <> 0 THEN
EXIT (STRSUBSTNO('%1%2',Letters[ColFirst],Letters[col]))
ELSE
EXIT (STRSUBSTNO('%1',Letters[col]));
LOCAL xlsCell(col : Integer;row : Integer) : Text[15]
EXIT (STRSUBSTNO('%1%2',xlsCol(col),row));
Edit:
I have tried to debug. Debugger says error is on line "xlWorksheet := xlSheets.Item(i);". There is only one sheet in the Excel file that I am trying to access. What I don't understand is that it would work on other Excel files, just not on this file that I am currently trying to access.
I also found out that if I copy the content into a new Excel file, then the code would work on the new Excel file. Could it also be a problem of Excel version?
I solved this on my own.
Because the Excel files were given to me by my HOD at work (and these Excel files were written by other department), I did not know that there was a hidden sheet inside those Excel files, hence it output the exception error because my code was only indexing Excel file that only has one sheet. I changed my index to 2 and it worked. Note to self, next time gotta check for hidden sheets.
I don't know if there is a for each sheet equivalent for C/AL programming because I'm new to the language, so I used 2 for loops to solve the problem.
CLEAR(xlApplication);
CLEAR(xlWorkbooks);
CLEAR(xlWorksheet);
IF CREATE(xlApplication, FALSE, TRUE) THEN BEGIN
xlApplication.SheetsInNewWorkbook := 1;
xlApplication.ScreenUpdating(TRUE);
xlWorkbooks := xlApplication.Workbooks;
END ELSE ERROR('Could not start Excel.');
xlWorkbooks.Open('C:\13. PROCESS CHECKSHEET\' + ProductionOrder."Source No." + '.xlsx');
xlWorkbook := xlApplication.ActiveWorkbook;
xlSheets := xlWorkbook.Worksheets;
FOR I := 1 TO xlSheets.Count DO BEGIN
xlWorksheet := xlSheets.Item(I);
xlWorksheet.Activate;
xlRange := xlWorksheet.Range(xlsCell(14,7));
xlRange.Value := ProductionOrder."No.";
xlRange := xlWorksheet.Range(xlsCell(14,8));
xlRange.Value := FORMAT(ProductionOrder.Quantity);
END;
FOR I := 2 TO xlSheets.Count DO BEGIN
xlWorksheet := xlSheets.Item(I);
xlWorksheet.Activate;
IF (FORMAT(xlWorksheet.Name) = 'PROCESS CHECKSHEET') OR (FORMAT(xlWorksheet.Name) = 'Process Checksheet') THEN BEGIN
xlRange := xlWorksheet.Range(xlsCell(14,7));
xlRange.Value := ProductionOrder."No.";
xlRange := xlWorksheet.Range(xlsCell(14,8));
xlRange.Value := FORMAT(ProductionOrder.Quantity);
END;
END;
xlWorkbook.SaveAs('C:\13. PROCESS CHECKSHEET\temp\' + ProductionOrder."Source No.");
MESSAGE('Success');
xlWorkbook.Close(TRUE);
xlApplication.Quit;
CurrReport.QUIT;
Related
I need to call a lot of procedures with incrementing names. Is it possible to generate the name of a procedure in a string and then call it using that string? Python has its own exec method which would be ideal but I'd be fine if I could dynamically pick the name of a function.
Edit:
To elaborate on what this is for, I have to write a performance test that repeats procedures of multiple codeunits. Depending on the parameters given, I could have 100 codeunits containing 30 procedures each, so to call all of them I currently have to explicitly call each and every one of them.
startTime := Time();
progressDialog.Update(1,'Proc0A');
for i := 1 to 1000 do begin
Set0_No0.Proc0A(1);
progressDialog.Update(2,i);
end;
timeElapsed := Time() - startTime;
if timeElapsed = 0 then begin
timeElapsed := 1;
end;
baseTime := timeElapsed;
FinishTest(timeElapsed, baseTime, 0, false, false, 1, 1, 1000);
startTime := Time();
progressDialog.Update(1,'Proc0B');
for i := 1 to 1000 do begin
Set0_No0.Proc0B(1);
progressDialog.Update(2,i);
end;
timeElapsed := Time() - startTime;
if timeElapsed = 0 then begin
timeElapsed := 1;
end;
FinishTest(timeElapsed, baseTime, 0, true, false, 1, 1, 1000);
Instead, I'd like it to work like this:
procedure CallMethod(setNo:Integer; codeunitNo:Integer; procedureNo:Integer; type:Code[5]; repetitions:Integer)
begin
for i := 1 to repetitions do begin
Exec('Set%1_No%2.Proc%3%4(1)',setNo,codeunitNo,procedureNo,type);
Set0_No0.Proc0A(1);
progressDialog.Update(2,i);
end;
end;
The feature you require is not available in AL.
There are however a couple of options you could consider:
Using a combination of enums, interfaces and codeunits you can remove a lot of boilerplate code.
Using a script (e.g. PowerShell) you could parse all your source files, find the codeunits and their procedures and then generate a new codeunit that invokes each procedure.
In APEX I created a page and here a region. The region reports a classic report. I have selected "PL/SQL function body that returns an SQL query" as the source. Here I have also deposited the following code
declare
l_date_string varchar2(32000);
l_date_diff number(4);
x NUMBER := 0;
l_script varchar2(32000);
l_script_pivot varchar2(32000);
new_date varchar(256);
begin
l_date_diff:=TO_DATE(:P2066_DATE_UNTIL, 'dd.mm.yyyy') - TO_DATE(:P2066_DATE_FROM, 'dd.mm.yyyy') ;
While X < l_date_diff+1 Loop
new_date := to_char(TO_DATE(:P2066_DATE_FROM, 'dd.mm.yyyy')+X,'dd.mm.yyyy');
l_date_string := l_date_string || ',''' || new_date || '''';
X := X + 1;
End Loop;
l_date_string := substr(l_date_string,2);
l_script := 'Select * from
(Select
pkey,
to_char(createdformat,''dd.mm.yyyy'') business_date,
regexp_substr(statistics, ''business_\w*'') business_statistics
from
gss.business_data
where
statistics like ''%business_%''
and createdformat between :P2066_DATE_FROM and :P2066_DATE_UNTIL
) ';
l_script_pivot := l_script || ' pivot(
count(pkey)
for business_date
in ('||l_date_string||'))';
sys.htp.p('<li>' || l_script_pivot || ' </li>' );
return l_script_pivot;
end;
The first column, Business_Statistis, is always displayed, the date in the subsequent columns should be displayed dynamically - depending on the selection of the period.
I also spent the respective code according to the time period selection and knew it successfully as a classic report with an SQL query. That's working.
How can I now dynamically update the classic report with a PL / SQL action. That means that depending on the result, the Classic Report is always displayed?
I once selected to use Generic Number of Columns in parallel, with a number of 365. Then he shows me the columns, but the column heading is not the date but Col2, Col3, Col4 and so on
There are a few ways to solve this issue.
Option 1: If you're happy to use "Generic Number of Columns", then select PL/SQL as your "Headings Type". Then define your headings with PL/SQL e.g.
DECLARE
v_heading varchar2(2000);
l_date_diff number(4);
BEGIN
v_headings := 'business_statistics';
l_date_diff:=TO_DATE(:P2066_DATE_UNTIL, 'dd.mm.yyyy') - TO_DATE(:P2066_DATE_FROM, 'dd.mm.yyyy') ;
WHILE X < l_date_diff+1 LOOP
new_date := to_char(TO_DATE(:P2066_DATE_FROM, 'dd.mm.yyyy')+X,'dd.mm.yyyy');
v_headings := v_headings ||':' || new_date ;
X := X + 1;
END LOOP;
END;
Option 2: I assume you are using "PL/SQL function body that returns an SQL query" to get around the "in clause" of the pivot? Another way to fix this is to define your pivot clause as a page item. E.g. P2066_PIVOT_CLAUSE. You can then set the pivot clause with a before header process. Then you can use standard SQL in your report.
i.e.
SELECT * FROM
(SELECT pkey,
to_char(createdformat,'dd.mm.yyyy') business_date,
regexp_substr(statistics, 'business_\w*') business_statistics
FROM
gss.business_data
WHERE
statistics like '%business_%'
AND createdformat BETWEEN :P2066_DATE_FROM AND :P2066_DATE_UNTIL)
PIVOT (COUNT(pkey)FOR business_date in (&P2066_PIVOT_CLAUSE.))
I'm trying to create slice of slices.
In all the examples the inner slices are integer based.
I'm trying to create a slice of string slices.
Example:
[
[Name1,State1,Tags.Owner1]
[Name2,State2,Tags.Owner2]
[Name3,State3,Tags.Owner3]
]
I'm trying to do it this way:
outerList := [][]string{}
i := 0
for _,c := range clusters {
input := &eks.DescribeClusterInput{
Name: aws.String(c),
}
resp,err := svc.DescribeCluster(input)
if err != nil {
errorOut(`clusterData function: `+err.Error())
}
record := resp.Cluster
data,_ := json.Marshal(record)
error := json.Unmarshal(data, &cluster)
if error != nil {errorOut(error.Error())}
innerList := [...]string{cluster.Name,cluster.Tags["Vsad"],cluster.Status}
outerList[string(i)] = innerList
}
I get the below error:
non-integer slice index string(i)
cannot use innerList (type [3]string) as type []string in assignment
I know in Python I can simply do:
outerList = list()
for c in cluster:
a = [c.Name,c.State,c.Tags.Owner]
outerList.append(a)
You can use append. Formatted as follows:
// make room for clusters
outerList := make([][]string, len(clusters))
// iterate and fill cluster data
for i, c := range clusters {
// some processing where cluster variable is setupped
// add new inner slice
outerList[i] = append(outerList[i], cluster.Name, cluster.Tags["Vsad"], cluster.Status)
}
I'm using Oracle 11g, and I would like to split a column (JobDescription) from the Persons table into separate words.
I.e. If Person A's Job Description is "Professional StackOverflow Contributor", I would like to populate another table with 3 rows containing the 3 words from the Job Description.
From another post here, I managed to get the following which is working for smaller sets of data. But my table contains just less that 500 000 records and the statement has now been running for 2 days and it's still going.
INSERT INTO WORDS (PersonID, Department, Word)
SELECT distinct PersonID, Department, trim(regexp_substr(str, '[^,]+', 1, level))
FROM (SELECT PersonID, Department, trim(Replace(JobDescription, ' ', ',')) str
FROM Persons) t
CONNECT BY instr( str , ',', 1, level - 1) > 0;
Are there another option that might result in quicker results?
For a one-off job, I see no reason not to go procedural. This should be quick enough (250 seconds for a 2.5 million row table on my system). Change the size of the varchar2 variables if your words can be longer than 40 characters.
create or replace procedure tmp_split_job as
TYPE wtype IS TABLE OF NUMBER INDEX BY VARCHAR2(40);
uwords wtype;
w varchar2(40);
i pls_integer;
n pls_integer;
p pls_integer;
cursor c_fetch is select PersonID, Department, JobDescription from Persons where JobDescription is not null;
begin
for v_row in c_fetch loop
n := length(v_row.JobDescription);
i := 1;
while i <= n loop
p := instr(v_row.JobDescription, ' ', i);
if p > 1 then
w := substr(v_row.JobDescription, i, p-i);
i := p + 1;
else
w := substr(v_row.JobDescription, i);
i := n + 1;
end if;
uwords(w) := 1;
end loop;
w := uwords.FIRST;
while w is not null loop
insert into words (PersonID, Department, Word) values (v_row.PersonID, v_row.Department, w);
w := uwords.next(w);
end loop;
uwords.DELETE;
end loop;
end;
/
exec tmp_split_job;
drop procedure tmp_split_job;
I'm trying to do a notification message when doing a process on submit but is not working
DECLARE
l_sendto NUMBER := :P22_SEND_TO;
l_user_groups NUMBER := :P22_USER_GROUP;
l_media NUMBER := :P22_MEDIA;
l_usersessionid NUMBER := :APP_SESSION;
SINGLE_USER CONSTANT number :=0;
l_aux NUMBER;
e EXCEPTION;
BEGIN
--verify if user
select userid into l_aux FROM SURV_TEMP_SENDTO WHERE userid = l_user_groups and usersessionid= l_usersessionid and rownum =1;
IF l_aux > 0 THEN
RAISE e;
ELSE
IF l_sendto = SINGLE_USER THEN
--if selected all types of medias of one user
INSERT INTO SURV_TEMP_SENDTO (userid, mediatypeid, usersessionid)
(SELECT zm.userid, zm.mediatypeid, l_usersessionid
FROM z.media zm, Z.media_type zmt
WHERE zmt.mediatypeid = zm.mediatypeid
AND zm.userid = l_user_groups);
apex_application.g_print_success_message := 'Inserting OK';
END IF;
EXCEPTION
WHEN e THEN
apex_application.g_global_notification:= 'Error inserting values';
--apex_application.g_print_success_message := 'Error inserting values';
END;
In the raise EXCEPTION it doesn't show any msg but if he enters the ELSE it writes the success msg.
Anyone know why it doesn't work in EXCEPTION?
In the ELSE you are using g_print_success_message. That will work in the EXCEPTION section too. When I tried it, g_global_notification didn't work in either main body or exception section, but only appeared if an exception was raised and not handled - I don't know why though.