Portal application - initial.
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//=======================================================================
|
||||
}
|
||||
Reference in New Issue
Block a user