Business Central - Previewing when printing gives printername as empty string - dynamics-business-central

When going to the sales invoices page in business central, you can print an invoice and you will get the following popup:
Here you can select a printer in the printer dropdown, we then catch that printer name in the OnBeforeMergeDocument event, which gives you the printername as an argument.
The problem is, this only works when you press the print button at the bottom of the popup in the image, if you press preview, it will give you an empty string.
I need the printername in both situations, is it possible to get the printername when previewing?

Apparently it's not possible in OnBeforeMergeDocument. This event is raised from a procedure MergeWordLayout which is a bit weird. It does not have the printer name in its parameters.
MergeWordLayout(ReportID: Integer; ReportAction: Option SaveAsPdf,SaveAsWord,SaveAsExcel,Preview,Print,SaveAsHtml; InStrXmlData: InStream; FileName: Text; var DocumentStream: OutStream)
But it assigns the file name to the printer name before calling the event if the selected action is "Print".
if ReportAction = ReportAction::Print then
PrinterName := FileName;
So I would assume that the printer name is actually passed to the procedure in the FileName parameter.
Anyway, this code is obsolete now. I don't know which version of BC you are using, but OnBeforeMergeDocument is replaced with 'OnCustomDocumentMerger' in version 20, and this one is in its turn replaced by OnCustomDocumentMergerEx in v.21.
The latter has the printer info in the ObjectPayload object.
https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/devenv-oncustomdocumentmergerex-event

Related

How can I focus and autofill a text field in Autohotkey?

I'm attempting to write a script to automatically fill a web-field with the current date using an AutoHotkey script. However, I'm not sure how to focus a specific field by its name or id.
My current hacky workaround is to use Send, {Tab 84} to scroll to the specific field, type the date with Send, 6/28/2017, and submit the field manually. While the script works most of the time, it's blatantly apparent there are better methods.
How can I focus autofill specific text-field on a webpage using an AutoHotkey script?
An IE COM object should do the trick, as long as you're comfortable navigating the DOM with some JS of your own.
Here's an example:
F3::
wb := ComObjCreate("InternetExplorer.Application") ; Create a IE instance
wb.Visible := True
wb.Navigate("http://google.com")
Sleep, 5000
SendInput, This is test.{Enter}
Sleep, 5000
wb.document.getElementById("lst-ib").value := "This is another test."
wb.document.getElementById("_fZl").click()
return

Ax 2012 X++ Report Controller class caching

Short Version :
If an error is thrown during the execution of an SSRS report after the associated RDP temporary tables have been updated, the next time the report is run for the same parameters, code execution does not insert fresh data into said tables. Any idea why and how to prevent it?
Long Version :
I am trying to modify the run conditions of a standard report (SalesInvoiceReport) so that the original of any invoice can only be printed once. For all reprints, the "Copy preview" option should be used.
For the most part, the customization is working as intended. I used the CustInvoiceJour.PrintedOriginals field and the CustInvoiceJour.updatePrintedOriginals() (both part of Standard Ax) to maintain the number of originals that have been printed and a slight modification to the SalesInvoiceController.outputReport() method throw an error of an original is being re-printed. Below is the code added to achieve this :
...
// <A147> Code to disable reprints if an original is already printed
isOriginalPrintable = CustInvoiceJour::findRecId(custInvoiceJour.RecId).PrintedOriginals == 0;
srsPrintDestinationSettings = formLetterReport.getCurrentPrintSetting().parmPrintJobSettings();
//srsPrintMediumType = srsPrintDestinationSettings.printMediumType();
if ((printCopyOriginal == PrintCopyOriginal::Original || printCopyOriginal == PrintCopyOriginal::OriginalPrint))
{
// If no originals printed, allow prints.
// Need to re-read the CustInvoiceJour record from the table to get updated data
// If the Original is printed but the prin form is not refreshed, the form's recordset does not update to the latest data.
if(isOriginalPrintable)
{
CustInvoiceJour::updatePrinted(custInvoiceJour, (srsPrintDestinationSettings.numberOfCopies() == 0 ? 1 : srsPrintDestinationSettings.numberOfCopies()));
}
// Don't allow the original document to be printed more than once.
else
{
error(strFmt("#GLS223085", custInvoiceJour.InvoiceId, custInvoiceJour.InvoiceDate));
return;
}
}
// </A147>
...
I placed this in the SalesInvoiceContreller.outputReport() because I found similar code present there as part of standard Ax, albeit for use in specific country regions.
When user uses the Original Preview button, the error is thrown and code execution stops but the RDP temporary tables used to hold the report data have already been updated with report data (specifically the field which hold the Report title is set to Invoice).
When the user reruns the report, this time with the Copy Preview button, code execution somehow skips inserting fresh data into the temp tables (which would set the Report Title field to Invoice Copy) and the first copy output still says Invoice, defeating the requirement.

Text Property Confusion

