Documente Academic
Documente Profesional
Documente Cultură
Use the following formula, replacing the items in Bold with your start/end date and the items in Italics with your holidays........ WhilePrintingRecords; //Set the values of Start Date and End Date DateVar StartDate := Date(2003,01,01); DateVar EndDate := Date(2003,12,31); //Find out the difference in days and subtract the weekends NumberVar DaysDiff := DateDiff("d",StartDate,EndDate) DateDiff("ww",StartDate,EndDate,crsaturday) DateDiff("ww",StartDate,EndDate,crsunday); //Create an array of Holiday dates Local DateVar Array Holidays := MakeArray( Date(2003,01,01), Date(2003,01,02), Date(2003,12,25), Date(2003,12,26), Date(2003,07,07)); //Loop through the array checking if each holiday is within the dates Numbervar Counter := 0; While UBound(Holidays) <> Counter do (Counter := Counter + 1; if Not(dayofweek(Holidays[Counter]) in [1,7]) and Holidays[Counter] in StartDate to EndDate then DaysDiff := DaysDiff -1;); //Display result to 0 decimal places and no thousand separator totext(DaysDiff,0,"");
Adding a Number of Business Days to a Date How to Print "Continued on Next Page" Finding the Last Friday of the Month Removing the Path from the "File Path and Name" field Find Monday of a specific week (using ISO standard week numbers)
identically as YYYYMMDD. Finally, your selection criterion uses these two formula fields: {@DOBString} >= {@ParamDateStr} or whatever makes sense under your circumstances.
How do I determine the correct accounting month based on '44-5' accounting periods
Recently, I was tasked to build a series of reports that were based on standard '4-45' accounting months (referred to as 'Operating Months' by my client and, therefore, in my formula). Since native Crystal Reports Date functions are based on calendar months, I had to come up with a formula to identify the specific accounting month for each record in my report. The first step in this process was to identify the rules by which a '4-4-5' accounting month is based. Following are the general rules: 1. The accounting year always starts on the first day of the month of the first month in the fiscal year
2. The Accounting Year always ends on the last day of the month in the last month of the fiscal year[li]Each quarter in the year is comprised of Accounting Months as follows:[ul] 3. Four weeks in the first month of the quarter o unless the first day of the fiscal year falls on the last day of the fiscal week- in this case the first fiscal month is comprised of four weeks plus 1 day 4. Four weeks in the second month of the quarter 5. Five weeks in the third month of the quarter o except for the last fiscal month since the last accounting month runs all the way to the end of the fiscal year, regardless of the number of weeks The next step was to identify the client-specific rules: 1. The fiscal year begins on January 1st 2. The fiscal year ends on December 31st 3. The fiscal week ends on Saturday (many companies end the week on Friday) Based on both the general rules and the client-specific rules listed above, I created the following formula*://@Operating MonthDateVar FirstDay;DateVar LastDay; DateVar FirstSat; StringVar YrText; StringVar OpMonth; //Defines the Date Value for the First Day of the Year FirstDay := Date(Year({TEST_TABLE.TEST_DATE}),1,1);//Substitute your fiscal Month and Day values if different //Defines the Date Value for the Last Day of the Year LastDay := Date(Year({TEST_TABLE.TEST_DATE}),12,31); //Substitute your fiscal Month and Day values if different //Defines the Date Value for the First Saturday of the Year - modify this formula if your fiscal week ends on Friday If DayOfWeek(FirstDay) < 7 Then FirstSat := FirstDay + (7 - DayOfWeek(FirstDay)) Else If DayOfWeek(FirstDay) = 7 Then FirstSat := FirstDay + 7;//Pushed out a week because the first day of the year was a Saturday //Defines the YrText Variable in 'YYYY' format YrText := Left(ToText(Year({TEST_TABLE.TEST_DATE}),0),1) + Right(ToText(Year({TEST_TABLE.TEST_DATE}),0),3); //Defines the Actual Operating Month for the Test Date //Modify the '/MM' values to your fiscal year OpMonth := Select {TEST_TABLE.TEST_DATE} Case FirstDay to (FirstSat + 21): YrText + '/01'//January 1 to the end of the 4th Saturday
Case (FirstSat + 22) to (FirstSat + 49): YrText + '/02'//28 Day Range (4 weeks, Sunday to Saturday) Case (FirstSat + 50) to (FirstSat + 84): YrText + '/03'//35 Day Range (5 weeks, Sunday to Saturday) Case (FirstSat + 85) to (FirstSat + 112): YrText + '/04'//28 Day Range (4 weeks, Sunday to Saturday) Case (FirstSat + 113) to (FirstSat + 140): YrText + '/05'//28 Day Range (4 weeks, Sunday to Saturday) Case (FirstSat + 141) to (FirstSat + 175): YrText + '/06'//35 Day Range (5 weeks, Sunday to Saturday) Case (FirstSat + 176) to (FirstSat + 203): YrText + '/07'//28 Day Range (4 weeks, Sunday to Saturday) Case (FirstSat + 204) to (FirstSat + 231): YrText + '/08'//28 Day Range (4 weeks, Sunday to Saturday) Case (FirstSat + 232) to (FirstSat + 266): YrText + '/09'//35 Day Range (5 weeks, Sunday to Saturday) Case (FirstSat + 267) to (FirstSat + 294): YrText + '/10'//28 Day Range (4 weeks, Sunday to Saturday) Case (FirstSat + 295) to (FirstSat + 322): YrText + '/11'//28 Day Range (4 weeks, Sunday to Saturday) Case (FirstSat + 323) to LastDay: YrText + '/12'//Beginning of the Operating month to December 31st; OpMonth;//This is the return value for the Operating Month in 'YYYY/MM' format for easy sorting and grouping *Due to the Case logic, this formula will only work in Crystal Reports 8.0 and above. In order to work in earlier versions, you will need to substitute If-Then-Else statements as follows: If {TEST_TABLE.TEST_DATE} in FirstDay to (FirstSat + 21) Then OpMonth := YrText + '/01'//January 1 to the end of the 4th Saturday Else If//etc I think you'll find that the above formula works very well. For my client, the database field date (referenced as {TEST_TABLE.TEST_DATE}) '01/26/2002' would return '2002/01' as the Operating Month, but '01/27/2002' would return '2002/02'. These are the correct Operating Months, based on my client's '4-4-5' accounting periods.
How do I subtract one datetime field from another and display days, hours, minutes and seconds?
When you subtract one datetime field from another datetime field in Crystal the result is the number of days and the decimal fraction of day. Assume the following dates: D1 := Datetime(2001,10,24,09,00,00) // 9:00 AM, October 24, 2001 D2 := Datetime(2001,10,24,08,59,59) // 8:59 AM, October 24, 2001 D3 := Datetime(2001,01,01,09,00,00) // 9:00 AM, January 1, 2001 D1 - D2 = .0000115741 There is a one-second difference between D1 and D2. There are 86,400 seconds in a
day. Dividing 1 by 86,400 yields .0000115741 D1 - D3 = 296.00000 There are exactly 296 days between D1 and D3. Since there are no hours, minutes or seconds between the two datetimes, the decimal portion of the difference is zero. D2 - D3 = 295.9999884259 There are 295 days, 23 hours, 59 minutes and 59 seconds between D2 and D3. 86400 seconds times .9999884259 is 86399 or one second less than a day. Let us suppose that our client required a report that showed the difference between D2 and D3 in days, hours, minutes and seconds formatted in a sentence. For example: "There are 295 days, 23 hours, 59 minutes and 59 seconds between the two dates." One would think that this is easy with the new DateDiff function. The DateDiff function requires the following syntax: DateDiff(intervaltype,StartDateTime,EndDateTime) Where intervaltype is a string with a value such as: "d" for days "h" for hours "n" for minutes "s" for seconds However, the expression DateDiff("d",{@D2},{@D3}) using the examples above, yields 296 days, not the correct 295. As a result, we must use the following expression: numbervar tsecs := datediff("s",{@d3},{@d2}); // number of seconds between the dates numbervar ndays := truncate(tsecs/86400); // divide by the seconds in a day tsecs := remainder(tsecs,86400); // find the left over seconds numbervar nhours := truncate(tsecs/3600); // divide by the seconds in an hour tsecs := remainder(tsecs,3600); // find the left over seconds numbervar nmin := truncate(tsecs/60); // divide by the seconds in a minute tsecs := remainder(tsecs,60); // find the left over seconds // now that we have all the components, we put it together in a sentence "There are "+totext(ndays,0)+" days, "+totext(nhours,0)+" hours, "+ totext(nmin,0)+
" minutes, "+totext(tsecs,0)+" seconds, between the two dates." The remainder function divides the second argument into the first and returns the remainder after the division. The Totext function changes numeric values into string values and must be used when appending numbers to text in this way. The zero argument indicates that we want no decimals.
//Record Selection Criteria - Direct Method 1 {Table.Date_Field} In Date(Right({?From_Date},4), Left({?From_Date},2), Mid({? From_Date},4,2)) to Date(Right({?To_Date},4), Left({?To_Date},2), Mid({? To_Date},4,2)) //Record Selection Criteria - Direct Method 2 ToText({Table.Date_Field},'MM/dd/yyyy') In {?From_Date} to {?To_Date} //Record Selection Criteria - Indirect Method {@Date} In {?From_Date} to {?To_Date} Where @Date is a formula defined as follows: //@Date ToText(ToText({Table.Date_Field},'MM/dd/yyyy')) Whichever method you choose, your Record Selection Criteria won't be evaluated until all records have been returned. Depending both on the size and integrity of your database and your machine's processor, RAM and Hard Drive space, you could be in for a very long wait. Please note that the following SQL Statement (based on the last example) does not contain a Where Clause: SELECT "TABLE"."DATE_FIELD" FROM "DATABASE"."TABLE" "TABLE_NAME" If you were to create a SQL Expression as follows (Oracle 8i Native Driver in this example): //%Date TO_CHAR(TO_CHAR("TABLE"."DATE_FIELD",'MM/DD/YYYY') ,'MM/DD/YYYY') Then you could use it in your Record Selection Statement as follows: //Record Selection using a SQL Expression {%Date} In {?From_Date} to {?To_Date} In this example, the Record Selection Criteria is passed to the Database and is, therefore, processed on the Server. This could result in a very significant performance increase. Please note that the Customized Parameter Selection is passed to the Database in the Where Clause of the following SQL Statement: SELECT TO_CHAR("TABLE"."DATE_FIELD",'MM/DD/YYYY') FROM "DATABASE"."TABLE" "TABLE_NAME" WHERE TO_CHAR("TABLE"."DATE_FIELD",'MM/DD/YYYY') >= '11/01/2001' AND TO_CHAR("TABLE"."DATE_FIELD",'MM/DD/YYYY') <= '11/05/2001' Q: When is it appropriate to use SQL Expressions?
A: Unfortunately, the answer to this question is a little more nebulous as it largely depends on a combination of your skills, database permissions and your Report Development environment. For example, many companies have defined barriers between Report Writers and Database Developers. In these cases, the Report writer may not have permission to create objects such as Views, Stored Procs or Functions on the Database. As such, the Report Writer must build reports using only the existing database objects. In this type of scenario, SQL Expressions can be very effective since you are basically building a subquery or Function (User Defined Function in SQL Server) outside of the actual database environment. That is, you have the ability to create customized fields that are handled as if they were actual database fields. If you, as a Report Writer or Developer, have the skills to create database objects and have the necessary level of database permissions and are in a development environment that supports the addition of new database objects then you probably don't need to use SQL Expressions since all of your database field manipulation can be done in the database object you create. Generally speaking, however, if you can substitute a SQL Expression for an equivalent Formula (based on your available SQL Functions) then it is appropriate to do so. As explained above, this is especially important when it comes to your Record Selection Criteria.
In the first line, I create a 12-position array of the month abbreviations. In the second line I create a memory variable, mon, which is equal to the month
number of the parameter field's date minus 1. I am subtracting 1 because it is in the second column. For column 3 I would subtract 2, etc. In the third line I test to see if I have crossed the boundary between years. If High Date were equal to January, mon would be equal to 0. In that case monF would be equal to 12 + 0 = 12 or December. In the fourth line I do the same to find the correct year. The fifth line puts it all together as a text string. I used the same logic to calculate the proper amounts for each group footer line. Note that this will work for any date.
Generating Reports using Stored Procedures for Visual Basic Front Ends
I have been working on this problem for a while now. I have finally found out how to make it work. The problem with Crystal Reports is that it cannot generate a report with a recordcount of -1. How is it possible to have a recordcount of -1 you might ask? When using a recordset, you can choose what cursor type you would like. The options are Dynamic, ForwardOnly, Keyset, and Static. Each of these cursor types are good for certain things but I'd like to talk about the ForwardOnly cursor type. ForwardOnly cursor types are fast, very good for retrieving data, However as the name might suggest, The cursor is 'forward only', so you can't navigate very well with this cursortype, you can only go forward. This is the reason that you receive -1 as a recordcount. To get a recordcount the cursor has to run all the way through the recordset to get the count and then go back to the top so you can navigate the recordset, but with a ForwardOnly cursor you cant do that. There is a way to get around this however. The recordset Object's cursor location is Server Side by default. This is a good thing because you typically want the server to do all the work. But if you change the cursor type from Server Side to Client Side, you can get a record count from a forward only RecordSet. I can't exactly explain why this is so, other than I think a recordset generated on a Server will not return the recordcount, but if you are generating the recordset on your own computer, it is easier to calculate a recordcount. So what does this have to do with Stored Procedures? Well, Stored Procedures only return ForwardOnly recordsets. So if you want to generate a report using a Stored Procedure, the cursor type needs to be Client Side. I hope this helps anyone who needs it.
If the ">=" join option is not available, remove the join to the REPEATER table and create this condition in the Record Selection condition:
CODE
{Order.quantity} >= {Repeater.How_Many} If you are restricted to using an equal join, you can also modify the repeater table to include N records for each quantity N: 1 2 2 3 3 3 ... This would cause each order to be duplicated as many times as the value of {Order.quantity}.
performance, as hiding a section which will calculate and save drill-down information takes longer than a section that is suppressed (no drill-down). h.)Make sure that Use Indexes for Speed is checked ON under File|Report Options, other-wise PC database reports will not use indexes to retrieve information, and SQL database reports will return the entire dataset before Crystal filters it on the local machine. i.)Creating a stored procedure or parameterized stored procedure, and reporting off that, is by far the quickest way to retrieve a dataset from the server. Database Dependent Issues Specific to PC Databases: a.)If you are accessing PC Databases, always try to use native database drivers to access your data rather than ODBC. ODBC will always perform slower than native access, as information must be "translated" by the ODBC driver before being sent to your database for retrieval, and then again on the way back, the returned information must pass through the ODBC driver to be translated into your report. Specific to SQL Databases: a.)With SQL databases, both ODBC and Native drivers that ship with Crystal 5.0 are comparable In performance. b.)If you modify the Database|Show SQL Query statement in Crystal, any selection criteria you add later to the report will be processed locally on the client side. You must make sure to reset the query first, and then add a selection criteria to the report, which in turn will then be appended to the existing SQL query so that processing occurs on the SERVER side. c.)Selections on Parameter fields WILL be sent off as part of the query that gets sent over to the server, with the exception of the Database|Show SQL Query statement being modified, in which case processing will occur locally. d.)Creating a report on a Query file (.QRY) and then adding a parameter field in the Selection Formula of the report will cause ONLY the query to be processed on the server side. The actual filtering of the parameter field selection will occur on the client side. e.)Do not use IFELSE conditions in your selection formula, as this also causes processing to occur on the local machine (for SQL databases).
How do I pass date parameters to sub reports and use them in the selection criteria?
. Introduction This document describes the steps that will allow nested sub reports to share some or all of the selection parameters of the parent report. This document shows reports that will share a date range as their parameters as this is a common link between reports. The instructions assume that both the Primary and Sub reports have been completed and that the sub report has been inserted into the footer of the Primary report. 2. Instructions 2.1. Set up the Primary Report In the primary report, create 2 parameter fields called startdate and enddate. You do not have to show these fields in the report.
These parameters are then shown in the report fields list as ?startdate and ? enddate. Then create 2 formulas called startdate and enddate. The startdate formula should only contain the report field value {?startdate} and the enddate formula should only contain the report field value {?enddate}. These Formulas are then shown in the report fields list as @startdate and @enddate. These formulas are required because you cannot create a subreport link based on a parameter field. You must then edit the selection criteria for the main report to include the data range. Go to the menu options Report|Edit Selection Formula|Record and enter/include the following record selection criteria: {table.datefield} >= {@startdate} and {table.datefield} <= {@enddate} 2.2. Set up the Sub Report Create the same 2 parameter fields and the same 2 formula fields in the subreport. These do not have to be on the actual report. 2.3. Link the Reports To link the reports go to the menu options Edit|Subreport Links. Select the formula @startdate from the available fields. This will activate field link section at the bottom of the screen. Remove the tick from the checkbox to deactivate this area. Repeat with the formula @enddate. Then select OK to close the window. Now edit the sub report. The above procedure will have created two parameter fields in the sub report, {?Pm-@startdate} and {?Pm-@enddate}. These parameters represent the range value passed from the main report. You will now have to edit the record selection formula for the sub report. Select Report|Edit Selection Formula|Record from the menu and change the record selection to read: {table.datefield} >= {?Pm-@startdate} and {table.datefield} <= {?Pm@enddate}. Now, when the report is refreshed, the user will enter in the start and end dates in the main report and these values will populate the record selection formula in the subreport.
You need to be sure that "keep together" is turned on for the group. If it's not, you will not get the "page" header when a group breaks at the end of a page.