Portal application - initial.

This commit is contained in:
2021-07-08 20:50:46 +01:00
parent 13c79a8da4
commit 01ae03ddfc
1208 changed files with 186342 additions and 1690 deletions
@@ -0,0 +1,237 @@
using Sarand.BusinessMgr.Net.Standard.Base.App;
using Sarand.BusinessMgr.Net.Standard.Base.Bus;
using Sarand.BusinessMgr.Net.Core.Procs.Interfaces;
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Bus;
using Sarand.Net.Standard.Base.Dbas;
using Sarand.Net.Standard.Base.Interfaces;
using Sarand.Net.Standard.Base.ItemOptions;
using Sarand.Net.Standard.Base.Procs;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Sarand.Net.Standard.Base.Utils;
namespace Sarand.BusinessMgr.Net.Core.Procs.Consoles
{
//=======================================================================
public class SbmProcessConsole
{
//-------------------------------------------------------------------
// I n t e r f a c e O p t i o n D e f i n i t i o n s
//-------------------------------------------------------------------
public static List<SaItemOptionDfn> GetInterfaceItemOptionDfns(SbmUserContext userContext, SaInterfaceCategory interfaceCategory, short interfaceType)
{
switch (interfaceCategory)
{
case SaInterfaceCategory.Website:
{
break;
}
case SaInterfaceCategory.Accounts:
{
return new SbmAccountsDfn(userContext).GetInterfaceItemOptionDfns((Sbm.InterfaceType_Accounts)interfaceType);
}
case SaInterfaceCategory.System:
{
//if (interfaceType == (int)Sbm.InterfaceType_System.MSOutlook)
//return SbmInterface_System_MSOutlook.GetInterfaceItemOptionDfns();
break;
}
case SaInterfaceCategory.File:
{
if (interfaceType == (short)Sbm.InterfaceType_File.CommodityExport)
{
SbmInterfaceProcess_Commodity_Export.Config processConfig = new SbmInterfaceProcess_Commodity_Export.Config(userContext);
SbmInterfaceProcess_Commodity_Export process = new SbmInterfaceProcess_Commodity_Export(processConfig);
return process.GetInterfaceItemOptionDfns();
}
break;
}
}
return new List<SaItemOptionDfn>();
}
//-------------------------------------------------------------------
// P r o c e s s i n g O p t i o n s
//-------------------------------------------------------------------
public static List<SaProcess_Base.GroupData> GetProcessingOptions(SbmUserContext userContext, SaInterface interfaceObj)
{
if (interfaceObj == null || !SysId.IsValid(interfaceObj.Id)) return new List<SaProcess_Base.GroupData>();
List<SaProcess_Base.GroupData> optionGroups = null;
switch (interfaceObj.Category)
{
case SaInterfaceCategory.System:
{
break;
}
case SaInterfaceCategory.Website:
{
break;
}
case SaInterfaceCategory.Accounts:
{
break;
}
case SaInterfaceCategory.File:
{
if (interfaceObj.Type == (short)Sbm.InterfaceType_File.CommodityExport)
optionGroups = SbmInterfaceProcess_Commodity_Export.GetProcessingOptions(userContext, interfaceObj);
break;
}
}
return optionGroups ?? new List<SaProcess_Base.GroupData>();
}
//-------------------------------------------------------------------
// C o n f i g u r e P r o c e s s
//-------------------------------------------------------------------
public static SbmInterfaceProcess ConfigureProcess(SbmUserContext userContext, SaInterface interfaceObj, SaProcess_Base.GroupData processGroup)
{
if (interfaceObj == null || !SysId.IsValid(interfaceObj.Id)) return null;
switch (interfaceObj.Category)
{
case SaInterfaceCategory.System:
{
break;
}
case SaInterfaceCategory.Website:
{
break;
}
case SaInterfaceCategory.Accounts:
{
break;
}
case SaInterfaceCategory.File:
{
if (interfaceObj.Type == (short)Sbm.InterfaceType_File.CommodityExport)
return SbmInterfaceProcess_Commodity_Export.ConfigureProcess(userContext, interfaceObj, processGroup);
break;
}
}
return null;
}
//-------------------------------------------------------------------
// P r o c e s s I n t e r f a c e
//-------------------------------------------------------------------
public static bool ProcessInterface(SbmUserContext userContext, SaInterface interfaceObj)
{
if (interfaceObj == null || !SysId.IsValid(interfaceObj.Id)) return false;
switch (interfaceObj.Category)
{
case SaInterfaceCategory.System:
{
break;
}
case SaInterfaceCategory.Website:
{
break;
}
case SaInterfaceCategory.Accounts:
{
break;
}
case SaInterfaceCategory.File:
{
break;
}
}
return false;
}
//-------------------------------------------------------------------
// I n v o k e P r o c e s s D e f i n i t i o n
//-------------------------------------------------------------------
// Invoke process based on definition from command line argument.
public static bool InvokeProcessDefinition(SbmUserContext userContext, SacsProcessDefinition processDfn)
{
//----------------------------------------------------------------
// Log process definition.
SaLogger.Info("Running process: " + processDfn.ProcessName + "." + (processDfn.Params.Count > 0 ? " Params: " + processDfn.Params.Concat() : ""), true);
SbmProcessConsole_Notifier processNotifier = new SbmProcessConsole_Notifier();
//----------------------------------------------------------------
// Hapag-Lloyd EDI.
if (processDfn.ProcessName.EqualsCI("ApplicationParameterName..."))
{
String caption = "System Interface ...";
SaInterface saInterface = GetSystemInterfaceByType(userContext, (short)Sbm.InterfaceType_System.MSOutlook, caption);
if (saInterface != null)
{
/*
SbmInterface_System_HapagLloyd_Import.Config configInterface = new SbmInterface_System_HapagLloyd_Import.Config(dbUpdateInfo);
configInterface.InterfaceId = saInterface.Id;
configInterface.ImportNotifications = true;
configInterface.Automated = true;
SbmInterface_System_HapagLloyd_Import process = new SbmInterface_System_HapagLloyd_Import(configInterface);
process.ProcessNotifier = processNotifier;
process.RunProcess();
if (process.HasProcessError())
SaLogger.Warn(caption + " reported: " + process.GetProcessError(), true);
else if (process.IsInterrupted)
SaLogger.Warn(caption + " interrupted - processing may be incomplete.", true);
else
SaLogger.Info(caption + " run successfully", true);
*/
}
}
//----------------------------------------------------------------
return false;
}
//-------------------------------------------------------------------
// Retrieve interface.
private static SaInterface GetSystemInterfaceByType(SbmUserContext userContext, short interfaceType, String caption)
{
return GetInterfaceByCategoryType(userContext, SaInterfaceCategory.System, interfaceType, caption);
}
private static SaInterface GetInterfaceByCategoryType(SbmUserContext userContext, SaInterfaceCategory interfaceCategory, short interfaceType, String caption)
{
SaInterface saInterface = null;
SaInterface.Params prmInterface = new SaInterface.Params();
prmInterface.InterfaceCategory = interfaceCategory;
prmInterface.IncludedInterfaceTypes.Add(interfaceType);
prmInterface.StatusCat = SaInterface.StatusCat.Any;
using (SaInterfaceConsole interfaceConsole = SbmGlobals.Application.CreateInterfaceConsole(userContext))
{
saInterface = interfaceConsole.GetInterface(prmInterface);
if (saInterface == null)
{
SaLogger.Warn("Unable to run " + caption + " - Interface not found", true);
return null;
}
if (!saInterface.Active)
{
SaLogger.Warn("Unable to run " + caption + " - Interface is not active", true);
return null;
}
}
return saInterface;
}
//-------------------------------------------------------------------
}
//=======================================================================
/// <summary>
/// Process Notificer for Process Console.
/// </summary>
class SbmProcessConsole_Notifier : ISacsProcessNotifier
{
public void OnStartProcess()
{
}
public void OnEndProcess()
{
}
public void SetProgressInfo(List<string> progressInfo)
{
}
}
//=======================================================================
}
@@ -0,0 +1,271 @@
using Sarand.BusinessMgr.Net.Standard.Base.App;
using Sarand.BusinessMgr.Net.Core.Procs.Procs;
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Bus;
using Sarand.Net.Standard.Base.Dbas;
using Sarand.Net.Standard.Base.Interfaces;
using Sarand.Net.Standard.Base.IO;
using Sarand.Net.Standard.Base.ItemOptions;
using System;
using System.Collections.Generic;
using System.Net;
using System.Text;
using Sarand.Net.Standard.Base.Utils;
using System.IO;
namespace Sarand.BusinessMgr.Net.Core.Procs.Interfaces
{
//=======================================================================
public abstract class SbmInterfaceProcess : SbmProcess_Base
{
//-------------------------------------------------------------------
// Configuration.
public enum InterfaceProcessCreation
{
None,
OnStartProcessing,
OnLogMessage,
}
public new class Config : SbmProcess_Base.Config
{
protected Config (SbmUserContext userContext) : base(userContext)
{
}
public SysId InterfaceId { get; set; }
public List<SysId> ItemIds { get; } = new List<SysId>();
public InterfaceProcessCreation InterfaceProcessCreation { get; set; } = InterfaceProcessCreation.OnLogMessage;
}
//-------------------------------------------------------------------
// Definitions.
/// <summary>Interface processing groups - used for popup 'Process Interface' form.</summary>
public enum InterfaceProcessGroup
{
Import,
Export,
}
/// <summary>Interface configuration parameters.</summary>
public class InterfaceParams : SbmProcess_Base.ProcessParams
{
}
/// <summary>Process parameters - processing control properties.</summary>
public new class ProcessParams : SbmProcess_Base.ProcessParams
{
}
//-------------------------------------------------------------------
// Data members.
private Config config = null;
private static List<TypeCodeDesc> interfaceProcessGroups = null;
private SaInterface interfaceObj = null;
private SaInterfaceProcess interfaceProcess = null;
//-------------------------------------------------------------------
// Static initialisation.
static SbmInterfaceProcess ()
{
// Interface process groups.
interfaceProcessGroups = new List<TypeCodeDesc>();
interfaceProcessGroups.Add(InterfaceProcessGroup.Import, "Import", "Import");
interfaceProcessGroups.Add(InterfaceProcessGroup.Export, "Export", "Export");
}
//-------------------------------------------------------------------
// Construction.
protected SbmInterfaceProcess (Config config) : base(config)
{
this.config = config;
interfaceObj = InterfaceConsole.GetInterfaceById(config.InterfaceId);
}
protected override void Dispose (bool disposing)
{
if (disposing)
{
if (interfaceProcess != null) interfaceProcess.Dispose(); interfaceProcess = null;
if (interfaceObj != null) interfaceObj.Dispose(); interfaceObj = null;
}
base.Dispose(disposing);
}
//-------------------------------------------------------------------
// Properties.
protected String InterfaceCaptionSuffix { get; set; } = " [Interface]";
protected static List<TypeCodeDesc> InterfaceProcessGroups { get { return new List<TypeCodeDesc>(interfaceProcessGroups); } }
protected SaInterface Interface { get { return interfaceObj; } }
//-------------------------------------------------------------------
// Validate configuration.
protected virtual bool ValidateConfig ()
{
return OK;
}
//-------------------------------------------------------------------
// P r o c e s s i n g
//-------------------------------------------------------------------
protected override void ResetProcess ()
{
base.ResetProcess();
}
//-------------------------------------------------------------------
// I t e m O p t i o n D e f i n i t i o n s
//-------------------------------------------------------------------
protected static SaItemOptionDfn InitItemOptionDfn (String itemOptionName, String itemOptionCaption, List<TypeCodeDesc> optionContexts = null)
{
SaItemOptionDfn itemOptionDfn = new SaItemOptionDfn();
itemOptionDfn.Name = itemOptionName;
itemOptionDfn.DisplayOptions.Caption = itemOptionCaption;
if (optionContexts != null)
{
itemOptionDfn.DisplayOptions.Contexts.AddRange(optionContexts);
itemOptionDfn.DisplayOptions.RequiresContext = true;
}
itemOptionDfn.DisplayOptions.NonDisplayTemplate = true;
itemOptionDfn.DisplayOptions.CanAddOption = true;
itemOptionDfn.DisplayOptions.CanRemoveOption = true;
return itemOptionDfn;
}
//-------------------------------------------------------------------
// P r o c e s s i n g E v e n t s
//-------------------------------------------------------------------
protected override void OnProcessStarting ()
{
base.OnProcessStarting();
if (config.InterfaceProcessCreation == InterfaceProcessCreation.OnStartProcessing)
OpenInterfaceProcess();
}
protected override void OnProcessEnding ()
{
CloseInterfaceProcess();
base.OnProcessEnding();
}
//-------------------------------------------------------------------
// I n t e r f a c e P r o c e s s
//-------------------------------------------------------------------
private bool OpenInterfaceProcess ()
{
if (!SysId.IsValid(config.InterfaceId)) return false;
// Check if interface process already created.
if (interfaceProcess != null && interfaceProcess.InterfaceId.Equals(config.InterfaceId)) return true;
// Create interface process.
SaInterfaceProcessDirection processDirection = SaInterfaceProcessDirection.Inbound;
if (config.Export) processDirection = SaInterfaceProcessDirection.Outbound;
int processSequence = (InterfaceConsole.GetMaxProcessSequence(config.InterfaceId, processDirection, false) + 1);
interfaceProcess = InterfaceConsole.StartInterfaceProcess(config.InterfaceId, processDirection, DateUtil.Format(StartedAt, "yyyyMMddHHmmss"), StartedAt, processSequence, DbUpdateInfo);
return (interfaceProcess != null && interfaceProcess.InterfaceId.Equals(config.InterfaceId));
}
private bool CloseInterfaceProcess ()
{
if (interfaceProcess == null) return false;
interfaceProcess.ElapseSecs = ElapsedSecs;
if (OK)
{
interfaceProcess.Status = SaStatus.Completed;
LogInterfaceProcessMessage("Processing completed successfully");
}
else
{
// Process has not completed.
interfaceProcess.Status = SaStatus.Cancelled;
if (HasProcessError()) LogInterfaceProcessMessage(GetProcessError(), SaMessageLevel.Error);
if (IsInterrupted) LogInterfaceProcessMessage("Processing cancelled by user", SaMessageLevel.Warning);
}
if (InterfaceConsole.StoreInterfaceProcess(interfaceProcess, DbUpdateInfo))
{ }
else if (!HasProcessError())
SetProcessError("Failed to store interface process: " + InterfaceConsole.ErrorStateMessage);
// Clean-up.
interfaceProcess.Dispose();
interfaceProcess = null;
return true;
}
//-------------------------------------------------------------------
protected bool LogInterfaceProcessMessage (String message, SaMessageLevel messageLevel = SaMessageLevel.Info)
{
if (!OpenInterfaceProcess()) return false;
// Log interface process message.
InterfaceConsole.LogInterfaceProcessMessage(interfaceProcess.Id, DbUpdateInfo, message, messageLevel);
// Errors and warnings wil retunr false.
return (messageLevel == SaMessageLevel.Info || messageLevel == SaMessageLevel.Debug || messageLevel == SaMessageLevel.Trace);
}
//-------------------------------------------------------------------
// I n t e r f a c e D a t a I t e m s
//-------------------------------------------------------------------
protected bool StoreInterfaceDataItem (SaInterfaceDataItem interfaceDataItem, SaStatus processStatus, SaStatus itemStatus, String itemCaption)
{
if (!OK) return false;
DateTime now = DateTime.Now;
// Process status.
if ((int)processStatus > 0)
{
interfaceDataItem.ProcessStatus = processStatus;
interfaceDataItem.ProcessStatusAt = now;
interfaceDataItem.ProcessStatusBy = DbUpdateInfo.UserId;
}
// Item status.
if ((int)itemStatus > 0)
{
interfaceDataItem.ItemStatus = itemStatus;
interfaceDataItem.ItemStatusAt = now;
interfaceDataItem.ItemStatusBy = DbUpdateInfo.UserId;
}
// Store interface data item.
if (!InterfaceConsole.StoreInterfaceDataItem(interfaceDataItem, DbUpdateInfo))
SetProcessError("Failed to save Interface data item" + (!StringUtil.IsEmpty(itemCaption) ? " for " + itemCaption : "") + " - " + InterfaceConsole.ErrorStateMessage);
return OK;
}
//-------------------------------------------------------------------
// A r c h i v e F i l e
//-------------------------------------------------------------------
protected bool SaveFileDataToArchive (String archiveRootFolder, String fileName, String fileExtension, String fileData, String fn)
{
if (!OK) return false;
DateTime today = DateTime.Today;
String archiveFolder =
archiveRootFolder.TrimEnd(new char[] { Path.DirectorySeparatorChar }) + Path.DirectorySeparatorChar +
DateUtil.Format(today, "yyyy") + Path.DirectorySeparatorChar +
DateUtil.Format(today, "yyyyMM");
SaLogger.Debug(fn + " Archive folder: " + archiveFolder);
if (FileUtil.ValidateDirectory(archiveFolder, true))
{
for (int fileIdx = 0; fileIdx < 10000; fileIdx++)
{
String archiveFilename = fileName + (fileIdx > 0 ? "." + StringUtil.PadLeft(fileIdx.ToString(), 4, '0') : "") + (!StringUtil.IsEmpty(fileExtension) ? "." + fileExtension : "");
String archivePath = archiveFolder + Path.DirectorySeparatorChar + archiveFilename;
SaLogger.Debug(fn + " Archive path: " + archivePath);
if (!FileUtil.IsFile(archivePath))
{
File.WriteAllText(archivePath, fileData);
break;
}
}
}
else return LogInterfaceProcessMessage("Failed to validate archive folder '" + archiveFolder + "'.", SaMessageLevel.Warning);
return OK;
}
//-------------------------------------------------------------------
}
//=======================================================================
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,39 @@
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Utils;
using Sarand.Net.Standard.Proc.Excel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Sarand.BusinessMgr.Net.Core.Procs.Procs
{
//=======================================================================
// D a t a E x p o r t P r o c e s s
//=======================================================================
partial class SbmProcess_Base
{
//-------------------------------------------------------------------
/// <summary>
/// Open Excel worksheet.
/// <para>Additional sheets are added up to the position specified if less are currently populated.</para>
/// </summary>
protected bool OpenWorkSheet (SaMsSpreadsheet spreadsheet, uint worksheetPos = 1, String worksheetName = "")
{
if (!OK) return false;
if (worksheetPos < 1) worksheetPos = 1;
while (spreadsheet.WorksheetCount < worksheetPos)
spreadsheet.AddWorksheet("Sheet " + (spreadsheet.WorksheetCount + 1));
spreadsheet.OpenWorksheet(worksheetPos);
if (!StringUtil.IsEmpty(StringUtil.TrimAll(worksheetName, new char[] { ' ' })))
spreadsheet.WorksheetName = worksheetName;
return OK;
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,276 @@
using Sarand.BusinessMgr.Net.Core.Procs.Procs;
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Utils;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
namespace Sarand.BusinessMgr.Net.Core.Procs.Procs
{
//=======================================================================
// F T P
//=======================================================================
partial class SbmProcess_Base
{
//-------------------------------------------------------------------
// Definitions.
public class FtpConfig
{
public String Host { get; set; }
public String Username { get; set; }
public String Password { get; set; }
}
//-------------------------------------------------------------------
// F T P U t i l i t i e s
//-------------------------------------------------------------------
private String GetFtpUrl (FtpConfig ftpConfig, String path)
{
return "ftp://" + ftpConfig.Host + "/" + FtpUtil.CleansePath(path);
}
private FtpWebRequest CreateFtpRequest (FtpConfig ftpConfig, String path, bool keepAlive = true)
{
FtpWebRequest ftpRequest = (FtpWebRequest)WebRequest.Create(GetFtpUrl(ftpConfig, path));
// Credentials.
if (!StringUtil.IsEmpty(ftpConfig.Username) || StringUtil.IsEmpty(ftpConfig.Password))
ftpRequest.Credentials = new NetworkCredential(ftpConfig.Username, ftpConfig.Password);
ftpRequest.UseBinary = true;
// Keep alive = true will reuse the connection (behind the scenes) even though
// a new FTP Web Request is created for each request. Don't know how!
// Ref. https://stackoverflow.com/questions/7132246/how-to-reuse-ftpwebrequest-connection/7132428#7132428
// Network tracing: https://stackoverflow.com/questions/9664650/output-log-using-ftpwebrequest/9666598#9666598
ftpRequest.KeepAlive = keepAlive;
ftpRequest.Timeout = 5000;
ftpRequest.UsePassive = true;
return ftpRequest;
}
private WebClient CreateFtpWebClient (FtpConfig ftpConfig)
{
WebClient webClient = new WebClient();
// Credentials.
if (!StringUtil.IsEmpty(ftpConfig.Username) || StringUtil.IsEmpty(ftpConfig.Password))
webClient.Credentials = new NetworkCredential(ftpConfig.Username, ftpConfig.Password);
return webClient;
}
//-------------------------------------------------------------------
// Test connection.
protected bool TestFtpConnection (FtpConfig ftpConfig)
{
String fn = GetType().Name + ".TestFtpConnection";
WebResponse response = null;
try
{
// FTP request.
FtpWebRequest ftpRequest = CreateFtpRequest(ftpConfig, "");
ftpRequest.Method = WebRequestMethods.Ftp.ListDirectory;
// Send request.
response = ftpRequest.GetResponse();
using (Stream responseStream = response.GetResponseStream())
using (StreamReader reader = new StreamReader(responseStream))
{
String responseData = reader.ReadToEnd();
SaLogger.Debug(fn + ": Items in root: " + responseData.ReplaceAll("\r\n", ", "));
}
}
catch (Exception exc)
{
SaLogger.Warn(fn + " " + exc.GetType().Name + ": " + exc.Message, true);
return SetProcessError("Unable to connect to FTP host");
}
finally
{
if (response != null) response.Dispose();
}
return true;
}
protected bool CloseFtpConnection (FtpConfig ftpConfig)
{
WebResponse response = null;
try
{
// FTP request with KeepAlive=false to close connection.
FtpWebRequest ftpRequest = CreateFtpRequest(ftpConfig, "", false);
ftpRequest.Method = WebRequestMethods.Ftp.ListDirectory;
response = ftpRequest.GetResponse();
}
catch { }
finally
{
if (response != null) response.Dispose();
}
return true;
}
//-------------------------------------------------------------------
protected bool FtpFolderExists (FtpConfig ftpConfig, String folder)
{
String fn = GetType().Name + ".FtpFolderExists";
WebResponse response = null;
try
{
String parentFolder = FtpUtil.GetParentFolder(folder);
String itemName = FtpUtil.GetItemName(folder);
// FTP request.
FtpWebRequest ftpRequest = CreateFtpRequest(ftpConfig, parentFolder);
ftpRequest.Method = WebRequestMethods.Ftp.ListDirectoryDetails;
// Send request.
String data = "";
response = ftpRequest.GetResponse();
using (Stream responseStream = response.GetResponseStream())
using (StreamReader reader = new StreamReader(responseStream))
{
data = reader.ReadToEnd().Replace("\r", "");
}
String[] directoryItems = data.Split(new String[] { "\n" }, StringSplitOptions.None);
foreach (String directoryItemData in directoryItems)
{
FtpUtil.FtpDirectoryItem directoryItem = FtpUtil.GetDirectoryItem(directoryItemData);
if (directoryItem != null && directoryItem.Name.EqualsCI(itemName))
return directoryItem.IsDirectory;
}
}
catch (Exception exc)
{
SaLogger.Warn(fn + " " + exc.GetType().Name + ": " + exc.Message, true);
return false;
}
finally
{
if (response != null) response.Dispose();
}
return false;
}
//-------------------------------------------------------------------
/// <summary>Get list of items in remote FTP folder.</summary>
protected List<FtpUtil.FtpDirectoryItem> GetFtpFileItems (FtpConfig ftpConfig, String folder)
{
return GetFtpItems(ftpConfig, folder).FindAll(item => !item.IsDirectory);
}
/// <summary>Get list of items in remote FTP folder.</summary>
protected List<FtpUtil.FtpDirectoryItem> GetFtpItems (FtpConfig ftpConfig, String folder)
{
String fn = this.GetType().Name + ".GetFtpItems";
List<FtpUtil.FtpDirectoryItem> files = new List<FtpUtil.FtpDirectoryItem>();
WebResponse response = null;
try
{
// FTP request.
FtpWebRequest ftpRequest = CreateFtpRequest(ftpConfig, folder);
ftpRequest.Method = WebRequestMethods.Ftp.ListDirectoryDetails;
// Send request.
// Send request.
response = ftpRequest.GetResponse();
String data = "";
using (Stream responseStream = response.GetResponseStream())
using (StreamReader reader = new StreamReader(responseStream))
{
data = reader.ReadToEnd().Replace("\r", "");
}
String[] directoryItems = data.Split(new String[] { "\n" }, StringSplitOptions.None);
foreach (String directoryItemData in directoryItems)
{
FtpUtil.FtpDirectoryItem directoryItem = FtpUtil.GetDirectoryItem(directoryItemData);
if (directoryItem != null)
files.Add(directoryItem);
}
}
catch (Exception exc)
{
SaLogger.Warn(fn + " " + exc.GetType().Name + ": " + exc.Message, true);
}
finally
{
if (response != null) response.Dispose();
}
return files;
}
//-------------------------------------------------------------------
protected String GetFtpFileData (FtpConfig ftpConfig, String folder, String filename)
{
String fn = GetType() + ".GetFtpFileData";
WebClient ftpClient = null;
try
{
ftpClient = CreateFtpWebClient(ftpConfig);
String ftpUrl = GetFtpUrl(ftpConfig, folder + "/" + filename);
byte[] fileData = ftpClient.DownloadData(ftpUrl);
return System.Text.Encoding.UTF8.GetString(fileData);
}
catch (Exception exc)
{
SaLogger.Warn(fn + " " + exc.GetType().Name + ": " + exc.Message, true);
}
finally
{
if (ftpClient != null) ftpClient.Dispose();
}
return "";
}
//-------------------------------------------------------------------
protected bool WriteDataToFtpFile (FtpConfig ftpConfig, String folder, String filename, String data)
{
String fn = GetType() + ".WriteDataToFtpFile";
bool dataWrittenToFile = false;
FtpWebResponse response = null;
try
{
FtpWebRequest ftpRequest = CreateFtpRequest(ftpConfig, folder + Path.DirectorySeparatorChar + filename);
ftpRequest.Method = WebRequestMethods.Ftp.UploadFile;
ftpRequest.ContentLength = data.Length;
using (Stream request_stream = ftpRequest.GetRequestStream())
{
byte[] bytes = Encoding.UTF8.GetBytes(data);
request_stream.Write(bytes, 0, data.Length);
request_stream.Close();
}
}
catch (Exception exc)
{
SaLogger.Warn(fn + " " + exc.GetType().Name + ": " + exc.Message, true);
}
finally
{
if (response != null) response.Dispose();
}
return dataWrittenToFile;
}
//-------------------------------------------------------------------
protected bool DeleteFtpFile (FtpConfig ftpConfig, String folder, String filename)
{
String fn = GetType() + ".DeleteFtpData";
bool fileDeleted = false;
FtpWebResponse response = null;
try
{
// FTP request.
FtpWebRequest ftpRequest = CreateFtpRequest(ftpConfig, folder + Path.DirectorySeparatorChar + filename);
ftpRequest.Method = WebRequestMethods.Ftp.DeleteFile;
response = (FtpWebResponse)ftpRequest.GetResponse();
if (EnhancedLogging) SaLogger.Info(ProcessDescription + " - Delete FTP file: Response: " + response.StatusDescription, true);
fileDeleted = true;
}
catch (Exception exc)
{
SaLogger.Warn(fn + " " + exc.GetType().Name + ": " + exc.Message, true);
}
finally
{
if (response != null) response.Dispose();
}
return fileDeleted;
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,300 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.CodeTypes;
using Sarand.Net.Standard.Base.Dbas;
using Sarand.BusinessMgr.Net.Standard.Base.App;
using Sarand.BusinessMgr.Net.Standard.Base.Mgr;
using Sarand.BusinessMgr.Net.Standard.Base.Bus;
using Sarand.Net.Standard.Base.Security;
using Sarand.Net.Standard.Base.UpdateLog;
using Sarand.Net.Standard.Base.Interfaces;
using Sarand.Net.Standard.Base.ItemDocs;
using Sarand.Net.Standard.Base.ReferenceData;
using Sarand.Net.Standard.Base.Bus;
using Sarand.Net.Standard.Base.ItemOptions;
using Sarand.Net.Standard.Base.Utils;
using Sarand.Net.Standard.Proc.Procs;
namespace Sarand.BusinessMgr.Net.Core.Procs.Procs
{
//=======================================================================
public abstract partial class SbmProcess_Base : SaProcess
{
//-------------------------------------------------------------------
// Configuration.
public enum DataFormatType
{
Unknown,
Flat,
Csv,
Excel,
Word,
}
public new class Config : SaProcess.Config
{
protected Config (SbmUserContext userContext) : base()
{
UserContext = userContext;
}
public SbmUserContext UserContext { get; }
/// <summary>Specific type of data item being handled.</summary>
public Sbm.ItemType ItemType { get; set; } = Sbm.ItemType.Unknown;
/// <summary>Format to import/export data.</summary>
public DataFormatType DataFormat { get; set; } = DataFormatType.Unknown;
/// <summary>Application managers and consoles - to share parent's internals.</summary>
public SbmInternals Internals { get; set; }
/// <summary>Extended logging messages.</summary>
public bool EnhancedLogging { get; set; }
}
//-------------------------------------------------------------------
// Definitions.
/// <summary>Process parameters - processing control properties</summary>
public class ProcessParams
{
}
//-------------------------------------------------------------------
// Data members.
private Config config;
private SbmInternals internals = null;
private List<SaItemOption> processingOptions = null;
//-------------------------------------------------------------------
// Construction.
protected SbmProcess_Base (Config config) : base(config)
{
this.config = config;
if (!EnhancedLogging) EnhancedLogging = config.EnhancedLogging;
}
protected override void Dispose (bool disposing)
{
if (disposing)
{
if (internals != null) internals.Dispose();
}
base.Dispose(disposing);
}
//-------------------------------------------------------------------
// Properties.
protected bool EnhancedLogging { get; set; }
/// <summary>Comments are populated with list of processing options by default.</summary>
public override List<string> ProcessComments
{
get
{
List<String> comments = new List<string>(base.ProcessComments);
List<SaItemOptionDfn> optionDfns = GetProcessingOptionDfns();
foreach (SaItemOption itemOption in GetProcessingOptions())
{
SaItemOptionDfn optionDfn = optionDfns.GetItemOptionDfnByName(itemOption.Name);
String context = optionDfn?.GetContextDesc(itemOption.Context) ?? itemOption.Context;
String caption = itemOption.Name + (!StringUtil.IsEmpty(context) ? " - " + context : "");
String value = StringUtil.EmptyOnNull(itemOption.Value);
comments.Add(caption + ": " + value);
}
return comments;
}
}
// Context.
public SbmUserContext UserContext { get { return config.UserContext; } }
public SbmSessionContext SessionContext { get { return UserContext.SessionContext; } }
protected SbmInternals Internals
{
get
{
// Use supplied internals if available.
if (config.Internals != null) return config.Internals;
// Otherwise use local instance.
if (internals == null)
internals = new SbmInternals(UserContext);
return internals;
}
}
protected SbmAccountsDfn AccountsDfn { get { return Internals.AccountsDfn; } }
protected DbUpdateInfo DbUpdateInfo { get { return Internals.DbUpdateInfo; } }
// Excel export.
public static Color ExcelColumnHeadingColor { get; } = Color.LightYellow;
// Security.
protected SbmSecurityProfile SecurityProfile { get { return Internals.SecurityProfile; } }
protected bool UserCanUpdateContacts { get { return (SecurityProfile != null && SecurityProfile.Contacts.CanUpdate); } }
protected bool UserCanCreateContacts { get { return (SecurityProfile != null && SecurityProfile.Contacts.CanCreate); } }
protected bool UserCanViewFinancials { get { return (SecurityProfile != null && SecurityProfile.Financial.CanViewFinancials); } }
protected bool UserCanUpdateFinancials { get { return (SecurityProfile != null && SecurityProfile.Financial.CanUpdateFinancials); } }
protected bool UserCanManageFinancials { get { return (SecurityProfile != null && SecurityProfile.Financial.CanViewFinancials); } }
// ToDo SbmForm SecurityProfile Assets.
/*
protected bool UserCanManageAssets { get { return (SecurityProfile != null && SecurityProfile.Assets.CanManage); } }
protected bool UserCanUpdateAssets { get { return (SecurityProfile != null && SecurityProfile.Assets.CanUpdate); } }
protected bool UserCanViewAssets { get { return (SecurityProfile != null && SecurityProfile.Assets.CanView); } }
*/
// Managers.
protected SbmAccountMgr AccountMgr { get { return Internals.AccountMgr; } }
protected SbmCommodityMgr CommodityMgr { get { return Internals.CommodityMgr; } }
protected SbmPriceMgr PriceMgr { get { return Internals.PriceMgr; } }
protected SbmTransactionMgr TransactionMgr { get { return Internals.TransactionMgr; } }
protected SbmStockMgr StockMgr { get { return Internals.StockMgr; } }
protected SbmProcessMgr ProcessMgr { get { return Internals.ProcessMgr; } }
protected SbmDataItemMgr DataItemMgr { get { return Internals.DataItemMgr; } }
// Consoles.
protected SbmConfigConsole ConfigConsole { get { return Internals.ConfigConsole; } }
protected SaSecurityConsole SecurityConsole { get { return Internals.SecurityConsole; } }
protected SaCodeTypeConsole CodeTypeConsole { get { return Internals.CodeTypeConsole; } }
protected SaReferenceDataConsole ReferenceDataConsole { get { return Internals.ReferenceDataConsole; } }
protected SaItemOptionConsole ItemOptionConsole { get { return Internals.ItemOptionConsole; } }
protected SaUpdateLogConsole UpdateLogConsole { get { return Internals.UpdateLogConsole; } }
protected SaInterfaceConsole InterfaceConsole { get { return Internals.InterfaceConsole; } }
protected SaItemDocConsole ItemDocConsole { get { return Internals.ItemDocConsole; } }
// Configurations.
protected SbmStockControlConfig StockControlConfig { get { return Internals.StockControlConfig; } }
protected SbmRequirementCostingConfig RequirementCostingConfig { get { return Internals.RequirementCostingConfig; } }
// Currencies.
protected List<SaCurrency> GetCurrencies ()
{
return Internals.GetCurrencies();
}
protected SaCurrency GetCurrencyById (SysId currencyId)
{
return Internals.GetCurrencyById(currencyId);
}
protected SaCurrency GetCurrencyByCode (String currencyCode)
{
return Internals.GetCurrencyByCode(currencyCode);
}
protected static byte CurrencyDecimalsDefault { get { return SbmInternals.CurrencyDecimalsDefault; } }
protected byte GetCurrencyDecimalsForCurrencyById (SysId currencyId)
{
return Internals.GetCurrencyDecimalsForCurrencyById(currencyId);
}
//-------------------------------------------------------------------
// P r o c e s s i n g
//-------------------------------------------------------------------
protected virtual void ResetProcess ()
{
ClearStatistics();
}
//-------------------------------------------------------------------
// P r o c e s s i n g O p t i o n s
//-------------------------------------------------------------------
/// <summary>Are options supported - False by default.</summary>
public virtual bool HasProcessingOptions ()
{
return false;
}
/// <summary>Are required options populated - True by default.</summary>
public virtual bool AreProcessingOptionsComplete ()
{
return true;
}
/// <summary>Processing option definitions.</summary>
public virtual List<SaItemOptionDfn> GetProcessingOptionDfns ()
{
return new List<SaItemOptionDfn>();
}
/// <summary>Processing options.</summary>
public virtual List<SaItemOption> GetProcessingOptions ()
{
return new List<SaItemOption>(processingOptions ?? new List<SaItemOption>());
}
public String GetProcessingOptionValue (String name, String context, String defaultValue = "")
{
if (processingOptions == null)
SetProcessingOptions(GetProcessingOptions());
SaItemOption itemOption = processingOptions.Find(item => item.Name.EqualsCI(name) && item.Context.EqualsCI(context));
return itemOption?.Value ?? defaultValue;
}
/// <summary>Store processing options.</summary>
public virtual bool SetProcessingOptions (List<SaItemOption> processingOptions)
{
this.processingOptions = new List<SaItemOption>(processingOptions);
return true;
}
/// <summary>Allow process to update its own options.</summary>
public virtual bool UpdateProcessingOptions ()
{
return true;
}
/// <summary>Allow process to update its own options.</summary>
public virtual bool UpdateOptions ()
{
return false;
}
//-------------------------------------------------------------------
// P r o c e s s i n g S t a t i s t i c s
//-------------------------------------------------------------------
protected class StatisticsData
{
public class Stat
{
public Stat (int statType, int initialValue = 0)
{
StatType = statType;
InitialValue = initialValue;
}
public int StatType { get; }
public int InitialValue { get; }
}
public void Clear () { }
public Dictionary<int, int> Statistics { get; } = new Dictionary<int, int>();
}
private readonly StatisticsData stats = new StatisticsData();
protected int GetStatistic (int statisticType)
{
return stats.Statistics.GetValue(statisticType, 0);
}
protected int GetStatistic<T> (T statisticType) where T : struct, IConvertible
{
if (!(statisticType is Enum))
throw new ArgumentException(this.GetFn("GetStatistic") + " parameter must be an enumerated type. Type supplied: " + typeof(T).Name);
return GetStatistic((int)(statisticType as IConvertible));
}
protected int SetStatistic (int statisticType, int value)
{
return stats.Statistics[statisticType] = value;
}
protected int SetStatistic<T> (T statisticType, int value) where T : struct, IConvertible
{
if (!(statisticType is Enum))
throw new ArgumentException(this.GetFn("SetStatistic") + " parameter must be an enumerated type. Type supplied: " + typeof(T).Name);
return SetStatistic((int)(statisticType as IConvertible), value);
}
protected int IncrementStatistic (int statisticType, int increment = 1)
{
return stats.Statistics[statisticType] = GetStatistic(statisticType) + increment;
}
protected int IncrementStatistic<T> (T statisticType, int increment = 1) where T : struct, IConvertible
{
if (!(statisticType is Enum))
throw new ArgumentException(this.GetFn("IncrementStatistic") + " parameter must be an enumerated type. Type supplied: " + typeof(T).Name);
return IncrementStatistic((int)(statisticType as IConvertible), increment);
}
protected void ClearStatistic (int statisticType)
{
if (stats.Statistics.ContainsKey(statisticType))
stats.Statistics.Remove(statisticType);
}
protected void ClearStatistic<T> (T statisticType) where T : struct, IConvertible
{
if (!(statisticType is Enum))
throw new ArgumentException(this.GetFn("ClearStatistic") + " parameter must be an enumerated type. Type supplied: " + typeof(T).Name);
ClearStatistic((int)(statisticType as IConvertible));
}
private void ClearStatistics ()
{
stats.Statistics.Clear();
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,859 @@
using Sarand.BusinessMgr.Net.Standard.Base.App;
using Sarand.BusinessMgr.Net.Standard.Base.Bus;
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Dbas;
using Sarand.Net.Standard.Base.IO;
using Sarand.Net.Standard.Base.ItemOptions;
using Sarand.Net.Standard.Base.Utils;
using Sarand.Net.Standard.Proc.Excel;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Sarand.BusinessMgr.Net.Core.Procs.Procs
{
//=======================================================================
public class SbmProcess_Commodity_Export : SbmProcess_Base
{
//-------------------------------------------------------------------
// Configuration.
public new class Config : SbmProcess_Base.Config
{
public Config (SbmUserContext userContext) : base(userContext)
{
ItemType = Sbm.ItemType.Commodity;
DataFormat = DataFormatType.Excel;
ProcessType = ProcessType.ExportData;
ConfirmInterruption = true;
}
public ExportType ExportType { get; set; } = ExportType.Overview;
public SbmCommodity.Params CommodityParams { get; set; }
}
//-------------------------------------------------------------------
// Definitions.
public enum ExportType
{
Overview,
StockControl,
PurchasePlanning,
PurchasePlanning_ImminentStock,
ActualSales,
ProjectedSales,
};
public enum ProcessOptionFlag
{
Unknown = 0,
IncludeCol_MaterialCost,
IncludeCol_CostPrice,
IncludeCol_RetailPrice,
IncludeCol_StockCurrent,
IncludeCol_StockAvailable,
IncludeCol_StockDue_MonthTo03,
IncludeCol_StockDue_MonthTo06,
};
/// <summary>Process parameters - processing control properties</summary>
public new class ProcessParams : SbmProcess_Base.ProcessParams
{
public bool CommodityProcessesExist { get; set; } = false;
public uint HomeColumn { get; set; } = 1;
public uint HomeRow { get; set; } = 1;
public long CommodityCountTotal { get; set; } = -1;
public uint HeaderCaptionRow { get; set; } = 1;
public uint FirstDataRow { get; set; } = 2;
public uint FirstDataColumn { get; set; } = 1;
// Processing options.
public bool IncludeCol_MaterialCost { get; set; }
public bool IncludeCol_CostPrice { get; set; }
public bool IncludeCol_RetailPrice { get; set; }
public bool IncludeCol_StockCurrent { get; set; }
public bool IncludeCol_StockAvailable { get; set; }
public bool IncludeCol_StockDue_3Months { get; set; }
public bool IncludeCol_StockDue_6Months { get; set; }
}
/// <summary>Processing statistics - keep track of number of items actioned.</summary>
public enum ProcessingStatistic
{
CommoditiesExported,
}
//-------------------------------------------------------------------
// Data members.
private Config config;
private static String OptionName_Flag = "Flag";
private List<TypeCodeDesc> optionContexts_Flag = new List<TypeCodeDesc>();
private List<SaItemOptionDfn> processingOptionDfns = new List<SaItemOptionDfn>();
private List<SaItemOption> processingOptions = new List<SaItemOption>();
private readonly List<ColumnDfn> displayColumns = new List<ColumnDfn>();
private readonly List<ColumnDfn> additionalQueryColumns = new List<ColumnDfn>();
//-------------------------------------------------------------------
// Construction.
public SbmProcess_Commodity_Export (Config config) : base(config)
{
this.config = config;
Params.CommodityProcessesExist = ProcessMgr.HasProcesses(Sbm.ProcessCategory.Commodity);
}
protected override void Dispose (bool disposing)
{
if (disposing)
{
}
base.Dispose(disposing);
}
//-------------------------------------------------------------------
// Properties.
protected ProcessParams Params { get; } = new ProcessParams();
public override string ProcessDescription
{
get
{
String processDescription = "Commodities to Excel";
if (config.ExportType == ExportType.Overview)
processDescription += " - Overview";
else if (config.ExportType == ExportType.StockControl)
processDescription += " - Stock";
else if (config.ExportType == ExportType.PurchasePlanning)
processDescription += " - Purchasing";
else if (config.ExportType == ExportType.PurchasePlanning_ImminentStock)
processDescription += " - Purchasing (imminent stock)";
else if (config.ExportType == ExportType.ActualSales)
processDescription += " - Actual Sales";
else if (config.ExportType == ExportType.ProjectedSales)
processDescription += " - Projected Sales";
return processDescription;
}
}
public override List<String> ProcessComments
{
get
{
List<String> processComments = new List<string>();
CalculateItemCount();
processComments.Add("Commodities to export: " + Params.CommodityCountTotal);
processComments.AddRange(config.CommodityParams.GetDisplayValues(UserContext).Concat("="));
processComments.AddRange(base.ProcessComments);
return processComments;
}
}
/// <summary>Processing statistics.</summary>
public override List<StringPair> ProcessingStatistics
{
get
{
List<StringPair> stats = new List<StringPair>();
if (GetStatistic(ProcessingStatistic.CommoditiesExported) > 0) stats.Add("Commodities exported", GetStatistic(ProcessingStatistic.CommoditiesExported).ToString());
return stats;
}
}
// Control values.
public static decimal ReorderPercGreen { get; } = 125.0m;
public static decimal ReorderPercRed { get; } = 75.0m;
public static decimal BudgetPercGreen { get; } = 125.0m;
public static decimal BudgetPercRed { get; } = 75.0m;
//-------------------------------------------------------------------
// Utilities.
private void CalculateItemCount ()
{
if (Params.CommodityCountTotal >= 0 || !OK) return;
Params.CommodityCountTotal = CommodityMgr.GetCommodityCount(config.CommodityParams);
}
//-------------------------------------------------------------------
// P r o c e s s i n g O p t i o n s
//-------------------------------------------------------------------
public override bool HasProcessingOptions ()
{
PopulateProcessingOptionDfns();
return true;
}
private void PopulateProcessingOptionDfns ()
{
if (processingOptionDfns.Count > 0) return;
if (config.ExportType == ExportType.ActualSales || config.ExportType == ExportType.ProjectedSales)
{
// Processing options - flags.
if (optionContexts_Flag.Count == 0)
{
optionContexts_Flag.Add(ProcessOptionFlag.IncludeCol_MaterialCost, "IncludeCol_MaterialCost", "Include column - " + ConfigConsole.GetAttributeCaption(UserContext, SbmDbDfn.ColumnIdx.Commodity_MaterialCost, "Material cost"));
optionContexts_Flag.Add(ProcessOptionFlag.IncludeCol_CostPrice, "IncludeCol_CostPrice", "Include column - " + ConfigConsole.GetAttributeCaption(UserContext, SbmDbDfn.ColumnIdx.Commodity_CostPrice, "Cost price"));
optionContexts_Flag.Add(ProcessOptionFlag.IncludeCol_RetailPrice, "IncludeCol_RetailPrice", "Include column - " + ConfigConsole.GetAttributeCaption(UserContext, SbmDbDfn.ColumnIdx.Commodity_RetailPrice, "Retail price"));
optionContexts_Flag.Add(ProcessOptionFlag.IncludeCol_StockCurrent, "IncludeCol_StockCurrent", "Include column - Stock: current");
optionContexts_Flag.Add(ProcessOptionFlag.IncludeCol_StockAvailable, "IncludeCol_StockAvailable", "Include column - Stock: available");
optionContexts_Flag.Add(ProcessOptionFlag.IncludeCol_StockDue_MonthTo03, "IncludeCol_StockDue_MonthTo03", "Include column - Stock: due to 3 months");
optionContexts_Flag.Add(ProcessOptionFlag.IncludeCol_StockDue_MonthTo06, "IncludeCol_StockDue_MonthTo06", "Include column - Stock: due to 6 months");
}
SaItemOptionDfn optionDfn = new SaItemOptionDfn();
optionDfn.Name = OptionName_Flag;
optionDfn.DisplayOptions.Contexts.AddRange(optionContexts_Flag.ToCodeDescList());
optionDfn.DisplayOptions.RequiresContext = true;
optionDfn.DisplayOptions.Values.AddRange(Sbm.ValuesYesNo);
optionDfn.DefaultValue = Sbm.ValueNo;
processingOptionDfns.Add(optionDfn);
// Item options - flags.
foreach (TypeCodeDesc optionContext_Flag in optionContexts_Flag)
{
// Ignore option if already defined.
if (processingOptions.IsOptionDefined(OptionName_Flag, optionContext_Flag.Code)) continue;
SaItemOption option = ItemOptionConsole.CreateItemOption(null, OptionName_Flag, optionContext_Flag.Code);
option.Value = Sbm.ValueNo;
processingOptions.Add(option);
}
}
}
//-------------------------------------------------------------------
// P r o c e s s i n g
//-------------------------------------------------------------------
protected override bool Run ()
{
SetProgressInfo("Preparing to export data to Excel.");
ResetProcess();
ValidateConfig();
// ToDo Save Folder...
String saveFolder = ""; //GetOption_SaveFolder();
if (OK && !StringUtil.IsEmpty(saveFolder) && !DirectoryUtil.DirectoryExists(saveFolder))
SetProcessError("Invalid output folder specified.");
// Perform export.
if (OK)
ExportData();
SetProgressInfo("Cleaning up ...");
return OK;
}
//-------------------------------------------------------------------
protected override void ResetProcess ()
{
base.ResetProcess();
// Reset parameters.
Params.HomeColumn = 1;
Params.HomeRow = Params.FirstDataRow;
}
//-------------------------------------------------------------------
private bool ValidateConfig ()
{
if (!OK) return false;
// Validate environment.
if (CommodityMgr == null) return SetProcessError("Unable to initialise commodity manager");
if (ProcessMgr == null) return SetProcessError("Unable to initialise process manager");
if (ReferenceDataConsole == null) return SetProcessError("Reference data console not available");
if (config.DataFormat != DataFormatType.Excel) return SetProcessError("Only Excel export format supported.");
// Data field columns.
Params.IncludeCol_MaterialCost = StringUtil.ToBool(GetProcessingOptionValue(OptionName_Flag, optionContexts_Flag.GetCodeForType(ProcessOptionFlag.IncludeCol_MaterialCost)));
Params.IncludeCol_CostPrice = StringUtil.ToBool(GetProcessingOptionValue(OptionName_Flag, optionContexts_Flag.GetCodeForType(ProcessOptionFlag.IncludeCol_CostPrice)));
Params.IncludeCol_RetailPrice = StringUtil.ToBool(GetProcessingOptionValue(OptionName_Flag, optionContexts_Flag.GetCodeForType(ProcessOptionFlag.IncludeCol_RetailPrice)));
Params.IncludeCol_StockCurrent = StringUtil.ToBool(GetProcessingOptionValue(OptionName_Flag, optionContexts_Flag.GetCodeForType(ProcessOptionFlag.IncludeCol_StockCurrent)));
Params.IncludeCol_StockAvailable = StringUtil.ToBool(GetProcessingOptionValue(OptionName_Flag, optionContexts_Flag.GetCodeForType(ProcessOptionFlag.IncludeCol_StockAvailable)));
Params.IncludeCol_StockDue_3Months = StringUtil.ToBool(GetProcessingOptionValue(OptionName_Flag, optionContexts_Flag.GetCodeForType(ProcessOptionFlag.IncludeCol_StockDue_MonthTo03)));
Params.IncludeCol_StockDue_6Months = StringUtil.ToBool(GetProcessingOptionValue(OptionName_Flag, optionContexts_Flag.GetCodeForType(ProcessOptionFlag.IncludeCol_StockDue_MonthTo06)));
return OK;
}
//-------------------------------------------------------------------
// E x p o r t D a t a
//-------------------------------------------------------------------
private bool ExportData ()
{
if (!OK) return false;
String fn = this.GetFn("ExportData");
String progressPrefix = "Preparing worksheet";
//String saveFolder = ""; //GetOption_SaveFolder();
// Create a new spreadsheet.
SetProgressInfo("Opening Excel application.");
try
{
using (SaMsSpreadsheet spreadsheet = new SaMsSpreadsheet_POI())
{
spreadsheet.CreateWorkbook();
SetProgressInfo(progressPrefix);
spreadsheet.SetWorksheetCount(1);
OpenWorkSheet(spreadsheet, 1, "Commodities");
PrepareColumns();
// Headings.
SetProgressInfo(progressPrefix + " - Populating headings");
SaMsExcelColumnHeading_Config configHeadings = new SaMsExcelColumnHeading_Config();
configHeadings.HeadingFontSize = 10;
configHeadings.FirstHeadingRow = 1;
configHeadings.InteriorColour = ColorTranslator.ToOle(ExcelColumnHeadingColor);
configHeadings.BorderWeight = SaXlBorderWeight.Thin;
configHeadings.FreezeColumn = (uint)(displayColumns.GetPosForId(SbmCommodity.Column.Code) + 1);
spreadsheet.SetColumnHeadings(displayColumns, configHeadings);
Params.HomeColumn = configHeadings.FreezeColumn + 1;
Params.HomeRow = Params.FirstDataRow;
ExportCommoditiesToExcel(spreadsheet);
// Complete worksheet and make visible.
SetProgressInfo("Releasing document and opening Excel.");
spreadsheet.OpenWorksheet(1);
spreadsheet.SetActiveCell(Params.HomeColumn, Params.HomeRow);
//spreadsheet.MakeVisible(true);
}
}
catch (Exception exc)
{
SaLogger.Error(fn + " " + ExceptionUtil.GetLogMessage(exc));
return SetProcessError(exc.GetType().Name + " exporting data to Excel.");
}
return OK;
}
//-------------------------------------------------------------------
private void PrepareColumns ()
{
displayColumns.Clear();
additionalQueryColumns.Clear();
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Pos));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Code, "Code", 140));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.DescBrief, "Description", 240));
if (CommodityMgr.CodeTypeHasCodes(Sbm.ApplCodeType.CommodityCategory))
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Category1_Desc));
if (UserContext.GetOwnerOption_ShowColumn(Sbm.OrgOption_ShowColumn.Commodity_CatalogRef))
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.CatalogRef));
if (config.ExportType == ExportType.StockControl)
{
// Stock control:
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_CurrentLevel));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_Allocated));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_Available));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.ReorderLevel));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.ReorderQty));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_CurrentVsReorder));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_AvailVsReorder));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.SalesYtdCalendar));
// Stock movements by future weeks.
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_DueIn_Week1));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_Allocated_Week1));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_DueIn_Week2));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_Allocated_Week2));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_DueIn_Week3To4));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_Allocated_Week3To4));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_DueIn_Week5To6));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_Allocated_Week5To6));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_DueIn_Week7To8));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_Allocated_Week7To8));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_DueIn_Week9To10));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_Allocated_Week9To10));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_DueIn_Week11To12));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_Allocated_Week11To12));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_DueIn_Week13On));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_Allocated_Week13On));
}
else if (config.ExportType == ExportType.PurchasePlanning || config.ExportType == ExportType.PurchasePlanning_ImminentStock)
{
// Purchasing planning:
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.SalesYtdCalendar));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.ReorderLevel));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.ReorderQty));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.StockTake_Date));
if (config.ExportType == ExportType.PurchasePlanning)
{
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_CurrentLevel));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_Allocated));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_Available));
}
else if (config.ExportType == ExportType.PurchasePlanning_ImminentStock)
{
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_CurrentLevel));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_ImminentLevel));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_ImminentVsReorder));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_Allocated));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_ImminentAvailable));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_ImminentAvailVsReorder));
// Stock sold in past 12 weeks:
config.CommodityParams.ProjectedSalesFrom = DateTime.Today.StartOfWeek().IncrementDate(-12 * 7);
config.CommodityParams.StockSoldFrom = config.CommodityParams.ProjectedSalesFrom;
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.SalesProj_Period, "Budget since " + DateUtil.Format(config.CommodityParams.ProjectedSalesFrom, "dd-MMM")));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.StockSold_Period, "Sold since " + DateUtil.Format(config.CommodityParams.StockSoldFrom, "dd-MMM")));
}
// Stock due by week from today.
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_DueIn_Week1));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_DueIn_Week2));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_DueIn_Week3));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_DueIn_Week4));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_DueIn_Week5));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_DueIn_Week6));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_DueIn_Week7));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_DueIn_Week8));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_DueIn_Week9));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_DueIn_Week10));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_DueIn_Week11));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_DueIn_Week12));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_DueIn_Week13On));
// Other commodity details:
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Supplier_Name));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Supplier_Ref));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.MaterialCost_Fmt));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.CostPrice));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.SalesPrice));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.RetailPrice));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.PackQtySale));
if (UserContext.GetOwnerOption_ShowColumn(Sbm.OrgOption_ShowColumn.Commodity_Weight))
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Weight_ValueUnits));
if (UserContext.GetOwnerOption_ShowColumn(Sbm.OrgOption_ShowColumn.Commodity_PackQtySale))
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.PackQtySale));
if (ConfigConsole.DisplayAttribute(UserContext, SbmDbDfn.ColumnIdx.Commodity_Volume))
{
// Aug16 Cadix Was just SbmCommodity.Column.Volume_ValueUnits
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Volume));
//displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Volume_Unit));
}
if (ConfigConsole.DisplayAttribute(UserContext, SbmDbDfn.ColumnIdx.Commodity_BoxQuantity))
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.BoxQuantity_ValueUnits));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Dimensions));
if (CommodityMgr.CodeTypeHasCodes(Sbm.ApplCodeType.CommodityMaterial)) displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.MaterialDesc));
if (CommodityMgr.CodeTypeHasCodes(Sbm.ApplCodeType.CommodityFinish)) displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.FinishDesc));
if (ConfigConsole.DisplayAttribute(UserContext, SbmDbDfn.ColumnIdx.Commodity_ICNTariffCode))
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.ICNTariffCode));
if (config.ExportType == ExportType.PurchasePlanning)
{
}
else if (config.ExportType == ExportType.PurchasePlanning_ImminentStock)
{
// Additional columns required for query:
additionalQueryColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_CurrentLevel));
additionalQueryColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_Due_Imminently));
// Sales budget:
if (UserContext.GetOwnerOption_AllowOption(Sbm.OrgOption_AllowOption.Commodity_SalesBudget))
{
for (int monthIdx = 0; monthIdx < 12; monthIdx++)
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn((SbmCommodity.Column)(SbmCommodity.Column.SalesProj_Jan + monthIdx)));
}
}
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.DescDetail));
}
else if (config.ExportType == ExportType.ActualSales)
{
// Optional columns:
if (Params.IncludeCol_MaterialCost)
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.MaterialCost));
if (Params.IncludeCol_CostPrice)
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.CostPrice));
if (Params.IncludeCol_RetailPrice)
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.RetailPrice));
if (Params.IncludeCol_StockCurrent)
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_CurrentLevel_All));
if (Params.IncludeCol_StockAvailable)
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_Allocated_All));
if (Params.IncludeCol_StockDue_3Months)
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_DueIn_MonthTo03));
if (Params.IncludeCol_StockDue_6Months)
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_DueIn_MonthTo06));
// Standard columns:
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.ReorderLevel));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.ReorderQty));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.SalesProj_Annual));
// Derive appropriate columns for the current month and previous 11 months.
int iMonthCount = 0;
for (DateTime columnDate = DateTime.Today.StartOfMonth(); iMonthCount < 12; columnDate.EndOfMonth().IncrementDate(1), iMonthCount++)
{
// Actual sales.
if (iMonthCount == 0)
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.SalesMonthCurrent, "Sales " + DateUtil.Format(columnDate, "MMM-yy")));
else if (iMonthCount <= 12)
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn((SbmCommodity.Column)(SbmCommodity.Column.SalesMonthPast01 + iMonthCount - 1), "Sales " + DateUtil.Format(columnDate, "MMM-yy")));
// Sales budget.
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn((SbmCommodity.Column)(SbmCommodity.Column.SalesProj_Jan + columnDate.Month - 1), "Budget " + DateUtil.Format(columnDate, "MMM-yy")));
}
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.SalesYtdCalendar));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.SalesPyCalendar));
}
else if (config.ExportType == ExportType.ProjectedSales)
{
// Optional columns:
if (Params.IncludeCol_MaterialCost)
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.MaterialCost));
if (Params.IncludeCol_CostPrice)
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.CostPrice));
if (Params.IncludeCol_RetailPrice)
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.RetailPrice));
if (Params.IncludeCol_StockCurrent)
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_CurrentLevel_All));
if (Params.IncludeCol_StockAvailable)
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_Allocated_All));
if (Params.IncludeCol_StockDue_3Months)
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_DueIn_MonthTo03));
if (Params.IncludeCol_StockDue_6Months)
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_DueIn_MonthTo06));
// Fixed columns:
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.ReorderLevel));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.ReorderQty));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.SalesProj_Annual));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_CurrentLevel));
// 'Purchase budget' to be total of the proposed 'planned' status for containers.
// For each month: Projected sales, Due in, End of month stock:
int iMonthCount = 0;
for (DateTime columnDate = DateTime.Today.StartOfMonth(); iMonthCount < 12; columnDate.EndOfMonth().IncrementDate(1), iMonthCount++)
{
// Projected sales:
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn((SbmCommodity.Column)(SbmCommodity.Column.SalesProj_Jan + columnDate.Month - 1), "Sales " + DateUtil.Format(columnDate, "MMM-yy")));
// Allocations - to be queried to check whether they are greater than the budget:
if (iMonthCount <= 12) // Allows for current month and up to 12 in the future.
{
additionalQueryColumns.Add(CommodityMgr.GetCommodityColumnDfn((SbmCommodity.Column)(SbmCommodity.Column.Stock_Allocated_MonthCurrent + iMonthCount)));
}
// Due in:
if (iMonthCount <= 12) // Allows for current month and up to 12 in the future.
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn((SbmCommodity.Column)(SbmCommodity.Column.Stock_DueIn_MonthCurrent + iMonthCount)));
// End of month stock:
if (iMonthCount <= 12) // Allows for current month and up to 12 in the future.
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn((SbmCommodity.Column)(SbmCommodity.Column.Stock_EndOfMonth_Current + iMonthCount)));
}
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_DueIn_Provisional));
}
else
{
// Standard column list.
if (ConfigConsole.DisplayAttribute(UserContext, SbmDbDfn.ColumnIdx.Commodity_DrawingIssue))
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.DrawingIssue));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Status_Current_X));
// Only show 'Type' column if type not selected:
if (config.CommodityParams.CommodityType <= 0)
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Type_Desc));
if (ConfigConsole.AllowOption(UserContext, Sbm.ConfigOption.CommodityExclusiveToCustomer))
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Customer_Name));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.SalesPrice));
if (CommodityMgr.CodeTypeHasCodes(Sbm.ApplCodeType.CommodityRange)) displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.RangeDesc));
if (CommodityMgr.CodeTypeHasCodes(Sbm.ApplCodeType.CommodityColour)) displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.ColourDesc));
if (CommodityMgr.CodeTypeHasCodes(Sbm.ApplCodeType.CommoditySource)) displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.SourceDesc));
if (CommodityMgr.CodeTypeHasCodes(Sbm.ApplCodeType.CommoditySupplyMethod)) displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.SupplyMethodDesc));
if (CommodityMgr.CodeTypeHasCodes(Sbm.ApplCodeType.CommodityHandling)) displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.HandlingDesc));
if (CommodityMgr.CodeTypeHasCodes(Sbm.ApplCodeType.CommodityDelivery)) displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.DeliveryDesc));
if (CommodityMgr.CodeTypeHasCodes(Sbm.ApplCodeType.CommodityMaterial)) displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.MaterialDesc));
if (CommodityMgr.CodeTypeHasCodes(Sbm.ApplCodeType.CommodityFinish)) displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.FinishDesc));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.CostPrice));
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Supplier_Name));
//displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.UnitOfSale));
//displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.TaxCode));
//displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.TaxRate));
if (Params.CommodityProcessesExist)
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Process_Count));
}
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Notes));
if (DebugMode)
displayColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Id));
}
//-------------------------------------------------------------------
private class ProjectedSalesData
{
public void Clear ()
{
SalesBudget = 0.0m;
Allocated = 0.0m;
ProjectedSales = DueIn = 0.0m;
EndOfMonth = 0.0m;
}
public decimal SalesBudget { get; set; }
public decimal Allocated { get; set; }
public decimal ProjectedSales { get; set; }
public decimal DueIn { get; set; }
public decimal EndOfMonth { get; set; }
}
//-------------------------------------------------------------------
// E x p o r t C o m m o d i t i e s T o E x c e l
//-------------------------------------------------------------------
private bool ExportCommoditiesToExcel (SaMsSpreadsheet spreadsheet)
{
if (!OK) return false;
String fn = this.GetFn("ExportCommoditiesToExcel");
String progressPrefix = "Exporting commodities";
SetProgressInfo(progressPrefix + " - Preparing data");
// Projected sales.
ProjectedSalesData[] projectedSales = new ProjectedSalesData [12];
for (int projectedSaleIdx = 0; projectedSaleIdx < projectedSales.Length; projectedSaleIdx++)
projectedSales[projectedSaleIdx] = new ProjectedSalesData();
decimal[]projectedSalesBudget = new decimal[12];
// Columns to query.
List<ColumnDfn> queryColumns = new List<ColumnDfn> (displayColumns);
foreach (ColumnDfn column in additionalQueryColumns)
{
if (!queryColumns.HasId(column.Id))
queryColumns.Add(column);
}
bool bHasProjSalesData = queryColumns.HasId(SbmCommodity.Column.SalesProj_Jan);
// Common column data names.
String col_Code = CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Code).DataName;
String col_Status = CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Status).DataName;
String col_DescDetail = CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.DescDetail).DataName;
String col_Notes = CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Notes).DataName;
String col_Type = CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Type_Desc).DataName;
String col_LastStockTake = CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.StockTake_Date).DataName;
String col_CurrentLevel = CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_CurrentLevel).DataName;
String col_DueImminently = CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_Due_Imminently).DataName;
String col_ReorderLevel = CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.ReorderLevel).DataName;
String col_TotalAllocated = CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_Allocated).DataName;
// Progress is refreshed every few rows.
long commodityCount = 0;
Params.CommodityCountTotal = CommodityMgr.GetCommodityCount(config.CommodityParams);
int progressRefreshRows = (int)Math.Max(5,Params.CommodityCountTotal/(Params.CommodityCountTotal>1000?1000:100));
using (DbResultSet resultSet = CommodityMgr.ExecuteCommodityQuery(queryColumns, config.CommodityParams))
{
if (resultSet.HasErrorState()) return SetProcessError(resultSet.ErrorState.Message);
if (resultSet == null) return SetProcessError("Failed to query commodity data");
while (OK && resultSet != null && resultSet.NextRecord())
{
if (++commodityCount % progressRefreshRows == 0 || commodityCount == 1)
SetProgressInfo(progressPrefix + " - Populating data" + (Params.CommodityCountTotal > 0 ? " [" + (commodityCount * 100.0 / Params.CommodityCountTotal).ToString("N0") + "%]" : ""));
NamedValue.Map values = resultSet.GetRecordValues();
uint iRow = (uint)(Params.FirstDataRow + commodityCount - 1);
String commodityCode = values.GetValue(col_Code).ToString();
if (config.ExportType == ExportType.ProjectedSales)
{
// For each month: Projected sales, Due in, End of month stock:
for (int monthIdx = 0; monthIdx < 12; monthIdx++)
{
DateTime monthDate = DateTime.Today.StartOfMonth().IncrementMonth(monthIdx);
ProjectedSalesData projSales = projectedSales[monthIdx];
projSales.Clear();
projSales.SalesBudget = values.GetDecimal(CommodityMgr.GetCommodityColumnDfn((SbmCommodity.Column)(SbmCommodity.Column.SalesProj_Jan + monthDate.Month - 1)).DataName);
projSales.Allocated = values.GetDecimal(CommodityMgr.GetCommodityColumnDfn((SbmCommodity.Column)(SbmCommodity.Column.Stock_Allocated_MonthCurrent + monthIdx)).DataName);
projSales.ProjectedSales = Math.Max(projSales.SalesBudget, projSales.Allocated);
projSales.DueIn = values.GetDecimal(CommodityMgr.GetCommodityColumnDfn((SbmCommodity.Column)(SbmCommodity.Column.Stock_DueIn_MonthCurrent + monthIdx)).DataName);
projSales.EndOfMonth = monthIdx > 0 ? projectedSales[monthIdx - 1].EndOfMonth : values.GetDecimal(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Stock_CurrentLevel).DataName);
projSales.EndOfMonth += projSales.DueIn - projSales.ProjectedSales;
}
}
// Retrieve budget sales figures by calendar month.
for (int i = 0; i < 12; i++)
projectedSalesBudget[i] = bHasProjSalesData ? values.GetDecimal(CommodityMgr.GetCommodityColumnDfn((SbmCommodity.Column)(SbmCommodity.Column.SalesProj_Jan + i)).DataName) : 0.0m;
// Common values used in calculations.
// Currently in stock.
decimal currentStock = !col_CurrentLevel.IsEmpty() ? values.GetDecimal(col_CurrentLevel) : 0.0m;
// Stock due imminently.
decimal imminentStock = Math.Max(0.0m, !col_DueImminently.IsEmpty() ? values.GetDecimal(col_DueImminently) : 0.0m);
decimal reorderLevel = !col_ReorderLevel.IsEmpty() ? values.GetDecimal(col_ReorderLevel) : 0.0m;
decimal allocatedStock = !col_TotalAllocated.IsEmpty() ? values.GetDecimal(col_TotalAllocated) : 0.0m;
decimal availableStock = currentStock - allocatedStock;
// Output the value for each column in turn.
List<DataValue> cellValues = new List<DataValue>();
for (int columnIdx = 0; columnIdx < displayColumns.Count; columnIdx++)
{
ColumnDfn column = displayColumns[columnIdx];
SaMsExcelItemAttr attrCell = new SaMsExcelItemAttr();
bool cellAttributesPopulated = false;
// Intercept specific values.
DataValue value = new DataValue();
if (column.Id == (int)SbmCommodity.Column.Pos)
{
value = new DataValue("'" + commodityCount + ".");
}
//---------------------------------------------------------
else if (column.Id == (int)SbmCommodity.Column.Status_Current_X)
{
String statusCode = values.GetString(col_Status);
if (SacsSystem.GetStatusForCode(statusCode) != SaStatus.Current)
{
value = new DataValue("X");
attrCell.Bold = true;
attrCell.FontColourIndex = SaXlColourIndex.Red;
cellAttributesPopulated = true;
}
}
//---------------------------------------------------------
// Stock take:
else if (column.Id == (int)SbmCommodity.Column.StockTake_Date)
{
value = values.GetValue(col_LastStockTake);
}
//---------------------------------------------------------
// Stock levels:
else if (column.Id == (int)SbmCommodity.Column.Stock_Available)
{
value = new DataValue(availableStock);
if (availableStock < -0.001m)
{
attrCell.Bold = true;
attrCell.FontColourIndex = SaXlColourIndex.Red; //38; // Pale red (pink).
cellAttributesPopulated = true;
}
}
else if (column.Id == (int)SbmCommodity.Column.Stock_CurrentVsReorder)
{
decimal currentPropReorder = (reorderLevel > 0.0m ? currentStock * 100.0m / reorderLevel : 0.0m);
value = new DataValue(currentPropReorder);
}
else if (column.Id == (int)SbmCommodity.Column.Stock_AvailVsReorder)
{
decimal availPropReorder = (reorderLevel > 0.0m ? Math.Max(0,availableStock) * 100.0m / reorderLevel : 0.0m);
value = new DataValue(availPropReorder);
}
//---------------------------------------------------------
// Stock levels taking imminent stock into account:
else if (column.Id == (int)SbmCommodity.Column.Stock_ImminentLevel)
{
value = new DataValue(currentStock + imminentStock);
}
else if (column.Id == (int)SbmCommodity.Column.Stock_ImminentAvailable)
{
decimal imminentAvailable = Math.Max(0, (currentStock + imminentStock) - allocatedStock);
value = new DataValue(imminentAvailable);
}
else if (column.Id == (int)SbmCommodity.Column.Stock_ImminentVsReorder)
{
decimal imminentPropReorder = (reorderLevel > 0.0m ? (currentStock + imminentStock) * 100.0m / reorderLevel : 0.0m);
value = new DataValue(imminentPropReorder);
}
else if (column.Id == (int)SbmCommodity.Column.Stock_ImminentAvailVsReorder)
{
if (reorderLevel > 0.0m)
{
decimal imminentAvailable = Math.Max(0, (currentStock + imminentStock) - allocatedStock);
decimal imminentAvailablePropReorder = (imminentAvailable * 100.0m) / reorderLevel;
value = new DataValue(imminentAvailablePropReorder);
if (imminentAvailablePropReorder < ReorderPercRed)
attrCell.InteriorColourIndex = SaXlColourIndex.Rose;
else if (imminentAvailablePropReorder < ReorderPercGreen)
attrCell.InteriorColourIndex = SaXlColourIndex.Tan;
cellAttributesPopulated = true;
}
}
//---------------------------------------------------------
// Projected sales:
else if (column.Id == (int)SbmCommodity.Column.SalesProj_Period || column.Id == (int)SbmCommodity.Column.StockSold_Period)
{
// Calculate the projected sales for the specified period:
decimal salesProjPeriod = 0.0m;
DateTime dTo = DateTime.Today;
if (DateUtil.IsValid(config.CommodityParams.ProjectedSalesTo)) dTo = config.CommodityParams.ProjectedSalesTo;
for (DateTime d = config.CommodityParams.ProjectedSalesFrom; DateUtil.IsValid(d) && d <= dTo; d.IncrementDate(1))
{
decimal salesProjMonth = projectedSalesBudget[d.Month-1];
salesProjPeriod += salesProjMonth / d.EndOfMonth().Day;
}
if (column.Id == (int)SbmCommodity.Column.SalesProj_Period)
value = new DataValue(salesProjPeriod);
else if (column.Id == (int)SbmCommodity.Column.StockSold_Period)
{
decimal stockSoldPeriod = values.GetDecimal(column.DataName);
decimal percSoldVsBudget = salesProjPeriod > 0.0m ? stockSoldPeriod / salesProjPeriod * 100.0m : 0.0m;
value = new DataValue(stockSoldPeriod);
if (percSoldVsBudget < BudgetPercRed)
attrCell.InteriorColourIndex = SaXlColourIndex.Rose;
else if (percSoldVsBudget < BudgetPercGreen)
attrCell.InteriorColourIndex = SaXlColourIndex.Tan;
cellAttributesPopulated = true;
}
}
else if (config.ExportType == ExportType.ProjectedSales &&
(column.Id >= (int)SbmCommodity.Column.SalesProj_Jan && column.Id <= (int)SbmCommodity.Column.SalesProj_Dec))
{
// Projected sales - the greater of either allocations or budget for the month:
int iMonth = column.Id - (int)SbmCommodity.Column.SalesProj_Jan;
if (iMonth >= 0 && iMonth <= 12)
{
ProjectedSalesData projSales = projectedSales[iMonth];
value = new DataValue(projSales.ProjectedSales);
attrCell.BorderLeft.Weight = SaXlBorderWeight.Thin;
attrCell.BorderLeft.LineStyle = SaXlLineStyle.Continuous;
cellAttributesPopulated = true;
}
}
else if (config.ExportType == ExportType.ProjectedSales &&
(column.Id >= (int)SbmCommodity.Column.Stock_EndOfMonth_Current && column.Id <= (int)SbmCommodity.Column.Stock_EndOfMonth_12))
{
// End of month projected stock level:
int iMonth = column.Id - (int)SbmCommodity.Column.Stock_EndOfMonth_Current;
if (iMonth >= 0 && iMonth <= 12)
{
ProjectedSalesData projSales = projectedSales[iMonth];
value = new DataValue(projSales.EndOfMonth);
// Check month end stock level against reorder level.
if (projSales.EndOfMonth < 0.0m)
attrCell.InteriorColourIndex = SaXlColourIndex.Rose;
else if (reorderLevel > 0.0m && projSales.EndOfMonth <= reorderLevel)
attrCell.InteriorColourIndex = SaXlColourIndex.Tan;
cellAttributesPopulated = true;
}
}
else if (config.ExportType == ExportType.ProjectedSales && column.Id == (int)SbmCommodity.Column.Stock_DueIn_Provisional)
{
attrCell.BorderLeft.Weight = SaXlBorderWeight.Thin;
attrCell.BorderLeft.LineStyle = SaXlLineStyle.Continuous;
cellAttributesPopulated = true;
}
//---------------------------------------------------------
// General cell value handling.
if (!value.IsValid())
{
value = CommodityMgr.GetCommodityDataValue(column, values);
if (!value.IsValid())
{
// General data value directly from the database:
value = values.GetValue(column.DataName);
}
if (config.ExportType == ExportType.ProjectedSales && column.Id == (int)SbmCommodity.Column.Stock_DueIn_Provisional)
{
attrCell.BorderLeft.Weight = SaXlBorderWeight.Thin;
attrCell.BorderLeft.LineStyle = SaXlLineStyle.Continuous;
cellAttributesPopulated = true;
}
}
if (!column.ShowZeroValues && value.IsNumeric() && value.ToNumeric() == 0.0)
value = new DataValue();
cellValues.Add(value);
// Populate the cell's attributes.
if (attrCell.InteriorColourIndex != SaXlColourIndex.NotSpecified)
{
attrCell.InteriorPattern = SaXlPattern.Solid;
attrCell.InteriorPatternColourIndex = SaXlColourIndex.Automatic;
cellAttributesPopulated = true;
}
if (cellAttributesPopulated)
spreadsheet.SetRangeAttr(new SaMsExcelRange(Params.FirstDataColumn + (uint)columnIdx, iRow), attrCell);
}
spreadsheet.SetRangeFormulas(new SaMsExcelRange(Params.FirstDataColumn, iRow, Params.FirstDataColumn + (uint)cellValues.Count - 1, iRow), cellValues.ToObjectList());
IncrementStatistic(ProcessingStatistic.CommoditiesExported);
}
}
return OK;
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,121 @@
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Utils;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Sarand.BusinessMgr.Net.Core.Procs.Reports
{
//==========================================================================
public class SbmPrinter_Footer : SacsDisposable
{
//----------------------------------------------------------------------
// Configuration.
public class Config
{
public Graphics Graphics { get; set; }
public Rectangle PrintArea { get; set; }
public int PageNumber { get; set; } = 0;
public int PageCount { get; set; } = 0;
public float PosY { get; set; } = 0.0f;
}
//----------------------------------------------------------------------
// Data members.
private String footerLeft = "";
// Drawing tools.
private Font fontFooter;
private Brush brushFooter = new SolidBrush(SbmReport.PrintColor);
//----------------------------------------------------------------------
// Construction.
public SbmPrinter_Footer ()
{
}
protected override void Dispose (bool disposing)
{
if (disposing)
{
// Data members.
// Drawing tools.
if (fontFooter != null) fontFooter.Dispose();
if (brushFooter != null) brushFooter.Dispose();
}
}
//----------------------------------------------------------------------
// Initialise tools.
private void InitialiseTools (Config config)
{
// Font proprtional to width of page.
float fontFooterHeight = 12.0f * (config.PrintArea.Width * 0.00080f);
if (fontFooter == null || fontFooter.Size != fontFooterHeight)
{
if (fontFooter != null) fontFooter.Dispose();
fontFooter = new Font(SbmReport.FontFamilyName, fontFooterHeight);
}
}
//----------------------------------------------------------------------
// Project info printing.
public void PrintFooter (Config config)
{
// Initialise tools.
InitialiseTools(config);
GraphicsUnit graphicsUnit = config.Graphics.PageUnit;
SmoothingMode smoothingMode = config.Graphics.SmoothingMode;
TextRenderingHint textRenderingHint = config.Graphics.TextRenderingHint;
// Construct footer text.
if (StringUtil.IsEmpty(footerLeft))
footerLeft = "Sarand::LogisticsMgr";
// Print footer - Format.
StringFormat footerLabelFormat = new StringFormat();
footerLabelFormat.Alignment = StringAlignment.Near;
footerLabelFormat.LineAlignment = StringAlignment.Far;
// Calculate text height to offset from bottom of page.
SizeF footerTextSize = config.Graphics.MeasureString("ABC", fontFooter);
float footerIndentX = config.PrintArea.Width * 0.02f;
float footerIndentY = config.PrintArea.Height * 0.02f;
float posYFooter = config.PrintArea.Bottom - footerIndentY;
// By default, footer wil be printed at bottom of page bounds (less a margin).
// Specific position - footer position has been stated by caling proc.
if (config.PosY > 0.0f)
{
// Footer will be printed with this position at the top.
posYFooter = config.PosY;
footerLabelFormat.LineAlignment = StringAlignment.Near;
}
// Print footer - Left.
PointF posFooterLeft = new PointF(config.PrintArea.Left + footerIndentX, posYFooter);
config.Graphics.DrawString(footerLeft, fontFooter, brushFooter, posFooterLeft, footerLabelFormat);
// Print footer - Center.
if (config.PageNumber > 0 && config.PageCount > 1)
{
String footerCenter = "Page " + config.PageNumber + (config.PageCount > 0 ? " of " + config.PageCount : "");
PointF posFooterCenter = new PointF(config.PrintArea.Left + (config.PrintArea.Width / 2.0f), posFooterLeft.Y);
footerLabelFormat.Alignment = StringAlignment.Center;
config.Graphics.DrawString(footerCenter, fontFooter, brushFooter, posFooterCenter, footerLabelFormat);
}
// Print footer - Right.
String footerRight = "Printed: " + DateTime.Now.Format("ddd d-MMM-yyyy HH:mm");
PointF posFooterRight = new PointF(config.PrintArea.Right - footerIndentX, posFooterLeft.Y);
footerLabelFormat.Alignment = StringAlignment.Far;
config.Graphics.DrawString(footerRight, fontFooter, brushFooter, posFooterRight, footerLabelFormat);
}
//----------------------------------------------------------------------
}
//==========================================================================
}
@@ -0,0 +1,335 @@
using Sarand.BusinessMgr.Net.Standard.Base.App;
using Sarand.BusinessMgr.Net.Standard.Base.Mgr;
using Sarand.BusinessMgr.Net.Standard.Base.Procs;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.CodeTypes;
using Sarand.Net.Standard.Base.Dbas;
using Sarand.Net.Standard.Base.Procs;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Printing;
using System.Drawing.Text;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Utils;
namespace Sarand.BusinessMgr.Net.Core.Procs.Reports
{
//=======================================================================
public abstract class SbmReport : SbmReport_Base
{
//-------------------------------------------------------------------
// Definitions.
public enum PrintPageResult
{
HasMorePages,
NoMorePages,
}
protected class PrintPageConfig
{
public PrintPageConfig (Graphics graphics, PageSettings pageSettings, Rectangle pageBounds, Rectangle marginBounds, int pageNumber, int pageCount)
{
// Graphics for printing to physical device (printer or print preview control).
Graphics = graphics;
// Printing configuration.
PageSettings = pageSettings;
PageBounds = pageBounds;
Bounds = marginBounds;
PixelsPerInch = new SizeF(Graphics.DpiX, Graphics.DpiY);
TabX = (int)Math.Ceiling(Bounds.Width * 0.025f);
PageNumber = pageNumber;
}
/// <summary>Current graphics for printing on page.</summary>
public Graphics Graphics { get; private set; }
/// <summary>Current page number</summary>
public int PageNumber { get; }
/// <summary>Virtual page number</summary>
public int PageNumberVirtual { get; set; } = 1;
/// <summary>Total number of pages</summary>
public int PageCount { get; }
/// <summary>Page settings from PrintPage event - eg. to modify page length for continuous feed.</summary>
public PageSettings PageSettings { get; }
/// <summary>Page bounds from PrintPage event - eg. to modify page length for continuous feed.</summary>
public Rectangle PageBounds { get; }
// Page bounds.
public Rectangle Bounds { get; set; }
// Pixel sizes.
public SizeF PixelsPerInch { get; }
// Horizontal printing positions.
public int PosXLeft { get { return Bounds.Left; } }
public int PosXCenter { get { return (int)(Bounds.Left + (Bounds.Width * 0.5f)); } }
public int PosXRight { get { return Bounds.Right; } }
public int TabX { get; set; }
public int[] ColsX { get; set; }
// Current vertical print position.
public int PosY { get; set; }
public int PosYBodyTop { get; set; }
public int PosYBodyBottom { get; set; }
public int PosYFooterTop { get; set; }
// Line spacing - proportional to font size.
public float HeadingTextLineSpacing { get; set; } = 1.25f;
public float IntroTextLineSpacing { get; set; } = 1.20f;
public float DetailTextLineSpacing { get; set; } = 1.15f;
}
//-------------------------------------------------------------------
// Configuration.
public new class Config : SbmReport_Base.Config
{
protected Config (SbmUserContext userContext) :
base(userContext)
{
}
}
//-------------------------------------------------------------------
// Data members.
protected SbmSystem system = SbmGlobals.System;
private Config config;
/// <summary>Print mode controls whether physical printing to printer or preview control or to dummy device to determine page size.</summary>
private enum PrintMode { Physical, Virtual }
private PrintMode printMode = PrintMode.Physical;
// Printing tools.
private StringFormat stringFormat = StringFormat.GenericTypographic;
private static ColorConverter colorConverter = new ColorConverter();
private SbmPrinter_Footer footerPrinter = new SbmPrinter_Footer();
protected Brush brushBlack = new SolidBrush(Color.Black);
//-------------------------------------------------------------------
// Construction.
protected SbmReport (Config config) : base(config)
{
this.config = config;
}
protected override void Dispose (bool disposing)
{
if (disposing)
{
stringFormat.Dispose();
footerPrinter.Dispose();
brushBlack.Dispose();
}
base.Dispose(disposing);
}
//-------------------------------------------------------------------
// Properties.
public static String FontFamilyName { get; } = "Arial";
public static Color PrintColor { get; } = Color.Black;
protected StringFormat StringFormat { get { return stringFormat; } }
protected static ColorConverter ColorConverter { get { return colorConverter; } }
// Report printing progress.
/// <summary>Check if currently printing to the preview device.</summary>
protected bool PreviewPrinting { get; private set; }
/// <summary>Check if the current print mode is 'Physical', ie. actually printing to the page.</summary>
protected bool PhysicalPrinting { get { return printMode == PrintMode.Physical; } }
/// <summary>Check if currently printing to file.</summary>
protected bool FilePrinting { get; private set; }
/// <summary>Total number of items to be printed</summary>
public virtual int ItemCount { get; protected set; }
/// <summary>Position of item currently being published in the list.</summary>
public virtual int ItemNumber { get; protected set; }
/// <summary>Progress based on the item being printed against the total number to be printed.</summary>
public override decimal ProgressPercent { get { return (ItemCount > 0 ? (decimal)ItemNumber / ItemCount : 0); } }
//-------------------------------------------------------------------
// Text utilities.
protected SizeF DrawString (Graphics graphics, String text, Font font, Brush brush, PointF position, StringFormat format)
{
if (!VirtualPrint)
graphics.DrawString(text, font, brush, position, format);
// Calculate size of text.
SizeF sizeText = graphics.MeasureString(text, font);
return sizeText;
}
protected SizeF DrawString (Graphics graphics, String text, Font font, Brush brush, RectangleF position, StringFormat format)
{
if (!VirtualPrint)
graphics.DrawString(text, font, brush, position, format);
// For single lines of text, the height can be different for Physical (printing) and
// Virtual (dummy) graphics even though they appear to be all but identical.
// Closest issue description: https://social.msdn.microsoft.com/Forums/windows/en-US/7a431b8c-3ad5-49d8-b012-f56a21c5cec5/measurestring-returns-different-height-after-text-width-exceeds-layout-width?forum=winforms
SizeF sizeText = graphics.MeasureString(text, font, (int)position.Width, format);
if (sizeText.Height < font.GetHeight(graphics))
// Aug-2018 Temporary fix.
sizeText.Height = font.GetHeight(graphics);
return sizeText;
}
//-------------------------------------------------------------------
// Printer configuration.
public abstract void ConfigurePrinter (PageSettings pageSettings);
public abstract void ConfigurePrintPreview (PageSettings pageSettings);
public abstract void ConfigurePageMargins (PageSettings pageSettings);
protected abstract void InitDrawingTools (Graphics graphics, Rectangle bounds);
//-------------------------------------------------------------------
// P r i n t P a g e S e c t i o n
//-------------------------------------------------------------------
// Solution to how to determine the number of pages in a report and
// print the report across multiple pages even though all printing is
// done via PrintPage() which prints a single page at a time.
/// <summary>
/// Print page section - portion of the report that is printed as a contiguous section, ie. all on one page.
/// <para>NB Page sections should be kept short for reports that may run over one page to ensure that the section doesn't overflow a page.</para>
/// <para>eg. A single row in a printed table with optional headings.</para>
/// <para>Method can be anonymous to allow other parameters to be passed in the context of the report's page printing, rathe rthan just relying on the Page Config.</para>
/// </summary>
protected delegate void PrintPageSectionFn ();
/// <summary> Print mode is either physical - printing to printer or preview control - or virtual - to test whether section fits on page. </summary>
protected bool VirtualPrint { get { return printMode == PrintMode.Virtual; } }
private void SwitchPrintMode (PrintMode mode, PrintPageConfig cfg)
{
printMode = mode;
}
/// <summary>
/// Test print the page section to verify that it fits on the page, before printing to the physical device.
/// <para>Returns false if section doesn't fit on the page.</para>
/// </summary>
protected bool PrintPageSectionIfSpace (PrintPageConfig cfg, PrintPageSectionFn printPageSectionFn)
{
// Make a note of the current print position and test print the section to the virtual page.
int posYInitial = cfg.PosY;
SwitchPrintMode(PrintMode.Virtual, cfg);
printPageSectionFn();
// Check if the section fits on the page.
if (cfg.PosY >= cfg.PosYBodyBottom)
{
// If the virtual page is the same or greater (shouldn't happen)
// than the current page, a new page is required.
if (cfg.PageNumberVirtual >= cfg.PageNumber)
{
SwitchPrintMode(PrintMode.Physical, cfg);
return false;
}
// Otherwise, we can increment the virtual page and reset printing to the top before reprinting the section.
cfg.PageNumberVirtual++;
cfg.PosY = cfg.PosYBodyTop;
if (cfg.PageNumberVirtual == cfg.PageNumber)
SwitchPrintMode(PrintMode.Physical, cfg);
}
else
{
// Section fits on virtual page.
if (cfg.PageNumberVirtual != cfg.PageNumber)
// Not yet on current page so no need to reprint section.
return true;
// On current page and section needs to be printed at original position to the physical device.
cfg.PosY = posYInitial;
SwitchPrintMode(PrintMode.Physical, cfg);
}
printPageSectionFn();
SwitchPrintMode(PrintMode.Physical, cfg);
return true;
}
//-------------------------------------------------------------------
// P r i n t i n g
//-------------------------------------------------------------------
public virtual void BeginPrint (PrintAction printAction, int pageNumberFrom = 0, int pageNumberTo = 0)
{
String fn = GetType().Name + ".BeginPrint";
if (DebugMode) SaLogger.Debug(fn + " PrintAction: " + printAction + ". Page range: " + pageNumberFrom + "-" + pageNumberTo + ". Page count: " + PageCount);
PageNumberFrom = PageNumberFrom;
PageNumberTo = pageNumberTo;
PageNumber = Math.Max(1, pageNumberFrom) - 1;
ItemCount = 0;
ItemNumber = 0;
PreviewPrinting = printAction == PrintAction.PrintToPreview;
FilePrinting = printAction == PrintAction.PrintToFile;
}
public virtual void QueryPageSettings (PageSettings pageSettings, PrintAction printAction) { }
public PrintPageResult PrintPage (Graphics graphics, PageSettings pageSettings, Rectangle pageBounds, Rectangle marginBounds)
{
String fn = GetType().Name + ".PrintPage";
PageNumber++;
// Configure graphics for printing document.
// NB Graphics properties set here muust also be copied to virtual device graphics in PrintPageConfig above.
graphics.SmoothingMode = SmoothingMode.AntiAlias;
graphics.TextRenderingHint = TextRenderingHint.AntiAlias;
InitDrawingTools(graphics, marginBounds);
// Configure default String Format.
StringFormat.Alignment = StringAlignment.Near;
StringFormat.LineAlignment = StringAlignment.Near;
StringFormat.FormatFlags = StringFormatFlags.FitBlackBox | StringFormatFlags.LineLimit | StringFormatFlags.NoClip /*| StringFormatFlags.NoWrap */;
StringFormat.Trimming = StringTrimming.Character;
// Page configuration.
PrintPageConfig cfg = new PrintPageConfig(graphics, pageSettings, pageBounds, marginBounds, PageNumber, PageCount);
cfg.Bounds = marginBounds;
cfg.PosY = marginBounds.Top;
cfg.PosYBodyTop = marginBounds.Top;
cfg.PosYBodyBottom = marginBounds.Bottom;
if (DebugMode) SaLogger.Debug(fn + " Page: " + PageNumber);
SwitchPrintMode(PrintMode.Physical, cfg);
// Log print configuration.
if (SbmGlobals.System.IsExtendedLogging(SbmSystem.ExtendedLogginOption.Printing))
LogPageSettings(graphics, pageSettings, pageBounds, marginBounds);
// Print page and interpret result.
PrintPageResult printPageResult = PrintPage(cfg);
// Update page range information depending on whether this is the last page in the report.
if (printPageResult == PrintPageResult.HasMorePages)
{
PageNumberMaxKnown = Math.Max(PageNumberMaxKnown, PageNumber + 1);
}
else if (printPageResult == PrintPageResult.NoMorePages)
{
PageNumberMaxKnown = PageNumber;
PageCount = PageNumber;
}
// Always return 'No more pages' if finished printing page range.
if (PageNumberTo > 0 && PageNumber + 1 > PageNumberTo) return PrintPageResult.NoMorePages;
return printPageResult;
}
protected abstract PrintPageResult PrintPage (PrintPageConfig cfg);
public virtual void EndPrint (PrintAction printAction) { }
//-------------------------------------------------------------------
protected void LogPageSettings (Graphics graphics, PageSettings pageSettings, Rectangle pageBounds, Rectangle marginBounds)
{
// Log print configuration.
List<StringPair> info = new List<StringPair>();
// Color=True, Landscape=False, Margins=[Margins Left=36 Right=36 Top=36 Bottom=36], PaperSize=[PaperSize Custom Kind=Custom Height=3600 Width=1800], PaperSource=[PaperSource unknown Kind=FormSource], PrinterResolution=[PrinterResolution X=600 Y=600]]
info.Add("Bounds", pageSettings.Bounds.ToString());
info.Add("Color", pageSettings.Color.ToString());
info.Add("Landscape", pageSettings.Landscape.ToString());
info.Add("Margins", pageSettings.Margins.ToString());
info.Add("PaperSize", pageSettings.PaperSize.ToString());
info.Add("PaperSource", pageSettings.PaperSource.ToString());
info.Add("PrintableArea", pageSettings.PrintableArea.ToString());
info.Add("PrinterResolution", pageSettings.PrinterResolution.ToString());
SaLogger.Info(GetType().Name + "Report: " + Caption + ". Page settings" + (PreviewPrinting ? " [preview]:" : "\n\tPrinter: " + pageSettings.PrinterSettings.PrinterName) + "\n\tPage bounds: " + pageBounds + "\n\tMargin bounds: " + marginBounds + "\n\tPage settings:" + info.Concat(": ").Concat("", "\n\t\t"), true);
}
//-------------------------------------------------------------------
// H e a d e r a n d F o o t e r
//-------------------------------------------------------------------
protected virtual void PrintHeader (PrintPageConfig cfg)
{
}
//-------------------------------------------------------------------
// Printing - Footer.
protected virtual void PrintFooter (PrintPageConfig cfg)
{
// Print footer - Format.
SbmPrinter_Footer.Config footerConfig = new SbmPrinter_Footer.Config();
footerConfig.Graphics = cfg.Graphics;
footerConfig.PrintArea = cfg.Bounds;
footerConfig.PosY = cfg.PosYFooterTop;
footerConfig.PageNumber = PageNumber;
footerConfig.PageCount = PageCount;
footerPrinter.PrintFooter(footerConfig);
}
//-------------------------------------------------------------------
}
//=======================================================================
}