Creating QBO3 C# Unit Test Classes

posted Jul 1, 2015, 10:51 AM by Eric Patrick   [ updated Jan 5, 2017, 5:43 AM ]

Creating a Unit Test

Application tier unit tests should be created to handle architect-level unit tests that are inappropriate or impractical for a power user to run via Jasmine.

Configuring app tier unit tests to establish a working QBO environment requires some extra configuration steps:
  • A working QBO3 website must be on the same machine that will be running unit tests: c:\inetpub\wwwroot
  • Pre and post-build events are used to copy the configuration files and DLLs from the working website to the test project's output directory
To create application-tier unit tests:
  • From Visual Studio, choose New Project > Visual C# > Tests > Unit Test Project:
    • Ensure that you have selected Templates > Visual C# > Unit Test Project
    • Ensure that the Name field adheres to the following pattern: qbo.*.Tests, where * is the Solution that you are testing. This is critical.
    • Ensure that the Solution field is set to: Add to solution
  • Create pre-build and post-build events
    • Open the Project properties (right-click on the Project and select Properties)
    • Navigate to Build Events
    • Populate the Build Events menu as shown, with the code snippets below
      • Pre-build:
md $(TargetDir)\Config
copy c:\inetpub\wwwroot\Config\*.* $(TargetDir)\Config
robocopy c:\inetpub\wwwroot\bin\ $(TargetDir) 
if %errorlevel% leq 3 exit 0 else exit %errorlevel%
      • Post-build:
copy c:\inetpub\wwwroot\web.config $(TargetDir)\$(ProjectName).dll.config

By convention, QBO3 unit tests should be created in Source > Trunk > qbo.3 > qbo.Core > Tests.  See the qbo.Core.Tests.sln in that folder for examples.

Note: originally, the pre- and post-build scripts leverages a .csproj variable called BaseWebSite. The intent of this was to allow different developers to locate their 'local configuration' wherever they wanted, and have the test project use that without deviating from source control. However, simply changing the .csproj file deviates from source control, so this never actually helped. Instead, make sure you have a 'good' install in c:\inetpub\wwwroot.

Testing Sugar

The qbo.Test.Helpers project contains a bunch of classes and extension methods that simplify your life as a developer.


This class does a few things to help you.
  • IPerson User: this is a QBO user that can be used when creating QBO3 classes. It defaults to, but you can override this value in your c:\inetpub\wwwroot\web.config if you wish.
  • string GetRandomString(): generates a short random string, typically use when naming new DB records (e.g. a DecisionTemplate, or a Contact)
  • Setup() / TearDown(): methods that create a new TransactionScope that database calls run under. By default, you can create DB records in your tests, and when the test completes, any DB changes will be rolled back, so you don't have to 'clean up'. This can be bypassed if necessary.

Part of qbo.Test.Helpers.Extensions, this method overrides an application setting (in test memory only).

using qbo.Test.Helpers.Extensions;
qbo.Message.Properties.Settings.Default.Override("DefaultFromAddress", Settings.Default.FromAddress, "");


This can be used to inspect what's happening with the QBO3 Queuing infrastructure. It is an in-memory queue, capable of pushing, popping, and executing items in a queue.

var item = new SmartWorklistMember(User) { ... };
// Wire a mock queue, and set Priority = 1 to trigger each queued message to also execute
SmartWorklistMemberObject.QueueConfig.Queues.Add(new Queue() Type = typeof(MockQueue), Name = "MockQueue", Priority = 1 });
// Call queue, and tell QBO3 to use our just-created MockQueue
item.Queue("AutoAssign", "MockQueue", "...".ToProperties());
// MockQueue.Items contains all the queued items, easily inspected for various properties


Part of qbo.Test.Helpers.ObjectConfigurationExtensions, this adds a statement "on the fly" to a QBO3 class, and wires it in memory (to handle inheritance, etc.).

