如何通过Print management 打印SSRS报表(how-to-print-ssrs-report-using-print-management)

报表打印管理Print management

添加一个新的Report format

如下图为Customer invoice添加两个新的Report format

图中的两个Report format可以通过添加一下代码可以实现

[ExtensionOf(classStr(PrintMgmtReportFormatPopulator))]
public final class PrintMgmtReportFormatPopulator_Demo_Extension
{
    #PrintMgmtSetup
 
    protected void addDocuments()
    {
        // Add new customized reports
        this.addOther(PrintMgmtDocumentType::SalesOrderInvoice, ssrsReportStr(DocSalesInvoice, Report), ssrsReportStr(DocSalesInvoice, Report), #NoCountryRegionId);
        this.addOther(PrintMgmtDocumentType::SalesOrderInvoice, ssrsReportStr(DocSalesInvoiceExt, Report), ssrsReportStr(DocSalesInvoiceExt, Report), #NoCountryRegionId);
        // Add the standard report design, because of PrintMgmtDocType.getDefaultReportFormatDelegate()
        this.addOther(PrintMgmtDocumentType::SalesOrderInvoice, ssrsReportStr(SalesInvoice, Report), ssrsReportStr(Sal
        next addDocuments();
    }
}

下次打开Print management setup的时候,PrintMgmtReportFormat 表中就会添加了 2 条新记录。

改变默认的 Report format

如果您需要更改Original/Copy preview的默认的 Report formatMicrosoft 建议修改PrintMgmtDocType.getDefaultReportFormatDelegate()

class THK_PrintMgmtDocTypeEventHandler
{
  [SubscribesTo(classStr(PrintMgmtDocType), delegateStr(PrintMgmtDocType, getDefaultReportFormatDelegate))]
  public static void PrintMgmtDocType_getDefaultReportFormatDelegate(PrintMgmtDocumentType _docType, EventHandlerResult _result)
  {
      switch (_docType)
      {
          case PrintMgmtDocumentType::SalesOrderInvoice:
              _result.result(ssrsReportStr(DocSalesInvoice, Report));
              break;
      }
  }
}

打印Original/Copy preview的时候,系统目前仍然会打印默认的Report format,而非用户下拉选择的Format(Bug?),因此需要通过一下代码修复:

class THK_PrintMgmtDocTypeEventHandler
{
  [SubscribesTo(classStr(PrintMgmtDocType), delegateStr(PrintMgmtDocType, getDefaultReportFormatDelegate))]
  public static void PrintMgmtDocType_getDefaultReportFormatDelegate(PrintMgmtDocumentType _docType, EventHandlerResult _result)
  {
      switch (_docType)
      {
          case PrintMgmtDocumentType::SalesOrderInvoice:
              _result.result(ssrsReportStr(DocSalesInvoice, Report));
                  PrintMgmtReportFormatName formatName = THK_PrintMgmtDocTypeEventHandler::getPrintMgmtReportFormat(_docType);
                  if (formatName)
                  {
                      _result.result(formatName);
                  }
                  else
                  {
                      _result.result(ssrsReportStr(SalesInvoice, Report));
                  } 
              break;
      }
  }
  public static PrintMgmtReportFormatName getPrintMgmtReportFormat(PrintMgmtDocumentType    _printMgmtDocumentType)
  {
      PrintMgmtDocInstance printMgmtDocInstance;
      PrintMgmtReportFormat printMgmtReportFormat;
      PrintMgmtSettings printMgmtSettings;
 
      select firstonly DocumentType, RecId from printMgmtDocInstance
      where printMgmtDocInstance.DocumentType == _printMgmtDocumentType
      join ParentId, ReportFormat from printMgmtSettings
          where printMgmtSettings.ParentId == printMgmtDocInstance.RecId
      join RecId, DocumentType, Name from printMgmtReportFormat
          where printMgmtReportFormat.RecId == printMgmtSettings.ReportFormat
          && printMgmtReportFormat.DocumentType == _printMgmtDocumentType;
 
      return printMgmtReportFormat.Name;
  }
}

添加一个新的Document type

通过以下步骤可以添加一个新的Document type

创建PrintMgmtDocumentType的Coc扩展,添加一个新的节点

创建Node class ( 例如PrintMgmtNode_Purch)中方法getDocumentTypes的Coc扩展 

[ExtensionOf(classStr(PrintMgmtNode_Purch))]
public final class PrintMgmtNode_Purch_Demo_Extension
{
    /// 
 
    /// Gets a list of valid document types for the node.
    /// 
 
    /// 
    ///    A list of valid document types for the node.
    /// 
    /// 
    /// The results can vary based on what is configured in the application because configuration keys are
    ///    used to determine this list.
    /// 
    public List getDocumentTypes()
    {
        List ret = next getDocumentTypes();
 
        ret.addEnd(PrintMgmtDocumentType::customPurchPromptLetter);
 
        return ret;
    }
 
}

之后就可以在Print management 中看到我们新加的节点了

创建新的Form letter类

这个类用来返回 hierarchy 和前面创建的document type

[PrintMgmtDocumentTypeFactoryAttribute(PrintMgmtDocumentType::customPurchPromptLetter)]
public class customPurchFormLetterReport_PromptLetter extends PurchFormLetterReport
{
    /// 
 
    /// Returns the default printer settings for the specified PrintSetupOriginalCopy enumeration value.
    /// 
 
    /// 
    /// The PrintSetupOriginalCopy enumeration value that specifies whether the Original
    /// or Copy destinations should be retrieved.
    /// 
    /// 
    /// The default printer settings for the specified PrintSetupOriginalCopy enumeration value.
    /// 
    /// 
    /// The general pattern for implementing this method is to use the printer destinations from the appropriate
    /// FormLetter class.  These printer destinations will be used if no Print Management destinations are
    /// found or used.
    /// 
    protected container getDefaultPrintJobSettings(PrintSetupOriginalCopy _printCopyOriginal)
    {
        if (_printCopyOriginal == PrintSetupOriginalCopy::Original)
        {
            return PurchFormLetter::getPrinterSettingsFormletter(DocumentStatus::None,PrintSetupOriginalCopy::Original);
        }
        else
        {
            return PurchFormLetter::getPrinterSettingsFormletter(DocumentStatus::None,PrintSetupOriginalCopy::Copy);
        }
    }
 
    /// 
 
    /// Returns the PrintMgmtDocumentType enumeration value that specifies what document this FormLetterReport class controls.
    /// 
 
    /// 
    /// The PrintMgmtDocumentType enumeration value that specifies what document this FormLetterReport class controls.
    /// 
    /// 
    /// This value is used to retrieve the appropriate Print Management settings for the report.
    /// 
    public PrintMgmtDocumentType getPrintMgmtDocumentType()
    {
        return PrintMgmtDocumentType::customPurchPromptLetter;
    }
 
    /// 
 
    /// Returns the PrintMgmtHierarchyType enumeration value that specifies what hierarchy this FormLetterReport class uses.
    /// 
 
    /// 
    /// The PrintMgmtHierarchyType enumeration value that specifies what hierarchy this FormLetterReport class uses.
    /// 
    /// 
    /// This value is used to retrieve the appropriate Print Management settings for the report.
    /// 
    protected PrintMgmtHierarchyType getPrintMgmtHierarchyType()
    {
        return PrintMgmtHierarchyType::Purch;
    }
 
    /// 
 
    /// Returns the PrintMgmtNodeType enumeration value that specifies what node this FormLetterReport class uses.
    /// 
 
    /// 
    /// The PrintMgmtNodeType enumeration value that specifies what node this FormLetterReport class uses.
    /// 
    /// 
    /// This value is used to retrieve the appropriate Print Management settings for the report.
    /// 
    protected PrintMgmtNodeType getPrintMgmtNodeType()
    {
        return PrintMgmtNodeType::PurchTable;
    }
}

最后Coc扩展populator类

这个类的作用可以参考前文“添加一个新的Report format”

/// 
 
/// Populates the PrintMgmtReportFormat table used for print management with the MyProject documents.
/// 
 
[ExtensionOf(classStr(PrintMgmtReportFormatPopulator))]
public final class PrintMgmtReportFormatPopulatorAppSuite_custom_Extension
{
    #ISOCountryRegionCodes
    #PrintMgmtSetup
 
    protected void addDocuments()
    {
        // Purchasing documents
        this.customAddPurchaseDocuments();
 
        next addDocuments();
    }
 
    /// 
 
    /// Adds purchase records to the PrintMgmtReportFormat table.
    /// 
 
    protected void customAddPurchaseDocuments()
    {
        this.addOther(PrintMgmtDocumentType::customPurchPromptLetter, ssrsReportStr(customPurchPromptLetterReport, Report), ssrsReportStr(customPurchPromptLetterReport, Report), #NoCountryRegionId);
    }
 
}

完成!

如何在D365FO创建SSRS报表(二)Controller Class(how-to-create-ssrs-report-in-d365fo-part2-controller-class)

Report controller

继承SrsReportRunController

如果没有特殊要求,以下代码就可以搞定。

public class Demo_InventJournalTransTransferController extends SrsReportRunController
{
 
    public static void main(Args _args)
    {
        Demo_InventJournalTransTransferController controller 
          = new Demo_InventJournalTransTransferController();
 
        controller.parmReportName(ssrsReportStr(Demo_InventJournalTransTransfer, Report));
        controller.parmDialogCaption("@Demo954");
        controller.parmArgs(_args);
        controller.startOperation();
    }
}

多选

如果需要打印用户所选的数据,多选需要用到 MultiSelectionHelper

public class Demo_InventJournalTransTransferController extends SrsReportRunController
{
    ......
 
    public void preRunModifyContract()
    {
        Demo_InventJournalTransTransferContract contract 
          = this.parmReportContract().parmRdpContract() as Demo_InventJournalTransTransferContract;        
    }
 
    public void prePromptModifyContract()
    {
        Demo_InventJournalTransTransferContract     contract;
 
        contract = this.parmReportContract().parmRdpContract() as Demo_InventJournalTransTransferContract;
 
        this.setRanges(
           this.parmReportContract().parmQueryContracts().lookup(this.getFirstQueryContractKey())
           , contract);
    }
 
    public void setRanges(
        Query _query,
        Demo_InventJournalTransTransferContract _contract)
    {
        MultiSelectionHelper    multiSelectionHelper;
 
        if (this.parmArgs().dataset() == tableNum(InventJournalTable) && this.parmArgs().caller())
        {
            _query.dataSourceTable(tableNum(InventJournalTable)).clearRanges();
            multiSelectionHelper = MultiSelectionHelper::createFromCaller(this.parmArgs().caller());
            multiSelectionHelper.createQueryRanges(_query.dataSourceTable(tableNum(InventJournalTable))
               , fieldStr(InventJournalTable, JournalId));
        }
 
    }
 
}

如果需要根据Print management的设定来打印,请参考文章:

如何在SSRS报表中创建条码、 二维码(how-to-create-barcode-qrcode-in-ssrs-report)

Barcode和QR code在SSRS报表中很常见,下面分别介绍如何添加他们。

二维码QRcode

在SSRS的临时表中创建一个字段存储二维码,类型扩展 Bitmap

在相关类中添加如下代码,举例说明: xyzTableBuffer.QRCode =QRHelper::QRCode(“123456789”)
public static container QRCode(Str _QRContents)
{
// QRContents is a formatted string provided as input to this function.
// For example _QRContents= ?name='Customer name'&rr='RFCno'&tt='totalInvoiceAmount';
 
    System.Drawing.Bitmap bm = null;
    try
    {
        var qrCodeEncoder = new Encoder();
        bm = qrCodeEncoder.Encode(_QRContents);
    }
    catch (Exception::CLRError)
    {
        error(CLRInterop::getLastException().ToString());
    }
    using (var stream = new System.IO.MemoryStream())
    {
        bm.Save(stream, System.Drawing.Imaging.ImageFormat::Bmp);
        bm.Dispose();
        return Binary::constructFromMemoryStream(stream).getContainer();
    }
}

在报表设计中添加图片,参数如下:

条码Barcode

在SSRS的临时表中创建一个字段存储二维码,类型扩展  BarCodeString
举例说明: xyzTableBuffer.BarCodeField= this.getBarcode(“123456789”)
在报表设计中使用字体 >BC C128 Narrow

public str getBarcode(str _barCodeText)
{
    BarcodeCode128 barcode;
    barcode = Barcode::construct(BarcodeType::Code128);
    barcode.string(true, _barCodeText);
    barcode.encode();
 
    return barcode.barcodeStr();
}

如何在D365FO创建SSRS报表(一)Report Data Provider Class(how-to-create-ssrs-report-in-d365fo-part1-report-data-provider-class)

Report data provider class

继承SrsReportDataProviderPreProcessTempDB

声明所用到的Contract 和 Query还有其他变量

[
    SRSReportParameterAttribute(classStr(Demo_InventJournalTransTransferContract)),
    SRSReportQueryAttribute(queryStr(Demo_InventJournalTransTransferQuery))
]
class Demo_InventJournalTransTransferDP extends SrsReportDataProviderPreProcessTempDB
{
}

SRSReportDataSetAttribute

定义临时表保存报表的Header,Footer 等数据

[
    SRSReportDataSetAttribute('Demo_InventJournalTransTransferHeaderTMP')
]
public Demo_InventJournalTransTransferHeaderTMP getHeader()
{
    select * from header;
    return header;
}
 
[
    SRSReportDataSetAttribute('Demo_InventJournalTransTransferFooterTMP')
]
public Demo_InventJournalTransTransferFooterTMP getFooter()
{
    select * from footer;
    return footer;
}

Process report

处理报表的数据

public void processReport()
{
    Demo_InventJournalTransTransferContract contract            = this.parmDataContract();
 
    super();
 
    inventDimParm       = this.initInventDimParm(contract);
    inventDimSetup      = this.getInventDimFieldList();
 
    this.fetchDetail();
    this.fetchHeader();
    this.fetchFooter();
 
}

如何添加公司LOGO

CompanyInfo companyInfo = CompanyInfo::find();
tableBuffer.CompanyLogo = CompanyImage::findByRecord(companyInfo).Image;

Query中使用临时表

注意:在报表抓取数据的时候我们经常会用到Query而不是while select,在Query中使用临时表,只需要加上setCursor() (or setRecord()),下面是一个例子:

TmpTableName name;
 
name.RefTableId = 1;
name.TableName = 'TableA';
name.insert();
 
TmpTableIdMap map;
map.MainTableId = 1;
map.MainFieldId = 42;
map.insert();
 
Query query = new Query();
QueryBuildDataSource nameDs = query.addDataSource(tableNum(TmpTableName));
 
QueryBuildDataSource mapDs = nameDs.addDataSource(tableNum(TmpTableIdMap));
mapDs.addLink(fieldNum(TmpTableName, RefTableId), fieldNum(TmpTableIdMap, MainTableId));
 
QueryRun qr = new QueryRun(query);
qr.setCursor(name);
qr.setCursor(map);
 
while (qr.next())
{
    TmpTableName nameFetched = qr.get(tableNum(TmpTableName));
    TmpTableIdMap mapFetched = qr.get(tableNum(TmpTableIdMap));
 
    info(strFmt('%1 - %2', nameFetched.TableName, mapFetched.MainFieldId));
}

有时候报表会需要显示条形码或者二维码,请参考一下链接: