How to use Crystal Reports without a tightly-linked DB connection? - visual-studio-2005

I'm learning to use Crystal Reports (with VB 2005).
Most of what I've seen so far involves slurping data directly from a database, which is fine if that's all you want to display in the report.
My DB has a lot of foreign keys, so the way I've tried to stay sane with presenting actual information in my app is to add extra members to my objects that contain strings (descriptions) of what the foreign keys represent. Like:
Class AssetIdentifier
Private ID_AssetIdentifier As Integer
Private AssetID As Integer
Private IdentifierTypeID As Integer
Private IdentifierType As String
Private IdentifierText As String
...
Here, IdentifierTypeID is a foreign key, and I look up the value in a different table and place it in IdentifierType. That way I have the text description right in the object and I can carry it around with the other stuff.
So, on to my Crystal Reports question.
Crystal Reports seems to make it straightforward to hook up to records in a particular table (especially with the Experts), but that's all you get.
Ideally, I'd like to make a list of my classes, like
Dim assetIdentifiers as New List(Of AssetIdentifier)
and pass that to a Crystal Report instead of doing a tight link to a particular DB, have most of the work done for me but leaving me to work around the part that it doesn't do. The closest I can see so far is an ADO.NET dataset, but even that seems far removed. I'm already handling queries myself fine: I have all kinds of functions that return List(Of Whatever) based on queries.
Is there an easy way to do this?
Thanks in advance!
UPDATE: OK, I found something here:
http://msdn.microsoft.com/en-us/library/ms227595(VS.80).aspx
but it only appears to give this capability for web projects or web applications. Am I out of luck if I want to integrate into a standalone application?

Go ahead and create the stock object as described in the link you posted and create the report (StockObjectsReport) as they specify. In this simplified example I simply add a report viewer (crystalReportViewer1) to a form (Form1) and then use the following code in the Form_Load event.
stock s1 = new stock("AWRK", 1200, 28.47);
stock s2 = new stock("CTSO", 800, 128.69);
stock s3 = new stock("LTWR", 1800, 12.95);
ArrayList stockValues = new ArrayList();
stockValues.Add(s1);
stockValues.Add(s2);
stockValues.Add(s3);
ReportDocument StockObjectsReport = new StockObjectsReport();
StockObjectsReport.SetDataSource(stockValues);
crystalReportViewer1.ReportSource = StockObjectsReport;
This should populate your report with the 3 values from the stock object in a Windows Form.
EDIT: Sorry, I just realized that your question was in VB, but my example is in C#. You should get the general idea. :)

I'm loading the report by filename and it is working perfect:
//........
ReportDocument StockObjectsReport;
string reportPath = Server.MapPath("StockObjectsReport.rpt");
StockObjectsReport.Load(reportPath);
StockObjectsReport.SetDataSource(stockValues);
//Export PDF To Disk
string filePath = Server.MapPath("StockObjectsReport.pdf");
StockObjectsReport.ExportToDisk(ExportFormatType.PortableDocFormat, filePath);

#Dusty had it. However in my case it turned out you had to wrap the object in a list even though it was a single item before I could get it to print. See full code example:
string filePath = null;
string fileName = null;
ReportDocument newDoc = new ReportDocument();
// Set Path to Report File
fileName = "JShippingParcelReport.rpt";
filePath = func.GetReportsDirectory();
// IF FILE EXISTS... THEN
string fileExists = filePath +#"\"+ fileName;
if (System.IO.File.Exists(fileExists))
{
// Must Convert Object to List for some crazy reason?
// See: https://stackoverflow.com/a/35055093/1819403
var labelList = new List<ParcelLabelView> { label };
newDoc.Load(fileExists);
newDoc.SetDataSource(labelList);
try
{
// Set User Selected Printer Name
newDoc.PrintOptions.PrinterName = report.Printer;
newDoc.PrintToPrinter(1, false, 0, 0); //copies, collated, startpage, endpage
// Save Printing
report.Printed = true;
db.Entry(report).State = System.Data.Entity.EntityState.Modified;
db.SaveChanges();
}
catch (Exception e2)
{
string err = e2.Message;
}
}

Related

Cannot specify child attributes in the columnset for Retrieve