var bindBySSN = contact.Configuration.MockStatement(new DbStatement()
Name = "AutoBindBySSN_Spec",
Query = "SELECT ContactID FROM Contact WHERE USSSN = @USSSN",
Parameters = new DbParameterCollection() 
new DbParameter() { Name = "USSSN", DbType = DbType.String },
new DbParameter() { Name = "SpecRequired", DbType = DbType.String }


Part of qbo.Test.Helpers.ObjectConfigurationExtensions, this adds an IService "on the fly" to a QBO3 class.

public class DummyService: AbstractService 
public override IServiceResult Invoke(IDictionary<string, object> parameters)
// do something easily tested from your test code here
PersonObject person = new PersonObject(User).Select<PersonObject>(User.UserID);

See Also

QDS 2.0 Core Branch / Tagging Build Procedures

posted Jun 18, 2013, 11:49 AM by Greg Kent   [ updated Jun 18, 2013, 11:50 AM ]

The following outlines the steps required to branch, tag and build for QDS 2.0 Core Deployments.

Create Branch From Trunk

·         Browse to

·         Right Click on qbo.Service (root for Service) -> TortoiseSVN->Branch/Tag…

·         To Path: /Branches/Services/{CurrentBranch} (Eg. 1.00

o   Add Message Eg. Branching 1.00

o   Keep default Settings “HEAD”

Download Branch Locally

·         Ensure you have a parent checkout folder that matches branch name

·         In target folder, Right Click -> SVN Checkout

·         At this point the project(s) can be built

Create Tag

·         Right click in branch root outside of a folder TortoiseSVN->Branch/Tag…

·         To Path: /Tags/services/{Branch}{RevisionNumber} Eg. /Tags/Services/1.01.00

o   Add Message Eg. Tagging 1.01.00 for release

o   Ensure HEAD revision is selected

Download Tag

·         Ensure /Tags/Services/{TagNumber} exists in file system. Eg. E:\Projects\Tags\Services\1.00.00

·         Ensure Export Directory contains project folder name. Eg. E:\Projects\Tags\Services\1.00.00\qbo.ServiceWeb

·         Right click TortoiseSVN->Export

o   Export specific projects:

o   qbo.Service.Core

o   qbo.Service.CoreWeb

o   qbo.ServiceWeb

Build From Tag

·         Build qbo.Service.Core.Setup in qbo.Service.Core

·         Build qbo.Setup.ServiceWeb in qbo.Service.CoreWeb

Amazon Windows Activation

posted Nov 29, 2012, 1:20 PM by Eric Patrick   [ updated Sep 19, 2013, 5:31 AM ]

Amazon instances that were initially created outside a VPC, then migrated into a VPC, may receive 'Windows is not Activated' error messages.

To activate Windows, you should:
  • Download the latest EC2 Services
  • Run the following from an administrative command prompt:
    cscript c:\windows\system32\slmgr.vbs -skms,, or

    cscript c:\windows\system32\slmgr.vbs -ato
  • Stop and restart the EC2Services
More details are at this AWS forum post.

IIS: Reading Certificates from C#

posted Nov 13, 2012, 4:20 PM by Eric Patrick

When instantiating an X509 digital certificate from code (using System.Security.Cryptography.X509Certificates.X509Certificate2), IIS on Windows 7 was working, but on Windows 2008 Server was raising an error:

An internal error occurred.

This was caused by IIS not having access to user profile stores (even though our code was reading from a file and not the certificate store).

The fix was to modify the IIS application pool (Advanced Settings) to load the user profile for the application pool identity. Note that the certificate need to be IN the subject user profile; the X509Certificate code must attempt to access the user profile regardless of where you are loading the certificate from.

Amazon SQL Server Configuration

posted Sep 21, 2012, 9:50 AM by Greg Kent   [ updated Oct 15, 2013, 8:26 AM by Kevin Foley ]


This post serves as a placeholder for information related to configuration and continual maintenance for SQL Servers in the EC2 Environment.

Configuring New Database On Amazon SQL Server

Please ensure that you consult Kevin Foley whenever creating a new database in ANY Amazon environment when taking the steps below.


The new pattern is to allocate and dedicate a single drive for a given database. This is accomplished by:

  • Creating the drive/volume in Amazon and adding to the EC2 instance
  • Configuring drive in windows instance using disk manager
  • Restoring database to new drive

Step 1 - Creating the volume in Amazon


  • Click "Create Volume" button
  • Choose Size. 100 GB seems to be popular
  • Ensure Availability Zone is the same zone as the EC2 instance you wish to add volume to.
  • No Snapshot - This is an empty drive
  • Volume Type-> Choose "Standard". Note Provisioned IOPS is a SSD drive
  • Choose EC2 SQL Server instance

Step 2 - Configuring new drive in Windows

From RDP session on SQL Server EC2 Instance
  • Start->Computer->Right Click->Manage
  • Server Manager->Disk Management
  • Note new Disk will appear as "Disk {x}" and shows as "Unallocated". If Disk is offline, Right click and choose "Online"
  • Right click Disk Section->New Simple Volume
  • Choose default size->Next
  • Assign a drive letter that is unused (Eg. "G")
  • Format as NTFS. Ensure you assign a volume label that is representative of the new database name (eg. Stratus). Ensure "Perform a quick format" is checked and "Enable file and folder compression" is NOT checked
Note: if a newly allocated disk is shown offline, and the context menu has the 'Online' option disabled, follow these disk partitioning instructions.

Step 3 - Restoring database to new drive

Create Folders for target file groups

From new drive, create folders in root. These will be used in restore script.
  • SQLBlobs
  • SQLData
  • SQLIndexes
  • SQLPrimary
  • SQLTranlog

Execute Restore Script

This script will create a new database on DBMS from restoring a backup and maps filegroups to new drive. It will also rename file groups to reflect new database name.

Download Database.Restore.sql

This script will need to be run separately in 3 steps. Note the steps are differentiated by comments.
  • Ensure database name reflects new target database
  • Ensure the drive references in steps 1 match the new drive path
  • Ensure the new name references in step 3 match the new database name
Run script by highlight steps and running each step at a time

Optional steps configuring OPENROWSET functionality to Excel/Office files in SQL 2008/12 64bit

Install the Microsoft Access Database Engine 2010 Redistributable, hotfix and service pack update. 

Please note that the hotfix must be installed BEFORE the service pack.
All items should be installed via "run as admin".
All files can be found in SVN under /Trunk/DataCenter/SQL

1) Install the engine: AccessDatabaseEngine_x64.exe

2) Verify obsolete MSO.DLL version is  14.0.4760.1000 as found in the folder below
C:\Program Files\Common Files\Microsoft Shared\OFFICE14 

