I'm creating a report in vfp. The report contains grouping. In the end of each group, i draw a line. Each row in the detail band doesn't contain any line, only at the end of each group. The problem is when the group expand to the next page, in the previous page i want to draw a line at the bottom. Like this :
(page 1)
group A
name, etc
x1,etc
x2,etc
???how do I add line here?
(page 2)
group A
name,etc
x3,etc
x4,etc
group B
name,etc
y1,etc
y2,etc
I've tried to place the line in the page footer band, but the last line of the report doesn't have exact position, so it doesn't look nice.
Hope I described the situation clear enough. Thank You for taking the time to help me.
Without some significant smoke-and-mirrors trickery: running the report twice, once hidden and track where the breaks are via function calls in the report, and then again for production, its not EASILY done.
The only thing I could suggest is putting a line at the TOP of a PAGE FOOTER which prints on EVERY page. How long have you been working with VFP. Depending, I MIGHT be able to guide you through it.
Ok, here are the steps I would take. This is under the assumption that you are pre-querying the results for your report and ordering them by some means into a temporary report cursor. You need to add 2 columns to your query as place-holders and be sure your do your cursor as " INTO CURSOR READWRITE " as we will be writing to this from within the report... that is the trick.
Next, modify your report. Go to the detail band and put a single line at the bottom of it. Adjust as needed if you need a few pixels under the last detail element. Double click the line and get to the tab where it allows you to put in a "Print When" condition for the line. Enter one of the new column names called "ShowLine" (but without the quotes).
Now, the "hook" for smoke and mirrors. Create another textbox field output in the report detail. It can be as small as 2 pixels wide and never actually prints anything. It can be put at the beginning or end of the report detail, no matter, just as long as its in the detail band. Double click it to bring up what it will print. In the expression, enter the following... WhatPageAmIOn( _PageNo )
This will actually call a function we'll add to your program which writes back to your report cursor... I'll hit that next.
Now, the code. The following is a sample snippet of code I've written to query the data for the report, have the extra columns, and put into a READWRITE cursor. From that, I run the report but to NOCONSOLE so it doesn't actually visually do anything, just runs in the background. It then cycles through and looks for the break between each page and goes backward 1 record from the break and stamps that record as "ShowLine" = .T... Then run the report again as normal and you have your one line appearing in the detail band regardless of a data group, but always the last data line at the end of each page.
Here's the code
*/ Query your data, order by whatever,
*/ but tack on the two extra fields and make it READWRITE
select;
YourData,;
AnotherField,;
MoreData,;
.f. as ShowLine,;
00000 as WhatPage;
FROM ;
YourData;
ORDER BY ;
WhateverForYourReport
INTO ;
CURSOR C_RptData READWRITE
*/ Pre-run the report NOCONSOLE so your windows don't get messed up / scrolled
REPORT FORM YourReport NOCONSOLE
*/ now, go back to the cursor that your report ran with
SELECT C_RptData
*/ set a variable for the first page you are looking to find a break for.
*/ in this case, the first detail that APPEARED on page 2.
lnLastPage = 2
*/ Start at top of the report cursor file and keep going until we reach
*/ the end of file where the LOCATE can no longer find "Pages".
GO TOP
DO WHILE NOT EOF()
*/ find the first record on ex: Page 2
LOCATE FOR WhatPage = lnLastPage
*/ Did we find one?
IF FOUND()
*/ Yes, go backwards 1 record
SKIP -1
*/ This is the last detail that appeared on the page before it (ie: pg 1)
*/ Mark this line as ok to "ShowLine" the next time the report is run.
replace ShowLine WITH .T.
*/ Now, advance the page counter to look for the NEXT page break...
*/ ex: between page 2&3, 3&4, 4&5, etc...
lnLastPage = lnLastPage +1
ENDIF
ENDDO
*/ Run your final version of the report
REPORT FORM YourReport Preview (or print)
RETURN
Here's the only hook below to track/update the page associated with the detail. I don't know if you have a main "SET PROCEDURE TO" file, or just a bunch of free .PRG files all in your project, or even if your reporting is done from within a PRG file itself. however, all you need is this function to be included in any of those locations. For simplest test, I would just create it as a stand-alone .prg file (if you are NOT using SET PROCEDURE, or doing your report within a PRG file and not from within a class method/event).
FUNCTION WhatPageAmIOn
LPARAMETERS lnPage
replace whatPage WITH lnPage
RETURN ""
As in the original description, the report is going to include a field in the detail band based on a function "WhatPageamIOn" and passes the parameter of _PageNo which is the internal VFP variable that keeps track of the current report page that is typically used in report header / footers. So, as each detail is getting processed, we are "STAMPING" the detail data with whatever the page is. We return an empty string "" so nothing actually gets printed, yet we've hooked what we needed. From this, the loop finding the first record at the beginning of every page (starting at page 2), and skipping backwards to the last entry for the prior page and we're done.
Good luck.
Related
I have like a POS application where I work, and I added new code to read barcodes in a datawindow, but been having some problems to use it correctly. The process needs to read the barcode and automatically search for the product, and insert it in another datawindow with the info regarding the product.
The problem is that I have the code in the editchanged event, and that the barcodes have different lenght; so, every time a number of the barcode is read, the search function is called, here is the code for that function:
ll_rtn = dw_search.Retrieve()
If ll_rtn = 1 Then
is_code = dw_search.GetItemString(1,'barcode')
wf_insert(False)
End If
If for example, a product of 10 digits is read and at length 5 the product is found, it's inserted but the editchanged is still running because the barcode is length 10, and depends of the search, might find it again with the last digits and insert it again.
I don't know how to stop reading after I find the product. Hope I explain it right.
Can you check if the barcode scanner can be configured to pass carriage return / Enter, upon scanning the barcode and this way you can move your code to itemchanged event
Bar Code Config
An editchanged event is fired for each keystroke in the control. You probably want to either move the code to the itemchanged event or to a totally separate event which is posted from the itemchanged event.
If you absolutely have to have the code in the editchanged event you need to set a boolean instance variable on the window which is set TRUE once the product is found. If it's true then don't search. Remember to reset the variable to FALSE prior to any new search.
What I am currently trying to do in BIRT 4.5 is repeat only a specific part of my tables header. However when I set it to repeat the header it always repeats the entire header.
Is there a way to only repeat a specific part of the header?
In my example - there is two headers, the "table name" and the "column headings". I only want the column headings to repeat. I have a feeling its likely going to be a scripted fix, but have no idea where to go to start it.
this might get tricky because headers can be repeated for groups but also for pagebreaks.
Not the most fancy, but you should get something working by defining a global variable on the page script
var headerCount = 0;
Then select an element in the header, open the onPrepare script and have it increment the counter:
headerCount = headerCount + 1;
with this information, you can make true/false statements in the visibility property.
Each object has onPrepare, onCreate, onRender and onPagebreak scripts. Not sure what the exact difference, so you have to experiment a bit with this. (because when an element should increase the counter, but is not rendered due to the visibility clause, you can get unexpected results.)
Good luck!
I'm trying to figure this out and I must be overlooking something basic. (It took me WAAAY longer than it should have just to realize I hadn't added the trigger.)
When forms are submitted, if that page runs out of rows, it automatically expands. I have a reconciliation page where it is pulling the submitted data over line by line and analyzing it for discrepancies (the form collects billable time and tasks.)
So while the Form Responses 1 page will expand, I want to use a trigger on form submit to add a line to the reconciliation page and copy the formulas down. I can't seem to get the line to add though. Looking at the google page for expanding the number of rows, I'm not sure what I am doing wrong there either but I THINK I need to add more java features to my computer.
If I simply copy and paste the example into a new sheet, most of the code is black instead of the standard editor colors. Saving pops up "Missing ; before statement. (line 1, file "Code")"
Line one is simply "import com.google.gdata.client.spreadsheet.*;"
So zerothly: Whats the most basic code I can use to add that blank row?
Then first: Do I need to import a bunch of stuff to get this (adding rows) to work?
Second: If so, and I transfer ownership of the sheet to someone, do they need to do the imports also?
Third: If so, and I want to do edits on another device, will I need to do imports there too?
Fourth: The example uses Update() but I can't seem to find an Update() function in javascript or googlesheets api documentation.
This is the code I am trying and variations commented out which doesn't seem to work:
function onFormSubmit(e) {
Logger.log('form submit triggered')
var sheet = SpreadsheetApp.getActive()
var sss = sheet.getSheetByName('Reconciliation')
var col2 = sss.getRange("B:B");
var col2val = col2.getValues();
var counter = 0;
var sssrange = sss.getDataRange();
// sss.Rows = sss.getLastRow() + 1 //Object does not allow properties to be added or changed if I uncomment - this seems to match the google example line though
Logger.log(sss.getLastRow());//=8
var newsssrange = sssrange.offset(1,0); // didn't actually think this would work (since it also had the .update() part that previously didn't work for me) but came across it and was getting desparate.
// sss.setRowCount(sss.getLastRow() + 1); // TypeError: Cannot find function setRowCount in object Sheet.
Logger.log(sss.getLastRow()); //=8
// sss.Update();//TypeError: Cannot find function Update in object Sheet.
}
Sigh... I am still wondering how to add more rows but I did answer my original need of adding a single line since form submits only add a single line. So I'm going to answer it since I had done so many searches and for some reason this never came up, maybe someone will find this useful if they are having the same issue.
function onFormSubmit(e) {
var sheet = SpreadsheetApp.getActive()
var sss = sheet.getSheetByName('Reconciliation')
sss.appendRow(['']);
}
Note that this adds a single blank line. If you run it a second time it won't add a second blank line as appendRow() adds after the last line with data. If you put a string in there or something and run it over and over you will get multiple lines.
I really would like to know about adding multiple lines though also since that will come up and I still seem to be missing something, probably obvious.
Did you know that arrayformula woluld make new lines automatically.? If you paste this formula in Sheet2:
=OFFSET(Sheet1!A1,,,counta(Sheet1!A:A))
and then paste new values in Sheet1 range A:A, then new rows would be added on Sheet2.
I got a problem using the TinyWebDB in App Inventor 2. Here's a Screenshot of the blockcode.
The goal of this Screen is to store a list(array) of images and later query them with a button but my problem starts already earlier. First there is a variable initialized called fotoList and declared as an empty list.
When this Screen initializes (left block) I store the empty fotoList under the tag FotoListTag. Then if the image under the tag "SteckbriefFoto" is not in this list -> getValue with tag "FotoListTag". Then he jumps into the block on the right and adds the photo .. other stuff not important .. at the end I store the list again in the TinyWebDB (and also in the TinyDB) with the tag "FotoListTag". Then it goes back to the block on the left where at the end I want to set an image.picture to the photo I stored in variable fotoList.
When I compile the code there is an error opening the page that says
Select list item: List index too large
Select list item: Attempt to get item number 1 of a list of length 0:()
I just don't get the problem with this code and i hope someone can help me.
For lists, valueIfTagNotThere should be create empty list instead of an empty string
On first run of your app, TinyDB is empty, which means, for tag = FotoListTag you get no value back, therefore this should be an empty list in the beginning.
Later you are trying to select the first item from the list (zahl is 1). As you know, the list is empty in the beginning, so probably you should add an if statement to check, if the list is not empty and only then select the first item... same for tag = Schriftlist.
You also have a timing issue. in Screen.Initialize you are trying to get a value from TinyWebDB. This is an asynchronous call, you get the result back in TinyWebDB.GotResult event and this takes a little bit (let's say 500 milliseconds), but meanwhile the complete blocks of the Screen.Initialize event will be executed. Probably you are expecting, that meanwhile tag = FotolistTag is not empty anymore, but this is not the case.
I am slimming down my databases, eliminating duplicate reports where I can and creating better code. I have one database that involves our welders and foremen. In this code, I can print a report for an individual foreman, which sends the string "strActive" through openargs The report looks at strActive and on the OpenForm action, sets the filter for active, inactive, or all welders, based on the string value passed through.
It works perfectly for the single page at a time code. There, the user chooses a foreman from a list or enters the foreman's clock number. The query the report is based on uses the global string "ForemanCLK" to only get results for that welder.
Formerly, I had three reports; one for all welders, one for active welders, and one for inactive welders.
Previously, I would set a variable based on strActive and open the appropriate report using another variable in the code in place of the report name. The loop worked fine and opened the report 39 times, each time with a new foreman name and data.
I'm baffled as to why opening the report now, with an openarg, doesn't work. I only get the first foreman name, 39 times. I've verified that I get the foremanCLK variable of the different foremen by commenting out the docmd line and debug.print(ing) the various values needed by the form. The report simply doesn't load it correctly.
Set rec = CurrentDb.OpenRecordset(sql)
rec.MoveFirst
For ctr = 0 To rec.RecordCount - 1
ForemanCLK = rec(0).Value
DoCmd.OpenReport "rptForeman", acViewNormal, , , , strActive
'DoCmd.OpenReport "rptForeman", acViewNormal
rec.MoveNext
Next
The above code gets me many copies of the same foreman's report filtered by strActive
For ctr = 0 To rec.RecordCount - 1
ForemanCLK = rec(0).Value
'DoCmd.OpenReport "rptForeman", acViewNormal, , , , strActive
DoCmd.OpenReport "rptForeman", acViewNormal
rec.MoveNext
But this gets me all the different foremen, except it is not filtered.
I've tried passing in a Where clause.
acViewNormal, ,"[active]=" & True
and
acViewNormal, ,"[active]=" & False
and
acViewNormal, ,"[active]=" & True & " OR [active]=" & False
I did the same verification with the single report, and it filters correctly, but in the loop, it does not filter at all. It does, however give me the different foremen.
The big question here is...
WHY? Does access not have enough time between reports to close it and therefore it doesn't perform the operations on the open event?
Any other ideas?
You must close the report explicitly inside your loop: DoCmd.Close acReport, "rptForeman". If OpenReport is called a second time on an already-open report, the object gets the focus in access, but it doesn't re-run the Open event.
Okay, I must hang my head in shame.
I error checked the hell out of that code above using every variable except strActive.
When I added that to my debug.print line, it came up with nothing. How can that be!?!!??
I simply forgot to set that at the beginning of the sub, like I did with the other action that opens a single report. strActive is a global variable, but was not set at any other point in the code up until this piece.
Once this was added to the beginning of the sub, all worked fine.