In attempting to merge contacts in Microsoft CRM, I am using the following code -
//c1ID and c2ID are GUIDs of duplicated contacts.
EntityReference target = new EntityReference();
target.LogicalName = Contact.EntityLogicalName;
target.Id = c2ID;
MergeRequest merge = new MergeRequest();
// SubordinateId is the GUID of the account merging.
merge.SubordinateId = c1ID;
merge.Target = target;
merge.PerformParentingChecks = true;
Contact updater = new Contact();
Contact updater2 = new Contact();
updater = (Contact)xrmSvc.ContactSet.Where(c => c.ContactId.Equals(c1ID)).First();
updater2 = (Contact)xrmSvc.ContactSet.Where(c => c.ContactId.Equals(c2ID)).First();
MergeResponse mergedR = (MergeResponse)xrmSvc.Execute(merge);
When I try my Execute call here,I get this error -
Cannot specify child attributes in the columnset for Retrieve. Attribute: owneridname.
Am I not setting something correctly?
Having updatecontent does not change the issue. In fact, I get the error on lookups entered into the updatecontent. I find you have to build new entityreferences:
if (match.Contains("new_mostrecentcampaign"))
master["new_mostrecentcampaign"] =
new EntityReference(match.GetAttributeValue<EntityReference>("new_mostrecentcampaign").LogicalName
, match.GetAttributeValue<EntityReference>("new_mostrecentcampaign").Id);
...
Merge.UpdateContent = master
...
I realize this is a pretty old question, but for those of you who have run into the same issue in 2021 and beyond, here's the reason this error happens.
TL;DR: Ensure the EntityReference values for the attributes does not specify the Name property.
Explanation:
Everything that gets added to the Entity set to UpdateContent will be applied to the Target contact. When programmatically executing a MergeRequest within a plugin/workflow, the attributes of the UpdateContent get applied (as desired).
Where this breaks down is for EntityReference value types (lookups). The internal Microsoft code that performs this operation tries to interpret all properties of the EntityReference object, including Name.
So when the existing values from the SubordinateId contact are pulled using IOrganizationService.Retrieve (to dynamically get the latest version), the Name property is automatically set for those lookup attributes (the child record). This operation is not valid, even though it's not the user code that's directly executing it.
This brings us full circle to explain the original error:
Cannot specify child attributes in the columnset for Retrieve
I wish I had some documentation for this, but although the official documentation notes that the UpdateContent is optional, experience proves that it is in fact necessary. In the MergeRequests I've tested, I always include that property in the request, and there's a post in the MSDN forums for Dynamics 3.0 that suggests the same.
In fact, when I try to merge two contacts in my org without UpdateContent assigned, I actually get a FaultException saying the following:
Required field 'UpdateContent' is missing
Even though the documentation says it's optional!
So I'd suggest populating the UpdateContent property with something as in the below and see if that works:
var merge = new MergeRequest
{
// SubordinateId is the GUID of the account merging.
SubordinateId = c1ID,
Target = target,
PerformParentingChecks = true,
UpdateContent = new Contact()
};

How to find a template in Alfresco with OpenCMIS from Spring application?

I am working in a Spring 3.1 application and I need to find a String template document located in Alfresco's repository. I can already create a file in alfresco with OpenCMIS, but I couldn't figure out how to find a template, so if anyone knows how to do it or point me an example, please let me know, thanks in advance!
There are a number of options you can use. First of all, you need to have a criteria that uniquely identifies your document. Here below I'll show some, hopefully your case falls in one of them or they will inspire you towards a proper solution. The following uses pseudo code, please have a look to the OpenCMIS dev guide for working with the Java client API.
BY ID
Once you create a Document via CMIS, you get the unique ID of it that you can store in your application for later retrieval.
Map<String, Object> templateProperties = createDocumentProperties();
Folder folder = getTemplatesFolder();
ObjectId templateId = createTemplateIn(folder);
storeTemplateId(templateId.getId(), templateProperties); // persist the ID
...
// later on
...
String id = getTemplateId(); // retrieve the ID
Session session = openCMISSession();
Document template = (Document)session.getObject(id);
BY PATH
Similar to the previous example, you will have to take note of where you stored the document instead of its ID, or having a way to construct the path by hand.
String path = getTemplatePath(); // either recover it from DB or construct a path
Document template = (Document)session.getObjectByPath(path);
BY PROPERTY VALUE
Let's say you can use a specific metadata field on a template Document that allows you to easily retrieve it afterwards (e.g. you created some specific Alfresco metadata model for your use case).
String meta = TemplateProperties.TEMPLATE_ID; // e.g. my:templateId
String type = TemplateProperties.TEMPLATE_TYPE; // e.g. my:template
String templateMeta = "TEMPLATE1";
Map<String, Object> templateProperties = createDocumentProperties();
templateProperties.put(meta, templateMeta);
templateProperties.put(PropertyIds.OBJECT_TYPE_ID, type);
createTemplate(templateProperties);
...
// later on
...
String type = TemplateProperties.TEMPLATE_TYPE; // e.g. my:template
String meta = TemplateProperties.TEMPLATE_ID;
String tplId = "TEMPLATE1";
String query = String.format("SELECT * FROM % WHERE % = '%'", type, meta, tplId);
ItemIterable<QueryResult> i = session.query(query, false);
QueryResult qr = i.iterator().next(); // let's assume we have one single match
String templateId = qr.getPropertyByQueryName("cmis:objectId").getFirstValue()
Document template = (Document)session.getObject(templateId);
BY QUERY
The previous approach is not really tied to search by property name, and can be easily extended to use any kind of query that identifies your templates. Have a look at the Alfresco page on its CMIS query language implementation details to learn more ways of querying the repository.

ABCpdf Table Help

First time poster so please bear with me. Basically I have a pdf document that I am creating on the fly using ABCpdf (via C# project) however the table rows seem to be rendering on top of each other. I have looked at all the documentation etc and searched for an answer but have not found anything relating to this.
I referred to the examples on creating tables which got me to the point I am at now but I cannot understand what is causing this issue. Below is an example of how I am constructing my table. Any help offered would be greatly appreciated. I have created a wrapper for ABCpdf to make it quicker and more code efficient to use but cannot see this causing the issue as it simply calls the same code as it would if I wrote it all out line by line.
PdfTable pdfTable = new PdfTable(_abcPdfWrapper.PdfDocument, 5, 3) {HorizontalAlignment = 1};
pdfTable.NextRow();
pdfTable.NextCell();
pdfTable.AddText(firstStageReference);
pdfTable.NextCell();
pdfTable.AddText(String.Format("{0:#,0.000}", materialWeight) + " Kg");
pdfTable.NextRow();
pdfTable.AddText(weighDepartmentMaterial.sMaterialCode ?? String.Empty);
pdfTable.NextCell();
pdfTable.AddText(weighDepartmentMaterial.sMaterialName ?? String.Empty);
pdfTable.NextCell();
pdfTable.AddText(String.Format("{0:#,0.000}", materialWeight) + " Kg");
pdfTable.NextCell();
pdfTable.AddText(weighDepartmentMaterial.Scale ?? String.Empty);
pdfTable.NextCell();
pdfTable.AddText(weighDepartmentMaterial.AddGroup ?? String.Empty);
There is other code in between these lines but they bear no significance to the construction of the table other than a loop in which the lines from number 2 down are contained to loop through a series of raw materials and create a row for each.
I solved this issue in the end by simply adding these two functions into my wrapper class and then calling them before and after using the table object.
public void PdfTableBegin()
{
PdfDocument.TopDown = false;
}
public void PdfTableEnd()
{
PdfDocument.TopDown = true;
}

Subsonic 3 Newtonsoft JSON "Self referencing loop Exception"

Hi I been searching for my error but I can't find anything that help me. The problem is this. I been working with Subsonic 3, Newtonsoft Json and the linq way of write so I have this easy query:
var found = from client in newclients.All() where client.Period == "sometext" select client;
string periodoJSON = JsonConvert.SerializeObject(periodoFound); //this get "Self referencing loop Exception"
the problem is when I run this script I get the horrible exception "Self referening loop exception" in the JsonConvert line, subsonic have all the objects without any problem but if I do the following.
var found = from client in newclients.All() where client.Period == "sometext" select new client{client.Name, client.LastName, etc};
string periodoJSON = JsonConvert.SerializeObject(periodoFound);
I get the object serialize with a any problem with all the properties. I'm doing the last way because I have to finish my work but is any other way or solution for this problem, if not I will have to write all the properties every time I want to get a full table properties.
hope any can solve my problem o help me in the path for find a solution....
what I have is a really basic query with linq and I try the three values for JsonSerializerSettings and any work, again I'm working with subsonic 3 this not happend either with subsnoic 2 and I can make it work if I specify one by one the properties of the object in the linq query does any have any clue of what is happend, ANY more help would be great!!! If I put the value of Serialize my page get crazy and in a infinity loop state, if I decide for error simple doesn't work and Ignore nothing happen... some more information about this self referencia loop?
var u = usuario.SingleOrDefault(x => x.TipoUsuario == "A" || x.TipoUsuario == "W");
JsonSerializerSettings setting = new JsonSerializerSettings();
setting.ReferenceLoopHandling = ReferenceLoopHandling.Error; //.Serialize .Ignore
Page.ClientScript.RegisterClientScriptBlock(this.GetType(),"usuario", "var usuario=" + JsonConvert.SerializeObject(u, Formatting.None, setting) + ";");
Update ------
I code the following
string jsU = JsonConvert.SerializeObject(u,Formatting.None,new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.Objects, ReferenceLoopHandling = ReferenceLoopHandling.Ignore });
and is workign but the only thing wrongs is that in the json object comes all the information about the columns of subsonic 3 and a BIG chunk of text explain it... does any one know how to not SEND this part of the object??
Without knowing more about you object model it is hard to provide a definitive answer, but I would take a look at the ReferenceLoopHandling enum.
You're calling string SerializeObject(object value) on JsonConvert. Try the string SerializeObject(object value, Formatting formatting, JsonSerializerSettings settings) method instead. The JsonSerializerSettings settings parameter lets you set a bunch of things, including the ReferenceLoopHandling ReferenceLoopHandling { get; set; } property.
You can try these values:
public enum ReferenceLoopHandling
{
Error,
Ignore,
Serialize
}
Obviously, Error is the default and that's what you're getting. Perhaps one of the others will help.

Handling parameters from dynamic form for one-to-many relationships in grails

My main question here is dealing with the pramas map when having a one-to-many relationship managed within one dynamic form, as well as best practices for dealing with one-to-many when editing/updating a domain object through the dynamic form. The inputs for my questions are as follows.
I have managed to hack away a form that allows me to create the domain objects shown below in one Dynamic form, since there is no point in having a separate form for creating phone numbers and then assigning them to a contact, it makes sense to just create everything in one form in my application. I managed to implement something similar to what I have asked in my Previous Question (thanks for the people who helped out)
class Contact{
String firstName
String lastName
// ....
// some other properties
// ...
static hasMany = [phones:Phone]
static mapping = {
phones sort:"index", cascade: "all-delete-orphan"
}
}
class Phone{
int index
String number
String type
Contact contact
static belongsTo = [contact:Contact]
}
I basically managed to get the values from the 'params' map and parse them on my own and create the domain object and association manually. I.e. i did not use the same logic that is used in the default scaffolding, i.e.
Contact c = new Contact(params)
etc...., i just looped through all the params and hand crafted my domain objects and saved them and everything works out fine.
My controller has code blocks that look like this (this is stripped down, just to show a point)
//create the contact by handpicking params values
def cntct = new Contact()
cntct.firstName = params.firstName
cntct.lastName = params.lastName
//etc...
//get array of values for number,type
def numbers = params['phone.number']
def types = params['phone.type']
//loop through one of the arrays and create the phones
numbers.eachWithIndex(){ num, i ->
//create the phone domain object from
def phone = new Phone()
phone.number = num
phone.type = types[i]
phone.index = i
cntct.addToPhones(phone)
}
//save
My questions are as follows:
What is the best practice of handeling such a situation, would using Command objects work in this case, if yes where can i found more info about this, all the examples I have found during my search deal with one-to-one relationships, I couldn't find an example for one-to-many?
What is the best way to deal with the relatiohsips of the phones in this case, in terms of add/removing phones when editing the contact object. I mean the creation logic is simple since I have to always create new phones on save, but when dealing with updating a contact, the user might have removed a phone and/or editing an exiting one and/or added some new phones. Right now what I do is just delete all the phones a contact has and re-create them according to what was posted by the form, but I feel that's not the best way to do it, I also don't think looping over the existing ones and comparing with the posted values and doing a manual diff is the best way to do it either, is there a best practice on how to deal with this?
Thanks, hopefully the questions are clear.
[edit] Just for more information, phone information can be added and deleted dynamically using javascript (jquery) within the form [/edit]
disclaimer: i do not know if the following approach works when using grails. Let me know later.
See better way for dynamic forms. The author says:
To add LineItems I have some js that calculates the new index and adds that to the DOM. When deleting a LineItem i have to renumber all the indexes and it is what i would like to avoid
So what i do
I have a variable which stores the next index
var nextIndex = 0;
When the page is loaded, i perform a JavaScript function which calculates how many child The collection has and configure nextIndex variable. You can use JQuery or YUI, feel free.
Adding a child statically
I create a variable which store the template (Notice {index})
var child = "<div>"
+= "<div>"
+= "<label>Name</label>"
+= "<input type="text" name=\"childList[{index}].name\"/>"
+= "</div>"
+= "</div>"
When the user click on the Add child button, i replace {index} - by using regex - by the value stored in the nextIndex variable and increment by one. Then i add to the DOM
See also Add and Remove HTML elements dynamically with Javascript
Adding a child dinamically
Here you can see The Paolo Bergantino solution
By removing
But i think it is the issue grow up when deleting. No matter how many child you remove, does not touch on the nextIndex variable. See here
/**
* var nextIndex = 3;
*/
<input type="text" name="childList[0].name"/>
<input type="text" name="childList[1].name"/> // It will be removed
<input type="text" name="childList[2].name"/>
Suppose i remove childList1 What i do ??? Should i renumber all the indexes ???
On the server side i use AutoPopulatingList. Because childList1 has been removed, AutoPopulatingList handles it as null. So on the initialization i do
List<Child> childList = new AutoPopulatingList(new ElementFactory() {
public Object createElement(int index) throws ElementInstantiationException {
/**
* remove any null value added
*/
childList.removeAll(Collections.singletonList(null));
return new Child();
}
});
This way, my collection just contains two child (without any null value) and i do not need to renumber all the indexes on the client side
About adding/removing you can see this link where i show a scenario wich can gives you some insight.
See also Grails UI plugin
Thanks,
Your answer brought some insight for me to do a wider search and I actually found a great post that covers all the inputs in my question. This is just a reference for anyone reading this. I will write a blog entry on how I implemented my case soon, but this link should provide a good source of ino with a working exmaple.
http://www.2paths.com/2009/10/01/one-to-many-relationships-in-grails-forms/
Most of the time I use ajax to manage such problem.
So when the user clicks add new phone I get the template UI from the server for manageability purpose ( the UI just same GSP template that I use to edit, update the phone), so this way you are not mixing your UI with your js code, whenever you want to change the UI you have to deal only with our GSP code.
Then after getting the UI I add it to the page using jquery DOM manipulation. Then after filling the form when they hit add(save) the request is sent to the server via ajax and is persisted immediately.
When the user clicks edit phone the same UI template is loaded from the server filled with existing phone data, then clicking update will update the corresponding phone immediately via ajax, and same thing applies to delete operation.
But one day I got an additional scenario for the use case that says, "until I say save contact no phone shall be saved on the backend, also after adding phones to the contact on the ui if navigate away to another page and come back later to the contact page the phones I added before must be still there." ugh..
To do this I started using the Session, so the above operations I explained will act on the phone list object I stored on the session instead of the DB. This is simple perform all the operation on the phonesInSession but finally dont forget to do this(delete update):
phonesToBeDeleted = phonesInDB - phonesInSession
phonesToBeDeleted.each{
contact.removeFromPhones(it)
it.delete()
}
I know I dont have to put a lot of data in session but this is the only solution I got for my scenario.
If someone has got similar problem/solution please leave a comment.
First, in all your input fields names you add an #:
<input type="text" name="references[#].name"/>
Second, add call a function before submitting:
<g:form action="save" onsubmit="replaceAllWildCardsWithConsecutiveNumbers();">
Third, this is the code for the function that you call before submitting the form:
function replaceAllWildCardsWithConsecutiveNumbers(){
var inputs = $('form').find("[name*='#']");
var names = $.map(inputs, function(el) { return el.name });
var uniqueNames = unique(names);
for (index in uniqueNames) {
var uniqueName = uniqueNames[index];
replaceWildCardsWithConsecutiveNumbers("input", uniqueName);
replaceWildCardsWithConsecutiveNumbers("select", uniqueName);
}
}
function unique(array){
return array.filter(function(el, index, arr) {
return index === arr.indexOf(el);
});
}
function replaceWildCardsWithConsecutiveNumbers(inputName, name){
counter = 0;
$(inputName + "[name='" + name + "']").each(function (i, el) {
var curName = $(this).attr('name');
var newName = curName.replace("#", counter);
$(this).attr('name', newName);
counter += 1;
});
}
Basically, what the code for replaceAllWildCardsWithConsecutiveNumbers() does, is to create a list for all input (or select) elements whose name contains an #. Removes the duplicates. And then iterates over them replacing the # with a number.
This works great if you have a table and you are submitting the values to a command object's list when creating a domain class for the first time. If you are updating I guess you'll have to change the value of counter to something higher.
I hope this helps someone else since I was stuck on this issue for a while myself.

Resources