3) Install the hotfix: office2010-kb2516475-fullfile-x64-glb.exe
Verify new MSO.DLL version is  14.0.5137.500 ( properties / details )

4) Install the service pack : accessdatabaseengine2010sp1-kb2460011-x64-fullfile-en-us.exe
Verify MSO.DLL version is still 14.0.5137.500 is retained.


Update SQL via SSMS 

USE [master]

sp_configure 'show advanced option', 1

sp_configure 'Ad Hoc Distributed Queries', 1

EXEC master.dbo.sp_MSset_oledb_prop N'Microsoft.ACE.OLEDB.12.0', N'AllowInProcess', 1

EXEC master.dbo.sp_MSset_oledb_prop N'Microsoft.ACE.OLEDB.12.0', N'DynamicParameters', 1

-- The two statements below are for future use if we ever want to insert into an existing XLS file
-- None of our servers currently have this functionality and it has not been tested.
EXEC master.dbo.sp_MSset_oledb_prop N'Microsoft.ACE.OLEDB.12.0', N'NestedQueries', 1
EXEC master.dbo.sp_MSset_oledb_prop N'Microsoft.ACE.OLEDB.12.0', N'NonTransactedUpdates', 1

Update registry via REGEDIT and add the key below.


Where DisallowAdhocAccess  is a REG_DWORD set to 0

Please note that the path "\MSSQL10_50.MSSQLSERVER\" will change by SQL version.
You can find the exact path by locating the "AllowInProcess" keys that were created by the above script.

Verify that the query works.
Sample query

'Excel 12.0;Database=D:\Documents\\NewOrderBatch\Sample.xls', 
'SELECT * FROM [Sheet1$]')

REBOOT and verify the query still works.

Amazon Web Services Configuration

posted Jul 18, 2012, 12:47 PM by Eric Patrick   [ updated Feb 10, 2016, 9:25 AM ]


Quandis now hosts significant infrastructure in the Amazon Web Services cloud. Key reasons include:
  • low cost of creating disaster recovery sites in different regions
  • ease and speed of launching new virtual machines
  • effectively unlimited storage
  • cheap fail-over and load balancing solutions


Amazon has data centers in different regions, or parts of the world. Quandis currently has infrastructure in us-east (Virginia), and us-west-1 (California). Each region includes data centers split into availability zones (which should be thought of as different buildings). Best practice in creating load balanced environments includes load balancing instances in different availability zones. Quandis currently leverages:
  • us-east-1a
  • us-east-1d

Networking includes the creation of a virtual private cloud (VPC), which is a block of private IP addresses. Quandis uses 10.0.* (masked as Think of a VPC as a firewall. Within a VPC, Quandis has several subnets, and can use security groups and routing tables to control what traffic the VPC "firewall" will allow between subnets and to or from the public internet. For example:
  • (10.0.0.*): this subnet is used for our UAT instances, and for our Network Address Translation (NAT) instance
    • this subnet routes to the internet via an internet gateway
  • (10.0.1.*): this subnet is used for our SQL instances
    • this subnet routes to the internet via our NAT instance
  • (10.0.2.*): this subnet routes our outbound traffic for our production servers in us-east-1a
    • this subnet routes to the internet via our NAT instance
    • traffic like geocoding against Google, or downloading Windows Updates, goes across this subnet
  • (10.0.3.*): this subnet routes our inbound traffic for our production server in us-east-1a
    • this subnet routes to the internet via our internet gateway
    • traffic from load balancers to our production web servers goes across this subnet
  • (10.0.4.*): same as, except in availability zone us-east-1d
    • (10.0.5.*): same as, except in availability zone us-east-1d
    Each virtual machine in AWS is called an instance, and normally is assigned only private IP addresses.

    Security Groups are rules that dictate firewall rules for subnets, instances or network interfaces. They control inbound and outbound traffic, ports and such by source and destination. When configuring new subnets, the following security groups are relevant:
    • NATSG: add each private subnet (allowing outbound connections without public IPs being assigned to the box)
    • VPCWebSG: add all subnets (so they can communicate with each other)

    Public IP Addresses

    Amazon Web Services limit the number of dedicated public IP addresses (elastic IPs) to five per organization. One can get dynamically assigned public IP addresses, but there is no guarantee that an instance will retain the same  dynamically assigned public IP over time. Thus, one cannot reliably use dynamically allocated public IPs for DNS entries.

    Amazon provides a solution for this problem, as long as Amazon is hosting our domain. As of this writing, is hosted by the Amazon Route 53 domain name service. Route 53 allows us to map a third-level domain to a load balancer, without assigning a public IP address. It's just an 'alias', pointing to a load balancer's name. If the load balancer is assigned new IP addresses (which can happen as new instances are added to the load balancer), no DNS modifications need to be made.

    Thus, production instances hosted in AWS will leverage this feature, meaning they will need to end with ''. 

    Unfortunately, one cannot assign a third-level domain directly to an instance; only to a load balancer. Thus, to do a permanently reliable DNS entry for a UAT site, we either need to:
    • front the UAT site with a load balance, and leverage Route 53, or
    • we need assign one of our 5 elastic IP addresses to the site

    Lessons Learned

    Private instances need 2 NICs

    Instances that have only a private IP address cannot initiate outbound internet traffic unless they are on a subnet that routes through a NAT. We initially configured instances on the 10.0.0.* subnet, only to find that geocoding (which hits Google) failed. This led us to create instances on the 10.0.2.* subnet (so they could geocode). Ultimately, production web servers should be configured with two NICs: one to a subnet routing to a NAT (for outbound traffic), and one to a subnet routing to an internet gateway (to handle inbound traffic from load balancers).

    For our code that initiates outbound web traffic (via HttpWebRequest, such as geocoding), we need to tell Windows to route such traffic through the NAT NIC. (Nic nat patty whack?) This is accomplished by adding permanent routes from the command prompt:
    • route -p change mask metric 2
      • this routes traffic through the gateway, and thus to the NAT, first
    • route -p add mask metric 13
      • this allows traffic through the gateway, but at a lower priority than the gateway

    Load balancers must use subnets that route to an internet gateway

    Load balancers must communicate with instances on subnets that route through an internet gateway (10.0.3.* or 10.0.5.*). When we configured the load balancer to use the 10.0.2.* subnet (which routes through a NAT), no sites responded. This led us to create the 10.0.3.* subnet, and add a NIC to the production instances. With two NICs, they can both respond to inbound load balancer traffic, and route outbound requests to Google for geocoding.

    Even after changing the subnets the load balancer was using, we found the site would sporadically be offline. This appears to have been a caching issue:
    • When we added the 10.0.2.* subnet, AWS automatically created a NIC on the load balancer bound to a public IP and 10.0.2.* private IP
    • When we added the 10.0.3.* subnet, AWS automatically created a NIC on the load balancer bound to a public IP and 10.0.3.* private IP
    • At this stage, the load balancer had two public IPs; which one a browser "got" was essentially random
    • If a browser "got" the public IP for the 10.0.2.* load balancer NIC, nothing would respond
    • Load balancer NICs cannot be explicitly created, but they can be explicitly deleted.

    Report Configuration in QBO 2.0

    posted Jan 31, 2012, 9:34 AM by Eric Patrick


    The QBO 2.0 Report module allows generation of reports in output in HTML, XML, Excel, Crystal Reports, and via "plug-in" pages.

    Each report comprises:
    • a Report name (Report.Report)
    • one or more ReportQuery rows, each of which is usually a stored procedures (though they could be web service calls)
    • a Prompt Url: a page that prompts users for parameters needed to run the report
    • a Prompt Xslt: an optional plugin XSLT that drives the layout of a parameter page
    • a Result Url: a Url that accepts the report parameters, executes the report, and renders the results
    • a Result Xslt: an optional plugin XSLT that drives the transformation of the resulting ADO.NET DataSet into some other format
    • a MimeType: optionally used by the Result Url to determine the mime type delivered to the browser

    Prompt URL

    The qbo.ReportWeb module includes the following "standard" URLs for prompting users for parameter data:
    • ReportPrompt.aspx: deprecated; do not use
    • ReportPrompt.Auto.aspx: this will scan SQL tables for stored procedure parameters, and prompt the user with any parameters found
    • ReportPrompt.Object.aspx: this is for "object-centric" reports, and prompt only for an Object/ID
    • ReportPrompt.Legacy.aspx: deprecated; do not use

    Prompt XSLT

    If a Prompt XSLT is specified, the ReportPrompt.Auto.aspx will transform the Report row from the database against the Prompt XSLT, enabling the power user to control precisely what prompts the user sees.  For complex or drop-down list based parameters, this is preferable.

    Result URL

    The qbo.ReportWeb module includes the following "standard" URLs for rendering data:
    • ReportViewer.Html.aspx: renders the data set as an HTML table. If a Result Xslt is specified the data set is transformed against the xslt.
    • ReportViewer.Xml.aspx: transforms the data set against a Result Xslt, and delivers the results using the Mime Type specified by the Report.
    • ReportViewer.MSExcel.aspx: delivers the data set as an Excel mime type
      • if no XSLT is specified, simply HTML tables are delivered, which Excel interprets as worksheets
      • if an XSLT is specified, it should produce Excel Xml
    • ReportViewer.CR.aspx: pass the ADO.NET dataset to a Crystal Report .rpt file, and exports the data in accordance with the Report Mime Type
      • the Prompt XSLT should point to a Crystal Report .rpt file
      • the Mime Type must specify a mime type that CR is capable of exporting to (Word, Excel, PDF, etc.)

    • The ReportEdit.aspx page includes a Report Output series of radio buttons; there buttons merely use javascript to pre-set the PromptURL and ResultURL. It is often more appropriate to key these values yourself.

    Attachment To PDF Documentation

    posted Jan 24, 2012, 4:50 PM by Unknown user   [ updated Mar 8, 2012, 4:05 PM by Greg Kent ]

    The latest version of the Attachment To PDF service is now stand alone. It no longer requires any interaction with the QDS site and can be hosted locally.
    Prior to deploying the AttachmentToPDF plugin the WebSupergoo ABCpdf.NET library must first be installed. It can be obtained from this site: The current version of the AttachmentToPDF plugin (released 01/24/2012) supports ABCpdf.NET 8.1. Both 32Bit and 64Bit versions are available. Please us the 32Bit version. If you need to use the 64Bit version please contact QDS support at
    A license will also need to be purchased from WebSupergoo.
    Once the ABCpdf.NET library has been installed the qbo.Attachment.ABCPdf.dll will need to be copied to the C:\Program Files (x86)\Quandis\Quandis Business Objects v2.0.0\Bin folder.
    The plugin supports ASP.NET v2 and ASP.NET v4
    This plugin can be used as both an Attachment Template or an Event end point as a local assembly.
    Attachment Template

    To use the plugin with an AttachmentTemplate first register the dll in the AttachmentTemplate.config

       <AssemblyFile>C:/Program Files (x86)/Quandis/Quandis Business Objects v2.0.0/Bin/qbo.Attachment.ABCPdf.dll</AssemblyFile>

    Then associate the plugin with an AttachmentTemplate. To Do so select it from the "Attachment Service" drop down.
    The plugin expects an xml payload. At a minimum the xml must conform to the following:


          <!--Html Content goes here-->

    The <Method> element will contain the type of PDF operation requested. The details of this element will be covered below.
    Typically the xml payload is produced by associating an xslt file with the Attachment Template which will also incorporate any associated report data and output the required xml specified above.
    If you wish to override the default PDF generation settings you may include optional override values in a <PDFAttributes> element.

           <!--Html Content goes here-->

    The details of these override elements will be covered below.
    To use the plugin with an event you must first register the dll in the Event.config
    • Design -> Configuration -> Commands -> Event Configuration
    • Name: HtmlToPDF     (LocalEvent)
    • Assembly File: C:/Program Files (x86)/Quandis/Quandis Business Objects v2.0.0/Bin/qbo.Attachment.ABCPdf.dll
    • Assembly Name: HtmlToPDF
    • Assembly Namespace: qbo.Attachment.ABCPdf 

       <Description />
       <AssemblyFile>C:\Program Files (x86)\Quandis\Quandis Business Objects v2.0.0\Bin\qbo.Attachment.ABCPdf.dll</AssemblyFile>


    Then associate the event assembly with an Event. It should appear as one of the radio button options.
    The event end-point expects an xml AttachmentCollection payload. At a minimum it must conform to the following:

       <Method Name="">

                <!--More than one AttachmentItem can be included in the request. The PDF gneration engine will iterate through each and convert them to PDFs-->
                   <!--***ONLY 1 of the 3 elements listed below should be provided***-->
                   <Content><!--Base 64 encoded html document--></Content>
                   <BodyHTML><!--Raw Html data--></BodyHTML>
                   <BodyText><!--Raw Html data--></BodyText>     

    The "Name" attribute of the <Method> element will contain the type of PDF operation requested. The details of this element will be covered below.
    Typically the xml payload is produced by associating an xslt file with the Event which will also incorporate any associated report data and output the required xml specified above.
    If you wish to override the default PDF generation settings you may include optional override values in a <Parameters> element.

       <Method Name="">

                <!--More than one AttachmentItem can be included in the request. The PDF gneration engine will iterate through each and convert them to PDFs-->
                   <!--***ONLY 1 of the 3 elements listed below should be provided***-->
                   <Content><!--Base 64 encoded html document--></Content>
                   <BodyHTML><!--Raw Html data--></BodyHTML>
                   <BodyText><!--Raw Html data--></BodyText>    
    The details of these elements will be covered below.
    Supported Methods
    The AttachmentToPDF plugin supports the following operations:
      • This operation converts Raw Html or Base64 binary html data from within a content node in an AttachmentItem to a PDF document.
      • This operation merges multiple PDF attachments within an attachment collection into a single PDF.
      • Attachments are appended from 1st to last. That is to say the 2nd attachment is appended to the end of the 1st. The 3rd is appended to the merged 1st and 2nd attachments, the 4th is appended to the merged 1st, 2nd, and 3rd attchments etc.
      • The final merged attachment will have the same name as the 1st AttachmentItem's <Attachment> element prefixed with "MergedPDFDoc_" and with the existing file extension replaced with "_.pdf". If you wish to change this value it is recommended that you use a poste-event transformation and replace the <Attachment> element's value as desired. The remaining AttachmentItem properties will remain unmodified. Only a single merged PDF will be returned within a single AttachmentItem.
      • This operation converts Raw Html or Base64 binary html data from within a content node in an AttachmentItem to a PDF document. It then merges the resulting PDF attachments into a single PDF.
    ***When the AttachmentToPDF plugin is used in an Attachment Template it only supports the HTML_ATTACHMENT_TO_PDF operation.
    The operation name is to be placed within the <Method> element when the AttachmentToPDF plugin is used in an Attachment Template.
    When using the AttachmentToPDF plugin from an event the operation name is to be placed in the "Name" attribute of the <Method> element.
    Optional PDF generation override properties

    Listed below are the AttachmentToPDF plugin properties. Each property has a default value that is automatically set. The default values maybe overridden as described below.

    • Landscape
      • Determines if the PDF produced is in Landscape mode or Portrait mode.
      • Accepts "true" or "false".
      • The DEFAULT value is "false".
    • PaperSize
      • Determines the paper size for each of the pages within the PDF
      • Accepts the following values:
        • "A3"
        • "A4"
        • "A5"
        • "B4"
        • "B5"
        • "Letter"
        • "Legal"
        • "Tabloid"
        • "Ledger"
        • "Statement"
        • "Executive"
        • "Folio"
        • "Quarto"
        • "10x14"
      • The DEFAULT value is "Letter"
    • AddFooter
      • Adds a footer to the bottom of each PDF page. Accepts "true" or "false".
      • The DEFAULT value is "true".
    • Footer
      • The default footer value can be overriden by providing a value in the <Footer> element. Doing so automatically sets the <AddFooter> value to "true".
      • Accepts a string value.
      • The default footer value is: "© 2004 - <CurrentYear> Quandis, All Rights Reserved. http: //"
    • AddPageNumber
      • Adds a page number to the bottom of each page.
      • Accepts "true" or "false".
      • The DEFAULT value is "false".
    • AddStandardFonts
      • When a client opens the PDF Acrobat will attempt to find the exact same font on the client system. If the exact font is not available then a substitute font will be chosen using the font description to determine the best match.
      • The following fonts are guaranteed to be available on every system.
        • Times-Roman 
        • Times-Bold
        • Times-Italic    
        • Times-BoldItalic 
        • Helvetica 
        • Helvetica-Bold     
        • Helvetica-Oblique       
        • Helvetica-BoldOblique     
        • Courier   
        • Courier-Bold   
        • Courier-Oblique    
        • Courier-BoldOblique     
        • Symbol     
        • ZapfDingbats
      • Accepts "true" or "false".
      • The DEFAULT value is "false".
      • ***If you wish to add a font not included in this list contact
    • EmbedStandardFonts
      • The font name, a description of the font style and the the font glyph descriptions themselves are placed into the document. This ensures that the document will always display perfectly on every system and that font substitutions will never need to be made. There are a number of reasons you may not wish to embed fonts and instead use the AddFont method. Embedding fonts can increase the size of your PDF considerably. Additionally by distributing a PDF with an embedded font you are actually redistributing the font itself. You should check with your font supplier or legal department that you have permission to do this.
      • Accepts "true" or "false".
      • The DEFAULT value is "false".
      • ***If you wish to embed a font contact
    • SaveTemporaryFiles
      • Whenever a PDF is generated a copy of the raw html file and the produced PDF file are created on the file system using globally unique names. During normal operations these two files are automatically deleted as soon as operation is completed. Being able to view these files can be an aid in trouble shooting. Setting this value to "true" will prevent the operation from deleting these files.
      • Accepts "true" or "false".
      • The DEFAULT value is "false".
    • Credentials
      • In the event that images referenced in the raw html are located on a secured website, a user name and password can be provided to use for authentication. Once logged in ABCpdf stays logged in until the session times out. These properties are related to authentication methods built into HTTP. Methods like Basic (forms) Authentication and Windows Integrated Authentication. If the html page requires ASP.NET forms authentication please contact
      • The <Credentials> element contains two child elements; <UserName> and <Password>.
      • Each accepts a string value.
      • The DEFAULT behavior is not to use authentication.
    • TempPath
      • As mentioned above, whenever a PDF is generated a copy of the raw html file and the produced PDF file are created on the file system using globally unique names. The location of these temporary files can be overridden by changing the value of the <TempPath> element. Provided that the new value is a valid file path the operation will attempt to create it if it does not already exist.
      • Accepts a string value which should be a valid file path.
      • The DEFAULT location is "D:\Websites\temp\AttachmentToPDF_ScratchPad".
      • This value can also be overriden in the appsettings.config file: <add key="qbo.ABCpdf.TempPath" value="D:\Websites\temp\AttachmentToPDF_ScratchPad4" />.
    • ImageQuality
      • The quality of compression acceptable for continuous tone images such as JPEGs embedded within the PDF. Qualities should range between 0 and 100 (75 is a reasonable value). Values higher than 100 will result in lossless compression being used in all situations.
      • Accepts an integer value between 0 and 100.
      • The DEFAULT value is "75".
      • This value can also be overridden in the appsettings.config file: <add key="qbo.ABCpdf.ImageQuality" value="75" />.
    • TimeOut
      • The maximum amount of time allowed for obtaining a page. Increase this value if you have a large page of a large number of images, or large images.
      • Accepts an integer value.
      • This value is measured in milliseconds.
      • The DEFAULT value is 15500.
      • This value can also be overridden in the appsettings.config file: <add key="qbo.ABCpdf.TimeOut" value="20000" />.
    • RetryCount
      • The number of times a page should be retried if unavailable or invalid. The ABCpdf.NET library has a built in web browser that it uses to process the Html payload. HTML rendering may fail one time but succeed the next. This is often for reasons outside the control of ABCpdf. So ABCpdf may attempt to re-request a page if it is not immediately available. This is analogous to clicking on the refresh button of a web browser if the page is failing to load.
      • Accepts an integer value.
      • The DEFAULT value is 5.
      • This value can also be overriden in the appsettings.config file: <add key="qbo.ABCpdf.RetryCount" value="10" />.
    • AddLinks
      • This property determines whether links in HTML are reproduced as links in the final PDF output. Links which are not live look exactly like links but do not link through to a destination when you click on them. Live links are reproduced exactly as specified on the page and link through to the web pages specified. These web pages will open in a new browser window.
      • Accepts "true" or "false".
      • The DEFAULT value is "true"
    Samples and DLLs
    The DLL can be found in "Source\\CompiledOutput\"
    The file name is qbo.Service.AttachmentToPDF_20.dll
    Samples seed scripts can be found in "Source\\qbo.Core\Database Tier\qbo.Database.Attachment\Create Scripts"
    You will need these two files for the Attachment Template:
    •  Minimal PDF.xslt
    • Setup.AttachmentTemplate.HtmlToPdf.xml
    You will need this file for the Event 
    • Setup.Event.HtmlToPDF.xml
    Changing Scratchpad path

    By default, abc.pdf will write temp files to a default scratchpad location in C:\windows\temp\. QDS has observed the component does not always clean up after itself and files accumulate. This can put the OS drivespace at risk. You can optionally override this folder by adding registry key:

    For 32 bit IIS running on x64 (default)
    • regedit
    • HKLM/SOFTWARE/Wow6432Node/WebSupergoo/ABCpdfNET/7
    • Add string value->"TempDirectory"
    • We use D:\temp\AttachmentToPDF\ScratchPad\
      • Ensure path exists and users have full control
    • IIS reset will clear the folder regardless of where the path is set

    Error Handling in QBO 2.0

    posted Dec 29, 2011, 6:37 AM by Eric Patrick

    QBO 2.0 ASPX pages all derive from a custom qbo.Web.Page class. This page include wrapping any untrapped errors in a qbo.Error.Exception, which is logged to an error folder in accordance with configuration settings.

    The following components enable customization of error behavior.

    qbo.ErrorFolder App Setting

    This app setting will determine what folder error messages are logged to. The qbo.Error classes will create a new folder for each day, and log error messages as xml files to this folder. Note that the Windows account under which IIS is operating (typically Network Service) must have full control of this folder to successfully write error messages.

    Sample setting (from /Config/AppSettings.config):

    <!-- qbo.ErrorFolder:
    Physical path to stored error logs in.  This folder must be manually created.
    The default error handler do not attempt to create this folder dynamically.
    Setup Note: Ensure the web account (typically Network Service or IIS_IUSRS) has full control of this path.
    <add key="qbo.ErrorFolder" value="c:\program files\Quandis\Quandis Business Objects v2.0.0\Logs\\"/>

    Custom Error Page

    The qbo.ErrorWeb.ErrorTrap.aspx page will provide for customized error message displays to the end user. This leverages the standard ASP.NET <customErrors> functionality available in web.config.  When ASP.NET redirects to this page, the page will transform a raw XML version of the error message into a "friendly" error message for the end users.

    Sample setting (from web.config):

    <customErrors mode="RemoteOnly" defaultRedirect="/Error/ErrorTrap.aspx"/>

    Customizing Error Messages

    The ErrorTrap.aspx page will accept either a parameter called 'ID', or a cookie called 'qbo.Error.ID', representing a relative path to the error file. This error file is read into an XML document, and transformed against /Templates/Error/ErrorMessage.xslt. 

    To customize error messages, modify the ErrorMessage.xslt. Examples are found in the ErrorMessage.xslt in source control. For example, if a user attempts to manually "hack" a URL to navigate to a Loan they don't have access to, the pLoanSelect stored procedure will return zero rows, but the LoanObject class expects a row to be returned. The raw error message includes, "There is no row at position 0", but we would prefer to display to the user, "You attempted to access data that no longer exists, or that you do not have permission to access."

    SubVersion Server Reboot

    posted Dec 20, 2011, 11:13 AM by Travis Croxford   [ updated Jul 9, 2015, 9:01 AM by Kevin Cassidy ]

    In the event of the SubVersion server getting rebooted, the following 2 steps must be performed to restore access to our repository:
    • sudo mount /dev/sdf /mnt/svn
    • sudo mount /dev/sdg /mnt/deploy
    • sudo service httpd restart
    • The server address is (SSH on port 22)
    • To specify the private key for authentication:
      • Along the left side of putty, expand the Connection settings
      • Expand the SSH
      • Choose Auth
      • Browse to the private key (See Eric, Kevin, or Travis for a copy of our private key)
    • Save your settings
      • Click on the Session option under Category
      • Enter a name under the Saved Sessions label
      • Click Save
    • Connect

    1-10 of 27