This commit is contained in:
2021-12-22 18:51:22 +00:00
parent 14ce0bbf5d
commit 1756f27b5a
30 changed files with 164 additions and 58 deletions

1
.vscode/launch.json vendored
View File

@@ -48,6 +48,7 @@
"AYANOVA_DATA_PATH": "c:\\temp\\ravendata",
"AYANOVA_USE_URLS": "http://*:7575;",
"AYANOVA_SERVER_TEST_MODE": "false",
"AYANOVA_REPORT_RENDERING_MAX_INSTANCES":"1",
// "AYANOVA_SERVER_TEST_MODE_SEEDLEVEL": "small",
"AYANOVA_BACKUP_PG_DUMP_PATH": "C:\\data\\code\\postgres_14\\bin\\"
},

View File

@@ -14,6 +14,7 @@ namespace AyaNova.Biz
Task<JArray> GetReportData(DataListSelectedRequest dataListSelectedRequest);
const int REPORT_DATA_BATCH_SIZE = 100;
}
}

View File

@@ -383,7 +383,9 @@ namespace AyaNova.Biz
public async Task<string> RenderReport(DataListReportRequest reportRequest, string apiUrl)
{
var log = AyaNova.Util.ApplicationLogging.CreateLogger("ReportBiz::RenderReport");
#if (DEBUG)
log.LogInformation($"DBG: ReportBiz::RenderReport called");
#endif
//Customer User Report?
bool RequestIsCustomerWorkOrderReport = false;
if (reportRequest.ReportId == -100)
@@ -398,6 +400,9 @@ namespace AyaNova.Biz
}
//get report, vet security, see what we need before init in case of issue
#if (DEBUG)
log.LogInformation($"DBG: ReportBiz::RenderReport get report from db");
#endif
var report = await ct.Report.FirstOrDefaultAsync(z => z.Id == reportRequest.ReportId);
if (report == null)
{
@@ -436,12 +441,18 @@ namespace AyaNova.Biz
reportRequest.IncludeWoItemDescendants = report.IncludeWoItemDescendants;
//Get data
#if (DEBUG)
log.LogInformation($"DBG: ReportBiz::RenderReport GetReportData");
#endif
var ReportData = await GetReportData(reportRequest, RequestIsCustomerWorkOrderReport);//###### TODO: SLOW NEEDS TIMEOUT HERE ONCE IT WORKS IS SUPPORTED
//if GetReportData errored then will return null so need to return that as well here
if (ReportData == null)
{
return null;
}
#if (DEBUG)
log.LogInformation($"DBG: ReportBiz::RenderReport got report data");
#endif
//initialization
log.LogDebug("Initializing report rendering system");
@@ -480,6 +491,9 @@ namespace AyaNova.Biz
}
else
{
#if (DEBUG)
log.LogInformation($"DBG: ReportBiz::calling browserfetcher");
#endif
log.LogDebug($"Windows: Calling browserFetcher download async now:");
await new BrowserFetcher().DownloadAsync(BrowserFetcher.DefaultChromiumRevision);
}
@@ -500,11 +514,14 @@ namespace AyaNova.Biz
//API DOCS http://www.puppeteersharp.com/api/index.html
log.LogDebug($"Launching headless Browser now:");
#if (DEBUG)
log.LogInformation($"DBG: ReportBiz::launching browser now");
#endif
using (var browser = await Puppeteer.LaunchAsync(lo))
using (var page = await browser.NewPageAsync())
{
//track this process so it can be cancelled if it times out
ReportRenderManager.AddProcess(browser.Process.Id);
ReportRenderManager.AddProcess(browser.Process.Id, log);
page.DefaultTimeout = 0;//infinite timeout as we are controlling how long the process can live for with the reportprocessmanager
try
{
@@ -623,17 +640,25 @@ namespace AyaNova.Biz
//prePareData / preRender
var ReportDataObject = $"{{ ayReportData:{ReportData}, ayReportMetaData:{reportMeta}, ayClientMetaData:{clientMeta}, ayServerMetaData:{serverMeta} }}";
log.LogDebug($"Calling ayPreRender...");
//PRE_RENDER WITH TIMEOUT
#if (DEBUG)
log.LogInformation($"DBG: ReportBiz::RenderReport - preRender");
#endif
//PRE_RENDER (WITH TIMEOUT???)
await page.WaitForExpressionAsync($"ayPreRender({ReportDataObject})");
//compile the template
log.LogDebug($"Calling Handlebars.compile...");
#if (DEBUG)
log.LogInformation($"DBG: ReportBiz::RenderReport - compiling handlebars template");
#endif
var compileScript = $"Handlebars.compile(`{report.Template}`)(PreParedReportDataObject);";
var compiledHTML = await page.EvaluateExpressionAsync<string>(compileScript);
//render report as HTML
log.LogDebug($"Setting page content to compiled HTML");
#if (DEBUG)
log.LogInformation($"DBG: ReportBiz::RenderReport - settting html on page contents");
#endif
await page.SetContentAsync(compiledHTML);
//add style (after page or it won't work)
@@ -650,6 +675,9 @@ namespace AyaNova.Biz
//Set PDF options
//https://pptr.dev/#?product=Puppeteer&version=v5.3.0&show=api-pagepdfoptions
log.LogDebug($"Resolving PDF Options from report settings");
#if (DEBUG)
log.LogInformation($"DBG: ReportBiz::RenderReport - settting pdf options");
#endif
var PdfOptions = new PdfOptions() { };
PdfOptions.DisplayHeaderFooter = report.DisplayHeaderFooter;
@@ -726,7 +754,9 @@ namespace AyaNova.Biz
//render to pdf and return
log.LogDebug($"Calling render page contents to PDF");
#if (DEBUG)
log.LogInformation($"DBG: ReportBiz::RenderReport - rendering to pdf");
#endif
await page.PdfAsync(outputFullPath, PdfOptions);//###### TODO: SLOW NEEDS TIMEOUT HERE ONCE SUPPORTED, open bug: https://github.com/hardkoded/puppeteer-sharp/issues/1710
log.LogDebug($"Completed, returning results");
@@ -748,6 +778,9 @@ namespace AyaNova.Biz
}
finally
{
#if (DEBUG)
log.LogInformation($"DBG: ReportBiz::RenderReport - closing browser");
#endif
log.LogDebug($"Closing browser");
await browser.CloseAsync();
ReportRenderManager.RemoveProcess(browser.Process.Id, log);

View File

@@ -299,6 +299,7 @@ namespace AyaNova.Biz
if (logTheGetEvent && ret != null)
await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, id, BizType, AyaEvent.Retrieved), ct);
}
return ret;
}
@@ -326,7 +327,7 @@ namespace AyaNova.Biz
ret.CustomerReferenceNumber = sourceWo.CustomerReferenceNumber;
ret.CustomerContactName = sourceWo.CustomerContactName;
ret.InvoiceNumber = sourceWo.InvoiceNumber;
ret.CanReport=customerEffectiveRights.ThisWOEffectiveWOReportId!=null;
ret.CanReport = customerEffectiveRights.ThisWOEffectiveWOReportId != null;
return ret;
}
@@ -857,6 +858,7 @@ namespace AyaNova.Biz
//
internal async Task<WorkOrder> WorkOrderGetPartialAsync(AyaType ayaType, long id, bool includeWoItemDescendants, bool populateForReporting)
{
//if it's the entire workorder just get, populate and return as normal
if (ayaType == AyaType.WorkOrder)
return await WorkOrderGetAsync(id, true, false, populateForReporting);
@@ -927,6 +929,7 @@ namespace AyaNova.Biz
ret.Items.Add(woitem);
await WorkOrderPopulateVizFields(ret, false, populateForReporting);
return ret;
}
@@ -935,6 +938,9 @@ namespace AyaNova.Biz
//
public async Task<JArray> GetReportData(DataListSelectedRequest dataListSelectedRequest)
{
#if (DEBUG)
var watch = System.Diagnostics.Stopwatch.StartNew();
#endif
//workorder reports for entire workorder or just sub parts all go through here
//if the ayatype is a descendant of the workorder then only the portion of the workorder from that descendant directly up to the header will be populated and returned
//however if the report template has includeWoItemDescendants=true then the woitems is fully populated
@@ -974,6 +980,10 @@ namespace AyaNova.Biz
ReportData.Add(jo);
}
}
#if (DEBUG)
watch.Stop();
System.Diagnostics.Debug.WriteLine($"Workorderbiz::GetReportData took ms: {watch.ElapsedMilliseconds}");
#endif
return ReportData;
}
@@ -2032,17 +2042,63 @@ namespace AyaNova.Biz
////////////////////////////////////////////////////////////////////////////////////////////////
//VIZ POPULATE
//
//CACHING TEST
private Dictionary<string, string> _vizCache = new Dictionary<string, string>();
private void vizAdd(string value, string key, long? id = 0)
{
_vizCache[$"{key}{id}"] = value;
}
private string vizGet(string key, long? id = 0)
{
string value = null;
if (_vizCache.TryGetValue($"{key}{id}", out value))
{
System.Diagnostics.Debug.WriteLine($"vizGet cache hit {key}{id}");
return value;
}
else
{
System.Diagnostics.Debug.WriteLine($"vizGet cache miss {key}{id}");
return null;
}
}
private async Task ItemPopulateVizFields(WorkOrderItem o, bool populateForReporting)
{
// System.Diagnostics.Debug.WriteLine($"ItemPopulateVizFields nCacheTestValue is {nCacheTest}");
if (o.FromCSRId != null)
o.FromCSRViz = await ct.CustomerServiceRequest.AsNoTracking().Where(x => x.Id == o.FromCSRId).Select(x => x.Name).FirstOrDefaultAsync();
{
string value = vizGet("csr", o.FromCSRId);
if (value == null)
{
value = await ct.CustomerServiceRequest.AsNoTracking().Where(x => x.Id == o.FromCSRId).Select(x => x.Name).FirstOrDefaultAsync();
vizAdd(value, "csr", o.FromCSRId);
}
o.FromCSRViz = value;
}
if (o.WorkOrderItemStatusId != null)
{
string value = vizGet("woistatname", o.WorkOrderItemStatusId);
if (value == null)
{
var StatusInfo = await ct.WorkOrderItemStatus.AsNoTracking().FirstOrDefaultAsync(x => x.Id == o.WorkOrderItemStatusId);
vizAdd(StatusInfo.Name, "woistatname", o.WorkOrderItemStatusId);
vizAdd(StatusInfo.Color, "woistatcolor", o.WorkOrderItemStatusId);
o.WorkOrderItemStatusNameViz = StatusInfo.Name;
o.WorkOrderItemStatusColorViz = StatusInfo.Color;
}
else
{
o.WorkOrderItemStatusNameViz = value;
o.WorkOrderItemStatusColorViz = vizGet("woistatcolor", o.WorkOrderItemStatusId);
}
}
if (o.WorkOrderItemPriorityId != null)
{
@@ -4687,7 +4743,7 @@ namespace AyaNova.Biz
if (o.PartId != 0)
part = await ct.Part.AsNoTracking().FirstOrDefaultAsync(x => x.Id == o.PartId);
o.PartNameViz = part.Name;
o.PartDescriptionViz=part.Description;
o.PartDescriptionViz = part.Description;
o.UpcViz = part.UPC;
PurchaseOrder po = null;

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
{"Name":"💡 Wiki displayed PO","Active":true,"Notes":"","Roles":124927,"AType":26,"IncludeWoItemDescendants":false,"Template":"<html>\n<body>\t\n\t<table>\n\n\t<thead>\n\t\t<tr>\n\t\t\t<th colspan=\"1\">column header 1</th>\n\t\t\t<th colspan=\"1\">column header 2</th>\n\t\t</tr>\n\t</thead>\n\n\t{{#each ayReportData}}<!-- this #each encompasses the area that repeats for each object (i.e. each PO) in your Sample Data -->\n\t<!-- if want the header repeated for every PO, the #each to encompass the thead area too, otherwise encompass the tbody -->\t\n\t<tbody>\t<!-- this indicates start of your table body section -->\t\n\t\t\t<tr><!-- this indicates start of one row in your tbody -->\n\t\t\t\t<td colspan=\"1\">{{ayT 'PurchaseOrder'}} {{Serial}}</td>\n\t\t\t\t<td colspan=\"1\">{{ayWiki Wiki}}</td> <!-- to display the Wiki, prefix ayWiki to the key encompass both in the mustache -->\n\t\t\t</tr>\n\t</tbody><!-- this indicates end of your table body section -->\n\t{{/each}}<!-- this closes the emcompassed area that repeats for each object (i.e. each PO) in your Sample Data -->\n\n\t<tfoot class=\"footertext\"><!-- this applies specific CSS class attributes to all of tfoot and its td's -->\n\t\t<tr>\n\t\t\t<td colspan=\"2\">and some footer text</td><!-- this inline colspan spans multiple columns -->\n\t\t</tr>\n\t</tfoot><!-- this indicates end of your table footer section -->\t\n\n\t</table><!-- end of your table-->\t\n\n</body>\n</html>","Style":".singlePage\n{\npage-break-after: always;\n}\n\n.footertext {\n font-size: 14pt;\n font-style: italic;\n background-color: pink;\n}\n\nbody {\n font-family: 'Helvetica', 'Helvetica Neue', Arial, sans-serif; \n}\n\n.reporttitle { \n margin-bottom: 20pt; \n font-weight: bold; \n font-size: 13pt; \n color: #9e9e9e;\n text-align: center;\n} \n\ntable { \n border-collapse: collapse;\n white-space: pre-wrap;\n width: 100%;\n table-layout: fixed; /* the # of columns set in the first row of the thead will be fixed and applied throughout table and rows */\n }\n\nth {\n border-bottom: solid 1pt #9e9e9e;\n height: 50px;\n font-size: 13pt; \n color: #9e9e9e;\n}\n\ntbody td {\n padding: 10px;\n word-wrap: break-word;\n font-size: 9pt;\n}\n\n\ntbody tr:nth-child(even) {\n background-color: #f8f8f8; /* MUST checkmark Print background in PDF Options for this to show */\n}\n\n\n.rightlean {\n text-align: right;\n}\n.leftlean {\n text-align: left;\n}\n.centerlean {\n text-align: center;\n}\n\n\n.fontgreen {\n color: green;\n}\n.fontblue {\n color: blue;\n}\n.fontred {\n color:red;\n}\n\n","JsPrerender":"async function ayPrepareData(reportData){ \n //this function (if present) is called with the report data \n //before the report is rendered\n //modify data as required here and return it to change the data before the report renders\n //see the help documentation for details\n\n await ayGetTranslations([\"PurchaseOrder\" ]);\n\n return reportData;\n}","JsHelpers":"//Register custom Handlebars helpers here to use in your report script\n//https://handlebarsjs.com/guide/#custom-helpers\nHandlebars.registerHelper('loud', function (aString) {\n return aString.toUpperCase()\n})","RenderType":0,"HeaderTemplate":"<span style=\"font-size:6pt;width: 94%; text-align:left; \">&nbsp;&nbsp;&nbsp;&nbsp;Todays date:&nbsp; <span class=\"date\"></span>&nbsp; set in PDF Options</span>","FooterTemplate":"<span style=\"font-size:6pt;width: 94%; text-align: right; \">&nbsp; set in PDF Options&nbsp;&nbsp;Page&nbsp;<span class=\"pageNumber\"></span>&nbsp;of&nbsp;<span class=\"totalPages\"></span>&nbsp;&nbsp;&nbsp;&nbsp;</span>","DisplayHeaderFooter":true,"PaperFormat":10,"Landscape":false,"MarginOptionsBottom":"15mm","MarginOptionsLeft":"10mm","MarginOptionsRight":"10mm","MarginOptionsTop":"15mm","PageRanges":null,"PreferCSSPageSize":false,"PrintBackground":true,"Scale":1.00000}
{"Name":"z_Wiki displayed PO","Active":true,"Notes":"","Roles":124927,"AType":26,"IncludeWoItemDescendants":false,"Template":"<html>\n<body>\t\n\t<table>\n\n\t<thead>\n\t\t<tr>\n\t\t\t<th colspan=\"1\">column header 1</th>\n\t\t\t<th colspan=\"1\">column header 2</th>\n\t\t</tr>\n\t</thead>\n\n\t{{#each ayReportData}}<!-- this #each encompasses the area that repeats for each object (i.e. each PO) in your Sample Data -->\n\t<!-- if want the header repeated for every PO, the #each to encompass the thead area too, otherwise encompass the tbody -->\t\n\t<tbody>\t<!-- this indicates start of your table body section -->\t\n\t\t\t<tr><!-- this indicates start of one row in your tbody -->\n\t\t\t\t<td colspan=\"1\">{{ayT 'PurchaseOrder'}} {{Serial}}</td>\n\t\t\t\t<td colspan=\"1\">{{ayWiki Wiki}}</td> <!-- to display the Wiki, prefix ayWiki to the key encompass both in the mustache -->\n\t\t\t</tr>\n\t</tbody><!-- this indicates end of your table body section -->\n\t{{/each}}<!-- this closes the emcompassed area that repeats for each object (i.e. each PO) in your Sample Data -->\n\n\t<tfoot class=\"footertext\"><!-- this applies specific CSS class attributes to all of tfoot and its td's -->\n\t\t<tr>\n\t\t\t<td colspan=\"2\">and some footer text</td><!-- this inline colspan spans multiple columns -->\n\t\t</tr>\n\t</tfoot><!-- this indicates end of your table footer section -->\t\n\n\t</table><!-- end of your table-->\t\n\n</body>\n</html>","Style":".singlePage\n{\npage-break-after: always;\n}\n\n.footertext {\n font-size: 14pt;\n font-style: italic;\n background-color: pink;\n}\n\nbody {\n font-family: 'Helvetica', 'Helvetica Neue', Arial, sans-serif; \n}\n\n.reporttitle { \n margin-bottom: 20pt; \n font-weight: bold; \n font-size: 13pt; \n color: #9e9e9e;\n text-align: center;\n} \n\ntable { \n border-collapse: collapse;\n white-space: pre-wrap;\n width: 100%;\n table-layout: fixed; /* the # of columns set in the first row of the thead will be fixed and applied throughout table and rows */\n }\n\nth {\n border-bottom: solid 1pt #9e9e9e;\n height: 50px;\n font-size: 13pt; \n color: #9e9e9e;\n}\n\ntbody td {\n padding: 10px;\n word-wrap: break-word;\n font-size: 9pt;\n}\n\n\ntbody tr:nth-child(even) {\n background-color: #f8f8f8; /* MUST checkmark Print background in PDF Options for this to show */\n}\n\n\n.rightlean {\n text-align: right;\n}\n.leftlean {\n text-align: left;\n}\n.centerlean {\n text-align: center;\n}\n\n\n.fontgreen {\n color: green;\n}\n.fontblue {\n color: blue;\n}\n.fontred {\n color:red;\n}\n\n","JsPrerender":"async function ayPrepareData(reportData){ \n //this function (if present) is called with the report data \n //before the report is rendered\n //modify data as required here and return it to change the data before the report renders\n //see the help documentation for details\n\n await ayGetTranslations([\"PurchaseOrder\" ]);\n\n return reportData;\n}","JsHelpers":"//Register custom Handlebars helpers here to use in your report script\n//https://handlebarsjs.com/guide/#custom-helpers\nHandlebars.registerHelper('loud', function (aString) {\n return aString.toUpperCase()\n})","RenderType":0,"HeaderTemplate":"<span style=\"font-size:6pt;width: 94%; text-align:left; \">&nbsp;&nbsp;&nbsp;&nbsp;Todays date:&nbsp; <span class=\"date\"></span>&nbsp; set in PDF Options</span>","FooterTemplate":"<span style=\"font-size:6pt;width: 94%; text-align: right; \">&nbsp; set in PDF Options&nbsp;&nbsp;Page&nbsp;<span class=\"pageNumber\"></span>&nbsp;of&nbsp;<span class=\"totalPages\"></span>&nbsp;&nbsp;&nbsp;&nbsp;</span>","DisplayHeaderFooter":true,"PaperFormat":10,"Landscape":false,"MarginOptionsBottom":"15mm","MarginOptionsLeft":"10mm","MarginOptionsRight":"10mm","MarginOptionsTop":"15mm","PageRanges":null,"PreferCSSPageSize":false,"PrintBackground":true,"Scale":1.00000}

View File

@@ -1 +1 @@
{"Name":"💡 Wiki displayed","Active":true,"Notes":"","Roles":124927,"AType":34,"IncludeWoItemDescendants":false,"Template":"<html>\n<body>\t\n\t<table><!-- beginning of your table-->\t\n\n\t<thead>\n\t\t<tr>\n\t\t\t<th colspan=\"1\">column header text repeats every page</th>\n\t\t\t<th colspan=\"1\">column header 2</th>\n\t\t</tr>\n\t</thead>\n\n\t{{#each ayReportData}}<!-- this #each encompasses the area that repeats for each object (i.e. each PO) in your Sample Data -->\n\t<!-- if want the header repeated for every PO, the #each to encompass the thead area too, otherwise encompass the tbody -->\t\n\t<tbody>\t<!-- this indicates start of your table body section -->\t\n\t\t\t<tr><!-- this indicates start of one row in your tbody -->\n\t\t\t\t<td colspan=\"1\">{{ayT 'WorkOrder'}} {{Serial}}</td>\n\t\t\t\t<td colspan=\"1\">{{ayWiki Wiki}}</td> <!-- to display the Wiki, prefix ayWiki to the key encompass both in the mustache -->\n\t\t\t</tr>\n\t\t\t<tr>\n\t\t\t\t<td colspan=\"2\"></td>\n\t\t\t</tr>\n\t</tbody><!-- this indicates end of your table body section -->\n\t{{/each}}<!-- this closes the emcompassed area that repeats for each object (i.e. each PO) in your Sample Data -->\n\n\t<tfoot class=\"footertext\"><!-- this applies specific CSS class attributes to all of tfoot and its td's -->\n\t\t<tr>\n\t\t\t<td colspan=\"2\">and some footer text that will be repeated every page</td><!-- this inline colspan spans multiple columns -->\n\t\t</tr>\n\t</tfoot><!-- this indicates end of your table footer section -->\t\n\n\t</table><!-- end of your table-->\t\n\n</body>\n</html>","Style":".singlePage\n{\npage-break-after: always;\n}\n\n.footertext {\n font-size: 14pt;\n font-style: italic;\n background-color: lightsteelblue;\n}\n\nbody {\n font-family: 'Helvetica', 'Helvetica Neue', Arial, sans-serif; \n}\n\n.reporttitle { \n margin-bottom: 20pt; \n font-weight: bold; \n font-size: 13pt; \n color: #9e9e9e;\n text-align: center;\n} \n\ntable { \n border-collapse: collapse;\n white-space: pre-wrap;\n width: 100%;\n table-layout: fixed; /* the # of columns set in the first row of the thead will be fixed and applied throughout table and rows */\n }\n\nth {\n border-bottom: solid 1pt #9e9e9e;\n height: 50px;\n font-size: 13pt; \n color: #9e9e9e;\n}\n\ntbody td {\n padding: 10px;\n word-wrap: break-word;\n font-size: 9pt;\n}\n\n\ntbody tr:nth-child(odd) {\n background-color: #f8f8f8; /* MUST checkmark Print background in PDF Options for this to show */\n}\n\n\n.rightlean {\n text-align: right;\n}\n.leftlean {\n text-align: left;\n}\n.centerlean {\n text-align: center;\n}\n\n\n.fontgreen {\n color: green;\n}\n.fontblue {\n color: blue;\n}\n.fontred {\n color:red;\n}\n\n","JsPrerender":"async function ayPrepareData(reportData){ \n //this function (if present) is called with the report data \n //before the report is rendered\n //modify data as required here and return it to change the data before the report renders\n //see the help documentation for details\n\n await ayGetTranslations([\"WorkOrder\" ]);\n\n return reportData;\n}","JsHelpers":"//Register custom Handlebars helpers here to use in your report script\n//https://handlebarsjs.com/guide/#custom-helpers\nHandlebars.registerHelper('loud', function (aString) {\n return aString.toUpperCase()\n})","RenderType":0,"HeaderTemplate":"<span style=\"font-size:6pt;width: 94%; text-align:left; \">&nbsp;&nbsp;&nbsp;&nbsp;Todays date:&nbsp; <span class=\"date\"></span>&nbsp; set in PDF Options</span>","FooterTemplate":"<span style=\"font-size:6pt;width: 94%; text-align: right; \">&nbsp; set in PDF Options&nbsp;&nbsp;Page&nbsp;<span class=\"pageNumber\"></span>&nbsp;of&nbsp;<span class=\"totalPages\"></span>&nbsp;&nbsp;&nbsp;&nbsp;</span>","DisplayHeaderFooter":true,"PaperFormat":10,"Landscape":false,"MarginOptionsBottom":"15mm","MarginOptionsLeft":"10mm","MarginOptionsRight":"10mm","MarginOptionsTop":"15mm","PageRanges":null,"PreferCSSPageSize":false,"PrintBackground":true,"Scale":1.00000}
{"Name":"z_Wiki displayed","Active":true,"Notes":"","Roles":124927,"AType":34,"IncludeWoItemDescendants":false,"Template":"<html>\n<body>\t\n\t<table><!-- beginning of your table-->\t\n\n\t<thead>\n\t\t<tr>\n\t\t\t<th colspan=\"1\">column header text repeats every page</th>\n\t\t\t<th colspan=\"1\">column header 2</th>\n\t\t</tr>\n\t</thead>\n\n\t{{#each ayReportData}}<!-- this #each encompasses the area that repeats for each object (i.e. each PO) in your Sample Data -->\n\t<!-- if want the header repeated for every PO, the #each to encompass the thead area too, otherwise encompass the tbody -->\t\n\t<tbody>\t<!-- this indicates start of your table body section -->\t\n\t\t\t<tr><!-- this indicates start of one row in your tbody -->\n\t\t\t\t<td colspan=\"1\">{{ayT 'WorkOrder'}} {{Serial}}</td>\n\t\t\t\t<td colspan=\"1\">{{ayWiki Wiki}}</td> <!-- to display the Wiki, prefix ayWiki to the key encompass both in the mustache -->\n\t\t\t</tr>\n\t\t\t<tr>\n\t\t\t\t<td colspan=\"2\"></td>\n\t\t\t</tr>\n\t</tbody><!-- this indicates end of your table body section -->\n\t{{/each}}<!-- this closes the emcompassed area that repeats for each object (i.e. each PO) in your Sample Data -->\n\n\t<tfoot class=\"footertext\"><!-- this applies specific CSS class attributes to all of tfoot and its td's -->\n\t\t<tr>\n\t\t\t<td colspan=\"2\">and some footer text that will be repeated every page</td><!-- this inline colspan spans multiple columns -->\n\t\t</tr>\n\t</tfoot><!-- this indicates end of your table footer section -->\t\n\n\t</table><!-- end of your table-->\t\n\n</body>\n</html>","Style":".singlePage\n{\npage-break-after: always;\n}\n\n.footertext {\n font-size: 14pt;\n font-style: italic;\n background-color: lightsteelblue;\n}\n\nbody {\n font-family: 'Helvetica', 'Helvetica Neue', Arial, sans-serif; \n}\n\n.reporttitle { \n margin-bottom: 20pt; \n font-weight: bold; \n font-size: 13pt; \n color: #9e9e9e;\n text-align: center;\n} \n\ntable { \n border-collapse: collapse;\n white-space: pre-wrap;\n width: 100%;\n table-layout: fixed; /* the # of columns set in the first row of the thead will be fixed and applied throughout table and rows */\n }\n\nth {\n border-bottom: solid 1pt #9e9e9e;\n height: 50px;\n font-size: 13pt; \n color: #9e9e9e;\n}\n\ntbody td {\n padding: 10px;\n word-wrap: break-word;\n font-size: 9pt;\n}\n\n\ntbody tr:nth-child(odd) {\n background-color: #f8f8f8; /* MUST checkmark Print background in PDF Options for this to show */\n}\n\n\n.rightlean {\n text-align: right;\n}\n.leftlean {\n text-align: left;\n}\n.centerlean {\n text-align: center;\n}\n\n\n.fontgreen {\n color: green;\n}\n.fontblue {\n color: blue;\n}\n.fontred {\n color:red;\n}\n\n","JsPrerender":"async function ayPrepareData(reportData){ \n //this function (if present) is called with the report data \n //before the report is rendered\n //modify data as required here and return it to change the data before the report renders\n //see the help documentation for details\n\n await ayGetTranslations([\"WorkOrder\" ]);\n\n return reportData;\n}","JsHelpers":"//Register custom Handlebars helpers here to use in your report script\n//https://handlebarsjs.com/guide/#custom-helpers\nHandlebars.registerHelper('loud', function (aString) {\n return aString.toUpperCase()\n})","RenderType":0,"HeaderTemplate":"<span style=\"font-size:6pt;width: 94%; text-align:left; \">&nbsp;&nbsp;&nbsp;&nbsp;Todays date:&nbsp; <span class=\"date\"></span>&nbsp; set in PDF Options</span>","FooterTemplate":"<span style=\"font-size:6pt;width: 94%; text-align: right; \">&nbsp; set in PDF Options&nbsp;&nbsp;Page&nbsp;<span class=\"pageNumber\"></span>&nbsp;of&nbsp;<span class=\"totalPages\"></span>&nbsp;&nbsp;&nbsp;&nbsp;</span>","DisplayHeaderFooter":true,"PaperFormat":10,"Landscape":false,"MarginOptionsBottom":"15mm","MarginOptionsLeft":"10mm","MarginOptionsRight":"10mm","MarginOptionsTop":"15mm","PageRanges":null,"PreferCSSPageSize":false,"PrintBackground":true,"Scale":1.00000}

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
{"Name":"💡 myData search results","Active":true,"Notes":"Examples of obtaining custom search results using custom Prepare ","Roles":50538,"AType":34,"IncludeWoItemDescendants":false,"Template":"<html>\n\n<body>\n <div>\n\n <div>\n <p>myData.SearchResults - Fetched dynamically from API route <strong>enum-list/list/AyaType</strong> using report template's Prepare</p>\n <p>specifically, Prepare searches for the text \"Purple\" in any object in the database and returns....</p>\n <p>{{ayJSON myData.SearchResults}}</p>\n </div>\n \n <table class=\"singlePage\"> <!--this class causes a page break for each object - for each work order -->\n <thead>\n <tr>\n <th colspan=\"5\">Column to the right displays the individual property myData.SearchResults.data.totalResultsFound obtained:</th>\n <td colspan=\"5\">{{ayJSON myData.SearchResults.data.totalResultsFound}}</td>\n </tr>\n <tr>\n <th colspan=\"5\">Column to the right displays the third array in the search results:</th>\n <td colspan=\"5\">{{ayJSON myData.SearchResults.data.searchResults.[2]}}</td>\n </tr>\n <tr>\n <th colspan=\"10\">&nbsp;</th>\n </tr>\n <tr>\n <th colspan=\"10\">Below displays each record that has the search result</th>\n </tr>\n <tr>\n <th colspan=\"6\">Name</th>\n <th colspan=\"2\">Type</th>\n <th colspan=\"2\">ID</th>\n </tr>\n </thead>\n\n {{#each myData.SearchResults.data.searchResults}}<!--iterates through for each result -->\n <tbody> \n <tr>\n <td colspan=\"6\">{{name}}</td>\n <td colspan=\"2\">{{type}}</td>\n <td colspan=\"2\">{{id}}</td>\n </tr> \n </tbody> \n {{/each}}\n\n </table>\n\n </div>\n</body>\n</html>","Style":".singlePage\n{\npage-break-after: always;\n}\n\np {\n word-wrap: break-word;\n font-size: 9pt;\n}\nbody {\n font-family: 'Helvetica', 'Helvetica Neue', Arial, sans-serif; \n}\n\n.reporttitle { \n margin-bottom: 20pt; \n font-weight: bold; \n font-size: 11pt; \n color: #9e9e9e;\n} \n\ntable, td, th {\n border: 1px solid black;\n}\n\ntable { \n white-space: pre-wrap;\n width: 100%;\n table-layout: fixed; /* the # of columns set in the first row of the thead will be fixed and applied throughout table and rows */\n }\n\nth {\n height: 30px;\n font-size: 9pt; \n color: #9e9e9e;\n}\n\ntd {\n padding: 10px;\n word-wrap: break-word;\n font-size: 9pt;\n text-align: center;\n}\n\n\ntbody tr:nth-child(even) {\n background-color: #f8f8f8; /* MUST checkmark Print background in PDF Options for this to show */\n}\n\n\n.rightlean {\n text-align: right;\n}\n.leftlean {\n text-align: left;\n}\n.centerlean {\n text-align: center;\n}\n\n\n.fontgreen {\n color: green;\n}\n.fontblue {\n color: blue;\n}\n.fontred {\n color:red;\n}\n\n","JsPrerender":"async function ayPrepareData(reportData){ \n //this function (if present) is called with the report data \n //before the report is rendered\n //modify data as required here and return it to change the data before the report renders\n //see the help documentation for details\n\n\n //Put the data into the main report data object so it's available to the template\n reportData.myData={ServerInfo:await ayGetFromAPI(\"server-info\")};\n \n //Example API POST method to fetch data from api server\n let searchPostData={phrase: \"Purple\"};\n reportData.myData.SearchResults=await ayPostToAPI(\"search\", searchPostData);\n\n\t\n\n return reportData;\n}","JsHelpers":"//Register custom Handlebars helpers here to use in your report script\n//https://handlebarsjs.com/guide/#custom-helpers\nHandlebars.registerHelper('loud', function (aString) {\n return aString.toUpperCase()\n})","RenderType":0,"HeaderTemplate":"<span>&nbsp; </span>","FooterTemplate":"<span style=\"font-size:6pt; width: 96%;text-align:left; \">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; Printed date: PDFDate</span>\n<span style=\"font-size:6pt;width: 96%; text-align: right; \">Page <span class=\"pageNumber\"></span> of <span class=\"totalPages\"></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span>","DisplayHeaderFooter":true,"PaperFormat":10,"Landscape":true,"MarginOptionsBottom":"15mm","MarginOptionsLeft":"15mm","MarginOptionsRight":"15mm","MarginOptionsTop":"10mm","PageRanges":null,"PreferCSSPageSize":false,"PrintBackground":true,"Scale":1.00000}
{"Name":"z_myData search results","Active":true,"Notes":"Examples of obtaining custom search results using custom Prepare ","Roles":50538,"AType":34,"IncludeWoItemDescendants":false,"Template":"<html>\n\n<body>\n <div>\n\n <div>\n <p>myData.SearchResults - Fetched dynamically from API route <strong>enum-list/list/AyaType</strong> using report template's Prepare</p>\n <p>specifically, Prepare searches for the text \"Purple\" in any object in the database and returns....</p>\n <p>{{ayJSON myData.SearchResults}}</p>\n </div>\n \n <table class=\"singlePage\"> <!--this class causes a page break for each object - for each work order -->\n <thead>\n <tr>\n <th colspan=\"5\">Column to the right displays the individual property myData.SearchResults.data.totalResultsFound obtained:</th>\n <td colspan=\"5\">{{ayJSON myData.SearchResults.data.totalResultsFound}}</td>\n </tr>\n <tr>\n <th colspan=\"5\">Column to the right displays the third array in the search results:</th>\n <td colspan=\"5\">{{ayJSON myData.SearchResults.data.searchResults.[2]}}</td>\n </tr>\n <tr>\n <th colspan=\"10\">&nbsp;</th>\n </tr>\n <tr>\n <th colspan=\"10\">Below displays each record that has the search result</th>\n </tr>\n <tr>\n <th colspan=\"6\">Name</th>\n <th colspan=\"2\">Type</th>\n <th colspan=\"2\">ID</th>\n </tr>\n </thead>\n\n {{#each myData.SearchResults.data.searchResults}}<!--iterates through for each result -->\n <tbody> \n <tr>\n <td colspan=\"6\">{{name}}</td>\n <td colspan=\"2\">{{type}}</td>\n <td colspan=\"2\">{{id}}</td>\n </tr> \n </tbody> \n {{/each}}\n\n </table>\n\n </div>\n</body>\n</html>","Style":".singlePage\n{\npage-break-after: always;\n}\n\np {\n word-wrap: break-word;\n font-size: 9pt;\n}\nbody {\n font-family: 'Helvetica', 'Helvetica Neue', Arial, sans-serif; \n}\n\n.reporttitle { \n margin-bottom: 20pt; \n font-weight: bold; \n font-size: 11pt; \n color: #9e9e9e;\n} \n\ntable, td, th {\n border: 1px solid black;\n}\n\ntable { \n white-space: pre-wrap;\n width: 100%;\n table-layout: fixed; /* the # of columns set in the first row of the thead will be fixed and applied throughout table and rows */\n }\n\nth {\n height: 30px;\n font-size: 9pt; \n color: #9e9e9e;\n}\n\ntd {\n padding: 10px;\n word-wrap: break-word;\n font-size: 9pt;\n text-align: center;\n}\n\n\ntbody tr:nth-child(even) {\n background-color: #f8f8f8; /* MUST checkmark Print background in PDF Options for this to show */\n}\n\n\n.rightlean {\n text-align: right;\n}\n.leftlean {\n text-align: left;\n}\n.centerlean {\n text-align: center;\n}\n\n\n.fontgreen {\n color: green;\n}\n.fontblue {\n color: blue;\n}\n.fontred {\n color:red;\n}\n\n","JsPrerender":"async function ayPrepareData(reportData){ \n //this function (if present) is called with the report data \n //before the report is rendered\n //modify data as required here and return it to change the data before the report renders\n //see the help documentation for details\n\n\n //Put the data into the main report data object so it's available to the template\n reportData.myData={ServerInfo:await ayGetFromAPI(\"server-info\")};\n \n //Example API POST method to fetch data from api server\n let searchPostData={phrase: \"Purple\"};\n reportData.myData.SearchResults=await ayPostToAPI(\"search\", searchPostData);\n\n\t\n\n return reportData;\n}","JsHelpers":"//Register custom Handlebars helpers here to use in your report script\n//https://handlebarsjs.com/guide/#custom-helpers\nHandlebars.registerHelper('loud', function (aString) {\n return aString.toUpperCase()\n})","RenderType":0,"HeaderTemplate":"<span>&nbsp; </span>","FooterTemplate":"<span style=\"font-size:6pt; width: 96%;text-align:left; \">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; Printed date: PDFDate</span>\n<span style=\"font-size:6pt;width: 96%; text-align: right; \">Page <span class=\"pageNumber\"></span> of <span class=\"totalPages\"></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span>","DisplayHeaderFooter":true,"PaperFormat":10,"Landscape":true,"MarginOptionsBottom":"15mm","MarginOptionsLeft":"15mm","MarginOptionsRight":"15mm","MarginOptionsTop":"10mm","PageRanges":null,"PreferCSSPageSize":false,"PrintBackground":true,"Scale":1.00000}

View File

@@ -1 +1 @@
{"Name":"💡 replace carriage return with space","Active":true,"Notes":"Example custom Prepare to replace carriage return with space for a specific key of this object ","Roles":124927,"AType":8,"IncludeWoItemDescendants":false,"Template":"<html>\n\n<body>\n\t<div>\n\t\t<div class=\"reporttitle\">\n\t\t\t<p>Custom Prepare to remove carriage returns</p>\n\t\t</div>\n\t\t<table>\n\t\t\t<thead>\n\t\t\t\t<tr>\n\t\t\t\t\t<th colspan=\"7\" class=\"leftlean\">{{ayT 'Customer'}}</th>\n\t\t\t\t\t<th colspan=\"1\">&nbsp;</th>\n\t\t\t\t\t<th colspan=\"7\">{{ayT 'CustomerNotes'}}</th>\n\t\t\t\t\t<th colspan=\"1\">&nbsp;</th>\n\t\t\t\t\t<th colspan=\"8\">Customized {{ayT 'CustomerNotes'}}</th>\n\t\t\t\t</tr>\n\t\t\t</thead>\n\n\t\t\t<tbody>\n\t\t\t\t{{#each ayReportData}}\n\t\t\t\t<tr>\n\t\t\t\t\t<td colspan=\"7\">{{Name}}</td>\n\t\t\t\t\t<td colspan=\"1\">&nbsp;</td>\n\t\t\t\t\t<td colspan=\"7\">{{Notes}}</td>\n\t\t\t\t\t<td colspan=\"1\">&nbsp;</td>\n\t\t\t\t\t<td colspan=\"8\">{{NotesNoCarriage}}</td>\n\t\t\t\t</tr>\n\t\t\t\t<tr>\n\t\t\t\t\t<td colspan=\"24\">&nbsp;</td>\n\t\t\t\t</tr>\n\t\t\t\t{{/each}}\n\t\t\t</tbody>\n\t\t</table>\n\n\t\t<table>\n {{#each ayReportData}}\n <tbody>\n\t\t\t \t<tr>\n\t\t\t\t\t<td colspan=\"24\">&nbsp;</td>\n\t\t\t\t</tr>\n <tr>\n <td colspan=\"24\">This is a printout of the data returned from the Custom Prepare that this report now uses, each \"group\" is an object. Compare against the data that shows in the \"Sample Data\" when editing this report template.</td>\n </tr>\n <tr>\n\t\t\t <td colspan=\"24\">{{ayJSON this}}</td>\n </tr>\n </tbody>\n {{/each}}\n </table>\n\t</div>\n</body>\n</html>","Style":"\r\n.singlePage\r\n{\r\npage-break-after: always;\r\n}\r\n\r\nbody {\r\n font-family: 'Helvetica', 'Helvetica Neue', Arial, sans-serif; \r\n}\r\n\r\n.reporttitle { \r\n margin-bottom: 20pt; \r\n font-weight: bold; \r\n font-size: 13pt; \r\n text-align: center;\r\n color: #9e9e9e;\r\n} \r\n\r\n\r\ntable { \r\n table-layout: fixed; //setting this to fixed causes columns to be evenly spaced for the entire table regardless of cell content, and then colspan then \"works\" as expected\r\n font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;\r\n border-collapse: collapse;\r\n white-space: pre-wrap;\r\n font-size: 8pt;\r\n width: 100%;\r\n }\r\n\r\n\r\nth {\r\n height: 30px;\r\n text-align: left;\r\n color: #9e9e9e;\r\n}\r\n\r\n\r\ntbody tr {\r\n height: 10px;\r\n word-wrap: break-word;\r\n}\r\n\r\ntbody tr:nth-child(even) {\r\n background-color: #f8f8f8; /* MUST checkmark Print background in PDF Options for this to show */\r\n}\r\n\r\n\r\n.fontgreen {\r\n color: green;\r\n}\r\n.fontblue {\r\n color: blue;\r\n}\r\n.fontred {\r\n color:red;\r\n}\r\n\r\n\r\n.rightlean {\r\n text-align: right;\r\n}\r\n.leftlean {\r\n text-align: left;\r\n}\r\n.centerlean {\r\n text-align: center;\r\n}","JsPrerender":"async function ayPrepareData(ayData){ \n\n await ayGetTranslations([ \"Customer\", \"CustomerNotes\" ]);\n\n //for each Customer, if the General Notes field is NOT null, creates a new key NotesNoCarriage and puts into it the text from Notes replacing ANY carriage returns with a space, so all text is on same line(s)\n for (EachCU of ayData.ayReportData) {\n if (EachCU.Notes != null) {\n n = EachCU.Notes;\n EachCU.NotesNoCarriage = n.replace(/[\\n\\r]+/g, ' ');\n }\n }\n\n return ayData;\n}","JsHelpers":"","RenderType":0,"HeaderTemplate":"<span style=\"font-size:6pt; width: 96%;text-align:left; \">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; Printed date: PDFDate</span>\n<span style=\"font-size:6pt;width: 96%; text-align: right; \">Page <span class=\"pageNumber\"></span> of <span class=\"totalPages\"></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span>","FooterTemplate":"<span>&nbsp; </span>","DisplayHeaderFooter":true,"PaperFormat":10,"Landscape":false,"MarginOptionsBottom":"20mm","MarginOptionsLeft":"15mm","MarginOptionsRight":"15mm","MarginOptionsTop":"15mm","PageRanges":null,"PreferCSSPageSize":false,"PrintBackground":true,"Scale":1.00000}
{"Name":"z_replace carriage return with space","Active":true,"Notes":"Example custom Prepare to replace carriage return with space for a specific key of this object ","Roles":124927,"AType":8,"IncludeWoItemDescendants":false,"Template":"<html>\n\n<body>\n\t<div>\n\t\t<div class=\"reporttitle\">\n\t\t\t<p>Custom Prepare to remove carriage returns</p>\n\t\t</div>\n\t\t<table>\n\t\t\t<thead>\n\t\t\t\t<tr>\n\t\t\t\t\t<th colspan=\"7\" class=\"leftlean\">{{ayT 'Customer'}}</th>\n\t\t\t\t\t<th colspan=\"1\">&nbsp;</th>\n\t\t\t\t\t<th colspan=\"7\">{{ayT 'CustomerNotes'}}</th>\n\t\t\t\t\t<th colspan=\"1\">&nbsp;</th>\n\t\t\t\t\t<th colspan=\"8\">Customized {{ayT 'CustomerNotes'}}</th>\n\t\t\t\t</tr>\n\t\t\t</thead>\n\n\t\t\t<tbody>\n\t\t\t\t{{#each ayReportData}}\n\t\t\t\t<tr>\n\t\t\t\t\t<td colspan=\"7\">{{Name}}</td>\n\t\t\t\t\t<td colspan=\"1\">&nbsp;</td>\n\t\t\t\t\t<td colspan=\"7\">{{Notes}}</td>\n\t\t\t\t\t<td colspan=\"1\">&nbsp;</td>\n\t\t\t\t\t<td colspan=\"8\">{{NotesNoCarriage}}</td>\n\t\t\t\t</tr>\n\t\t\t\t<tr>\n\t\t\t\t\t<td colspan=\"24\">&nbsp;</td>\n\t\t\t\t</tr>\n\t\t\t\t{{/each}}\n\t\t\t</tbody>\n\t\t</table>\n\n\t\t<table>\n {{#each ayReportData}}\n <tbody>\n\t\t\t \t<tr>\n\t\t\t\t\t<td colspan=\"24\">&nbsp;</td>\n\t\t\t\t</tr>\n <tr>\n <td colspan=\"24\">This is a printout of the data returned from the Custom Prepare that this report now uses, each \"group\" is an object. Compare against the data that shows in the \"Sample Data\" when editing this report template.</td>\n </tr>\n <tr>\n\t\t\t <td colspan=\"24\">{{ayJSON this}}</td>\n </tr>\n </tbody>\n {{/each}}\n </table>\n\t</div>\n</body>\n</html>","Style":"\r\n.singlePage\r\n{\r\npage-break-after: always;\r\n}\r\n\r\nbody {\r\n font-family: 'Helvetica', 'Helvetica Neue', Arial, sans-serif; \r\n}\r\n\r\n.reporttitle { \r\n margin-bottom: 20pt; \r\n font-weight: bold; \r\n font-size: 13pt; \r\n text-align: center;\r\n color: #9e9e9e;\r\n} \r\n\r\n\r\ntable { \r\n table-layout: fixed; //setting this to fixed causes columns to be evenly spaced for the entire table regardless of cell content, and then colspan then \"works\" as expected\r\n font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;\r\n border-collapse: collapse;\r\n white-space: pre-wrap;\r\n font-size: 8pt;\r\n width: 100%;\r\n }\r\n\r\n\r\nth {\r\n height: 30px;\r\n text-align: left;\r\n color: #9e9e9e;\r\n}\r\n\r\n\r\ntbody tr {\r\n height: 10px;\r\n word-wrap: break-word;\r\n}\r\n\r\ntbody tr:nth-child(even) {\r\n background-color: #f8f8f8; /* MUST checkmark Print background in PDF Options for this to show */\r\n}\r\n\r\n\r\n.fontgreen {\r\n color: green;\r\n}\r\n.fontblue {\r\n color: blue;\r\n}\r\n.fontred {\r\n color:red;\r\n}\r\n\r\n\r\n.rightlean {\r\n text-align: right;\r\n}\r\n.leftlean {\r\n text-align: left;\r\n}\r\n.centerlean {\r\n text-align: center;\r\n}","JsPrerender":"async function ayPrepareData(ayData){ \n\n await ayGetTranslations([ \"Customer\", \"CustomerNotes\" ]);\n\n //for each Customer, if the General Notes field is NOT null, creates a new key NotesNoCarriage and puts into it the text from Notes replacing ANY carriage returns with a space, so all text is on same line(s)\n for (EachCU of ayData.ayReportData) {\n if (EachCU.Notes != null) {\n n = EachCU.Notes;\n EachCU.NotesNoCarriage = n.replace(/[\\n\\r]+/g, ' ');\n }\n }\n\n return ayData;\n}","JsHelpers":"","RenderType":0,"HeaderTemplate":"<span style=\"font-size:6pt; width: 96%;text-align:left; \">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; Printed date: PDFDate</span>\n<span style=\"font-size:6pt;width: 96%; text-align: right; \">Page <span class=\"pageNumber\"></span> of <span class=\"totalPages\"></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span>","FooterTemplate":"<span>&nbsp; </span>","DisplayHeaderFooter":true,"PaperFormat":10,"Landscape":false,"MarginOptionsBottom":"20mm","MarginOptionsLeft":"15mm","MarginOptionsRight":"15mm","MarginOptionsTop":"15mm","PageRanges":null,"PreferCSSPageSize":false,"PrintBackground":true,"Scale":1.00000}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -17,19 +17,24 @@ namespace AyaNova.Util
internal static class ReportRenderManager
{
/*
expired processes are removed by the act of tryign to get a new slot so in this way it still supports running super long reports overnight for example as long as there is no contention
expired processes are removed by the act of trying to get a new slot so in this way it still supports running super long reports overnight for example as long as there is no contention
The other way was by a job that looks for expired processes but that would mean all old jobs would expire all the time so there would be an issue with huge reports never working
*/
//thread safe collection for unordered items, optimized for single thread produce/consume (which is the norm here) but supports multithread produce / consume (which is needed for separate cleanup job)
private static ConcurrentBag<ReportRenderInstanceInfo> _baginstances = new ConcurrentBag<ReportRenderInstanceInfo>();
internal static ConcurrentBag<ReportRenderInstanceInfo> _baginstances;// = new ConcurrentBag<ReportRenderInstanceInfo>();
public class ReportRenderInstanceInfo
static ReportRenderManager()
{
public int ReporterProcessId { get; set; }
public DateTime Expires { get; set; }
_baginstances = new ConcurrentBag<ReportRenderInstanceInfo>();
}
public ReportRenderInstanceInfo(int processId)
internal class ReportRenderInstanceInfo
{
internal int ReporterProcessId { get; set; }
internal DateTime Expires { get; set; }
internal ReportRenderInstanceInfo(int processId)
{
ReporterProcessId = processId;
Expires = DateTime.UtcNow.AddMilliseconds(ServerBootConfig.AYANOVA_REPORT_RENDERING_TIMEOUT);
@@ -37,16 +42,16 @@ namespace AyaNova.Util
}
public static bool RenderSlotAvailable(ILogger log)
internal static bool RenderSlotAvailable(ILogger log)
{
log.LogTrace("RenderSlotAvailable check");
var count = _baginstances.Count;
// #if (DEBUG)
// log.LogInformation($"DBG: RenderSlotAvailable check, there are currently {count} instances in the bag");
// #endif
if (count >= ServerBootConfig.AYANOVA_REPORT_RENDERING_MAX_INSTANCES)
//var count = _baginstances.Count;
#if (DEBUG)
log.LogInformation($"DBG: RenderSlotAvailable check, there are currently {_baginstances.Count} instances in the bag");
#endif
if (_baginstances.Count >= ServerBootConfig.AYANOVA_REPORT_RENDERING_MAX_INSTANCES)
{
log.LogTrace($"RenderSlotAvailable there are no free report rendering slots available, current count is {count}, checking for expired slots to force closed");
log.LogTrace($"RenderSlotAvailable there are no free report rendering slots available, current count is {_baginstances.Count}, checking for expired slots to force closed");
//check for expired and remove
var Instances = _baginstances.ToArray();
var dtNow = DateTime.UtcNow;
@@ -54,9 +59,9 @@ namespace AyaNova.Util
{
if (i.Expires < dtNow)
{
// #if (DEBUG)
// log.LogInformation($"DBG: RenderSlotAvailable attempting kill of expired process {i.ReporterProcessId}");
// #endif
#if (DEBUG)
log.LogInformation($"DBG: RenderSlotAvailable attempting kill of expired process {i.ReporterProcessId}");
#endif
ForceCloseProcess(i, log);
}
}
@@ -65,7 +70,7 @@ namespace AyaNova.Util
return _baginstances.Count < ServerBootConfig.AYANOVA_REPORT_RENDERING_MAX_INSTANCES;
}
private static bool ForceCloseProcess(ReportRenderInstanceInfo instance, ILogger log)
internal static bool ForceCloseProcess(ReportRenderInstanceInfo instance, ILogger log)
{
log.LogTrace($"ForceCloseProcess on instance id {instance.ReporterProcessId} started {instance.Expires.ToString()} utc");
try
@@ -100,13 +105,23 @@ namespace AyaNova.Util
}
}
internal static void AddProcess(int processId)
internal static void AddProcess(int processId, ILogger log)
{
#if (DEBUG)
log.LogInformation($"DBG: RenderSlotAvailable::AddProcess {processId} in the bag");
#endif
_baginstances.Add(new ReportRenderInstanceInfo(processId));
#if (DEBUG)
log.LogInformation($"DBG: RenderSlotAvailable::AddProcess, there are currently {_baginstances.Count} instances in the bag");
#endif
}
internal static void RemoveProcess(int processId, ILogger log)
{
#if (DEBUG)
log.LogInformation($"DBG: RenderSlotAvailable::RemoveProcess {processId} from the bag");
#endif
foreach (var i in _baginstances)
{
if (i.ReporterProcessId == processId)