I am not sure what is happening, maybe someone can clarify.
The scenario is simple, I have a form that I am submitting to update a DB.
So on Page_Load, I set each field to the current value in the current object.
Example:
txtFirstName.Text = empInfo.FirstName // FirstName = Jane
txtLastName.Text = empInfo.LastName
// Etc
Now at runtime it may be edited by the user, typical textbox stuff.
When I run my button click to update it will always return the assigned Text value previously and not the new user-edited value.
Let's say the user edits the field:
First Name: [ Joe ]
If I were to print txtFirstName.Text, it is STILL Jane
Note: This does not happen if the Text property is never set, in that case, it works as expected
Sounds like the code that assigns your txtFirstName.Text control/property is running again after post back and overwriting the new value. Make sure that you initialization code is wrapped in a check for (!IsPostBack) to ensure it is only run the first time the page is accessed an not with every post back (update) to the page.
Post your Page_Load code or where you do the initialization and we can probably confirm this is the issue.

Open print dialog for report from code

I am attempting to force open the print dialog so that all the user has to do is set the email address and press ok. I've found multiple tutorials on how to print a report to file or a printer without the print dialog, but that's not what I'm looking for.
Typically to email a report, the user displays the report, clicks the print icon in the tool bar, and then chooses email and sends it. I want to cut out the first two steps automatically.
This is one of my many attempts at doing this so far, but to no avail.
void emailInvoice()
{
Args args;
ReportRun rr;
Report rb;
PrintJobSettings pjs;
CustInvoiceJour record;
;
select record where record.RecId == 5637175089;
args = new Args("SalesInvoice");
args.record(record);
args.parmEnum(PrintCopyOriginal::OriginalPrint);
// Set report run properties
rr = new ReportRun(args,'');
rr.suppressReportIsEmptyMessage(true);
rr.query().interactive(false);
// set report properties
rb = rr.report();
rb.interactive(true);
// set print job settings
pjs = rr.printJobSettings();
pjs.fileName(strfmt("C:\\Users\\gbonzo\\Desktop\\%1.pdf", record.SalesId));
pjs.fitToPage(true);
// break the report info pages using the height of the current printer's paper
pjs.virtualPageHeight(-1);
// force PDF printing
pjs.format(PrintFormat::PDF);
pjs.setTarget(PrintMedium::Mail);
pjs.viewerType(ReportOutputUserType::PDF);
// lock the print job settings so can't be changed
// X++ code int the report may try to change the destination
// to the screen for example but this does not make
// sense when running a report here
pjs.lockDestinationProperties(true);
// Initialize the report
rr.init();
rr.run();
}
Thanks in advance for your help!
Have you tried to develop a RunBase standard dialog class (RunBaseBatchPrintable if you need to select the printer) that get all the dialog fields you need and from there, programatically run the report passing all the desired parameters? I'm prety sure it will work, and probably will left a cleaner code separating the report of the logic needed to the user interaction.
Read an example here:
http://waikeatng.blogspot.com.es/2010/10/using-runbasebatchprintable-class.html
You have to call the prompt() method of the ReportRun class before calling the run() method.
if (rr.prompt())
{
rr.run();
}
The prompt() method will show the print dialog. If you want it easier for your users you could use the SysMailer class, take a look to the quickSend() method.

What is the most efficient way of filling in details on a web form?

Take a standard web page with lots of text fields, drop downs etc.
What is the most efficient way in webdriver to fill out the values and then verify if the values have been entered correctly.
You only have to test that the values are entered correctly if you have some javascript validation or other magic happening at your input fields. You don't want to test that webdriver/selenium works correctly.
There are various ways, depending if you want to use webdriver or selenium. Here is a potpourri of the stuff I'm using.
Assert.assertEquals("input field must be empty", "", selenium.getValue("name=model.query"));
driver.findElement(By.name("model.query")).sendKeys("Testinput");
//here you have to wait for javascript to finish. E.g wait for a css Class or id to appear
Assert.assertEquals("Testinput", selenium.getValue("name=model.query"));
With webdriver only:
WebElement inputElement = driver.findElement(By.id("input_field_1"));
inputElement.clear();
inputElement.sendKeys("12");
//here you have to wait for javascript to finish. E.g wait for a css Class or id to appear
Assert.assertEquals("12", inputElement.getAttribute("value"));
Hopefully, the results of filling out your form are visible to the user in some manner. So you could think along these BDD-esque lines:
When I create a new movie
Then I should see my movie page
That is, your "new movie" steps would do the field entry & submit. And your "Then" would assert that the movie shows up with your entered data.
element = driver.find_element(:id, "movie_title")
element.send_keys 'The Good, the Bad, the Ugly'
# etc.
driver.find_element(:id, "submit").click
I'm just dabbling in this now, but this is what I came up with so far. It certainly seems more verbose than something like Capybara:
fill_in 'movie_title', :with => 'The Good, the Bad, the Ugly'
Hope this helps.

Resources