Edit an Invoice XML Generation Event Script

The XML generation event script is used to customise or extend the original XML document generated by MineMarket. If extra nodes are required, the invoice XSD that the original XML document is based on must be modified to include the extra nodes. After the XSD has been edited, the Crystal Report RPT file data source must be refreshed in the design environment to display the new nodes.

The names of the default data items included in the invoice XML output can be determined from the InvoiceDataSet.xsd file.

Security Note: You need the Allow script maintenance and Allow the user to maintain object events user group security rights in the Scripting user group security rights group for this activity.

Activity Steps

  1. Open the Invoice Data XML Script (script editor).

    The Scripting Ribbon Tab displays.

  2. Set the UseScript method to set return true.
    Copy
    // <summary>
    // Will indicate to the system if the script must be used or not
    // </summary>
    public bool UseScript(Invoice inv)
    {
        return true;
    }
  3. Edit the C# script in the UpdateDataSet method to reflect the invoice xml object content requirements.
    Copy
    // <summary>
    // Enables to update the Core generated XML document that represents the invoice.
    // </summary>
    public XmlDocument UpdateDataSet(Invoice inv, XmlDocument originalDocument, ArrayList parameters)
    {
        return originalDocument;
    }

    The parameters received by this method are:

    • inv—The invoice itself
    • originalDocument—Contains the XML generated by MineMarket core, of type XmlDocument, which allows programmers to add, delete or update entries in the generated XML
    • ArrayList—An array of parameters, used to specify the parameters that need to be passed directly to the invoice RPT
  4. Append the public class code after any other public classes in the analyte formula code.
  5. Click the Build Script icon to compile the script.
  6. Click the Save Script icon on the Script Editor toolbar.

Changing the Value of an Existing Node

Example: Change the invoice name

  1. Add the following lines of code to the UpdateDataSet method. Note that the "InvoiceName" tag was obtained from the XSD structure that was used to create the InvoiceDefault.rpt file.
    Copy
    XmlNodeList list = originalDocument.GetElementsByTagName("InvoiceName");
    foreach(XmlNode node in list)
    {
    node.InnerText =" NEW NAME ";
    }

    Note that this does not change the invoice in the database. It is only changed in the generated XML object that is used to render the invoice report that displays and is subsequently printed.

Changing the Invoice XSD Structure

If a new field/node is added to the XML, the InvoiceDataSet.xsd also needs to be updated. New nodes can be added to the InvoiceDataSet.xsd structure using the XmlDocument class. To add a new node, use the CreateElement and CreateNode methods in this class. Note that when new nodes are created, the XSD that was used to create the RPT needs to be updated as well. The XSD that is used to create the RPT must have the same structure as the generated XML.

Example: Include ShippingDate and ArrivalDate on the invoice

  1. Modify the XSD definition. For this particular example, the fields must be placed at the despatch order level within the XSD as the values could be different per despatch order.
    Copy
    <xs:complexType name="DespatchOrder">
      <xs:sequence>
        <xs:element name="DespatchOrderName">
          <xs:simpleType>
            <xs:restriction base="xs:string">
              <xs:minLength value="1" />
              <xs:maxLength value="50" />
            </xs:restriction>
          </xs:simpleType>
        </xs:element>
        <xs:element name="InvoiceType" />
        <xs:element name="ContractName" type="xs:string" />
        <xs:element name="QuotaName" />
        <xs:element name="Despatch" type="xs:string" />
        <xs:element name="DespatchType" type="xs:string" />
    ...
        <xs:element name="DespatchOrderID" type="xs:int" />
        <xs:element name="ShippingDate" type="xs:string" />
        <xs:element name="ArrivalDate" type="xs:string" />
        <xs:element name="ContractDocumentRef" type="xs:string" />
        <xs:element name="ProductCode" type="xs:string" />
        <xs:element name="UnloadingPort" type="xs:string" />
        <xs:element name="UnloadingCountry" type="xs:string" />
        <xs:element name="Lots" type="Lot" minOccurs="0" maxoccurs="unbounded" />
      </xs:sequence>
    </xs:complexType>
  2. Save and update the RPT to include the fields. The new fields in the database should be auto-detected by the report builder. Then drag them onto the report.
  3. Modify the script to populate the new fields. Note that, by default, MineMarket builds an XML that matches the original XSD, so the new fields will not be part of the original XML document.
    Copy
    // <summary>
    // Enables to update the Core generated XML document that represents the invoice.
    // </summary>
    public XmlDocument UpdateDataSet(Invoice inv, XmlDocument originalDocument, ArrayList parameters)
    {
        // Get the despatch orders node from the original XMLDocument
        // XSD Reference = <xx:element name="DespatchOrders" type="DespatchOrder" minOccurs="0" maxOccurs="unbounded" />
        XmlNodeList despatchorders = originalDocument.GetElementsByTagName("DespatchOrders");
        foreach(XmlNode node in despatchorders)
        (
          XmlElement newElement = originalDocument.CreateElement("ShippingDate");
          XmlNode newNode = node.AppendChild(newElement)';
          newElement.InnerText = "test1";
          XmlElement newElement = originalDocument.CreateElement("ArrivalDate");
          XmlNode newNode = node.AppendChild(newElement2)';
          newElement2.InnerText = "test2";
        )
        return originalDocument;
    }
  4. Compile and run the report to test the new output.
  5. Now obtain actual data from MineMarket. Direct queries are not executed on the database because the MineMarket object model provides most of the needed data. Therefore, the despatch order will be determined from the XML and the required object will be instanced. The DespatchOrder object has a lot of methods to obtain the relevant dates (refer to the DespatchOrder class and go to Region #region Reference Date estimation methods). Two methods will be used from this region, one to get the ATD and one to get the ATA, which is what this example is trying to achieve.
    Copy
    // <summary>
    // Enables to update the Core generated XML document that represents the invoice.
    // </summary>
    public XmlDocument UpdateDataSet(Invoice inv, XmlDocument originalDocument, ArrayList parameters)
    {
      // Get the despatch orders node from the original XMLDocument
      // XSD Reference = <xx:element name="DespatchOrders" type="DespatchOrder" minOccurs="0" maxOccurs="unbounded" />
      XmlNodeList despatchorders = originalDocument.GetElementsByTagName("DespatchOrders");
      foreach(XmlNode node in despatchorders)
      (
        DespatchOrder order = null; // We need to obtain the despatchorder for this iteration, so will use the DespatchOrderID already in the xml
        XmlNode desporderid = node.SelectSingleNode("DepatchOrderID");
        if(desporderid != null)
        (
          foreach(InvDespatchTotal idt in inv.DespatchTotals)
          (
            //Compare the ID in the xml with the DO's of the invoice
            if(idt.DespatchOrder != null && idt.DespatchOrder.ID.ToString().CompareTo(desporderid.InnerText) == 0)
                                order = idt.DespatchOrder;
          )
          If(order != null)
          (
            //Add additional info at despatch order level
            //min(drpl.atd) As ShippingDate, so it is actual time of departure (origin)
            DateTime ATD = order.getDepartureFromOrigin(QuotationPeriodEstimationMode.Invoicing);
            XmlElement newElement = originalDocument.CreateElement("ShippingDate");
            XmlNode newNode = node.AppendChild(newElement);
            newElement.InnerText = ATD.ToString();
            //min(drpu.ata) As ArrivalDate, so it is actual time of arrival (destination)
            DateTime ATD = order.getArrivalAtDestination(QuotationPeriodEstimationMode.Invoicing);
            XmlElement newElement2 = originalDocument.CreateElement("ArrivalDate");
            XmlNode newNode2 = node.AppendChild(newElement2);
            newElement2.InnerText = ATA.ToString();
          )
        )
      )
      return originalDocument;
    }
  6. Compile and run the report to show the required dates.

Using a Completely Different Invoice XSD Structure

public XmlDocument UpdateDataSet(Invoice inv, XmlDocument originalDocument, ArrayList parameters)

Interrogating the Invoice Object to Determine the Type of Invoice

The invoice object can also be interrogated to determine which invoice type applies, and then use that to change the content and/or structure of the XML accordingly.

Example: A few different invoice types exist, such as a vessel invoice, truck invoice, domestic invoice, export invoice

Copy
If
{
  invoice is SalesInvoice
  or
  invoice is ServiceInvoice
}

Example: To go into more detail and determine the type of despatch associated with the invoice

Copy
If
{
  invoice.InvoiceDespatchTotals[0].DespatchOrder.GerfulfillmentDespatch() is Shipment
  or
  invoice.InvoiceDespatchTotals[0].DespatchOrder,GerFulfillmentDespatch() is Train
}

Database Login

A database login prompt reflects that there is a difference between the XSD and the generated XML. New fields can be added and bugs with the existing fields fixed without waiting for a new build. If a database login displays, it is because there is a problem in the XML that needs to be fixed. The database prompt is displayed by Crystal Reports and not by MineMarket because Crystal Reports validates that the XML corresponds to the XSD.

Subreports

The data listed in a subreport cannot be changed. The subreport is the responsibility of the RPT as it is managed internally. It should use data from the XML as the entry point (parameter) to run the subreport. If subreports are based on something not exposed in the XML, then it needs to be added using the event script and then the subreport needs to be developed.

Calling a DLL Instead of Calling a Report, with it Displaying in the Invoice Printing Area

In the Script Editor of the event, add the custom DLL.

Example: Using ClientCustom.dll

Then in the method, instance a class from that DLL and execute a custom method, ensuring that the method returns, and the XmlDocument is passed to the invoice screen for the invoice to be rendered, as follows:

Copy
using System;
using Mincom.MineMarket.DAL;
using Mincom.MineMarket;
using Mincom.MineMarket.Server;
using System.Data;
using System.Xml;
using CustomReports.Dll
/// <summary>
/// Exposes events for business objects
/// </summary>
public class InvoiceDataXMLScriptEvent : IInvoiceDataXMLScript
{
    public InvoiceDataXMLScriptEvent()
    {

    }
    #region IInvoiceDataXMLScript Members
    // <summary>
    // Enables to update the Core generated XML document that represents the invoice.
    // </summary>
    public XmlDocument UpdateDataSet(Invoice inv, XmlDocument originalDocument, ArrayList parameters)
    {
        originalDocument = CustomReports.GenXCmlDocument(inv);
    }

    // <summary>
    // Will indicate to the system if the script must be used or not
    // </summary>
    public bool UseScript(Invoice inv)
    {
        return true;
    }
    #endregion
}