6644 lines
301 KiB
C#
6644 lines
301 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using System.Text;
|
||
using System.Text.RegularExpressions;
|
||
using System.ComponentModel;
|
||
using System.Windows.Forms;
|
||
using System.Reflection;
|
||
using AyaNova.PlugIn;
|
||
using GZTW.AyaNova.BLL;
|
||
using System.IO;
|
||
using System.IO.Compression;
|
||
using Newtonsoft.Json;
|
||
using Newtonsoft.Json.Serialization;
|
||
using Newtonsoft.Json.Linq;
|
||
using System.Net.Http;
|
||
using System.Net.Http.Headers;
|
||
|
||
|
||
|
||
namespace AyaNova.PlugIn.V8
|
||
{
|
||
class V8 : IAyaNovaPlugin
|
||
{
|
||
|
||
#region Plugin interface
|
||
System.Resources.ResourceManager resman = null;
|
||
private static List<RootObjectTypes> ObjectsWeCanDealWith = null;
|
||
|
||
|
||
public string PluginName
|
||
{
|
||
get { return "V8 Migrate"; }
|
||
}
|
||
|
||
public string PluginVersion
|
||
{
|
||
get { return "8.0.0"; }
|
||
}
|
||
|
||
public string About
|
||
{
|
||
get
|
||
{
|
||
return "AyaNova V8 migrate plugin";
|
||
}
|
||
}
|
||
|
||
public Guid PluginID
|
||
{
|
||
get { return new Guid("{BD608E8A-820C-46D1-9D81-4CCB6E71B213}"); }
|
||
}
|
||
|
||
public System.Drawing.Image PluginSmallIcon
|
||
{
|
||
get { return Resource1.Dump16; }
|
||
}
|
||
|
||
public System.Drawing.Image PluginLargeIcon
|
||
{
|
||
get { return Resource1.Dump32; }
|
||
}
|
||
|
||
public System.Resources.ResourceManager AyaNovaResourceManager
|
||
{
|
||
set { resman = value; }
|
||
get { return resman; }
|
||
}
|
||
|
||
public bool Initialize(Version AyaNovaVersion, LocalizedTextTable localizedText)
|
||
{
|
||
if (AyaNovaVersion.Major < 7)
|
||
{
|
||
MessageBox.Show("The V8 migrate plugin requires AyaNova version 7.6 or newer");
|
||
return false;
|
||
}
|
||
|
||
if (AyaNovaVersion.Minor < 6)
|
||
{
|
||
MessageBox.Show("The V8 migrate plugin requires AyaNova version 7.6 or newer");
|
||
return false;
|
||
}
|
||
|
||
util.LocaleText = localizedText;
|
||
|
||
ObjectsWeCanDealWith = new List<RootObjectTypes>();
|
||
ObjectsWeCanDealWith.Add(RootObjectTypes.Nothing);
|
||
return true;
|
||
}
|
||
|
||
public void Close()
|
||
{
|
||
;
|
||
}
|
||
|
||
public bool SingleObjectMenuShow(RootObjectTypes objectType)
|
||
{
|
||
return (ObjectsWeCanDealWith.Contains(objectType));
|
||
}
|
||
|
||
public bool MultipleObjectsMenuShow(RootObjectTypes objectType)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
public List<AyaNovaPluginMenuItem> SingleObjectMenuOptions(RootObjectTypes objectType, object ayaNovaObject)
|
||
{
|
||
if (!ObjectsWeCanDealWith.Contains(objectType)) return null;
|
||
List<AyaNovaPluginMenuItem> list = new List<AyaNovaPluginMenuItem>();
|
||
list.Add(new AyaNovaPluginMenuItem("V8 Migrate", "Migrate to AyaNova 8 server", null, null));
|
||
return list;
|
||
}
|
||
|
||
public List<AyaNovaPluginMenuItem> MultipleObjectsMenuOptions(RootObjectTypes objectType)
|
||
{
|
||
return null;
|
||
}
|
||
|
||
public bool CommandSelectedForList(string commandKey, RootObjectTypes objectType, List<Guid> objectIDList, object listObject)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
public void CommandSelectedForSingleObject(string commandKey, RootObjectTypes objectType, object ayaNovaObject)
|
||
{
|
||
util.RELEASE_VERSION_STRING = this.PluginVersion;
|
||
|
||
if (!User.CurrentUserIsAnAdministrator)
|
||
{
|
||
MessageBox.Show("This action can only be done by the AyaNova Administrator account");
|
||
return;
|
||
}
|
||
|
||
#if(!DEBUG)
|
||
if (!AyaBizUtils.AyaNovaConnectionSetting.SingleUserConnection)
|
||
{
|
||
MessageBox.Show("** WARNING: before proceeding ensure no other users are logged into AyaNova to ensure the integrity of your exported data. Failing to do so *will* result in damaged data. ***");
|
||
MessageBox.Show("** WARNING: before proceeding make sure your AyaNova Generator service is STOPPED to ensure the integrity of your exported data. Failing to do so *will* result in damaged data. ***");
|
||
}
|
||
#endif
|
||
|
||
|
||
Auth d = new Auth();
|
||
var res = d.ShowDialog(); ;
|
||
if (res == DialogResult.Cancel)
|
||
{
|
||
return;
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
Opt dOpt = new Opt();
|
||
var ro = dOpt.ShowDialog();
|
||
if (ro == DialogResult.Cancel)
|
||
{
|
||
return;
|
||
}
|
||
ExportAssignedDocs = dOpt.ExportAssignedDocs;
|
||
PartNumberNameCombined = dOpt.PartNumberNameCombined;
|
||
ImportTag = util.NormalizeTag(dOpt.Tags);
|
||
util.HTTPCLIENT_TIMEOUT_SECONDS = dOpt.HTTPTimeOut;
|
||
util.InitClient();
|
||
|
||
//here because we logged in fine and can proceed
|
||
|
||
//MessageBox.Show("Login successful! JWT is " + util.JWT);
|
||
//Only one command
|
||
DoExport();
|
||
}
|
||
|
||
#endregion
|
||
|
||
|
||
private Dictionary<Guid, long> V7ToV8IdMap = new Dictionary<Guid, long>();
|
||
private void Addv7v8IdMap(Guid v7id, long v8id)
|
||
{
|
||
if (V7ToV8IdMap.ContainsKey(v7id))
|
||
{
|
||
//this is likely a coding issue so just throw
|
||
throw new Exception("Error: AddMap - v7id already mapped previously, id is " + v7id.ToString());
|
||
}
|
||
else V7ToV8IdMap.Add(v7id, v8id);
|
||
}
|
||
|
||
private Dictionary<Guid, long> V7ToV8WorkOrderItemStatusIdMap = new Dictionary<Guid, long>();
|
||
private void Addv7v8WorkOrderItemStatusIdMap(Guid v7id, long v8id)
|
||
{
|
||
if (V7ToV8WorkOrderItemStatusIdMap.ContainsKey(v7id))
|
||
throw new Exception("Error: V7ToV8WorkOrderItemStatusIdMap - v7id already mapped previously, id is " + v7id.ToString());//would be a coding issue
|
||
else V7ToV8WorkOrderItemStatusIdMap.Add(v7id, v8id);
|
||
}
|
||
private long? Getv7v8WorkOrderItemStatusIdNullOk(Guid id)
|
||
{
|
||
if (id == Guid.Empty) return null;
|
||
|
||
if (!V7ToV8WorkOrderItemStatusIdMap.ContainsKey(id)) return null;
|
||
|
||
return V7ToV8WorkOrderItemStatusIdMap[id];
|
||
}
|
||
|
||
private bool IsDuplicatev7v8IdMapItem(Guid v7id, string name, ProgressForm progress)
|
||
{
|
||
if (V7ToV8IdMap.ContainsKey(v7id))
|
||
{
|
||
progress.Append("DUPLICATE: " + name + " - has duplicate ID, not exported");
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
|
||
|
||
private async System.Threading.Tasks.Task<long> Getv7v8IdMap(Guid id, RootObjectTypes desiredType, bool allowPatch = true, bool doNotFail = true, long? extraV8Id = null)
|
||
{
|
||
|
||
if (!V7ToV8IdMap.ContainsKey(id))
|
||
{
|
||
if (!allowPatch && !doNotFail)
|
||
throw new Exception("Error: Getv7v8IdMap (source id: " + id.ToString() + " for type: " + desiredType.ToString() + ") was not found in v7 database and can't be substituted export can not complete until fixed");
|
||
if (!allowPatch && doNotFail)
|
||
return 0;
|
||
|
||
//not found, patch allowed, create a substitute record for it and put it in the map
|
||
if (id == Guid.Empty)
|
||
throw new Exception("Error: Getv7v8IdMap (source id: " + id.ToString() + " for type: " + desiredType.ToString() + ") attempt to create substitute record for empty id, this is an unexpected error, please provide full export log to technical support");
|
||
await CreateSubstitute(id, desiredType, extraV8Id);
|
||
}
|
||
return V7ToV8IdMap[id];
|
||
}
|
||
|
||
private async System.Threading.Tasks.Task<long?> Getv7v8IdMapNullOk(Guid id, RootObjectTypes desiredType, bool allowPatch = true, bool doNotFail = false, long? extraV8Id = null)
|
||
{
|
||
if (id == Guid.Empty) return null;
|
||
long l = await Getv7v8IdMap(id, desiredType, allowPatch, doNotFail, extraV8Id);
|
||
if (l == 0) return null;//this is the no patch no fail path
|
||
return l;
|
||
}
|
||
|
||
|
||
//UNKNOWN / MISSING RECORD OBJECT STAND INs
|
||
|
||
private string MissingDataNamePrefix = "zV8migrate_substitute";//append guid to ensure uniqueness
|
||
|
||
|
||
|
||
private async System.Threading.Tasks.Task CreateSubstitute(Guid id, RootObjectTypes desiredType, long? extraV8Id = null)
|
||
{
|
||
//create the simplest possible version of this object desired
|
||
dynamic d = new JObject();
|
||
string sName = MissingDataNamePrefix + " " + id.ToString();
|
||
d.name = sName;//usually this will suffice, for the rare cases it doesn't add to the correct property below, the name property will be ignored if it's not applicable
|
||
d.active = false;
|
||
d.notes = "This object created during v8 migrate as a substitute for a missing linked record in v7";
|
||
string route = "";
|
||
switch (desiredType)
|
||
{
|
||
case RootObjectTypes.Part:
|
||
{
|
||
route = "part";
|
||
d.partNumber = sName;
|
||
}
|
||
break;
|
||
case RootObjectTypes.PartWarehouse:
|
||
{
|
||
route = "part-warehouse";
|
||
}
|
||
break;
|
||
case RootObjectTypes.Client:
|
||
{
|
||
route = "customer";
|
||
}
|
||
break;
|
||
case RootObjectTypes.Vendor:
|
||
{
|
||
route = "vendor";
|
||
}
|
||
break;
|
||
case RootObjectTypes.HeadOffice:
|
||
{
|
||
route = "head-office";
|
||
}
|
||
break;
|
||
case RootObjectTypes.User:
|
||
{
|
||
route = "user";
|
||
d.userType = 1;//scheduleable user in case on workorder but not active so not consuming a license
|
||
}
|
||
break;
|
||
case RootObjectTypes.Project:
|
||
{
|
||
route = "project";
|
||
}
|
||
break;
|
||
case RootObjectTypes.TaxCode:
|
||
{
|
||
route = "tax-code";
|
||
}
|
||
break;
|
||
case RootObjectTypes.UnitModel:
|
||
{
|
||
route = "unit-model";
|
||
}
|
||
break;
|
||
case RootObjectTypes.Unit:
|
||
{
|
||
route = "unit";
|
||
d.serial = sName;
|
||
d.customerId = extraV8Id;
|
||
}
|
||
break;
|
||
case RootObjectTypes.WorkorderStatus:
|
||
{
|
||
route = "work-order-status";
|
||
}
|
||
break;
|
||
|
||
case RootObjectTypes.LoanItem:
|
||
{
|
||
route = "loan-unit";
|
||
}
|
||
break;
|
||
case RootObjectTypes.Rate:
|
||
{
|
||
if (extraV8Id == 1)
|
||
route = "service-rate";
|
||
else
|
||
route = "travel-rate";
|
||
}
|
||
break;
|
||
|
||
|
||
default:
|
||
throw new Exception("Error: CreateSubstitute (source id: " + id.ToString() + " for type: " + desiredType.ToString() + ") THIS TYPE NOT CODED YET");
|
||
}
|
||
|
||
//post route and dynamic object, insert into map and return
|
||
var rMainObject = await util.PostAsync(route, d);
|
||
long RavenId = util.IdFromResponse(rMainObject);
|
||
Addv7v8IdMap(id, RavenId);
|
||
}
|
||
|
||
|
||
|
||
// private long XXXSafeGetUserMap(Guid id)
|
||
// {
|
||
// if (!V7ToV8IdMap.ContainsKey(id)) return 1;//1=raven administrator account
|
||
// return V7ToV8IdMap[id];
|
||
// }
|
||
|
||
|
||
private enum RavenUserType : int
|
||
{
|
||
Service = 1,
|
||
NotService = 2,
|
||
Customer = 3,
|
||
HeadOffice = 4,
|
||
ServiceContractor = 5
|
||
}
|
||
|
||
private Dictionary<Guid, string> TagMap = new Dictionary<Guid, string>();
|
||
private Dictionary<string, long> LocaleMap = new Dictionary<string, long>();
|
||
|
||
private string ImportTag = string.Empty;
|
||
|
||
private void AddImportTag(List<string> tags)
|
||
{
|
||
if (!string.IsNullOrWhiteSpace(ImportTag))
|
||
tags.Add(ImportTag);
|
||
}
|
||
|
||
private bool ExportAssignedDocs = false;
|
||
private bool PartNumberNameCombined = true;
|
||
|
||
private long ClosedWorkOrderStatus = 0;
|
||
private long ServiceCompletedWorkOrderStatus = 0;
|
||
|
||
private bool V7UseInventory = false;
|
||
private List<long> Allv8WarehouseIds = new List<long>();
|
||
|
||
private const long AYANOVA_SERVER_MAX_ATTACH_SIZE_LIMIT = 10737418241;
|
||
private long SmallestFileUploadRejectedAsTooBig = AYANOVA_SERVER_MAX_ATTACH_SIZE_LIMIT;
|
||
|
||
private string MaxFileSizeInfo(long lSize)
|
||
{
|
||
if (lSize > AYANOVA_SERVER_MAX_ATTACH_SIZE_LIMIT)
|
||
{
|
||
return "AyaNova 8 allows 10GiB Maximum attachment size";
|
||
}
|
||
else
|
||
{
|
||
return "Adjust destination proxy / front end server settings to allow files of this size (AyaNova 8 limit is 10GiB)";
|
||
}
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
/// <summary>
|
||
/// export
|
||
/// </summary>
|
||
private async void DoExport()
|
||
{
|
||
//Show progress form
|
||
ProgressForm progress = new ProgressForm();
|
||
progress.Show();
|
||
progress.StartedImport();
|
||
progress.Append("Exporting data to AyaNova server @ " + util.ApiBaseUrl);
|
||
progress.Append(util.RELEASE_VERSION_STRING);
|
||
progress.Append("Option HTTP Timeout: " + util.HTTPCLIENT_TIMEOUT_SECONDS);
|
||
progress.Append("Option Migrate assigned docs: " + ExportAssignedDocs.ToString() + "");
|
||
progress.Append("Option Migrate tag: \"" + ImportTag + "\"");
|
||
progress.Append("Option PartNumber format: " + (PartNumberNameCombined ? "COMBINED" : "SEPARATE"));
|
||
|
||
|
||
|
||
Exception ExceptionDuringMigrate = null;
|
||
try
|
||
{
|
||
progress.Op("Preparing to export....");
|
||
//MIGRATE MODE
|
||
progress.Append("Setting v8 Server State to 'Migrate Mode'");
|
||
await util.PostAsync("server-state", "{\"serverState\":\"MigrateMode\"}");
|
||
|
||
//delay to allow migrate mode to take effect
|
||
//this is a result of pm's generating in a loop at server causing issues with erasure
|
||
//this delay should not need to be more time than it takes for a single PM to generate into a workorder
|
||
//as of this time it's the only slow thing affecting migrate mode settling in
|
||
progress.SubOp("Pausing 7 seconds to give server time to complete current jobs and settle into migrate mode");
|
||
await System.Threading.Tasks.Task.Delay(7000);
|
||
mSubstitutePartId = 0;
|
||
ResetUniqueUserNames();
|
||
ResetUniqueNames();
|
||
Allv8WarehouseIds.Clear();
|
||
V7ToV8IdMap.Clear();
|
||
V7ToV8WorkOrderItemStatusIdMap.Clear();
|
||
TagMap.Clear();
|
||
LocaleMap.Clear();
|
||
SmallestFileUploadRejectedAsTooBig = long.MaxValue;
|
||
|
||
|
||
//add default raven language and codes
|
||
LocaleMap.Add("English", 1);
|
||
LocaleMap.Add("Español", 2);
|
||
LocaleMap.Add("Français", 3);
|
||
LocaleMap.Add("Deutsch", 4);
|
||
//admin user (not exported but is there already)
|
||
V7ToV8IdMap.Add(User.AdministratorID, 1);
|
||
|
||
|
||
|
||
|
||
|
||
//ERASE DB
|
||
progress.Append("Erasing AyaNova 8 data");
|
||
|
||
var a = await util.PostAsync("license/permanently-erase-all-data", "\"I understand\"");
|
||
|
||
|
||
//TAGS
|
||
progress.Op("Compiling tags");
|
||
if (progress.KeepGoing)
|
||
ExportUnitModelCategories(progress);
|
||
if (progress.KeepGoing)
|
||
ExportUnitServiceTypes(progress);
|
||
if (progress.KeepGoing)
|
||
ExportWorkorderItemTypes(progress);
|
||
if (progress.KeepGoing)
|
||
ExportRegions(progress);
|
||
if (progress.KeepGoing)
|
||
ExportClientGroups(progress);
|
||
if (progress.KeepGoing)
|
||
ExportWorkorderCategories(progress);
|
||
if (progress.KeepGoing)
|
||
ExportPartCategories(progress);
|
||
if (progress.KeepGoing)
|
||
ExportScheduleableUserGroups(progress);
|
||
if (progress.KeepGoing)
|
||
ExportDispatchZones(progress);
|
||
if (progress.KeepGoing)
|
||
ExportUserSkills(progress);
|
||
if (progress.KeepGoing)
|
||
ExportUserCertifications(progress);
|
||
if (progress.KeepGoing)
|
||
ExportClientNoteTypes(progress);
|
||
|
||
progress.Op("Exporting Business objects");
|
||
|
||
//are we using inventory for this?
|
||
V7UseInventory = AyaBizUtils.GlobalSettings.UseInventory;
|
||
|
||
//BIZ objects
|
||
|
||
await ExportLocales(progress);
|
||
await ExportStaffUsers(progress);
|
||
await ExportRates(progress);
|
||
await ExportTaxCodes(progress);
|
||
await ExportContracts(progress);
|
||
await ExportHeadOffices(progress);
|
||
await ExportClients(progress);
|
||
await ExportVendors(progress);
|
||
await ExportLoanItems(progress);
|
||
await InitInventory(progress);
|
||
await ExportWarehouses(progress);
|
||
await ExportParts(progress);
|
||
await ExportPartAssemblies(progress);
|
||
await ExportProjects(progress);
|
||
await ExportPurchaseOrders(progress);
|
||
await ExportInventoryAdjustments(progress);
|
||
await ExportUnitModels(progress);
|
||
await ExportUnits(progress);
|
||
await ExportExternalUsers(progress);//needs vendors, clients and headoffices already exported so needs to be here late
|
||
await ExportMemos(progress);
|
||
await ExportTaskGroups(progress);
|
||
await ExportWorkorderStatus(progress);//item and workorder both as split in v8
|
||
await ExportWorkOrderItemPriorities(progress);
|
||
await ExportQuotes(progress);
|
||
await ExportPMs(progress);
|
||
//await SynchronizeSerials(progress);
|
||
await ExportServiceWorkorders(progress);
|
||
await ExportUnitMeterReadings(progress);
|
||
|
||
|
||
//Must be after all root objects due to followups
|
||
await ExportScheduleMarkers(progress);
|
||
|
||
//must be after all clients, ho, units and workorders
|
||
//await ExportServiceBank(progress);//deprecated for v8 (so far)
|
||
|
||
await ExportClientServiceRequests(progress);
|
||
await SynchronizeInventory(progress);
|
||
//await SynchronizeSerials(progress);
|
||
await ExportGlobalSettings(progress);
|
||
await ExportGlobalRegionalWiki(progress);
|
||
|
||
if (progress.KeepGoing)
|
||
{
|
||
|
||
goto End;
|
||
}
|
||
|
||
progress.Append("Export cancelled before completing");
|
||
progress.Op("");
|
||
|
||
End:
|
||
;
|
||
//MIGRATE MODE
|
||
progress.Append("Setting v8 Server State to 'Open'");
|
||
await util.PostAsync("server-state", "{\"serverState\":\"Open\"}");
|
||
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
ExceptionDuringMigrate = ex;//can't await in a catch block so this is done this way so can send memo about error to v8
|
||
}
|
||
|
||
if (ExceptionDuringMigrate != null)
|
||
{
|
||
progress.Append("ERROR, During operation:");
|
||
progress.Append(progress.LastOp + ", " + progress.LastSubOp);
|
||
progress.Append("Export failed with error:");
|
||
progress.Append(ExceptionDuringMigrate.Message);
|
||
progress.Append("StackTrace:");
|
||
progress.Append(ExceptionDuringMigrate.StackTrace);
|
||
if (ExceptionDuringMigrate.InnerException != null)
|
||
{
|
||
progress.Append("Inner error:");
|
||
progress.Append(ExceptionDuringMigrate.InnerException.Message);
|
||
progress.Append("Inner StackTrace:");
|
||
progress.Append(ExceptionDuringMigrate.InnerException.StackTrace);
|
||
}
|
||
}
|
||
progress.FinishedImport();//just enables close / cancel etc
|
||
|
||
progress.Op("Saving export log to v8 memo for SuperUser...");
|
||
await PostExportLogToV8(progress);
|
||
|
||
progress.Append("Log and help links saved in v8 Memo to SuperUser account (Home->Memos)");
|
||
|
||
|
||
progress.Append("###################################");
|
||
progress.Append("###################################");
|
||
progress.Append("Export completed");
|
||
progress.Op("");
|
||
progress.SubOp("");
|
||
|
||
//-----------------------------------
|
||
//endof method
|
||
}
|
||
|
||
|
||
|
||
|
||
#region BIZ OBJECT Export methods
|
||
List<string> UsedNames = new List<string>();
|
||
long LUniqueName = 1;
|
||
private string GetUniqueName(string tryName)
|
||
{
|
||
|
||
string retName = tryName;
|
||
do
|
||
{
|
||
if (!UsedNames.Contains(retName))
|
||
{
|
||
UsedNames.Add(retName);
|
||
return retName;
|
||
}
|
||
retName = util.UniqueNameBuilder(tryName, LUniqueName++, 255);
|
||
} while (true);
|
||
|
||
}
|
||
|
||
private void ResetUniqueNames()
|
||
{
|
||
LUniqueName = 1;
|
||
UsedNames.Clear();
|
||
}
|
||
|
||
|
||
|
||
List<string> UsedUserNames = new List<string>();
|
||
long LUniqueUserName = 1;
|
||
private string GetUniqueUserName(string tryName)
|
||
{
|
||
|
||
string retName = tryName;
|
||
do
|
||
{
|
||
if (!UsedUserNames.Contains(retName))
|
||
{
|
||
UsedUserNames.Add(retName);
|
||
return retName;
|
||
}
|
||
retName = util.UniqueNameBuilder(tryName, LUniqueUserName++, 255);
|
||
} while (true);
|
||
|
||
}
|
||
|
||
private void ResetUniqueUserNames()
|
||
{
|
||
LUniqueUserName = 1;
|
||
UsedUserNames.Clear();
|
||
}
|
||
|
||
|
||
private async System.Threading.Tasks.Task PostExportLogToV8(ProgressForm progress)
|
||
{
|
||
try
|
||
{
|
||
dynamic d = new JObject();
|
||
dynamic dmemo = new JObject();
|
||
dynamic duserid = new JArray();
|
||
|
||
dmemo.fromId = 1;
|
||
dmemo.toId = 1;
|
||
dmemo.name = "V8 Migrate - post migrate log and instructions";
|
||
dmemo.notes = "Hello, this is an automated memo from the v8 migrate utility.\n\n"
|
||
+ "Important manual pages related to Migration:\n\nChanges in AyaNova 8 for users:\n"
|
||
+ util.GuessClientUrl + "docs/ay-start-changes-from-v7/\n\nTechnical changes in AyaNova 8 for system administrators:\n"
|
||
+ util.GuessClientUrl + "docs/ops-technical-changes-from-v7/\n\nMigration guide and recommended post migrate steps:\n"
|
||
+ util.GuessClientUrl + "docs/ay-biz-admin-import-v7/\n\n\nLog:\n"
|
||
+ "######## V8 Migrate log ########\n"
|
||
+ progress.AllText
|
||
+ "\n################################\n";
|
||
dmemo.replied = false;
|
||
dmemo.sent = util.DateToV8(DateTime.UtcNow.ToString("o", System.Globalization.CultureInfo.InvariantCulture));
|
||
dmemo.viewed = false; duserid.Add(-7);//special code to server to treat as an import single
|
||
d.users = duserid;
|
||
d.memo = dmemo;
|
||
|
||
await util.PostAsync("memo", d);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
progress.Append("Error attempting to post migrate log to v8 superuser memo:\n" + ex.Message);
|
||
}
|
||
}
|
||
|
||
#region Users
|
||
|
||
private async System.Threading.Tasks.Task ExportStaffUsers(ProgressForm progress)
|
||
{
|
||
if (!progress.KeepGoing) return;
|
||
progress.Op("Start internal (staff) User export");
|
||
progress.SubOp("");
|
||
ResetUniqueNames();
|
||
|
||
//Step 1: export the CustomFields to FormCustom if applicable so that when doing individual items we can export their custom data too
|
||
var ocf = ObjectHasCustomFieldDataToExport("User");
|
||
bool ShouldExportCustom = ocf != null;
|
||
|
||
var DateCustomFields = await ExportCustomFieldSchema(ocf, "User", "User");
|
||
|
||
//Step 2: export the objects
|
||
|
||
UserPickList pl = UserPickList.GetList(false);
|
||
progress.Append("Exporting " + pl.Count.ToString() + " Users (step 1/2)");
|
||
|
||
#region Export administrator wiki and attached files if present
|
||
{
|
||
User admin = User.GetItem(User.AdministratorID);
|
||
progress.Op("Administrator account");
|
||
var adminTid = new TypeAndID(RootObjectTypes.User, User.AdministratorID);
|
||
//Attachments and wiki
|
||
string AdminWikiContent = null;
|
||
var hasWiki = WikiPage.HasWiki(User.AdministratorID);
|
||
if (hasWiki)
|
||
{
|
||
await ExportAttachments(adminTid, progress, util.AyaType.NoType);//admin account
|
||
AdminWikiContent = await GetWikiContent(adminTid);
|
||
}
|
||
|
||
//docs
|
||
string NonFileUrls = await ExportDocs(adminTid, admin.Docs, progress, util.AyaType.NoType);//admin account
|
||
if (!string.IsNullOrEmpty(NonFileUrls))
|
||
{
|
||
//need to repost the user with the notes modified
|
||
var a = await util.GetAsync("user/1");
|
||
dynamic d = a.ObjectResponse["data"];
|
||
d.login = null;
|
||
d.password = null;
|
||
d.notes = d.notes + "\n-----------------\n" + NonFileUrls;
|
||
await util.PutAsync("user", d);
|
||
}
|
||
|
||
string adminCustomFields = null;
|
||
//Custom fields?
|
||
if (ShouldExportCustom)
|
||
{
|
||
adminCustomFields = CustomFieldData(admin, DateCustomFields);
|
||
}
|
||
|
||
//check if we need to do anything with the manager account itself
|
||
if (hasWiki || adminCustomFields != null)
|
||
{
|
||
//yes, so fetch it and modify it and put it back again
|
||
var a = await util.GetAsync("user/1");
|
||
dynamic d = a.ObjectResponse["data"];
|
||
d.id = 1;
|
||
d.wiki = AdminWikiContent;
|
||
d.customFields = CustomFieldData(admin, DateCustomFields);
|
||
d.login = null;//do not update the login / password
|
||
d.password = null;
|
||
await util.PutAsync("user", d);
|
||
}
|
||
|
||
|
||
}
|
||
|
||
#endregion admin export
|
||
|
||
|
||
foreach (UserPickList.UserPickListInfo i in pl)
|
||
{
|
||
if (!progress.KeepGoing) return;
|
||
|
||
|
||
List<string> tags = new List<string>();
|
||
AddImportTag(tags);
|
||
|
||
//skip administrator user fields
|
||
//but do export administrator
|
||
if (i.ID == User.AdministratorID) continue;
|
||
|
||
User c = User.GetItem(i.ID);
|
||
|
||
//skip any external users
|
||
if (i.Type == UserTypes.Client || i.Type == UserTypes.HeadOffice || c.VendorID != Guid.Empty)
|
||
continue;
|
||
|
||
var ObjectTID = new TypeAndID(RootObjectTypes.User, c.ID);
|
||
|
||
dynamic d = new JObject();
|
||
d.name = GetUniqueUserName(c.FirstName + " " + c.LastName);
|
||
if (IsDuplicatev7v8IdMapItem(c.ID, c.FirstName + " " + c.LastName, progress)) continue;
|
||
progress.Op("User " + d.name);
|
||
|
||
//RAVEN user types are different numbers so even the regular ones need to be set alternatively
|
||
switch (c.UserType)
|
||
{
|
||
case UserTypes.Administrator:
|
||
d.userType = 2;
|
||
break;
|
||
case UserTypes.NonSchedulable:
|
||
d.userType = 2;
|
||
break;
|
||
case UserTypes.Schedulable:
|
||
d.userType = 1;
|
||
break;
|
||
}
|
||
|
||
|
||
|
||
d.active = false;//all imported users are inactive to start
|
||
d.roles = 0;//todo: try to determine role from v7 member of group? or is that even possible?
|
||
d.login = util.RandomString();
|
||
d.password = util.RandomString();
|
||
|
||
d.employeeNumber = c.EmployeeNumber;
|
||
d.notes = c.Notes;
|
||
TagFromv7Guid(c.RegionID, tags);
|
||
TagFromv7Guid(c.DispatchZoneID, tags);
|
||
foreach (UserSkillAssigned skill in c.UserSkills)
|
||
{
|
||
TagFromv7Guid(skill.UserSkillID, tags);
|
||
}
|
||
foreach (UserCertificationAssigned cert in c.UserCertifications)
|
||
{
|
||
TagFromv7Guid(cert.UserCertificationID, tags);
|
||
}
|
||
SetTags(d, tags);
|
||
|
||
//Custom fields?
|
||
if (ShouldExportCustom)
|
||
d.customFields = CustomFieldData(c, DateCustomFields);
|
||
|
||
var rMainObject = await util.PostAsync("user", d);
|
||
long RavenId = util.IdFromResponse(rMainObject);
|
||
Addv7v8IdMap(c.ID, RavenId);
|
||
|
||
//USER OPTIONS
|
||
|
||
var rOptions = await util.GetAsync("user-option/" + RavenId.ToString());
|
||
d = rOptions.ObjectResponse["data"];
|
||
d.uiColor = System.Drawing.ColorTranslator.ToHtml(System.Drawing.Color.FromArgb(c.ScheduleBackColor));
|
||
d.emailAddress = string.IsNullOrWhiteSpace(c.EmailAddress) ? null : c.EmailAddress;
|
||
d.phone1 = c.Phone1;
|
||
d.phone2 = c.Phone2;
|
||
d.phone3 = c.PageAddress;
|
||
|
||
|
||
if (LocaleMap.ContainsKey(c.DefaultLanguage))
|
||
d.translationId = LocaleMap[c.DefaultLanguage];
|
||
else
|
||
d.translationId = 1;
|
||
|
||
await util.PutAsync("user-option/" + RavenId.ToString(), d);
|
||
|
||
|
||
//Attachments / FILES
|
||
await ExportAttachments(ObjectTID, progress, util.AyaType.User);
|
||
|
||
//-----
|
||
bool repost = false;
|
||
d = rMainObject.ObjectResponse["data"];
|
||
// wiki
|
||
if (WikiPage.HasWiki(c.ID))
|
||
{
|
||
d.wiki = await GetWikiContent(ObjectTID);
|
||
repost = true;
|
||
}
|
||
|
||
//docs
|
||
string NonFileUrls = await ExportDocs(ObjectTID, c.Docs, progress, util.AyaType.User);
|
||
if (!string.IsNullOrEmpty(NonFileUrls))
|
||
{
|
||
//need to repost the user with the notes modified
|
||
|
||
d.login = null;
|
||
d.password = null;
|
||
d.notes = d.notes + "\n-----------------\n" + NonFileUrls;
|
||
repost = true;
|
||
}
|
||
if (repost)
|
||
await util.PutAsync("user", d);
|
||
//-----
|
||
|
||
}
|
||
progress.Append("ACTION REQUIRED: Set Scheduleable Users Active in v8 to see scheduled items");
|
||
progress.Append("ACTION REQUIRED: Set password and login in v8 so Users can login to v8");
|
||
|
||
//## NOT ALL USERS YET, SO ONLY CALL THIS WHEN THE EXTERNAL USERS LATER ARE EXPORTED
|
||
////EVENT LOG
|
||
////Because this is the User's we need to do the event log *after* they have all been posted as event log requires all user's id
|
||
//foreach (UserPickList.UserPickListInfo i in pl)
|
||
//{
|
||
// if (!progress.KeepGoing) return;
|
||
// User c = User.GetItem(i.ID);
|
||
// var newId = GetMapMap[c.ID];
|
||
// var creator = await Getv7v8IdMap(c.Creator,RootObjectTypes.User);
|
||
// var modifier = await Getv7v8IdMap(c.Modifier,RootObjectTypes.User);
|
||
|
||
// await util.EventLog(util.AyaType.User, newId, creator, modifier, c.Created, c.Modified);
|
||
//}
|
||
|
||
}
|
||
|
||
private async System.Threading.Tasks.Task ExportExternalUsers(ProgressForm progress)
|
||
{
|
||
if (!progress.KeepGoing) return;
|
||
progress.Op("Start external User export");
|
||
progress.SubOp("");
|
||
ResetUniqueNames();
|
||
|
||
//Step 1: export the CustomFields to FormCustom if applicable so that when doing individual items we can export their custom data too
|
||
var ocf = ObjectHasCustomFieldDataToExport("User");
|
||
bool ShouldExportCustom = ocf != null;
|
||
|
||
var DateCustomFields = await ExportCustomFieldSchema(ocf, "User", "User");
|
||
|
||
//Step 2: export the objects
|
||
|
||
UserPickList pl = UserPickList.GetList(false);
|
||
progress.Append("Exporting " + pl.Count.ToString() + " Users (step 2/2)");
|
||
|
||
|
||
|
||
foreach (UserPickList.UserPickListInfo i in pl)
|
||
{
|
||
if (!progress.KeepGoing) return;
|
||
|
||
|
||
List<string> tags = new List<string>();
|
||
AddImportTag(tags);
|
||
|
||
//skip administrator user fields
|
||
//but do export administrator
|
||
if (i.ID == User.AdministratorID) continue;
|
||
|
||
User c = User.GetItem(i.ID);
|
||
|
||
//skip any internal or non subcontractor Users
|
||
if (c.VendorID == Guid.Empty && (i.Type == UserTypes.Administrator || i.Type == UserTypes.NonSchedulable
|
||
|| i.Type == UserTypes.Schedulable || i.Type == UserTypes.Utility))
|
||
continue;
|
||
|
||
var ObjectTID = new TypeAndID(RootObjectTypes.User, c.ID);
|
||
|
||
dynamic d = new JObject();
|
||
d.name = GetUniqueUserName(c.FirstName + " " + c.LastName);
|
||
if (IsDuplicatev7v8IdMapItem(c.ID, c.FirstName + " " + c.LastName, progress)) continue;
|
||
progress.Op("User " + d.name);
|
||
//throw new System.Exception("TODO: UserTypes sb done differently now to align");
|
||
//TODO: User type
|
||
//d.userType = (int)c.UserType;
|
||
|
||
//todo: this needs to be adjusted later
|
||
//after vendor, customer and headoffice objects are all exported
|
||
//then loop through all users again here and set them accordingly if required
|
||
|
||
//RAVEN user types are different numbers so even the regular ones need to be set alternatively
|
||
switch (c.UserType)
|
||
{
|
||
case UserTypes.Administrator:
|
||
d.userType = RavenUserType.NotService;
|
||
break;
|
||
case UserTypes.NonSchedulable:
|
||
d.userType = RavenUserType.NotService;
|
||
break;
|
||
case UserTypes.Schedulable:
|
||
d.userType = RavenUserType.Service;
|
||
break;
|
||
case UserTypes.Client:
|
||
d.userType = RavenUserType.Customer;//raven not service type
|
||
break;
|
||
case UserTypes.HeadOffice:
|
||
d.userType = RavenUserType.HeadOffice;
|
||
break;
|
||
}
|
||
if (c.VendorID != Guid.Empty)
|
||
{
|
||
d.userType = RavenUserType.ServiceContractor;
|
||
d.vendorId = await Getv7v8IdMap(c.VendorID, RootObjectTypes.Vendor);
|
||
}
|
||
|
||
if (c.ClientID != Guid.Empty)
|
||
{
|
||
d.customerId = await Getv7v8IdMap(c.ClientID, RootObjectTypes.Client);
|
||
}
|
||
|
||
if (c.HeadOfficeID != Guid.Empty)
|
||
{
|
||
d.headOfficeId = await Getv7v8IdMap(c.HeadOfficeID, RootObjectTypes.HeadOffice);
|
||
}
|
||
|
||
|
||
d.active = false;//all imported users are inactive to start
|
||
d.roles = 0;//todo: try to determine role from v7 member of group? or is that even possible?
|
||
d.login = util.RandomString();
|
||
d.password = util.RandomString();
|
||
|
||
d.employeeNumber = c.EmployeeNumber;
|
||
d.notes = c.Notes;
|
||
TagFromv7Guid(c.RegionID, tags);
|
||
TagFromv7Guid(c.DispatchZoneID, tags);
|
||
foreach (UserSkillAssigned skill in c.UserSkills)
|
||
{
|
||
TagFromv7Guid(skill.UserSkillID, tags);
|
||
}
|
||
foreach (UserCertificationAssigned cert in c.UserCertifications)
|
||
{
|
||
TagFromv7Guid(cert.UserCertificationID, tags);
|
||
}
|
||
SetTags(d, tags);
|
||
|
||
//Custom fields?
|
||
if (ShouldExportCustom)
|
||
d.customFields = CustomFieldData(c, DateCustomFields);
|
||
|
||
var rMainObject = await util.PostAsync("user", d);
|
||
long RavenId = util.IdFromResponse(rMainObject);
|
||
Addv7v8IdMap(c.ID, RavenId);
|
||
|
||
//USER OPTIONS
|
||
|
||
var rOptions = await util.GetAsync("user-option/" + RavenId.ToString());
|
||
d = rOptions.ObjectResponse["data"];
|
||
//d.uiColor = System.Drawing.ColorTranslator.ToHtml(System.Drawing.Color.FromArgb(c.ScheduleBackColor));
|
||
d.emailAddress = string.IsNullOrWhiteSpace(c.EmailAddress) ? null : c.EmailAddress;
|
||
d.phone1 = c.Phone1;
|
||
d.phone2 = c.Phone2;
|
||
d.phone3 = c.PageAddress;
|
||
|
||
if (LocaleMap.ContainsKey(c.DefaultLanguage))
|
||
d.translationId = LocaleMap[c.DefaultLanguage];
|
||
else
|
||
d.translationId = 1;
|
||
|
||
await util.PutAsync("user-option/" + RavenId.ToString(), d);
|
||
|
||
|
||
//Attachments / FILES
|
||
await ExportAttachments(ObjectTID, progress, util.AyaType.User);
|
||
|
||
//-----
|
||
bool repost = false;
|
||
d = rMainObject.ObjectResponse["data"];
|
||
// wiki
|
||
if (WikiPage.HasWiki(c.ID))
|
||
{
|
||
d.wiki = await GetWikiContent(ObjectTID);
|
||
repost = true;
|
||
}
|
||
|
||
//docs
|
||
string NonFileUrls = await ExportDocs(ObjectTID, c.Docs, progress, util.AyaType.User);
|
||
if (!string.IsNullOrEmpty(NonFileUrls))
|
||
{
|
||
//need to repost the user with the notes modified
|
||
|
||
d.login = null;
|
||
d.password = null;
|
||
d.notes = d.notes + "\n-----------------\n" + NonFileUrls;
|
||
repost = true;
|
||
}
|
||
if (repost)
|
||
await util.PutAsync("user", d);
|
||
//-----
|
||
|
||
}
|
||
|
||
|
||
//EVENT LOG
|
||
//Because this is the User's we need to do the event log *after* they have all been posted as event log requires all user's id
|
||
foreach (UserPickList.UserPickListInfo i in pl)
|
||
{
|
||
if (!progress.KeepGoing) return;
|
||
User c = User.GetItem(i.ID);
|
||
var newId = await Getv7v8IdMap(c.ID, RootObjectTypes.User);
|
||
var creator = await Getv7v8IdMap(c.ID, RootObjectTypes.User);//await Getv7v8IdMap(c.Creator,RootObjectTypes.User);
|
||
var modifier = await Getv7v8IdMap(c.ID, RootObjectTypes.User);//await Getv7v8IdMap(c.Modifier,RootObjectTypes.User);
|
||
|
||
await util.EventLog(util.AyaType.User, newId, creator, modifier, c.Created, c.Modified);
|
||
}
|
||
|
||
}
|
||
|
||
|
||
#endregion users
|
||
|
||
#region Clients
|
||
private async System.Threading.Tasks.Task ExportClients(ProgressForm progress)
|
||
{
|
||
if (!progress.KeepGoing) return;
|
||
progress.Op("Start Client export");
|
||
progress.SubOp("");
|
||
ResetUniqueNames();
|
||
var ObjectTypeName = "Client";
|
||
//Step 1: export the CustomFields to FormCustom if applicable so that when doing individual items we can export their custom data too
|
||
var ocf = ObjectHasCustomFieldDataToExport(ObjectTypeName);
|
||
bool ShouldExportCustom = ocf != null;
|
||
var DateCustomFields = await ExportCustomFieldSchema(ocf, ObjectTypeName, "Customer");
|
||
|
||
//Step 2: export the objects
|
||
ClientPickList pl = ClientPickList.GetList();
|
||
progress.Append("Exporting " + pl.Count.ToString() + " " + ObjectTypeName + "s");
|
||
|
||
foreach (ClientPickList.ClientPickListInfo i in pl)
|
||
{
|
||
if (!progress.KeepGoing) return;
|
||
List<string> tags = new List<string>();
|
||
AddImportTag(tags);
|
||
|
||
Client c = Client.GetItem(i.ID);
|
||
var ObjectTID = new TypeAndID(RootObjectTypes.Client, c.ID);
|
||
/*v8
|
||
## route [Route("api/v{version:apiVersion}/customer")]
|
||
## object format
|
||
[
|
||
{
|
||
"Id": 100,
|
||
"Concurrency": 90525,
|
||
"Name": "Parker - Kling 321",
|
||
"Active": true,
|
||
"Notes": "Cross-group system-worthy installation",
|
||
"Wiki": null,
|
||
"CustomFields": null,
|
||
"Tags": [
|
||
"red"
|
||
],
|
||
"WebAddress": "http://esther.name",
|
||
"alertNotes": null,
|
||
"BillHeadOffice": false,
|
||
"HeadOfficeID": null,
|
||
"TechNotes": null,
|
||
"AccountNumber": "31070722",
|
||
"UsesBanking": false,
|
||
"ContractID": null,
|
||
"ContractExpires": null,
|
||
"DefaultServiceTemplateID": null,
|
||
"Phone1": "(651) 496-8691",
|
||
"Phone2": "919.303.4097 x10230",
|
||
"Phone3": "1-829-391-7162",
|
||
"Phone4": null,
|
||
"Phone5": null,
|
||
"EmailAddress": "Domingo.White@example.net",
|
||
"PostAddress": null,
|
||
"PostCity": null,
|
||
"PostRegion": null,
|
||
"PostCountry": null,
|
||
"PostCode": null,
|
||
"Address": "470 Conroy Island",
|
||
"City": "Lake Ambrose",
|
||
"Region": "Massachusetts",
|
||
"Country": "Argentina",
|
||
"Latitude": -60.473000,
|
||
"Longitude": 25.380100
|
||
}
|
||
]
|
||
*/
|
||
dynamic d = new JObject();
|
||
d.name = GetUniqueName(c.Name);
|
||
if (IsDuplicatev7v8IdMapItem(c.ID, c.Name, progress)) continue;
|
||
progress.Op(ObjectTypeName + " " + d.name);
|
||
d.active = c.Active;
|
||
d.notes = c.Notes;
|
||
//No contact but has contact notes so append to general notes
|
||
if (!string.IsNullOrWhiteSpace(c.ContactNotes))
|
||
{
|
||
d.notes = c.Notes + "\nCONTACT NOTES:\n" + c.ContactNotes;
|
||
}
|
||
|
||
d.webAddress = c.WebAddress;
|
||
d.alertNotes = c.PopUpNotes;
|
||
d.billHeadOffice = c.BillHeadOffice;
|
||
|
||
|
||
if (c.HeadOfficeID != Guid.Empty)
|
||
{
|
||
d.headOfficeId = await Getv7v8IdMap(c.HeadOfficeID, RootObjectTypes.HeadOffice);
|
||
}
|
||
d.techNotes = c.TechNotes;
|
||
d.accountNumber = c.AccountNumber;
|
||
d.usesBanking = c.UsesBanking;
|
||
//if (c.UsesBanking)
|
||
// BankedObjects.Add(c.ID, RootObjectTypes.Client);
|
||
|
||
if (c.ContractID != Guid.Empty)
|
||
{
|
||
long? ctId = await Getv7v8IdMapNullOk(c.ContractID, RootObjectTypes.Contract, false, true);
|
||
if (ctId != 0 && ctId != null)
|
||
{
|
||
|
||
d.contractId = ctId;
|
||
d.contractExpires = c.ContractExpires;
|
||
}
|
||
}
|
||
|
||
|
||
d.phone1 = c.Phone1;
|
||
d.phone2 = c.Phone2;
|
||
d.phone3 = c.Phone3;
|
||
d.phone4 = c.Phone4;
|
||
d.phone5 = c.Phone5;
|
||
d.emailAddress = c.Email;
|
||
d.postAddress = c.MailToAddress.DeliveryAddress;
|
||
d.postCity = c.MailToAddress.City;
|
||
d.postRegion = c.MailToAddress.StateProv;
|
||
d.postCode = c.MailToAddress.Postal;
|
||
d.address = c.GoToAddress.DeliveryAddress;
|
||
d.city = c.GoToAddress.City;
|
||
d.region = c.GoToAddress.StateProv;
|
||
d.country = c.GoToAddress.Country;
|
||
d.latitude = c.GoToAddress.Latitude;
|
||
d.longitude = c.GoToAddress.Longitude;
|
||
|
||
|
||
TagFromv7Guid(c.RegionID, tags);
|
||
TagFromv7Guid(c.DispatchZoneID, tags);
|
||
TagFromv7Guid(c.ClientGroupID, tags);
|
||
SetTags(d, tags);
|
||
|
||
|
||
//Custom fields?
|
||
if (ShouldExportCustom)
|
||
d.customFields = CustomFieldData(c, DateCustomFields);
|
||
|
||
var rMainObject = await util.PostAsync("customer", d);
|
||
long RavenId = util.IdFromResponse(rMainObject);
|
||
Addv7v8IdMap(c.ID, RavenId);
|
||
|
||
//Attachments / FILES
|
||
await ExportAttachments(ObjectTID, progress, util.AyaType.Customer);
|
||
|
||
//-----
|
||
bool repost = false;
|
||
d = rMainObject.ObjectResponse["data"];
|
||
// wiki
|
||
if (WikiPage.HasWiki(c.ID))
|
||
{
|
||
d.wiki = await GetWikiContent(ObjectTID);
|
||
repost = true;
|
||
}
|
||
|
||
//docs
|
||
string NonFileUrls = await ExportDocs(ObjectTID, c.Docs, progress, util.AyaType.Customer);
|
||
if (!string.IsNullOrEmpty(NonFileUrls))
|
||
{
|
||
d.notes = d.notes + "\n-----------------\n" + NonFileUrls;
|
||
repost = true;
|
||
}
|
||
if (repost)
|
||
await util.PutAsync("customer", d);
|
||
//-----
|
||
|
||
//Event log fixup
|
||
await util.EventLog(util.AyaType.Customer, RavenId, await Getv7v8IdMap(c.Creator, RootObjectTypes.User), await Getv7v8IdMap(c.Modifier, RootObjectTypes.User), c.Created, c.Modified);
|
||
|
||
//RAVEN Contact type user from Client contact
|
||
//note: this is not a login account, it could be in raven but here it's the way to get the
|
||
//contact created
|
||
if (!string.IsNullOrWhiteSpace(c.Contact))
|
||
{
|
||
//create user here
|
||
//--------------------------------
|
||
|
||
d = new JObject();
|
||
d.name = GetUniqueUserName(c.Contact);
|
||
|
||
progress.Op("Contact " + d.name);
|
||
d.userType = RavenUserType.Customer;
|
||
d.customerId = RavenId;
|
||
d.active = false;//all imported users are inactive to start
|
||
d.roles = 0;//todo: try to determine role from v7 member of group? or is that even possible?
|
||
d.login = util.RandomString();
|
||
d.password = util.RandomString();
|
||
SetTags(d, tags);
|
||
|
||
|
||
var rContact = await util.PostAsync("user", d);
|
||
long RavenContactId = util.IdFromResponse(rContact);
|
||
|
||
|
||
//USER OPTIONS
|
||
var rOptions = await util.GetAsync("user-option/" + RavenContactId.ToString());
|
||
d = rOptions.ObjectResponse["data"];
|
||
d.phone1 = c.Phone1;
|
||
d.phone2 = c.Phone2;
|
||
d.phone3 = c.Phone3;
|
||
|
||
d.emailAddress = c.Email;
|
||
|
||
|
||
// d.translationId = 1;
|
||
|
||
await util.PutAsync("user-option/" + RavenContactId.ToString(), d);
|
||
//Event log fixup (using customer data since the contact is created from the customer)
|
||
await util.EventLog(util.AyaType.User, RavenContactId, await Getv7v8IdMap(c.Creator, RootObjectTypes.User), await Getv7v8IdMap(c.Modifier, RootObjectTypes.User), c.Created, c.Modified);
|
||
|
||
|
||
|
||
//###########
|
||
//CUSTOMER NOTES
|
||
//
|
||
//
|
||
/* CLIENT NOTE
|
||
V8
|
||
### ROUTE [Route("api/v{version:apiVersion}/customer-note")]
|
||
### v8 format
|
||
{
|
||
"Id": 2,
|
||
"Concurrency": 90604,
|
||
"CustomerId": 100,
|
||
"UserId": 1,
|
||
"NoteDate": "2020-11-24T17:54:05.68",
|
||
"Notes": "The quick brown fox jumped over the six lazy dogs.\nPack my box with five dozen liquor jugs.\nThe quick brown fox jumped over the six lazy dogs.\nPack my box with five dozen liquor jugs.\nThe quick brown fox jumped over the six lazy dogs.\nPack my box with five dozen liquor jugs.\nThe getDate() method returns the current day of the month (1-31).\n\nThe getMonth() method returns the month of the specified date. One point to note about the getMonth() method is that it returns 0-indexed values (0-11) where 0 is for January and 11 for December. Hence the addition of 1 to normalize the month's value.\nDate now\n\nnow() is a static method of the Date object. It returns the value in milliseconds that represents the time elapsed since the Epoch. You can pass in the milliseconds returned from the now() method into the Date constructor to instantiate a new Date object:\neot",
|
||
"Tags": [
|
||
"gold",
|
||
"known-user",
|
||
"olive",
|
||
"test-role-user"
|
||
]
|
||
}
|
||
*/
|
||
ClientNotes cnl = ClientNotes.GetItems(c.ID);
|
||
foreach (ClientNote cn in cnl)
|
||
{
|
||
tags.Clear();
|
||
d = new JObject();
|
||
d.customerId = RavenId;
|
||
d.userId = await Getv7v8IdMap(cn.Creator, RootObjectTypes.User);
|
||
d.noteDate = util.DateToV8(cn.Created, true);
|
||
d.notes = cn.Notes;
|
||
TagFromv7Guid(cn.ClientNoteTypeID, tags);
|
||
SetTags(d, tags);
|
||
var newId = util.IdFromResponse(await util.PostAsync("customer-note", d.ToString()));
|
||
//Event log fixup
|
||
await util.EventLog(util.AyaType.CustomerNote, newId, await Getv7v8IdMap(cn.Creator, RootObjectTypes.User), await Getv7v8IdMap(cn.Modifier, RootObjectTypes.User), cn.Created, cn.Modified);
|
||
|
||
|
||
|
||
}
|
||
//----------------------------------
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
#endregion Clients
|
||
|
||
#region Headoffices
|
||
private async System.Threading.Tasks.Task ExportHeadOffices(ProgressForm progress)
|
||
{
|
||
if (!progress.KeepGoing) return;
|
||
progress.Op("Start Head office export");
|
||
progress.SubOp("");
|
||
ResetUniqueNames();
|
||
var ObjectTypeName = "HeadOffice";
|
||
//Step 1: export the CustomFields to FormCustom if applicable so that when doing individual items we can export their custom data too
|
||
var ocf = ObjectHasCustomFieldDataToExport(ObjectTypeName);
|
||
bool ShouldExportCustom = ocf != null;
|
||
var DateCustomFields = await ExportCustomFieldSchema(ocf, ObjectTypeName, ObjectTypeName);
|
||
|
||
//Step 2: export the objects
|
||
PickListAutoComplete pl = PickListAutoComplete.GetList("**", "headoffice");
|
||
|
||
progress.Append("Exporting " + pl.Count.ToString() + " " + ObjectTypeName + "s");
|
||
|
||
foreach (PickListAutoComplete.PickListAutoCompleteInfo i in pl)
|
||
{
|
||
if (!progress.KeepGoing) return;
|
||
List<string> tags = new List<string>();
|
||
AddImportTag(tags);
|
||
|
||
HeadOffice c = HeadOffice.GetItem(i.ID);
|
||
var ObjectTID = new TypeAndID(RootObjectTypes.HeadOffice, c.ID);
|
||
|
||
dynamic d = new JObject();
|
||
d.name = GetUniqueName(c.Name);
|
||
if (IsDuplicatev7v8IdMapItem(c.ID, c.Name, progress)) continue;
|
||
progress.Op(ObjectTypeName + " " + d.name);
|
||
d.active = c.Active;
|
||
d.notes = c.Notes;
|
||
//No contact but has contact notes so append to general notes
|
||
if (!string.IsNullOrWhiteSpace(c.ContactNotes))
|
||
{
|
||
d.notes = c.Notes + "\nCONTACT NOTES:\n" + c.ContactNotes;
|
||
}
|
||
|
||
|
||
d.webAddress = c.WebAddress;
|
||
|
||
d.accountNumber = c.AccountNumber;
|
||
d.usesBanking = c.UsesBanking;
|
||
//if (c.UsesBanking)
|
||
// BankedObjects.Add(c.ID, RootObjectTypes.HeadOffice);
|
||
|
||
if (c.ContractID != Guid.Empty)
|
||
{
|
||
long? ctId = await Getv7v8IdMapNullOk(c.ContractID, RootObjectTypes.Contract, false, true);
|
||
if (ctId != 0 && ctId != null)
|
||
{
|
||
|
||
d.contractId = ctId;
|
||
d.contractExpires = c.ContractExpires;
|
||
}
|
||
}
|
||
|
||
|
||
d.phone1 = c.Phone1;
|
||
d.phone2 = c.Phone2;
|
||
d.phone3 = c.Phone3;
|
||
d.phone4 = c.Phone4;
|
||
d.phone5 = c.Phone5;
|
||
d.emailAddress = c.Email;
|
||
d.postAddress = c.MailToAddress.DeliveryAddress;
|
||
d.postCity = c.MailToAddress.City;
|
||
d.postRegion = c.MailToAddress.StateProv;
|
||
d.postCode = c.MailToAddress.Postal;
|
||
d.address = c.GoToAddress.DeliveryAddress;
|
||
d.city = c.GoToAddress.City;
|
||
d.region = c.GoToAddress.StateProv;
|
||
d.country = c.GoToAddress.Country;
|
||
d.latitude = c.GoToAddress.Latitude;
|
||
d.longitude = c.GoToAddress.Longitude;
|
||
|
||
|
||
|
||
|
||
|
||
|
||
TagFromv7Guid(c.RegionID, tags);
|
||
TagFromv7Guid(c.ClientGroupID, tags);
|
||
SetTags(d, tags);
|
||
|
||
|
||
//Custom fields?
|
||
if (ShouldExportCustom)
|
||
d.customFields = CustomFieldData(c, DateCustomFields);
|
||
|
||
var rMainObject = await util.PostAsync("head-office", d);
|
||
long RavenId = util.IdFromResponse(rMainObject);
|
||
Addv7v8IdMap(c.ID, RavenId);
|
||
|
||
//Attachments / FILES
|
||
await ExportAttachments(ObjectTID, progress, util.AyaType.HeadOffice);
|
||
|
||
//-----
|
||
bool repost = false;
|
||
d = rMainObject.ObjectResponse["data"];
|
||
// wiki
|
||
if (WikiPage.HasWiki(c.ID))
|
||
{
|
||
|
||
d.wiki = await GetWikiContent(ObjectTID);
|
||
repost = true;
|
||
}
|
||
|
||
//docs
|
||
string NonFileUrls = await ExportDocs(ObjectTID, c.Docs, progress, util.AyaType.HeadOffice);
|
||
if (!string.IsNullOrEmpty(NonFileUrls))
|
||
{
|
||
d.notes = d.notes + "\n-----------------\n" + NonFileUrls;
|
||
repost = true;
|
||
}
|
||
if (repost)
|
||
await util.PutAsync("head-office", d);
|
||
//-----
|
||
|
||
//Event log fixup
|
||
await util.EventLog(util.AyaType.HeadOffice, RavenId, await Getv7v8IdMap(c.Creator, RootObjectTypes.User), await Getv7v8IdMap(c.Modifier, RootObjectTypes.User), c.Created, c.Modified);
|
||
|
||
|
||
|
||
//RAVEN Contact type user from Client contact
|
||
//note: this is not a login account, it could be in raven but here it's the way to get the
|
||
//contact created
|
||
if (!string.IsNullOrWhiteSpace(c.Contact))
|
||
{
|
||
//create user here
|
||
//--------------------------------
|
||
|
||
d = new JObject();
|
||
d.name = GetUniqueUserName(c.Contact);
|
||
|
||
progress.Op("Contact " + d.name);
|
||
d.userType = RavenUserType.HeadOffice;
|
||
d.headOfficeId = RavenId;
|
||
d.active = false;//all imported users are inactive to start
|
||
d.roles = 0;//todo: try to determine role from v7 member of group? or is that even possible?
|
||
d.login = util.RandomString();
|
||
d.password = util.RandomString();
|
||
SetTags(d, tags);
|
||
|
||
|
||
var rContact = await util.PostAsync("user", d);
|
||
long RavenContactId = util.IdFromResponse(rContact);
|
||
|
||
|
||
//USER OPTIONS
|
||
var rOptions = await util.GetAsync("user-option/" + RavenContactId.ToString());
|
||
d = rOptions.ObjectResponse["data"];
|
||
d.phone1 = c.Phone1;
|
||
d.phone2 = c.Phone2;
|
||
d.phone3 = c.Phone3;
|
||
|
||
d.emailAddress = c.Email;
|
||
|
||
|
||
// d.translationId = 1;
|
||
|
||
await util.PutAsync("user-option/" + RavenContactId.ToString(), d);
|
||
|
||
//Event log fixup (using customer data since the contact is created from the customer)
|
||
await util.EventLog(util.AyaType.User, RavenContactId, await Getv7v8IdMap(c.Creator, RootObjectTypes.User), await Getv7v8IdMap(c.Modifier, RootObjectTypes.User), c.Created, c.Modified);
|
||
|
||
|
||
}
|
||
}
|
||
}
|
||
#endregion ho
|
||
|
||
#region Contracts
|
||
private async System.Threading.Tasks.Task ExportContracts(ProgressForm progress)
|
||
{
|
||
ResetUniqueNames();
|
||
if (!progress.KeepGoing) return;
|
||
progress.Op("Start Contract export");
|
||
progress.SubOp("");
|
||
var ObjectTypeName = "Contract";
|
||
//Step 1: export the CustomFields to FormCustom if applicable so that when doing individual items we can export their custom data too
|
||
var ocf = ObjectHasCustomFieldDataToExport(ObjectTypeName);
|
||
bool ShouldExportCustom = ocf != null;
|
||
var DateCustomFields = await ExportCustomFieldSchema(ocf, ObjectTypeName, ObjectTypeName);
|
||
|
||
//Step 2: export the objects
|
||
PickListAutoComplete pl = PickListAutoComplete.GetList("**", "contract");
|
||
progress.Append("Exporting " + pl.Count.ToString() + " " + ObjectTypeName + "s");
|
||
|
||
RatePickList RateList = RatePickList.GetList(false);
|
||
|
||
foreach (PickListAutoComplete.PickListAutoCompleteInfo i in pl)
|
||
{
|
||
if (!progress.KeepGoing) return;
|
||
List<string> tags = new List<string>();
|
||
AddImportTag(tags);
|
||
|
||
Contract c = Contract.GetItem(i.ID);
|
||
var ObjectTID = new TypeAndID(RootObjectTypes.Contract, c.ID);
|
||
|
||
dynamic d = new JObject();
|
||
d.name = GetUniqueName(c.Name);
|
||
if (IsDuplicatev7v8IdMapItem(c.ID, c.Name, progress)) continue;
|
||
progress.Op(ObjectTypeName + " " + d.name);
|
||
d.active = c.Active;
|
||
d.notes = c.Notes;
|
||
d.responseTime = "00:00:00";
|
||
d.contractServiceRatesOnly = c.ContractRatesOnly;
|
||
d.contractTravelRatesOnly = false;//mimic v7 behaviour as this is a new feature
|
||
d.partsOverridePct = c.DiscountParts * 100m;
|
||
d.partsOverrideType = 1;//price - percentage, only type in v7
|
||
d.serviceRatesOverridePct = 0;//v8 only
|
||
d.serviceRatesOverrideType = 1;
|
||
d.travelRatesOverridePct = 0;
|
||
d.travelRatesOverrideType = 1;
|
||
|
||
dynamic dServiceRateItems = new JArray();
|
||
dynamic dTravelRateItems = new JArray();
|
||
|
||
foreach (ContractRate cr in c.Rates)
|
||
{
|
||
if (RateList.Contains(cr.RateID))
|
||
{
|
||
dynamic dcr = new JObject();
|
||
if (RateList[cr.RateID].RateType == RateTypes.Service)
|
||
{
|
||
dcr.serviceRateId = await GetRate(false, cr.RateID, true);
|
||
dServiceRateItems.Add(dcr);
|
||
}
|
||
else
|
||
{
|
||
dcr.travelRateId = await GetRate(true, cr.RateID, true);
|
||
dTravelRateItems.Add(dcr);
|
||
}
|
||
}
|
||
}
|
||
d.serviceRateItems = dServiceRateItems;
|
||
d.travelRateItems = dTravelRateItems;
|
||
|
||
TagFromv7Guid(c.RegionID, tags);
|
||
SetTags(d, tags);
|
||
|
||
|
||
//Custom fields?
|
||
if (ShouldExportCustom)
|
||
d.customFields = CustomFieldData(c, DateCustomFields);
|
||
|
||
var rMainObject = await util.PostAsync("contract", d);
|
||
long RavenId = util.IdFromResponse(rMainObject);
|
||
Addv7v8IdMap(c.ID, RavenId);
|
||
|
||
//Attachments / FILES
|
||
await ExportAttachments(ObjectTID, progress, util.AyaType.Contract);
|
||
|
||
//-----
|
||
bool repost = false;
|
||
d = rMainObject.ObjectResponse["data"];
|
||
// wiki
|
||
if (WikiPage.HasWiki(c.ID))
|
||
{
|
||
|
||
d.wiki = await GetWikiContent(ObjectTID);
|
||
repost = true;
|
||
}
|
||
|
||
//docs
|
||
string NonFileUrls = await ExportDocs(ObjectTID, c.Docs, progress, util.AyaType.Contract);
|
||
if (!string.IsNullOrEmpty(NonFileUrls))
|
||
{
|
||
d.notes = d.notes + "\n-----------------\n" + NonFileUrls;
|
||
repost = true;
|
||
}
|
||
if (repost)
|
||
await util.PutAsync("contract", d);
|
||
//-----
|
||
|
||
//Event log fixup
|
||
await util.EventLog(util.AyaType.Contract, RavenId, await Getv7v8IdMap(c.Creator, RootObjectTypes.User), await Getv7v8IdMap(c.Modifier, RootObjectTypes.User), c.Created, c.Modified);
|
||
|
||
}
|
||
}
|
||
#endregion Contracts
|
||
|
||
#region LoanItems
|
||
private async System.Threading.Tasks.Task ExportLoanItems(ProgressForm progress)
|
||
{
|
||
ResetUniqueNames();
|
||
if (!progress.KeepGoing) return;
|
||
progress.Op("Start Loan item export");
|
||
progress.SubOp("");
|
||
var ObjectTypeName = "LoanItem";
|
||
var RavenObjectName = "LoanUnit";
|
||
//Step 1: export the CustomFields to FormCustom if applicable so that when doing individual items we can export their custom data too
|
||
var ocf = ObjectHasCustomFieldDataToExport(ObjectTypeName);
|
||
bool ShouldExportCustom = ocf != null;
|
||
var DateCustomFields = await ExportCustomFieldSchema(ocf, ObjectTypeName, RavenObjectName);
|
||
|
||
//Step 2: export the objects
|
||
PickListAutoComplete pl = PickListAutoComplete.GetList("**", "loanitem");
|
||
progress.Append("Exporting " + pl.Count.ToString() + " " + ObjectTypeName + "s");
|
||
|
||
foreach (PickListAutoComplete.PickListAutoCompleteInfo i in pl)
|
||
{
|
||
if (!progress.KeepGoing) return;
|
||
List<string> tags = new List<string>();
|
||
AddImportTag(tags);
|
||
|
||
LoanItem c = LoanItem.GetItem(i.ID);
|
||
var ObjectTID = new TypeAndID(RootObjectTypes.LoanItem, c.ID);
|
||
|
||
dynamic d = new JObject();
|
||
d.name = GetUniqueName(c.Name);
|
||
if (IsDuplicatev7v8IdMapItem(c.ID, c.Name, progress)) continue;
|
||
progress.Op(ObjectTypeName + " " + d.name);
|
||
d.active = c.Active;
|
||
d.notes = c.Notes;
|
||
d.serial = c.Serial;
|
||
//Note: workorder id it's loaned out on if any will be set when workorders are exportedlater
|
||
d.rateHour = c.RateHour;
|
||
d.rateHalfDay = c.RateHalfDay;
|
||
d.rateDay = c.RateDay;
|
||
d.rateWeek = c.RateWeek;
|
||
d.rateMonth = c.RateMonth;
|
||
d.rateYear = c.RateYear;
|
||
d.defaultRate = 1;
|
||
|
||
|
||
TagFromv7Guid(c.RegionID, tags);
|
||
SetTags(d, tags);
|
||
|
||
//Custom fields?
|
||
if (ShouldExportCustom)
|
||
d.customFields = CustomFieldData(c, DateCustomFields);
|
||
|
||
var rMainObject = await util.PostAsync("loan-unit", d);
|
||
long RavenId = util.IdFromResponse(rMainObject);
|
||
Addv7v8IdMap(c.ID, RavenId);
|
||
|
||
//Attachments / FILES
|
||
await ExportAttachments(ObjectTID, progress, util.AyaType.LoanUnit);
|
||
|
||
//-----
|
||
bool repost = false;
|
||
d = rMainObject.ObjectResponse["data"];
|
||
// wiki
|
||
if (WikiPage.HasWiki(c.ID))
|
||
{
|
||
|
||
d.wiki = await GetWikiContent(ObjectTID);
|
||
repost = true;
|
||
}
|
||
|
||
//docs
|
||
string NonFileUrls = await ExportDocs(ObjectTID, c.Docs, progress, util.AyaType.LoanUnit);
|
||
if (!string.IsNullOrEmpty(NonFileUrls))
|
||
{
|
||
d.notes = d.notes + "\n-----------------\n" + NonFileUrls;
|
||
repost = true;
|
||
}
|
||
if (repost)
|
||
await util.PutAsync("loan-unit", d);
|
||
//-----
|
||
|
||
//Event log fixup
|
||
await util.EventLog(util.AyaType.LoanUnit, RavenId, await Getv7v8IdMap(c.Creator, RootObjectTypes.User), await Getv7v8IdMap(c.Modifier, RootObjectTypes.User), c.Created, c.Modified);
|
||
|
||
}
|
||
}
|
||
|
||
#endregion LoanItems
|
||
|
||
|
||
|
||
|
||
#region InitInventory
|
||
private async System.Threading.Tasks.Task InitInventory(ProgressForm progress)
|
||
{
|
||
if (!progress.KeepGoing) return;
|
||
if (V7UseInventory)
|
||
progress.Append("Info: v7 Use inventory is Active and will be migrated");
|
||
else
|
||
progress.Append("Info: v7 Use inventory is OFF, not migrating inventory");
|
||
var a = await util.GetAsync("global-biz-setting");
|
||
dynamic d = a.ObjectResponse["data"];
|
||
d.useInventory = V7UseInventory;
|
||
await util.PutAsync("global-biz-setting", d);
|
||
|
||
|
||
}
|
||
#endregion InitInventory
|
||
|
||
|
||
#region Global settings
|
||
//Global / ops / notification settings
|
||
private async System.Threading.Tasks.Task ExportGlobalSettings(ProgressForm progress)
|
||
{
|
||
if (!progress.KeepGoing) return;
|
||
progress.Op("Start Global settings export");
|
||
|
||
|
||
//GlobalBizSettings
|
||
{
|
||
progress.Append("Exporting Global business settings");
|
||
var a = await util.GetAsync("global-biz-setting");
|
||
dynamic d = a.ObjectResponse["data"];
|
||
d.allowScheduleConflicts = true;//was not exposed in v7 so setting to same functionality people are used to for v8
|
||
d.filterCaseSensitive = false;//v8 default, not a v7 setting
|
||
d.taxPartPurchaseId = await Getv7v8IdMapNullOk(AyaBizUtils.GlobalSettings.TaxPartPurchaseID, RootObjectTypes.TaxCode, false, true);//ok if doesn't migrate
|
||
d.taxPartSaleId = await Getv7v8IdMapNullOk(AyaBizUtils.GlobalSettings.TaxPartSaleID, RootObjectTypes.TaxCode, false, true);//ok if doesn't migrate
|
||
d.taxRateSaleId = await Getv7v8IdMapNullOk(AyaBizUtils.GlobalSettings.TaxRateSaleID, RootObjectTypes.TaxCode, false, true);//ok if doesn't migrate
|
||
d.workLaborScheduleDefaultMinutes = AyaBizUtils.GlobalSettings.LaborSchedUserDfltTimeSpan;
|
||
d.workOrderTravelDefaultMinutes = AyaBizUtils.GlobalSettings.TravelDfltTimeSpan;
|
||
d.workOrderCompleteByAge = TimeSpan.FromMinutes(AyaBizUtils.GlobalSettings.WorkorderCloseByAge);
|
||
d.customerAllowCSR = false;
|
||
d.customerAllowViewWO = false;
|
||
d.customerAllowWOWiki = false;
|
||
d.customerAllowUserSettings = false;
|
||
d.customerAllowNotifyServiceImminent = false;
|
||
d.customerAllowNotifyCSRAccepted = false;
|
||
d.customerAllowNotifyCSRRejected = false;
|
||
d.customerAllowNotifyWOCreated = false;
|
||
d.customerAllowNotifyWOCompleted = false;
|
||
await util.PutAsync("global-biz-setting", d);
|
||
progress.Append("ACTION REQUIRED: Confirm 'Administration -> Global settings'");
|
||
}
|
||
//GlobalOpsBackupSettingsController
|
||
{
|
||
progress.Append("Setting default backup settings");
|
||
var a = await util.GetAsync("global-ops-backup-setting");
|
||
DateTime utcNow = DateTime.UtcNow;
|
||
dynamic d = a.ObjectResponse["data"];
|
||
d.backupTime = new DateTime(utcNow.Year, utcNow.Month, utcNow.Day, 23, 50, 0, DateTimeKind.Local).ToUniversalTime();
|
||
d.backupSetsToKeep = 3;
|
||
d.active = false;
|
||
await util.PutAsync("global-ops-backup-setting", d);
|
||
progress.Append("ACTION REQUIRED: Confirm 'Server operations -> Backup' settings and set to Active");
|
||
}
|
||
|
||
//GlobalOpsNotificationSettingsController
|
||
{
|
||
progress.Append("Exporting Global SMTP Notification settings");
|
||
var a = await util.GetAsync("global-ops-notification-setting");
|
||
dynamic d = a.ObjectResponse["data"];
|
||
|
||
d.smtpDeliveryActive = false;//start inactive
|
||
if (AyaBizUtils.GlobalSettings.NotifySMTPHost.Contains(":"))
|
||
{
|
||
//per v7 rules the port number is specified on the host if necessary
|
||
d.smtpServerAddress = AyaBizUtils.GlobalSettings.NotifySMTPHost.Split(':')[0];
|
||
d.smtpServerPort = AyaBizUtils.GlobalSettings.NotifySMTPPort;
|
||
}
|
||
else
|
||
{
|
||
d.smtpServerAddress = AyaBizUtils.GlobalSettings.NotifySMTPHost;
|
||
d.smtpServerPort = 587;//default IETF port 2021
|
||
}
|
||
|
||
d.smtpAccount = AyaBizUtils.GlobalSettings.NotifySMTPAccount;
|
||
d.smtpPassword = AyaBizUtils.GlobalSettings.NotifySMTPPassword;
|
||
d.notifyFromAddress = AyaBizUtils.GlobalSettings.NotifySMTPFrom;
|
||
d.ayanovaServerURL = util.GuessClientUrl.TrimEnd('/');
|
||
d.connectionSecurity = 3;//default is SSLTLS, 1 would be StartTls but we're assuming a modern mail server here
|
||
await util.PutAsync("global-ops-notification-setting", d);
|
||
progress.Append("ACTION REQUIRED: Confirm 'Server Operations -> Notification settings' and set SMTP notification to Active");
|
||
}
|
||
|
||
|
||
}
|
||
#endregion Global settings
|
||
|
||
|
||
#region Parts
|
||
private async System.Threading.Tasks.Task ExportParts(ProgressForm progress)
|
||
{
|
||
ResetUniqueNames();
|
||
if (!progress.KeepGoing) return;
|
||
progress.Op("Start Parts export");
|
||
progress.SubOp("");
|
||
var ObjectTypeName = "Part";
|
||
var RavenObjectName = "Part";
|
||
//Step 1: export the CustomFields to FormCustom if applicable so that when doing individual items we can export their custom data too
|
||
var ocf = ObjectHasCustomFieldDataToExport(ObjectTypeName);
|
||
bool ShouldExportCustom = ocf != null;
|
||
var DateCustomFields = await ExportCustomFieldSchema(ocf, ObjectTypeName, RavenObjectName);
|
||
|
||
//Step 2: export the objects
|
||
PartPickList pl = PartPickList.GetAllParts();
|
||
// PickListAutoComplete pl = PickListAutoComplete.GetList("**", "loanitem");
|
||
progress.Append("Exporting " + pl.Count.ToString() + " " + ObjectTypeName + "s");
|
||
|
||
//cache unit of measures
|
||
UnitOfMeasurePickList umpl = UnitOfMeasurePickList.GetList(false);
|
||
|
||
foreach (PartPickList.PartPickListInfo i in pl)
|
||
{
|
||
if (!progress.KeepGoing) return;
|
||
List<string> tags = new List<string>();
|
||
AddImportTag(tags);
|
||
|
||
Part c = Part.GetItem(i.ID);
|
||
var ObjectTID = new TypeAndID(RootObjectTypes.Part, c.ID);
|
||
|
||
dynamic d = new JObject();
|
||
if (IsDuplicatev7v8IdMapItem(c.ID, c.PartNumber, progress)) continue;
|
||
if (PartNumberNameCombined)
|
||
{
|
||
string newName = string.Empty;
|
||
if (string.IsNullOrWhiteSpace(c.Name))
|
||
{
|
||
d.name = GetUniqueName(c.PartNumber);
|
||
}
|
||
else
|
||
{
|
||
d.name = GetUniqueName(c.PartNumber + " " + c.Name);
|
||
}
|
||
|
||
}
|
||
else
|
||
{
|
||
d.name = GetUniqueName(c.PartNumber);
|
||
d.description = c.Name;
|
||
|
||
}
|
||
|
||
progress.SubOp("");
|
||
progress.Op(ObjectTypeName + " " + d.partNumber + " " + d.name);
|
||
d.manufacturerId = await Getv7v8IdMapNullOk(c.ManufacturerID, RootObjectTypes.Vendor);
|
||
d.manufacturerNumber = c.ManufacturerNumber;
|
||
d.wholeSalerId = await Getv7v8IdMapNullOk(c.WholesalerID, RootObjectTypes.Vendor);
|
||
d.wholeSalerNumber = c.WholesalerNumber;
|
||
d.alternativeWholeSalerId = await Getv7v8IdMapNullOk(c.AlternativeWholesalerID, RootObjectTypes.Vendor);
|
||
d.alternativeWholeSalerNumber = c.AlternativeWholesalerNumber;
|
||
d.cost = c.Cost;
|
||
d.retail = c.Retail;
|
||
d.uPC = c.UPC;
|
||
if (c.UnitOfMeasureID != Guid.Empty && umpl.Contains(c.UnitOfMeasureID))
|
||
{
|
||
d.unitOfMeasure = umpl[c.UnitOfMeasureID].Name;
|
||
}
|
||
d.active = c.Active;
|
||
d.notes = c.Notes;
|
||
TagFromv7Guid(c.PartCategoryID, tags);
|
||
|
||
SetTags(d, tags);
|
||
|
||
//Custom fields?
|
||
if (ShouldExportCustom)
|
||
d.customFields = CustomFieldData(c, DateCustomFields);
|
||
|
||
var rMainObject = await util.PostAsync("part", d);
|
||
long RavenId = util.IdFromResponse(rMainObject);
|
||
Addv7v8IdMap(c.ID, RavenId);
|
||
|
||
//Attachments / FILES
|
||
await ExportAttachments(ObjectTID, progress, util.AyaType.Part);
|
||
|
||
//-----
|
||
bool repost = false;
|
||
d = rMainObject.ObjectResponse["data"];
|
||
// wiki
|
||
if (WikiPage.HasWiki(c.ID))
|
||
{
|
||
|
||
d.wiki = await GetWikiContent(ObjectTID);
|
||
repost = true;
|
||
}
|
||
|
||
//docs
|
||
string NonFileUrls = await ExportDocs(ObjectTID, c.Docs, progress, util.AyaType.Part);
|
||
if (!string.IsNullOrEmpty(NonFileUrls))
|
||
{
|
||
d.notes = d.notes + "\n-----------------\n" + NonFileUrls;
|
||
repost = true;
|
||
}
|
||
if (repost)
|
||
await util.PutAsync("part", d);
|
||
//-----
|
||
|
||
//Event log fixup
|
||
await util.EventLog(util.AyaType.Part, RavenId, await Getv7v8IdMap(c.Creator, RootObjectTypes.User), await Getv7v8IdMap(c.Modifier, RootObjectTypes.User), c.Created, c.Modified);
|
||
|
||
//INVENTORY?
|
||
if (V7UseInventory)
|
||
{
|
||
progress.SubOp("Creating temp opening inventory for part in all warehouses");
|
||
foreach (long l in Allv8WarehouseIds)
|
||
{
|
||
//create opening initial inventory just so migrate can proceed without v8 balking
|
||
dynamic di = new JObject();
|
||
di.description = "v8 migrate temporary initial inventory to allow migration";
|
||
di.partId = RavenId;
|
||
di.partWarehouseId = l;
|
||
di.quantity = 1000000000m;//one billion should cover it
|
||
await util.PostAsync("part-inventory", di.ToString());
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
private long mSubstitutePartId = 0;
|
||
private async System.Threading.Tasks.Task<long> GetSubstitutePartId()
|
||
{
|
||
if (mSubstitutePartId != 0) return mSubstitutePartId;
|
||
//needs to be made
|
||
//UntrackedV7PartId
|
||
//create a part just to hold untracked parts from v7 which are partid guid.empty on workorderitempart records which was allowed in v7 but not in v8
|
||
dynamic dx = new JObject();
|
||
|
||
dx.name = MissingDataNamePrefix;
|
||
dx.partNumber = GetUniqueName(MissingDataNamePrefix);
|
||
dx.cost = 0;
|
||
dx.retail = 0;
|
||
dx.uPC = string.Empty;
|
||
dx.unitOfMeasure = "each";
|
||
dx.active = false;
|
||
dx.notes = "This part created by v8 migrate plugin so work order item part records with no (or no valid) selected part can be migrated to v8\r\nv7 allowed work order item part entry without a corresponding Part record but v8 does not\r\nThis part stands in for those missing parts from v7\r\n";
|
||
List<string> dxtags = new List<string>();
|
||
AddImportTag(dxtags);
|
||
SetTags(dx, dxtags);
|
||
mSubstitutePartId = util.IdFromResponse(await util.PostAsync("part", dx.ToString()));
|
||
|
||
if (V7UseInventory)
|
||
{
|
||
foreach (long l in Allv8WarehouseIds)
|
||
{
|
||
//create opening initial inventory just so migrate can proceed without v8 balking
|
||
dynamic di = new JObject();
|
||
di.description = "v8 migrate temporary initial inventory to allow migration";
|
||
di.partId = mSubstitutePartId;
|
||
di.partWarehouseId = l;
|
||
di.quantity = 1000000000m;//one billion should cover it
|
||
await util.PostAsync("part-inventory", di.ToString());
|
||
}
|
||
}
|
||
return mSubstitutePartId;
|
||
}
|
||
|
||
|
||
|
||
#endregion Parts
|
||
|
||
|
||
#region Part assemblies
|
||
private async System.Threading.Tasks.Task ExportPartAssemblies(ProgressForm progress)
|
||
{
|
||
ResetUniqueNames();
|
||
if (!progress.KeepGoing) return;
|
||
progress.Op("Start part assemblies export");
|
||
progress.SubOp("");
|
||
var ObjectTypeName = "Part Assembly";
|
||
PartAssemblies pa = PartAssemblies.GetItems();
|
||
|
||
progress.Append("Exporting " + pa.Count.ToString() + " " + ObjectTypeName + " part records");
|
||
foreach (PartAssembly p in pa)
|
||
{
|
||
if (!progress.KeepGoing) return;
|
||
string crit = "<?xml version=\"1.0\" encoding=\"utf-16\" standalone=\"yes\"?><GRIDCRITERIA> <COLUMNITEM CM=\"aPartAssembly.aName\" UI=\"LT_O_PartAssembly\" PIN=\"0\" WIDTH=\"120\" SORT=\"ASC\" /> <COLUMNITEM CM=\"aPart.aPartNumber\" UI=\"LT_Part_Label_PartNumber\" PIN=\"0\" WIDTH=\"111\" /> <COLUMNITEM CM=\"aPart.aName\" UI=\"LT_O_Part\" PIN=\"0\" WIDTH=\"135\" /> <COLUMNITEM CM=\"aManufacturers.aName\" UI=\"LT_Part_Label_ManufacturerID\" PIN=\"0\" WIDTH=\"114\" /> "
|
||
+ "<WHEREITEMGROUP GROUPLOGICALOPERATOR=\"And\" UI=\"LT_O_PartAssembly\">"
|
||
+ "<WHEREITEM COMPAREOPERATOR=\"Equals\" CM=\"aPartAssembly.aID\" UICOMPAREVALUE=\"\" TYPE=\"System.Guid\" COMPAREVALUE=\"{" + p.ID.ToString().ToUpperInvariant() + "}\" /> "
|
||
+ "</WHEREITEMGROUP></GRIDCRITERIA>";
|
||
PartList pl = PartList.GetList(crit);
|
||
dynamic d = new JObject();
|
||
d.name = GetUniqueName(p.Name);
|
||
progress.Op(ObjectTypeName + " " + d.name);
|
||
d.active = p.Active;
|
||
d.notes = p.Description;
|
||
JArray dItems = new JArray();
|
||
foreach (PartList.PartListInfo i in pl)
|
||
{
|
||
dynamic ditem = new JObject();
|
||
ditem.partId = await Getv7v8IdMap(i.LT_O_Part.Value, RootObjectTypes.Part);
|
||
ditem.partAssemblyId = 0;
|
||
ditem.quantity = 1;
|
||
dItems.Add(ditem);
|
||
}
|
||
d.items = dItems;
|
||
|
||
List<string> tags = new List<string>();
|
||
AddImportTag(tags);
|
||
SetTags(d, tags);
|
||
var rMainObject = await util.PostAsync("part-assembly", d);
|
||
long RavenId = util.IdFromResponse(rMainObject);
|
||
//Event log fixup
|
||
long lCreator = await Getv7v8IdMap(p.Creator, RootObjectTypes.User);
|
||
long lModifier = await Getv7v8IdMap(p.Modifier, RootObjectTypes.User);
|
||
await util.EventLog(util.AyaType.Project, RavenId, lCreator, lModifier, p.Created, p.Modified);
|
||
|
||
}
|
||
}
|
||
#endregion part assemblies
|
||
|
||
#region Warehouses
|
||
private async System.Threading.Tasks.Task ExportWarehouses(ProgressForm progress)
|
||
{
|
||
ResetUniqueNames();
|
||
if (!progress.KeepGoing) return;
|
||
progress.Op("Start Part Warehouse export");
|
||
progress.SubOp("");
|
||
var ObjectTypeName = "Part Warehouse";
|
||
PartWarehouses pl = PartWarehouses.GetItems(false);
|
||
progress.Append("Exporting " + pl.Count.ToString() + " " + ObjectTypeName + "s");
|
||
foreach (PartWarehouse i in pl)
|
||
{
|
||
if (!progress.KeepGoing) return;
|
||
if (i.ID == PartWarehouse.DefaultWarehouseID)
|
||
{
|
||
Allv8WarehouseIds.Add(1);
|
||
Addv7v8IdMap(i.ID, 1);
|
||
{
|
||
//update notes/name
|
||
var awhs = await util.GetAsync("part-warehouse/1");
|
||
dynamic dwhs = awhs.ObjectResponse["data"];
|
||
dwhs.notes = i.Description;
|
||
dwhs.name = GetUniqueName(i.Name);
|
||
await util.PutAsync("part-warehouse", dwhs);
|
||
}
|
||
continue;
|
||
}
|
||
List<string> tags = new List<string>();
|
||
AddImportTag(tags);
|
||
dynamic d = new JObject();
|
||
d.name = GetUniqueName(i.Name);
|
||
if (IsDuplicatev7v8IdMapItem(i.ID, i.Name, progress)) continue;
|
||
progress.Op(ObjectTypeName + " " + d.name);
|
||
d.active = i.Active;
|
||
d.notes = i.Description;
|
||
TagFromv7Guid(i.RegionID, tags);
|
||
SetTags(d, tags);
|
||
var rMainObject = await util.PostAsync("part-warehouse", d);
|
||
long RavenId = util.IdFromResponse(rMainObject);
|
||
Addv7v8IdMap(i.ID, RavenId);
|
||
Allv8WarehouseIds.Add(RavenId);
|
||
await util.EventLog(util.AyaType.PartWarehouse, RavenId, await Getv7v8IdMap(i.Creator, RootObjectTypes.User), await Getv7v8IdMap(i.Modifier, RootObjectTypes.User), i.Created, i.Modified);
|
||
}
|
||
}
|
||
#endregion warehouses
|
||
|
||
#region Projects
|
||
private async System.Threading.Tasks.Task ExportProjects(ProgressForm progress)
|
||
{
|
||
ResetUniqueNames();
|
||
if (!progress.KeepGoing) return;
|
||
progress.Op("Start Projects export");
|
||
progress.SubOp("");
|
||
var ObjectTypeName = "Project";
|
||
//Step 1: export the CustomFields to FormCustom if applicable so that when doing individual items we can export their custom data too
|
||
var ocf = ObjectHasCustomFieldDataToExport(ObjectTypeName);
|
||
bool ShouldExportCustom = ocf != null;
|
||
var DateCustomFields = await ExportCustomFieldSchema(ocf, ObjectTypeName, ObjectTypeName);
|
||
|
||
//Step 2: export the objects
|
||
PickListAutoComplete pl = PickListAutoComplete.GetList("**", "project");
|
||
progress.Append("Exporting " + pl.Count.ToString() + " " + ObjectTypeName + "s");
|
||
|
||
foreach (PickListAutoComplete.PickListAutoCompleteInfo i in pl)
|
||
{
|
||
if (!progress.KeepGoing) return;
|
||
List<string> tags = new List<string>();
|
||
AddImportTag(tags);
|
||
|
||
Project c = Project.GetItem(i.ID);
|
||
var ObjectTID = new TypeAndID(RootObjectTypes.Project, c.ID);
|
||
|
||
dynamic d = new JObject();
|
||
d.name = GetUniqueName(c.Name);
|
||
if (IsDuplicatev7v8IdMapItem(c.ID, c.Name, progress)) continue;
|
||
progress.Op(ObjectTypeName + " " + d.name);
|
||
d.active = c.Active;
|
||
d.notes = c.Notes;
|
||
d.dateStarted = util.DateToV8(c.DateStarted, true);
|
||
d.dateCompleted = util.DateToV8(c.DateCompleted, false);
|
||
d.accountNumber = c.AccountNumber;
|
||
d.projectOverseerId = await Getv7v8IdMapNullOk(c.ProjectOverseerID, RootObjectTypes.User);
|
||
TagFromv7Guid(c.RegionID, tags);
|
||
SetTags(d, tags);
|
||
//Custom fields?
|
||
if (ShouldExportCustom)
|
||
d.customFields = CustomFieldData(c, DateCustomFields);
|
||
var rMainObject = await util.PostAsync("project", d);
|
||
long RavenId = util.IdFromResponse(rMainObject);
|
||
Addv7v8IdMap(c.ID, RavenId);
|
||
//Attachments / FILES
|
||
await ExportAttachments(ObjectTID, progress, util.AyaType.Project);
|
||
//-----
|
||
bool repost = false;
|
||
d = rMainObject.ObjectResponse["data"];
|
||
// wiki
|
||
if (WikiPage.HasWiki(c.ID))
|
||
{
|
||
|
||
d.wiki = await GetWikiContent(ObjectTID);
|
||
repost = true;
|
||
}
|
||
//docs
|
||
string NonFileUrls = await ExportDocs(ObjectTID, c.Docs, progress, util.AyaType.Project);
|
||
if (!string.IsNullOrEmpty(NonFileUrls))
|
||
{
|
||
d.notes = d.notes + "\n-----------------\n" + NonFileUrls;
|
||
repost = true;
|
||
}
|
||
if (repost)
|
||
await util.PutAsync("project", d);
|
||
//-----
|
||
|
||
//Event log fixup
|
||
await util.EventLog(util.AyaType.Project, RavenId, await Getv7v8IdMap(c.Creator, RootObjectTypes.User), await Getv7v8IdMap(c.Modifier, RootObjectTypes.User), c.Created, c.Modified);
|
||
|
||
}
|
||
}
|
||
#endregion Projects
|
||
|
||
#region PurchaseOrders
|
||
//map poitems to workorderitempartrequests in future
|
||
public class poitemtowoitempartrequest
|
||
{
|
||
public Guid v7woitempartrequestid { get; set; }
|
||
public Guid v7poid { get; set; }
|
||
//will use new v8 vendorpartnumber field to temporarily store the v7woitempartrequest
|
||
//check list of this item on woitempartrequest creation, if it matches then need to fixup that po during wo export
|
||
}
|
||
List<poitemtowoitempartrequest> PoItemPartRequestMap = new List<poitemtowoitempartrequest>();
|
||
int _highestPurchaseOrderNumberExported = 0;
|
||
private async System.Threading.Tasks.Task ExportPurchaseOrders(ProgressForm progress)
|
||
{
|
||
if (!progress.KeepGoing) return;
|
||
if (!V7UseInventory) return;
|
||
var ObjectTypeName = "PurchaseOrder";
|
||
progress.Op("Start Purchase orders export");
|
||
progress.SubOp("");
|
||
//Step 1: export the CustomFields to FormCustom if applicable so that when doing individual items we can export their custom data too
|
||
var ocf = ObjectHasCustomFieldDataToExport(ObjectTypeName);
|
||
bool ShouldExportCustom = ocf != null;
|
||
var DateCustomFields = await ExportCustomFieldSchema(ocf, ObjectTypeName, ObjectTypeName);
|
||
|
||
//Step 2: export the objects
|
||
//get pos in order by created date oldest to newest to preserve order
|
||
//PurchaseOrderList pl = PurchaseOrderList.GetListByCriteria("<?xml version=\"1.0\" encoding=\"utf-16\" standalone=\"yes\"?><GRIDCRITERIA> <COLUMNITEM CM=\"aPurchaseOrder.aCreated\" UI=\"LT_Common_Label_Created\" PIN=\"0\" WIDTH=\"146\" SORT=\"ASC\" /> <COLUMNITEM CM=\"aPurchaseOrder.aPONumber\" UI=\"LT_PurchaseOrder_Label_PONumber\" PIN=\"0\" WIDTH=\"107\" /></GRIDCRITERIA>");
|
||
PurchaseOrderList pl = PurchaseOrderList.GetListByCriteria("<?xml version=\"1.0\" encoding=\"utf-16\" standalone=\"yes\"?><GRIDCRITERIA> <COLUMNITEM CM=\"aPurchaseOrder.aCreated\" UI=\"LT_Common_Label_Created\" PIN=\"0\" WIDTH=\"146\" SORT=\"ASC\" /></GRIDCRITERIA>");
|
||
progress.Append("Exporting " + pl.Count.ToString() + " " + ObjectTypeName + "s");
|
||
|
||
foreach (PurchaseOrderList.PurchaseOrderListInfo i in pl)
|
||
{
|
||
if (!progress.KeepGoing) return;
|
||
List<string> tags = new List<string>();
|
||
AddImportTag(tags);
|
||
PurchaseOrder c = PurchaseOrder.GetItem(i.LT_PurchaseOrder_Label_PONumber.Value);
|
||
var ObjectTID = new TypeAndID(RootObjectTypes.PurchaseOrder, c.ID);
|
||
dynamic d = new JObject();
|
||
d.serial = c.PONumber;
|
||
if (IsDuplicatev7v8IdMapItem(c.ID, i.LT_PurchaseOrder_Label_PONumber.Display, progress)) continue;
|
||
|
||
progress.Op(ObjectTypeName + " " + d.serial);
|
||
if (c.PONumber > _highestPurchaseOrderNumberExported)
|
||
_highestPurchaseOrderNumberExported = c.PONumber;
|
||
d.notes = c.Notes;
|
||
d.vendorMemo = c.VendorMemo;
|
||
d.dropShipToCustomerId = await Getv7v8IdMapNullOk(c.DropShipToClientID, RootObjectTypes.Client);
|
||
d.referenceNumber = c.ReferenceNumber;
|
||
d.vendorId = await Getv7v8IdMap(c.VendorID, RootObjectTypes.Vendor);
|
||
d.orderedDate = util.DateToV8(c.OrderedDate);
|
||
d.expectedReceiveDate = util.DateToV8(c.ExpectedReceiveDate);
|
||
d.status = (int)c.Status;//same enum id's so this is kosher
|
||
d.projectId = await Getv7v8IdMapNullOk(c.ProjectID, RootObjectTypes.Project);
|
||
|
||
StringBuilder sbText1 = new StringBuilder();
|
||
StringBuilder sbText2 = new StringBuilder();
|
||
|
||
//get receipts for this PO
|
||
PurchaseOrderReceiptListDetailed prl = PurchaseOrderReceiptListDetailed.GetList(
|
||
"<?xml version=\"1.0\" encoding=\"utf-16\" standalone=\"yes\"?><GRIDCRITERIA><COLUMNITEM CM=\"aPurchaseOrderReceipt.aReceivedDate\" UI=\"LT_PurchaseOrderReceipt_Label_ReceivedDate\" PIN=\"0\" WIDTH=\"121\" SORT=\"ASC\" /><COLUMNITEM CM=\"aPurchaseOrder.aPONumber\" UI=\"LT_PurchaseOrder_Label_PONumber\" PIN=\"0\" WIDTH=\"105\" /><COLUMNITEM CM=\"grid\" UI=\"LT_O_PurchaseOrderReceipt\" PIN=\"0\" WIDTH=\"155\" /><WHEREITEMGROUP GROUPLOGICALOPERATOR=\"And\" UI=\"LT_PurchaseOrder_Label_PONumber\"><WHEREITEM COMPAREOPERATOR=\"Equals\" CM=\"aPurchaseOrder.aID\" UICOMPAREVALUE=\"\" TYPE=\"System.Guid\" COMPAREVALUE=\"{" +
|
||
c.ID.ToString().ToUpper()
|
||
+ "}\" /></WHEREITEMGROUP></GRIDCRITERIA>");
|
||
|
||
//compile all actual receipts into collection for later recombination
|
||
//above list is missing crucial bit which is which exact poitemid they are received against so need actual full record
|
||
//even though list has almost everything else needed
|
||
List<PurchaseOrderReceipt> receipts = new List<PurchaseOrderReceipt>();
|
||
foreach (PurchaseOrderReceiptListDetailed.PurchaseOrderReceiptListDetailedInfo prli in prl)
|
||
{
|
||
receipts.Add(PurchaseOrderReceipt.GetItem(prli.LT_O_PurchaseOrderReceipt.Value));
|
||
}
|
||
|
||
dynamic dItems = new JArray();
|
||
|
||
foreach (PurchaseOrderItem pi in c.OrderItems)
|
||
{
|
||
//here combine potiem and poreceipts into singular items for v8
|
||
//also compile all text1,text2 to put into header for v8 later
|
||
|
||
//iterate receipt list find id's fetch actual receipts which contain exactly which poitem they were received against
|
||
//also, keep track of required woitempartrequestid's for later so can fixup when the time comes
|
||
|
||
dynamic dItem = new JObject();
|
||
dItem.purchaseOrderId = 0;
|
||
dItem.partId = await Getv7v8IdMap(pi.PartID, RootObjectTypes.Part);
|
||
dItem.partWarehouseId = await Getv7v8IdMap(pi.PartWarehouseID, RootObjectTypes.PartWarehouse);
|
||
dItem.quantityOrdered = pi.QuantityOrdered;
|
||
dItem.quantityReceived = pi.QuantityReceived;
|
||
//dItem.serials = string.Empty;
|
||
StringBuilder sbSerials = new StringBuilder();
|
||
dItem.purchaseOrderCost = pi.PurchaseOrderCost;
|
||
dItem.partRequestedById = await Getv7v8IdMapNullOk(pi.PartRequestedByID, RootObjectTypes.User);
|
||
dItem.purchaseTaxCodeId = await Getv7v8IdMapNullOk(pi.PurchaseTaxCodeID, RootObjectTypes.TaxCode);
|
||
if (pi.WorkorderItemPartRequestID != Guid.Empty)
|
||
{
|
||
dItem.vendorPartNumber = pi.WorkorderItemPartRequestID.ToString();//temp holder for guid, not used in v7 so available
|
||
PoItemPartRequestMap.Add(new poitemtowoitempartrequest() { v7woitempartrequestid = pi.WorkorderItemPartRequestID, v7poid = c.ID });
|
||
}
|
||
|
||
|
||
|
||
foreach (PurchaseOrderReceipt r in receipts)
|
||
{
|
||
if (!string.IsNullOrWhiteSpace(r.Text1))
|
||
sbText1.Append(r.Text1 + ",");
|
||
if (!string.IsNullOrWhiteSpace(r.Text2))
|
||
sbText2.Append(r.Text2 + ",");
|
||
|
||
foreach (PurchaseOrderReceiptItem ri in r.Items)
|
||
{
|
||
if (ri.PurchaseOrderItemID == pi.ID)
|
||
{
|
||
//Note: in v7 must receive to same warehouse as ordered, no option otherwise so no need
|
||
//to worry about that here
|
||
//No, use the poitem value above, this one can be wonky dItem.quantityReceived += ri.QuantityReceived;
|
||
dItem.receivedCost = ri.ReceiptCost;//yes, last receipt wins for cost
|
||
foreach (PartSerial sn in ri.SerialNumbers)
|
||
sbSerials.Append(sn.SerialNumber + ", ");
|
||
dItem.receivedDate = util.DateToV8(ri.Created);//yes, last receipt wins for the official received date, no options otherwise
|
||
|
||
}
|
||
}//end of poreceiptitem loop
|
||
}//end of poreceipt loop
|
||
if (sbSerials.Length > 0)
|
||
dItem.serials = sbSerials.ToString().Trim().TrimEnd(',');
|
||
dItems.Add(dItem);
|
||
}//end of poitem loop
|
||
|
||
if (sbText1.Length > 0)
|
||
d.text1 = sbText1.ToString().TrimEnd(',');
|
||
if (sbText2.Length > 0)
|
||
d.text2 = sbText2.ToString().TrimEnd(',');
|
||
|
||
d.items = dItems;
|
||
|
||
// Tagit(c.RegionID, tags);
|
||
SetTags(d, tags);
|
||
|
||
//Custom fields?
|
||
if (ShouldExportCustom)
|
||
d.customFields = CustomFieldData(c, DateCustomFields);
|
||
|
||
var rMainObject = await util.PostAsync("purchase-order", d);
|
||
long RavenId = util.IdFromResponse(rMainObject);
|
||
Addv7v8IdMap(c.ID, RavenId);
|
||
|
||
//Attachments / FILES
|
||
await ExportAttachments(ObjectTID, progress, util.AyaType.PurchaseOrder);
|
||
|
||
//-----
|
||
bool repost = false;
|
||
d = rMainObject.ObjectResponse["data"];
|
||
// wiki
|
||
if (WikiPage.HasWiki(c.ID))
|
||
{
|
||
|
||
d.wiki = await GetWikiContent(ObjectTID);
|
||
repost = true;
|
||
}
|
||
|
||
|
||
if (repost)
|
||
await util.PutAsync("purchase-order", d);
|
||
//-----
|
||
|
||
//Event log fixup
|
||
await util.EventLog(util.AyaType.PurchaseOrder, RavenId, await Getv7v8IdMap(c.Creator, RootObjectTypes.User), await Getv7v8IdMap(c.Modifier, RootObjectTypes.User), c.Created, c.Modified);
|
||
|
||
}//end of PO loop
|
||
|
||
//set new seed number for work orders
|
||
_highestPurchaseOrderNumberExported += 1;
|
||
{
|
||
await util.PutAsync("global-biz-setting/seeds/26/" + _highestPurchaseOrderNumberExported.ToString());
|
||
}
|
||
}
|
||
#endregion PurchaseOrders
|
||
|
||
#region Inventory adjustments
|
||
private async System.Threading.Tasks.Task ExportInventoryAdjustments(ProgressForm progress)
|
||
{
|
||
if (!progress.KeepGoing) return;
|
||
|
||
|
||
progress.Op("Start inventory adjustments");
|
||
progress.SubOp("");
|
||
|
||
//Step 2: export the objects
|
||
PartInventoryAdjustmentList pil = PartInventoryAdjustmentList.GetList(
|
||
"<?xml version=\"1.0\" encoding=\"utf-16\" standalone=\"yes\"?><GRIDCRITERIA> <COLUMNITEM CM=\"aPartInventoryAdjustment.aDateAdjusted\" UI=\"LT_PartInventoryAdjustment_Label_DateAdjusted\" PIN=\"0\" WIDTH=\"118\" SORT=\"ASC\" /> <COLUMNITEM CM=\"aPartInventoryAdjustment.AADJUSTMENTNUMBER\" UI=\"LT_PartInventoryAdjustment_Label_AdjustmentNumber\" PIN=\"0\" WIDTH=\"87\" /></GRIDCRITERIA>");
|
||
progress.Append("Exporting " + pil.Count.ToString() + " inventory adjustments");
|
||
foreach (PartInventoryAdjustmentList.PartInventoryAdjustmentListInfo i in pil)
|
||
{
|
||
if (!progress.KeepGoing) return;
|
||
|
||
PartInventoryAdjustment c = PartInventoryAdjustment.GetItem(i.LT_PartInventoryAdjustment_Label_AdjustmentNumber.Value);
|
||
foreach (PartInventoryAdjustmentItem pi in c.Items)
|
||
{
|
||
progress.Op("Adjustment " + c.AdjustmentNumber.ToString());
|
||
dynamic d = new JObject();
|
||
d.description = "v7Adjustment " + c.AdjustmentNumber.ToString() + " " + c.ReasonForAdjustment + " on " + c.DateAdjusted.ToString();
|
||
d.partId = await Getv7v8IdMap(pi.PartID, RootObjectTypes.Part);
|
||
d.partWarehouseId = await Getv7v8IdMap(pi.PartWarehouseID, RootObjectTypes.PartWarehouse);
|
||
d.quantity = pi.QuantityAdjustment;
|
||
await util.PostAsync("part-inventory", d);
|
||
|
||
//post to part-inventory route
|
||
|
||
//now, separately, do serial numbers if any
|
||
if (pi.SerialNumbers.Count > 0)
|
||
{
|
||
//get the part serial record
|
||
var a = await util.GetAsync("part/serials/" + d.partId.ToString());
|
||
dynamic f = a.ObjectResponse["data"];
|
||
//turn the v8 serials into a string list
|
||
var v = (JArray)f;
|
||
List<string> v8Serials = new List<string>();
|
||
foreach (JValue j in v)
|
||
v8Serials.Add(j.Value<string>());
|
||
|
||
if (pi.QuantityAdjustment < 0)
|
||
{
|
||
//remove serials
|
||
foreach (PartSerial ps in pi.SerialNumbers)
|
||
v8Serials.Remove(ps.SerialNumber);
|
||
}
|
||
else
|
||
{
|
||
//append serials
|
||
foreach (PartSerial ps in pi.SerialNumbers)
|
||
v8Serials.Add(ps.SerialNumber);
|
||
}
|
||
|
||
foreach (PartSerial ps in pi.SerialNumbers)
|
||
{
|
||
dynamic dItem = new JObject();
|
||
dItem = ps.SerialNumber;
|
||
f.Add(dItem);
|
||
}
|
||
await util.PutAsync("part/serials/" + d.partId.ToString(), new JArray(v8Serials));
|
||
}
|
||
}
|
||
|
||
|
||
}//end of inventory item loop
|
||
}
|
||
#endregion inventory adjustments
|
||
|
||
#region Synchronize serials
|
||
private async System.Threading.Tasks.Task SynchronizeSerials(ProgressForm progress)
|
||
{
|
||
if (!progress.KeepGoing) return;
|
||
|
||
progress.Append("Synchronizing serial numbers");
|
||
progress.Op("Start synch serials");
|
||
progress.SubOp("");
|
||
|
||
//Step 2: export the objects
|
||
PartInventoryList pl = PartInventoryList.GetList(
|
||
"<?xml version=\"1.0\" encoding=\"utf-16\" standalone=\"yes\"?><GRIDCRITERIA> <COLUMNITEM CM=\"aPartWarehouse.aName\" UI=\"LT_O_PartWarehouse\" PIN=\"0\" WIDTH=\"144\" SORT=\"ASC\" /> <COLUMNITEM CM=\"aPart.aPartNumber\" UI=\"LT_O_Part\" PIN=\"0\" WIDTH=\"172\" /> <COLUMNITEM CM=\"aPartByWarehouseInventory.aQuantityOnHand\" UI=\"LT_PartByWarehouseInventory_Label_QuantityOnHand\" PIN=\"0\" WIDTH=\"81\" /> <COLUMNITEM CM=\"aPartByWarehouseInventory.aMinStockLevel\" UI=\"LT_PartByWarehouseInventory_Label_MinStockLevel\" PIN=\"0\" WIDTH=\"76\" /> <COLUMNITEM CM=\"AREORDERQUANTITY\" UI=\"LT_PartByWarehouseInventory_Label_ReorderQuantity\" PIN=\"0\" WIDTH=\"100\" /> <COLUMNITEM CM=\"aPartByWarehouseInventory.aQuantityOnOrder\" UI=\"LT_PartByWarehouseInventory_Label_QuantityOnOrder\" PIN=\"0\" WIDTH=\"179\" /> <COLUMNITEM CM=\"AVENDWHOLE.ANAME\" UI=\"LT_Part_Label_WholesalerID\" PIN=\"0\" WIDTH=\"104\" /> <COLUMNITEM CM=\"aPartCategory.aName\" UI=\"LT_O_PartCategory\" PIN=\"0\" WIDTH=\"139\" /> <COLUMNITEM CM=\"aPart.aActive\" UI=\"LT_Part_Label_Active\" PIN=\"0\" WIDTH=\"78\" /> <COLUMNITEM CM=\"aPart.aRetail\" UI=\"LT_Part_Label_Retail\" PIN=\"0\" WIDTH=\"79\" /> <COLUMNITEM CM=\"aPart.aCost\" UI=\"LT_Part_Label_Cost\" PIN=\"0\" WIDTH=\"79\" /></GRIDCRITERIA>");
|
||
|
||
foreach (PartInventoryList.PartInventoryListInfo i in pl)
|
||
{
|
||
if (!progress.KeepGoing) return;
|
||
//if no serial then continue to the next one until there is
|
||
if (i.LT_PartSerial_Label_List.Count == 0) continue;
|
||
long partNumber = await Getv7v8IdMap(i.LT_O_Part.Value, RootObjectTypes.Part, false, false);
|
||
if (partNumber == 0)
|
||
{
|
||
progress.Append("Part inventory part missing; skipping serial number synchronization: PartID " + i.LT_O_Part.Value.ToString());
|
||
continue;
|
||
}
|
||
//get the part serial record
|
||
var a = await util.GetAsync("part/serials/" + partNumber.ToString());
|
||
dynamic d = a.ObjectResponse["data"];
|
||
var v = (JArray)d;
|
||
bool hasUpdate = false;
|
||
|
||
foreach (PartSerialPickList.PartSerialPickListInfo si in i.LT_PartSerial_Label_List)
|
||
{
|
||
bool notFound = true;
|
||
foreach (JValue j in v)
|
||
{
|
||
if (j.Value<string>() == si.SerialNumber)
|
||
{
|
||
notFound = false;
|
||
continue;
|
||
}
|
||
}
|
||
if (notFound)
|
||
{
|
||
hasUpdate = true;
|
||
dynamic dItem = new JObject();
|
||
dItem = si.SerialNumber;
|
||
d.Add(dItem);
|
||
}
|
||
}
|
||
if (hasUpdate)
|
||
await util.PutAsync("part/serials/" + partNumber, d);
|
||
}//end of inventory item loop
|
||
}
|
||
#endregion Synchronize serials
|
||
|
||
#region Task groups
|
||
private async System.Threading.Tasks.Task ExportTaskGroups(ProgressForm progress)
|
||
{
|
||
ResetUniqueNames();
|
||
if (!progress.KeepGoing) return;
|
||
progress.Op("Start Task Groups export");
|
||
progress.SubOp("");
|
||
var ObjectTypeName = "TaskGroup";
|
||
|
||
|
||
//Step 2: export the objects
|
||
PickListAutoComplete pl = PickListAutoComplete.GetList("**", "taskgroup");
|
||
progress.Append("Exporting " + pl.Count.ToString() + " " + ObjectTypeName + "s");
|
||
//task picklist used over and over
|
||
var AllTasks = TaskPickList.GetList();
|
||
foreach (PickListAutoComplete.PickListAutoCompleteInfo i in pl)
|
||
{
|
||
if (!progress.KeepGoing) return;
|
||
|
||
TaskGroup c = TaskGroup.GetItem(i.ID);
|
||
var ObjectTID = new TypeAndID(RootObjectTypes.TaskGroup, c.ID);
|
||
dynamic d = new JObject();
|
||
d.name = GetUniqueName(c.Name);
|
||
if (IsDuplicatev7v8IdMapItem(c.ID, c.Name, progress)) continue;
|
||
progress.Op(ObjectTypeName + " " + d.name);
|
||
d.active = c.Active;
|
||
d.notes = c.Description;
|
||
dynamic dTasks = new JArray();
|
||
int sequence = 0;
|
||
foreach (TaskGroupTask t in c.Tasks)
|
||
{
|
||
dynamic dtask = new JObject();
|
||
dtask.id = 0;
|
||
dtask.taskGroupId = 0;
|
||
dtask.sequence = ++sequence;
|
||
dtask.task = AllTasks[t.TaskID].Name;
|
||
dTasks.Add(dtask);
|
||
}
|
||
d.items = dTasks;
|
||
var rMainObject = await util.PostAsync("task-group", d);
|
||
long RavenId = util.IdFromResponse(rMainObject);
|
||
Addv7v8IdMap(c.ID, RavenId);
|
||
await util.EventLog(util.AyaType.Project, RavenId, await Getv7v8IdMap(c.Creator, RootObjectTypes.User), await Getv7v8IdMap(c.Modifier, RootObjectTypes.User), c.Created, c.Modified);
|
||
}
|
||
}
|
||
#endregion Task groups
|
||
|
||
#region Units
|
||
private async System.Threading.Tasks.Task ExportUnits(ProgressForm progress)
|
||
{
|
||
ResetUniqueNames();
|
||
if (!progress.KeepGoing) return;
|
||
|
||
progress.Op("Start Units export");
|
||
progress.SubOp("");
|
||
var ObjectTypeName = "Unit";
|
||
//Step 1: export the CustomFields to FormCustom if applicable so that when doing individual items we can export their custom data too
|
||
var ocf = ObjectHasCustomFieldDataToExport(ObjectTypeName);
|
||
bool ShouldExportCustom = ocf != null;
|
||
var DateCustomFields = await ExportCustomFieldSchema(ocf, ObjectTypeName, ObjectTypeName);
|
||
|
||
//Step 2: export the objects
|
||
UnitPickList pl = UnitPickList.GetListOfAll();
|
||
progress.Append("Exporting " + pl.Count.ToString() + " " + ObjectTypeName + "s (step 1/2)");
|
||
|
||
foreach (UnitPickList.UnitPickListInfo i in pl)
|
||
{
|
||
if (!progress.KeepGoing) return;
|
||
List<string> tags = new List<string>();
|
||
AddImportTag(tags);
|
||
|
||
Unit c = Unit.GetItem(i.ID);
|
||
var ObjectTID = new TypeAndID(RootObjectTypes.Unit, c.ID);
|
||
|
||
dynamic d = new JObject();
|
||
|
||
string tempNumber = c.Serial;
|
||
if (string.IsNullOrWhiteSpace(tempNumber))
|
||
tempNumber = "0";//was not required in v7, is required in v8
|
||
|
||
//UNIT SERIAL NUMBER and MODEL ID combo must be unique!!
|
||
//this ensures that by appending a extra number character that is unique then removing the vendorid from the start
|
||
//resulting in a unique name/vendorid combo
|
||
d.serial = GetUniqueName(c.UnitModelID.ToString() + tempNumber).Replace(c.UnitModelID.ToString(), "");
|
||
|
||
if (IsDuplicatev7v8IdMapItem(c.ID, c.Serial, progress)) continue;
|
||
progress.Op(ObjectTypeName + " " + d.serial);
|
||
d.active = c.Active;
|
||
d.notes = c.Notes;
|
||
d.customerId = await Getv7v8IdMap(c.ClientID, RootObjectTypes.Client);
|
||
d.unitModelId = await Getv7v8IdMapNullOk(c.UnitModelID, RootObjectTypes.UnitModel);
|
||
d.unitHasOwnAddress = c.UnitHasOwnAddress;
|
||
d.boughtHere = c.BoughtHere;
|
||
d.purchasedFromVendorId = await Getv7v8IdMapNullOk(c.PurchasedFromID, RootObjectTypes.Vendor);
|
||
d.receipt = c.Receipt;
|
||
d.purchasedDate = util.DateToV8(c.PurchasedDate);
|
||
d.description = c.Description;
|
||
d.replacedByUnitId = null;
|
||
d.parentUnitId = null;
|
||
d.overrideModelWarranty = c.OverrideModelWarranty;
|
||
if (c.WarrantyLength != 0)
|
||
d.warrantyLength = c.WarrantyLength;
|
||
d.warrantyTerms = c.WarrantyTerms;
|
||
d.usesBanking = c.UsesBanking;
|
||
d.metered = c.Metered;
|
||
d.lifeTimeWarranty = c.LifeTimeWarranty;
|
||
d.text1 = c.Text1;
|
||
d.text2 = c.Text2;
|
||
d.text3 = c.Text3;
|
||
d.text4 = c.Text4;
|
||
if (c.UnitHasOwnAddress)
|
||
{
|
||
d.address = c.GoToAddress.DeliveryAddress;
|
||
d.city = c.GoToAddress.City;
|
||
d.region = c.GoToAddress.StateProv;
|
||
d.country = c.GoToAddress.Country;
|
||
d.latitude = c.GoToAddress.Latitude;
|
||
d.longitude = c.GoToAddress.Longitude;
|
||
}
|
||
|
||
|
||
SetTags(d, tags);
|
||
|
||
//if (c.UsesBanking)
|
||
// BankedObjects.Add(c.ID, RootObjectTypes.Unit);
|
||
|
||
//Custom fields?
|
||
if (ShouldExportCustom)
|
||
d.customFields = CustomFieldData(c, DateCustomFields);
|
||
|
||
var rMainObject = await util.PostAsync("unit", d);
|
||
long RavenId = util.IdFromResponse(rMainObject);
|
||
Addv7v8IdMap(c.ID, RavenId);
|
||
|
||
//Attachments / FILES
|
||
await ExportAttachments(ObjectTID, progress, util.AyaType.Unit);
|
||
|
||
//-----
|
||
bool repost = false;
|
||
d = rMainObject.ObjectResponse["data"];
|
||
// wiki
|
||
if (WikiPage.HasWiki(c.ID))
|
||
{
|
||
|
||
d.wiki = await GetWikiContent(ObjectTID);
|
||
repost = true;
|
||
}
|
||
|
||
//docs
|
||
string NonFileUrls = await ExportDocs(ObjectTID, c.Docs, progress, util.AyaType.Unit);
|
||
if (!string.IsNullOrEmpty(NonFileUrls))
|
||
{
|
||
d.notes = d.notes + "\n-----------------\n" + NonFileUrls;
|
||
repost = true;
|
||
}
|
||
if (repost)
|
||
await util.PutAsync("unit", d);
|
||
//-----
|
||
|
||
//DONT DO THIS for second steppers UNTIL THE SECOND STEP BELOW OR IT WILL BE WRONG for them
|
||
//Event log fixup
|
||
if (c.ParentID == Guid.Empty && c.ReplacedByUnitID == Guid.Empty)
|
||
await util.EventLog(util.AyaType.Unit, RavenId, await Getv7v8IdMap(c.Creator, RootObjectTypes.User), await Getv7v8IdMap(c.Modifier, RootObjectTypes.User), c.Created, c.Modified);
|
||
|
||
}
|
||
|
||
//################# Now loop through the units again and set parent and replaced ID's
|
||
progress.Append("Exporting " + pl.Count.ToString() + " " + ObjectTypeName + "s (step 2/2)");
|
||
foreach (UnitPickList.UnitPickListInfo i in pl)
|
||
{
|
||
if (!progress.KeepGoing) return;
|
||
|
||
Unit c = Unit.GetItem(i.ID);
|
||
if (c.ParentID == Guid.Empty && c.ReplacedByUnitID == Guid.Empty) continue;
|
||
var ObjectTID = new TypeAndID(RootObjectTypes.Unit, c.ID);
|
||
long ThisUnitRavenId = await Getv7v8IdMap(c.ID, RootObjectTypes.Unit);
|
||
var a = await util.GetAsync("unit/" + ThisUnitRavenId.ToString());
|
||
dynamic d = a.ObjectResponse["data"];
|
||
|
||
progress.Op(ObjectTypeName + " " + d.serial);
|
||
d.replacedByUnitId = await Getv7v8IdMapNullOk(c.ReplacedByUnitID, RootObjectTypes.Unit);
|
||
d.parentUnitId = await Getv7v8IdMapNullOk(c.ParentID, RootObjectTypes.Unit);
|
||
if (d.parentUnitId != null || d.replacedByUnitId != null)
|
||
await util.PutAsync("unit", d);
|
||
//-----
|
||
//Event log fixup
|
||
await util.EventLog(util.AyaType.Unit, ThisUnitRavenId, await Getv7v8IdMap(c.Creator, RootObjectTypes.User), await Getv7v8IdMap(c.Modifier, RootObjectTypes.User), c.Created, c.Modified);
|
||
}
|
||
}
|
||
|
||
|
||
|
||
private async System.Threading.Tasks.Task ExportUnitMeterReadings(ProgressForm progress)
|
||
{
|
||
|
||
if (!progress.KeepGoing) return;
|
||
|
||
progress.Op("Start Unit meter readings export");
|
||
progress.SubOp("");
|
||
|
||
//Step 2: export the objects
|
||
UnitPickList pl = UnitPickList.GetListOfAll();
|
||
progress.Append("Checking " + pl.Count.ToString() + " units for exportable meter readings");
|
||
|
||
foreach (UnitPickList.UnitPickListInfo i in pl)
|
||
{
|
||
if (!progress.KeepGoing) return;
|
||
Unit c = Unit.GetItem(i.ID);
|
||
|
||
//METER READINGS
|
||
if (c.Metered)
|
||
{
|
||
var RavenId = await Getv7v8IdMap(c.ID, RootObjectTypes.Unit, false, false);
|
||
UnitMeterReadingList uml = UnitMeterReadingList.GetList(c.ID, null);
|
||
if (uml.Count > 0)
|
||
{
|
||
progress.Op(c.Serial + " exporting " + uml.Count.ToString() + " readings");
|
||
foreach (UnitMeterReadingList.UnitMeterReadingListInfo ui in uml)
|
||
{
|
||
dynamic dm = new JObject();
|
||
dm.meter = ui.LT_UnitMeterReading_Label_Meter;
|
||
dm.meterDate = util.DateToV8(ui.LT_UnitMeterReading_Label_MeterDate, true);
|
||
dm.notes = ui.LT_UnitMeterReading_Label_Description;
|
||
dm.unitId = RavenId;
|
||
if (ui.LT_UnitMeterReading_Label_WorkorderItemID.Value != Guid.Empty)
|
||
{
|
||
//workorder items in v7 are only mapped for this one op and only return the workorderitemUnit id which is
|
||
//what v8 uses here if it fails, then if fails and null is ok here
|
||
dm.workOrderItemUnitId = await Getv7v8IdMapNullOk(ui.LT_UnitMeterReading_Label_WorkorderItemID.Value, RootObjectTypes.WorkorderItem, false);
|
||
}
|
||
await util.PostAsync("unit/meter-reading", dm);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
#endregion Units
|
||
|
||
#region UnitModels
|
||
private async System.Threading.Tasks.Task ExportUnitModels(ProgressForm progress)
|
||
{
|
||
ResetUniqueNames();
|
||
if (!progress.KeepGoing) return;
|
||
|
||
progress.Op("Start Unit models export");
|
||
progress.SubOp("");
|
||
|
||
var ObjectTypeName = "UnitModel";
|
||
//Step 1: export the CustomFields to FormCustom if applicable so that when doing individual items we can export their custom data too
|
||
var ocf = ObjectHasCustomFieldDataToExport(ObjectTypeName);
|
||
bool ShouldExportCustom = ocf != null;
|
||
var DateCustomFields = await ExportCustomFieldSchema(ocf, ObjectTypeName, ObjectTypeName);
|
||
|
||
//Step 2: export the objects
|
||
PickListAutoComplete pl = PickListAutoComplete.GetList("**", "unitmodel");
|
||
progress.Append("Exporting " + pl.Count.ToString() + " " + ObjectTypeName + "s");
|
||
|
||
//model number and vendorid must be unique
|
||
List<string> UnqNumberVendorId = new List<string>();
|
||
|
||
foreach (PickListAutoComplete.PickListAutoCompleteInfo i in pl)
|
||
{
|
||
if (!progress.KeepGoing) return;
|
||
List<string> tags = new List<string>();
|
||
AddImportTag(tags);
|
||
|
||
UnitModel c = UnitModel.GetItem(i.ID);
|
||
var ObjectTID = new TypeAndID(RootObjectTypes.UnitModel, c.ID);
|
||
|
||
dynamic d = new JObject();
|
||
|
||
|
||
string tempModelNumber = c.ModelNumber + " " + c.Name;
|
||
tempModelNumber = tempModelNumber.Trim();
|
||
progress.Op(ObjectTypeName + " " + tempModelNumber);
|
||
if (string.IsNullOrWhiteSpace(tempModelNumber))
|
||
tempModelNumber = "0";//was not required in v7, is required in v8
|
||
|
||
|
||
d.name = GetUniqueName(tempModelNumber);
|
||
|
||
d.active = c.Active;
|
||
d.notes = c.Notes;
|
||
if (c.VendorID != Guid.Empty)
|
||
d.vendorId = await Getv7v8IdMap(c.VendorID, RootObjectTypes.Vendor);
|
||
d.upc = c.UPC;
|
||
d.lifeTimeWarranty = c.LifeTimeWarranty;
|
||
d.introduceDate = util.DateToV8(c.IntroducedDate);
|
||
d.discontinued = c.Discontinued;
|
||
d.discontinuedDate = util.DateToV8(c.DiscontinuedDate);
|
||
d.warrantyLength = c.WarrantyLength;
|
||
d.warrantyTerms = c.WarrantyTerms;
|
||
|
||
|
||
TagFromv7Guid(c.UnitModelCategoryID, tags);
|
||
SetTags(d, tags);
|
||
|
||
|
||
//Custom fields?
|
||
if (ShouldExportCustom)
|
||
d.customFields = CustomFieldData(c, DateCustomFields);
|
||
|
||
var rMainObject = await util.PostAsync("unit-model", d);
|
||
long RavenId = util.IdFromResponse(rMainObject);
|
||
Addv7v8IdMap(c.ID, RavenId);
|
||
|
||
//Attachments / FILES
|
||
await ExportAttachments(ObjectTID, progress, util.AyaType.UnitModel);
|
||
//-----
|
||
bool repost = false;
|
||
d = rMainObject.ObjectResponse["data"];
|
||
// wiki
|
||
if (WikiPage.HasWiki(c.ID))
|
||
{
|
||
|
||
d.wiki = await GetWikiContent(ObjectTID);
|
||
repost = true;
|
||
}
|
||
|
||
//docs
|
||
string NonFileUrls = await ExportDocs(ObjectTID, c.Docs, progress, util.AyaType.UnitModel);
|
||
if (!string.IsNullOrEmpty(NonFileUrls))
|
||
{
|
||
d.notes = d.notes + "\n-----------------\n" + NonFileUrls;
|
||
repost = true;
|
||
}
|
||
if (repost)
|
||
await util.PutAsync("unit-model", d);
|
||
//-----
|
||
|
||
//Event log fixup
|
||
await util.EventLog(util.AyaType.UnitModel, RavenId, await Getv7v8IdMap(c.Creator, RootObjectTypes.User), await Getv7v8IdMap(c.Modifier, RootObjectTypes.User), c.Created, c.Modified);
|
||
|
||
}
|
||
}
|
||
#endregion UnitModels
|
||
|
||
#region Vendors
|
||
private async System.Threading.Tasks.Task ExportVendors(ProgressForm progress)
|
||
{
|
||
ResetUniqueNames();
|
||
if (!progress.KeepGoing) return;
|
||
progress.Op("Start Vendors export");
|
||
progress.SubOp("");
|
||
var ObjectTypeName = "Vendor";
|
||
//Step 1: export the CustomFields to FormCustom if applicable so that when doing individual items we can export their custom data too
|
||
var ocf = ObjectHasCustomFieldDataToExport(ObjectTypeName);
|
||
bool ShouldExportCustom = ocf != null;
|
||
var DateCustomFields = await ExportCustomFieldSchema(ocf, ObjectTypeName, ObjectTypeName);
|
||
|
||
//Step 2: export the objects
|
||
PickListAutoComplete pl = PickListAutoComplete.GetList("**", "vendor");
|
||
progress.Append("Exporting " + pl.Count.ToString() + " " + ObjectTypeName + "s");
|
||
|
||
foreach (PickListAutoComplete.PickListAutoCompleteInfo i in pl)
|
||
{
|
||
if (!progress.KeepGoing) return;
|
||
List<string> tags = new List<string>();
|
||
AddImportTag(tags);
|
||
|
||
Vendor c = Vendor.GetItem(i.ID);
|
||
var ObjectTID = new TypeAndID(RootObjectTypes.Vendor, c.ID);
|
||
|
||
dynamic d = new JObject();
|
||
d.name = GetUniqueName(c.Name);
|
||
if (IsDuplicatev7v8IdMapItem(c.ID, c.Name, progress)) continue;
|
||
progress.Op(ObjectTypeName + " " + d.name);
|
||
d.active = c.Active;
|
||
|
||
|
||
|
||
d.notes = c.Notes;
|
||
d.contact = c.Contact;
|
||
d.contactNotes = c.ContactNotes;
|
||
d.webAddress = c.WebAddress;
|
||
d.accountNumber = c.AccountNumber;
|
||
d.phone1 = c.Phone1;
|
||
d.phone2 = c.Phone2;
|
||
d.phone3 = c.Phone3;
|
||
d.phone4 = c.Phone4;
|
||
d.phone5 = c.Phone5;
|
||
d.emailAddress = c.Email;
|
||
d.postAddress = c.MailToAddress.DeliveryAddress;
|
||
d.postCity = c.MailToAddress.City;
|
||
d.postRegion = c.MailToAddress.StateProv;
|
||
d.postCode = c.MailToAddress.Postal;
|
||
d.address = c.GoToAddress.DeliveryAddress;
|
||
d.city = c.GoToAddress.City;
|
||
d.region = c.GoToAddress.StateProv;
|
||
d.country = c.GoToAddress.Country;
|
||
d.latitude = c.GoToAddress.Latitude;
|
||
d.longitude = c.GoToAddress.Longitude;
|
||
switch (c.VendorType)
|
||
{
|
||
case VendorTypes.Manufacturer:
|
||
tags.Add("manufacturer.vendortype");
|
||
break;
|
||
case VendorTypes.Shipper:
|
||
tags.Add("shipper.vendortype");
|
||
break;
|
||
case VendorTypes.SubContractor:
|
||
tags.Add("subcontractor.vendortype");
|
||
break;
|
||
case VendorTypes.ThirdPartyRepair:
|
||
tags.Add("thirdpartyrepair.vendortype");
|
||
break;
|
||
case VendorTypes.Wholesaler:
|
||
tags.Add("wholesaler.vendortype");
|
||
break;
|
||
}
|
||
|
||
SetTags(d, tags);
|
||
|
||
|
||
//Custom fields?
|
||
if (ShouldExportCustom)
|
||
d.customFields = CustomFieldData(c, DateCustomFields);
|
||
|
||
var rMainObject = await util.PostAsync("vendor", d);
|
||
long RavenId = util.IdFromResponse(rMainObject);
|
||
Addv7v8IdMap(c.ID, RavenId);
|
||
|
||
//Attachments / FILES
|
||
await ExportAttachments(ObjectTID, progress, util.AyaType.Vendor);
|
||
//-----
|
||
bool repost = false;
|
||
d = rMainObject.ObjectResponse["data"];
|
||
// wiki
|
||
if (WikiPage.HasWiki(c.ID))
|
||
{
|
||
|
||
d.wiki = await GetWikiContent(ObjectTID);
|
||
repost = true;
|
||
}
|
||
|
||
//docs
|
||
string NonFileUrls = await ExportDocs(ObjectTID, c.Docs, progress, util.AyaType.Vendor);
|
||
if (!string.IsNullOrEmpty(NonFileUrls))
|
||
{
|
||
d.notes = d.notes + "\n-----------------\n" + NonFileUrls;
|
||
repost = true;
|
||
}
|
||
if (repost)
|
||
await util.PutAsync("vendor", d);
|
||
//-----
|
||
|
||
//Event log fixup
|
||
await util.EventLog(util.AyaType.Vendor, RavenId, await Getv7v8IdMap(c.Creator, RootObjectTypes.User), await Getv7v8IdMap(c.Modifier, RootObjectTypes.User), c.Created, c.Modified);
|
||
|
||
}
|
||
}
|
||
#endregion Vendors
|
||
|
||
#region Rates
|
||
private Dictionary<Guid, long> TravelRateMap = new Dictionary<Guid, long>();
|
||
private Dictionary<Guid, long> ServiceRateMap = new Dictionary<Guid, long>();
|
||
private async System.Threading.Tasks.Task ExportRates(ProgressForm progress)
|
||
{
|
||
ResetUniqueNames();
|
||
TravelRateMap.Clear();
|
||
ServiceRateMap.Clear();
|
||
if (!progress.KeepGoing) return;
|
||
progress.Op("Start Rates export");
|
||
progress.SubOp("");
|
||
var ObjectTypeName = "Rate";
|
||
|
||
|
||
//Step 2: export the objects
|
||
Rates pl = Rates.GetItems(false);
|
||
progress.Append("Exporting " + pl.Count.ToString() + " " + ObjectTypeName + "s");
|
||
|
||
//get the rate unit charge descriptions for export
|
||
RateUnitChargeDescriptions rud = RateUnitChargeDescriptions.GetItems();
|
||
|
||
foreach (Rate i in pl)
|
||
{
|
||
if (!progress.KeepGoing) return;
|
||
List<string> tags = new List<string>();
|
||
AddImportTag(tags);
|
||
|
||
dynamic d = new JObject();
|
||
d.name = GetUniqueName(i.Name);
|
||
bool isTravelRate = i.RateType == RateTypes.Travel;
|
||
|
||
if (!isTravelRate)
|
||
{
|
||
if (ServiceRateMap.ContainsKey(i.ID)) continue;
|
||
}
|
||
else
|
||
if (TravelRateMap.ContainsKey(i.ID)) continue;
|
||
|
||
if (IsDuplicatev7v8IdMapItem(i.ID, i.Name, progress)) continue;
|
||
progress.Op(ObjectTypeName + " " + d.name);
|
||
d.active = i.Active;
|
||
d.notes = i.Description;
|
||
d.accountNumber = i.AccountNumber;
|
||
RateUnitChargeDescription rd = rud[i.RateUnitChargeDescriptionID];
|
||
if (rd != null)
|
||
d.unit = rd.Name;
|
||
d.cost = i.Cost;
|
||
d.charge = i.Charge;
|
||
|
||
if (i.ContractRate)
|
||
{
|
||
d.contractOnly = true;
|
||
}
|
||
else
|
||
{
|
||
d.contractOnly = false;
|
||
}
|
||
|
||
|
||
TagFromv7Guid(i.RegionID, tags);
|
||
TagFromv7Guid(i.ClientGroupID, tags);
|
||
SetTags(d, tags);
|
||
|
||
|
||
string route = "service-rate";
|
||
util.AyaType ravenType = util.AyaType.ServiceRate;
|
||
if (i.RateType == RateTypes.Travel)
|
||
{
|
||
route = "travel-rate";
|
||
ravenType = util.AyaType.TravelRate;
|
||
}
|
||
|
||
var rMainObject = await util.PostAsync(route, d);
|
||
long RavenId = util.IdFromResponse(rMainObject);
|
||
if (isTravelRate)
|
||
TravelRateMap.Add(i.ID, RavenId);
|
||
else
|
||
ServiceRateMap.Add(i.ID, RavenId);
|
||
|
||
|
||
//-----
|
||
|
||
//Event log fixup
|
||
await util.EventLog(ravenType, RavenId, await Getv7v8IdMap(i.Creator, RootObjectTypes.User), await Getv7v8IdMap(i.Modifier, RootObjectTypes.User), i.Created, i.Modified);
|
||
|
||
}
|
||
}
|
||
|
||
//rates in v7 are combined travel and service and the ui allows to change them so some records may need
|
||
//a historical travel rate but it is now a service rate in v7 and was exported as such
|
||
//so this code either returns the rate if ok or cross exports the rate to the other type then returns that rate
|
||
private async System.Threading.Tasks.Task<long?> GetRate(bool wantTravel, Guid rateId, bool mustExist = false)
|
||
{
|
||
if (!mustExist && rateId == Guid.Empty) return null;
|
||
|
||
if (!wantTravel)//looking for a service rate
|
||
{
|
||
//we have it already?
|
||
if (ServiceRateMap.ContainsKey(rateId)) return ServiceRateMap[rateId];
|
||
//not in service rates, maybe it's a cross rate from travel check there...
|
||
if (TravelRateMap.ContainsKey(rateId))
|
||
{
|
||
//yup, it is now a travel rate but once was a service rate so dupe this travel to service, add to service mapt and return the id
|
||
long sourceRavenRateId = TravelRateMap[rateId];
|
||
//get the source rate and save to the cross rate add to map and return
|
||
util.ApiResponse a = await util.GetAsync("travel-rate/" + sourceRavenRateId);
|
||
dynamic d = a.ObjectResponse["data"];
|
||
d.active = false;//assume it's no longer active
|
||
|
||
d.notes = d.notes + "\n### V8 Migrate note: This record created automatically by v8 migrate because a migrated record is using this as a Service rate but it's currently a Travel rate in v7";
|
||
d.Remove("id");
|
||
d.Remove("concurrency");
|
||
long ravenId = util.IdFromResponse((await util.PostAsync("service-rate", d.ToString())));
|
||
ServiceRateMap.Add(rateId, ravenId);
|
||
return ravenId;
|
||
}
|
||
//nope, it's non-existant anywhere
|
||
if (mustExist)
|
||
return await Getv7v8IdMap(rateId, RootObjectTypes.Rate, true, true, 1);
|
||
//throw new Exception("Mandatory Rate missing, cannot proceed, last logged item needs to be fixed");
|
||
else
|
||
return null;
|
||
}
|
||
else
|
||
{//looking for travel rate
|
||
//we have it already?
|
||
if (TravelRateMap.ContainsKey(rateId)) return TravelRateMap[rateId];
|
||
//not in travel rates, maybe it's a cross rate from service check there...
|
||
if (ServiceRateMap.ContainsKey(rateId))
|
||
{
|
||
//yup, it is now a service rate but once was a travel rate so dupe this service to travel
|
||
//, add to travel map and return the id
|
||
long sourceRavenRateId = ServiceRateMap[rateId];
|
||
//get the source rate and save to the cross rate add to map and return
|
||
dynamic d = (await util.GetAsync("service-rate/" + sourceRavenRateId)).ObjectResponse["data"];
|
||
d.active = false;//assume it's no longer active
|
||
d.notes = d.notes + "\n### V8 Migrate note: This record created automatically by v8 migrate because a migrated record is using this as a Travel rate but it's currnetly a Service rate in v7";
|
||
d.Remove("id");
|
||
d.Remove("concurrency");
|
||
long ravenId = util.IdFromResponse((await util.PostAsync("travel-rate", d.ToString())));
|
||
TravelRateMap.Add(rateId, ravenId);
|
||
return ravenId;
|
||
}
|
||
//nope, it's non-existant anywhere
|
||
if (mustExist)
|
||
return await Getv7v8IdMap(rateId, RootObjectTypes.Rate, true, true, 0);
|
||
else
|
||
return null;
|
||
}
|
||
|
||
|
||
}
|
||
#endregion Rates
|
||
|
||
#region TaxCodes
|
||
private async System.Threading.Tasks.Task ExportTaxCodes(ProgressForm progress)
|
||
{
|
||
ResetUniqueNames();
|
||
if (!progress.KeepGoing) return;
|
||
progress.Op("Start Tax codes export");
|
||
progress.SubOp("");
|
||
var ObjectTypeName = "TaxCode";
|
||
|
||
|
||
//Step 2: export the objects
|
||
TaxCodes pl = TaxCodes.GetItems();
|
||
progress.Append("Exporting " + pl.Count.ToString() + " " + ObjectTypeName + "s");
|
||
|
||
|
||
|
||
foreach (TaxCode i in pl)
|
||
{
|
||
if (!progress.KeepGoing) return;
|
||
List<string> tags = new List<string>();
|
||
AddImportTag(tags);
|
||
|
||
dynamic d = new JObject();
|
||
d.name = GetUniqueName(i.Name);
|
||
if (IsDuplicatev7v8IdMapItem(i.ID, i.Name, progress)) continue;
|
||
progress.Op(ObjectTypeName + " " + d.name);
|
||
d.active = i.Active;
|
||
|
||
d.taxAPct = i.TaxA * 100;//v8 tax amounts are in percentage not decimal notation so .07 in v7 is 7 in v8
|
||
d.taxBPct = i.TaxB * 100;
|
||
d.taxOnTax = i.TaxOnTax;
|
||
|
||
SetTags(d, tags);
|
||
|
||
|
||
var rMainObject = await util.PostAsync("tax-code", d);
|
||
long RavenId = util.IdFromResponse(rMainObject);
|
||
Addv7v8IdMap(i.ID, RavenId);
|
||
|
||
|
||
//-----
|
||
|
||
//Event log fixup
|
||
await util.EventLog(util.AyaType.TaxCode, RavenId, await Getv7v8IdMap(i.Creator, RootObjectTypes.User), await Getv7v8IdMap(i.Modifier, RootObjectTypes.User), i.Created, i.Modified);
|
||
|
||
}
|
||
}
|
||
#endregion Tax codes
|
||
|
||
#region Memos
|
||
private async System.Threading.Tasks.Task ExportMemos(ProgressForm progress)
|
||
{
|
||
if (!progress.KeepGoing) return;
|
||
progress.Op("Start Memos export");
|
||
progress.SubOp("");
|
||
var ObjectTypeName = "Memo";
|
||
progress.Append("Exporting all " + " " + ObjectTypeName + "s");
|
||
List<string> tags = new List<string>();
|
||
AddImportTag(tags);
|
||
|
||
//Get all user id's then get all memo id's for each user id then fetch each memo and finally export
|
||
UserPickList upl = UserPickList.GetList(false);
|
||
foreach (UserPickList.UserPickListInfo upli in upl)
|
||
{
|
||
if (!progress.KeepGoing) return;
|
||
UserMemoIdList umidl = UserMemoIdList.GetList(upli.ID);
|
||
foreach (UserMemoIdList.UserMemoIdListInfo mid in umidl)
|
||
{
|
||
if (!progress.KeepGoing) return;
|
||
Memo mem = Memo.GetItem(mid.mID, PluginID);
|
||
dynamic d = new JObject();
|
||
dynamic dmemo = new JObject();
|
||
dynamic duserid = new JArray();
|
||
dmemo.name = mem.Subject;
|
||
dmemo.notes = mem.Message;
|
||
dmemo.viewed = mem.Viewed;
|
||
dmemo.replied = mem.Replied;
|
||
var RavenFromId = await Getv7v8IdMap(mem.FromID, RootObjectTypes.User);
|
||
dmemo.fromId = RavenFromId;
|
||
dmemo.sent = util.DateToV8(mem.Created, true);
|
||
dmemo.toId = await Getv7v8IdMap(mem.ToID, RootObjectTypes.User);
|
||
SetTags(dmemo, tags);
|
||
duserid.Add(-7);//special code to server to treat as an import single
|
||
d.users = duserid;
|
||
d.memo = dmemo;
|
||
|
||
progress.Op(ObjectTypeName + " " + mem.ID.ToString());
|
||
|
||
var rMainObject = await util.PostAsync("memo", d);
|
||
long RavenId = util.IdFromResponse(rMainObject);
|
||
//Event log fixup
|
||
await util.EventLog(util.AyaType.Memo, RavenId, RavenFromId, RavenFromId, mem.Created, mem.Modified);
|
||
|
||
}
|
||
}
|
||
|
||
|
||
//------
|
||
|
||
|
||
}
|
||
#endregion Memos
|
||
|
||
|
||
#region WorkOrderItemPriorities
|
||
private async System.Threading.Tasks.Task ExportWorkOrderItemPriorities(ProgressForm progress)
|
||
{
|
||
if (!progress.KeepGoing) return;
|
||
progress.Op("Start WorkOrderItem Priority export");
|
||
progress.SubOp("");
|
||
var ObjectTypeName = "WorkorderItem Priority";
|
||
progress.Append("Exporting all " + ObjectTypeName + " objects");
|
||
List<string> tags = new List<string>();
|
||
AddImportTag(tags);
|
||
Priorities w = Priorities.GetItems();
|
||
ResetUniqueNames();
|
||
foreach (Priority s in w)
|
||
{
|
||
if (!progress.KeepGoing) return;
|
||
dynamic d = new JObject();
|
||
d.name = GetUniqueName(s.Name);
|
||
progress.Op(ObjectTypeName + " " + d.name);
|
||
d.active = s.Active;
|
||
d.color = System.Drawing.ColorTranslator.ToHtml(System.Drawing.Color.FromArgb(s.Color));
|
||
SetTags(d, tags);
|
||
var rMainObject = await util.PostAsync("work-order-item-priority", d);
|
||
long RavenId = util.IdFromResponse(rMainObject);
|
||
Addv7v8IdMap(s.ID, RavenId);
|
||
await util.EventLog(util.AyaType.WorkOrderItemPriority, RavenId, await Getv7v8IdMap(s.Creator, RootObjectTypes.User), await Getv7v8IdMap(s.Modifier, RootObjectTypes.User), s.Created, s.Modified);
|
||
}
|
||
|
||
|
||
//------
|
||
|
||
|
||
}
|
||
#endregion workorder priorities
|
||
|
||
|
||
#region WorkorderStatus
|
||
private async System.Threading.Tasks.Task ExportWorkorderStatus(ProgressForm progress)
|
||
{
|
||
if (!progress.KeepGoing) return;
|
||
progress.Op("Start WorkOrder/Item Status export");
|
||
progress.SubOp("");
|
||
var ObjectTypeName = "Workorder/Item Status";
|
||
progress.Append("Exporting all " + " " + ObjectTypeName);
|
||
List<string> tags = new List<string>();
|
||
AddImportTag(tags);
|
||
WorkorderStatuses w = WorkorderStatuses.GetItems();
|
||
//in v8 workorderstatus and workorderitemstatus are split apart but in v7 same, so split here...
|
||
|
||
//First export for workorders
|
||
ResetUniqueNames();
|
||
foreach (WorkorderStatus s in w)
|
||
{
|
||
if (!progress.KeepGoing) return;
|
||
dynamic d = new JObject();
|
||
d.name = GetUniqueName(s.Name);
|
||
d.notes = s.Description;
|
||
progress.Op("WorkOrder status " + d.name);
|
||
d.active = s.Active;
|
||
d.locked = false;
|
||
d.completed = false;
|
||
d.selectRoles = 1290;//biz-admin, service full, service tech, subcontractor
|
||
d.removeRoles = 1290;
|
||
d.color = System.Drawing.ColorTranslator.ToHtml(System.Drawing.Color.FromArgb(s.ARGB));
|
||
SetTags(d, tags);
|
||
var rMainObject = await util.PostAsync("work-order-status", d);
|
||
long RavenId = util.IdFromResponse(rMainObject);
|
||
Addv7v8IdMap(s.ID, RavenId);
|
||
//Event log fixup
|
||
await util.EventLog(util.AyaType.WorkOrderStatus, RavenId, await Getv7v8IdMap(s.Creator, RootObjectTypes.User), await Getv7v8IdMap(s.Modifier, RootObjectTypes.User), s.Created, s.Modified);
|
||
}
|
||
|
||
//Second export for workorder items
|
||
ResetUniqueNames();
|
||
foreach (WorkorderStatus s in w)
|
||
{
|
||
if (!progress.KeepGoing) return;
|
||
dynamic d = new JObject();
|
||
d.name = GetUniqueName(s.Name);
|
||
d.notes = s.Description;
|
||
progress.Op("WorkOrder ITEM status " + d.name);
|
||
d.active = s.Active;
|
||
d.color = System.Drawing.ColorTranslator.ToHtml(System.Drawing.Color.FromArgb(s.ARGB));
|
||
SetTags(d, tags);
|
||
var rMainObject = await util.PostAsync("work-order-item-status", d);
|
||
long RavenId = util.IdFromResponse(rMainObject);
|
||
Addv7v8WorkOrderItemStatusIdMap(s.ID, RavenId);
|
||
await util.EventLog(util.AyaType.WorkOrderItemStatus, RavenId, await Getv7v8IdMap(s.Creator, RootObjectTypes.User), await Getv7v8IdMap(s.Modifier, RootObjectTypes.User), s.Created, s.Modified);
|
||
}
|
||
|
||
|
||
//Third export for closed status
|
||
ResetUniqueNames();
|
||
{
|
||
if (!progress.KeepGoing) return;
|
||
dynamic d = new JObject();
|
||
d.name = GetUniqueName("Closed (v7)");
|
||
progress.Op("WorkOrder status " + d.name);
|
||
d.active = true;
|
||
d.locked = true;
|
||
d.completed = true;
|
||
d.selectRoles = 1290;//biz-admin, service full, service tech, subcontractor
|
||
d.removeRoles = 10;//bizadmin,servicefull
|
||
d.color = "#000000";
|
||
d.notes = "This status automatically created by the v8 migrate process for closed work orders";
|
||
SetTags(d, tags);
|
||
var rMainObject = await util.PostAsync("work-order-status", d);
|
||
ClosedWorkOrderStatus = util.IdFromResponse(rMainObject);
|
||
//Event log fixup
|
||
//await util.EventLog(util.AyaType.WorkOrderStatus, RavenId, await Getv7v8IdMap(s.Creator, RootObjectTypes.User),await Getv7v8IdMap(s.Modifier, RootObjectTypes.User), s.Created, s.Modified);
|
||
}
|
||
|
||
//Fourth export for service completed status
|
||
ResetUniqueNames();
|
||
{
|
||
if (!progress.KeepGoing) return;
|
||
dynamic d = new JObject();
|
||
d.name = GetUniqueName("Service Completed (v7)");
|
||
progress.Op("WorkOrder status " + d.name);
|
||
d.active = true;
|
||
d.locked = true;
|
||
d.completed = false;
|
||
d.selectRoles = 1290;//biz-admin, service full, service tech, subcontractor
|
||
d.removeRoles = 10;//bizadmin,servicefull
|
||
d.color = "#BBBBBB";
|
||
d.notes = "This status automatically created by the v8 migrate process for service completed work orders";
|
||
SetTags(d, tags);
|
||
var rMainObject = await util.PostAsync("work-order-status", d);
|
||
ServiceCompletedWorkOrderStatus = util.IdFromResponse(rMainObject);
|
||
//Event log fixup
|
||
//await util.EventLog(util.AyaType.WorkOrderStatus, RavenId, await Getv7v8IdMap(s.Creator, RootObjectTypes.User),await Getv7v8IdMap(s.Modifier, RootObjectTypes.User), s.Created, s.Modified);
|
||
}
|
||
|
||
|
||
|
||
//------
|
||
|
||
|
||
}
|
||
#endregion workorder status
|
||
|
||
|
||
|
||
|
||
|
||
/*
|
||
██╗ ██╗ ██████╗ ██████╗ ██╗ ██╗ ██████╗ ██████╗ ██████╗ ███████╗██████╗
|
||
██║ ██║██╔═══██╗██╔══██╗██║ ██╔╝ ██╔═══██╗██╔══██╗██╔══██╗██╔════╝██╔══██╗
|
||
██║ █╗ ██║██║ ██║██████╔╝█████╔╝█████╗██║ ██║██████╔╝██║ ██║█████╗ ██████╔╝
|
||
██║███╗██║██║ ██║██╔══██╗██╔═██╗╚════╝██║ ██║██╔══██╗██║ ██║██╔══╝ ██╔══██╗
|
||
╚███╔███╔╝╚██████╔╝██║ ██║██║ ██╗ ╚██████╔╝██║ ██║██████╔╝███████╗██║ ██║
|
||
╚══╝╚══╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═════╝ ╚══════╝╚═╝ ╚═╝
|
||
*/
|
||
#region Service Workorders
|
||
int _highestWorkOrderNumberExported = 0;
|
||
private async System.Threading.Tasks.Task ExportServiceWorkorders(ProgressForm progress)
|
||
{
|
||
if (!progress.KeepGoing) return;
|
||
progress.Op("Start Service workorders export");
|
||
progress.SubOp("");
|
||
var ObjectTypeName = "Workorder";
|
||
|
||
//Step 1: export the CustomFields to FormCustom if applicable so that when doing individual items we can export their custom data too
|
||
var ocf = ObjectHasCustomFieldDataToExport("WorkorderItem");
|
||
bool ShouldExportCustom = ocf != null;
|
||
var DateCustomFields = await ExportCustomFieldSchema(ocf, "WorkorderItem", "WorkOrderItem");
|
||
|
||
//Step 2: export the objects
|
||
//sort by woid
|
||
var crit = @"<?xml version=""1.0"" encoding=""utf-16"" standalone=""yes""?>
|
||
<GRIDCRITERIA>
|
||
<COLUMNITEM CM=""aWorkorderService.aServiceNumber"" UI=""LT_O_Workorder"" PIN=""0"" WIDTH=""92"" SORT=""ASC"" />
|
||
</GRIDCRITERIA>";
|
||
|
||
WorkorderServiceList pl = WorkorderServiceList.GetList(crit);
|
||
progress.Append("Exporting " + pl.Count.ToString() + " Service " + ObjectTypeName + "s");
|
||
|
||
//task picklist used over and over
|
||
var AllTasks = TaskPickList.GetList();
|
||
|
||
foreach (WorkorderServiceList.WorkorderServiceListInfo i in pl)
|
||
{
|
||
if (!progress.KeepGoing) return;
|
||
List<string> tags = new List<string>();
|
||
AddImportTag(tags);
|
||
|
||
|
||
Workorder c = Workorder.GetItem(i.LT_O_Workorder.Value);
|
||
if (IsDuplicatev7v8IdMapItem(c.ID, c.WorkorderService.ServiceNumber.ToString(), progress)) continue;
|
||
var ObjectTID = new TypeAndID(RootObjectTypes.WorkorderService, c.ID);
|
||
dynamic d = new JObject();
|
||
progress.Op(ObjectTypeName + " " + c.WorkorderService.ServiceNumber);
|
||
|
||
if (c.FromPMID != Guid.Empty)
|
||
{
|
||
d.fromPMId = await Getv7v8IdMapNullOk(c.FromPMID, RootObjectTypes.WorkorderPreventiveMaintenance, false, true);
|
||
}
|
||
|
||
if (c.FromQuoteID != Guid.Empty)
|
||
{
|
||
d.fromQuoteId = await Getv7v8IdMapNullOk(c.FromQuoteID, RootObjectTypes.WorkorderPreventiveMaintenance, false, true);
|
||
}
|
||
|
||
|
||
d.serial = c.WorkorderService.ServiceNumber;
|
||
if (c.WorkorderService.ServiceNumber > _highestWorkOrderNumberExported)
|
||
_highestWorkOrderNumberExported = c.WorkorderService.ServiceNumber;
|
||
d.customerId = await Getv7v8IdMap(c.ClientID, RootObjectTypes.Client);
|
||
d.notes = c.Summary;
|
||
d.projectId = await Getv7v8IdMapNullOk(c.ProjectID, RootObjectTypes.Project);
|
||
d.internalReferenceNumber = c.InternalReferenceNumber;
|
||
d.customerReferenceNumber = c.CustomerReferenceNumber;
|
||
d.customerContactName = c.CustomerContactName;
|
||
d.createdDate = util.DateToV8(c.Created, true);
|
||
d.serviceDate = util.DateToV8(c.WorkorderService.ServiceDate);
|
||
d.completeByDate = util.DateToV8(c.WorkorderService.CloseByDate);
|
||
d.invoiceNumber = c.WorkorderService.InvoiceNumber;
|
||
d.onsite = c.Onsite;
|
||
long? ctId = await Getv7v8IdMapNullOk(c.ContractIDResolved(), RootObjectTypes.Contract, false, true);
|
||
if (ctId == 0) ctId = null;
|
||
d.contractId = ctId;
|
||
d.lastStatusId = await Getv7v8IdMapNullOk(c.WorkorderService.WorkorderStatusID, RootObjectTypes.WorkorderStatus);//workorder status is in regular map, only item status is special
|
||
|
||
var client = c.uiClient;
|
||
if (client != null)
|
||
{
|
||
d.postAddress = client.MailToAddress.DeliveryAddress;
|
||
d.postCity = client.MailToAddress.City;
|
||
d.postRegion = client.MailToAddress.StateProv;
|
||
d.postCode = client.MailToAddress.Postal;
|
||
d.address = client.GoToAddress.DeliveryAddress;
|
||
d.city = client.GoToAddress.City;
|
||
d.region = client.GoToAddress.StateProv;
|
||
d.country = client.GoToAddress.Country;
|
||
d.latitude = client.GoToAddress.Latitude;
|
||
d.longitude = client.GoToAddress.Longitude;
|
||
}
|
||
|
||
if (c.uiHasSignature)
|
||
{
|
||
d.customerSignature = util.v7StrokePathTov8SVG(c.WorkorderService.Signature.StrokePath, c.WorkorderService.Signature.Width, c.WorkorderService.Signature.Height);
|
||
d.customerSignatureCaptured = util.DateToV8(c.WorkorderService.Signature.ClientCapturedDateTime);
|
||
}
|
||
|
||
TagFromv7Guid(c.RegionID, tags);
|
||
TagFromv7Guid(c.WorkorderCategoryID, tags);
|
||
|
||
SetTags(d, tags);
|
||
|
||
var rMainObject = await util.PostAsync("workorder", d);
|
||
long RavenId = util.IdFromResponse(rMainObject);
|
||
Addv7v8IdMap(c.ID, RavenId);
|
||
d = rMainObject.ObjectResponse["data"];
|
||
|
||
|
||
//Attachments / FILES
|
||
await ExportAttachments(ObjectTID, progress, util.AyaType.WorkOrder);
|
||
|
||
//-----
|
||
|
||
// wiki
|
||
if (WikiPage.HasWiki(c.ID))
|
||
{
|
||
|
||
d.wiki = await GetWikiContent(ObjectTID);
|
||
|
||
}
|
||
|
||
//docs
|
||
string NonFileUrls = await ExportDocs(ObjectTID, c.Docs, progress, util.AyaType.WorkOrder);
|
||
if (!string.IsNullOrEmpty(NonFileUrls))
|
||
{
|
||
////need to repost the user with the notes modified
|
||
|
||
//d.login = null;
|
||
//d.password = null;
|
||
d.notes = d.notes + "\n-----------------\n" + NonFileUrls;
|
||
|
||
}
|
||
|
||
//put the header object
|
||
await util.PutAsync("workorder", d);
|
||
//-----
|
||
|
||
//Event log fixup
|
||
await util.EventLog(util.AyaType.WorkOrder, RavenId, await Getv7v8IdMap(c.Creator, RootObjectTypes.User), await Getv7v8IdMap(c.Modifier, RootObjectTypes.User), c.Created, c.Modified);
|
||
|
||
//######### STATE #######
|
||
//if closed then set that state as well but always first set the current state
|
||
{
|
||
progress.SubOp("STATE");
|
||
if (c.WorkorderService.WorkorderStatusID != Guid.Empty)
|
||
{
|
||
dynamic state = new JObject();
|
||
state.workOrderId = RavenId;
|
||
state.workOrderStatusId = await Getv7v8IdMap(c.WorkorderService.WorkorderStatusID, RootObjectTypes.WorkorderStatus);
|
||
state.created = util.DateToV8(c.Created, true);
|
||
state.userId = await Getv7v8IdMap(c.Creator, RootObjectTypes.User);
|
||
await util.PostAsync("workorder/states", state.ToString());
|
||
}
|
||
if (c.ServiceCompleted)
|
||
{
|
||
dynamic state = new JObject();
|
||
state.workOrderId = RavenId;
|
||
state.workOrderStatusId = ServiceCompletedWorkOrderStatus;
|
||
state.created = util.DateToV8(c.Created, true);
|
||
state.userId = await Getv7v8IdMap(c.Creator, RootObjectTypes.User);
|
||
await util.PostAsync("workorder/states", state.ToString());
|
||
}
|
||
if (c.Closed)
|
||
{
|
||
dynamic state = new JObject();
|
||
state.workOrderId = RavenId;
|
||
state.workOrderStatusId = ClosedWorkOrderStatus;
|
||
state.created = util.DateToV8(c.Created, true);
|
||
state.userId = await Getv7v8IdMap(c.Creator, RootObjectTypes.User);
|
||
await util.PostAsync("workorder/states", state.ToString());
|
||
}
|
||
}
|
||
|
||
|
||
//##### ITEMS
|
||
int nSequence = 0;
|
||
foreach (WorkorderItem wi in c.WorkorderItems)
|
||
{
|
||
List<string> witags = new List<string>();
|
||
progress.SubOp("WorkorderItem " + wi.ID.ToString());
|
||
dynamic dwi = new JObject();
|
||
dwi.workOrderId = RavenId;
|
||
dwi.sequence = ++nSequence;
|
||
if (!string.IsNullOrWhiteSpace(wi.Summary))//in v8 summary is reqd.
|
||
dwi.notes = wi.Summary;
|
||
else
|
||
dwi.notes = "Item " + nSequence.ToString();
|
||
dwi.techNotes = wi.TechNotes;
|
||
dwi.requestDate = util.DateToV8(wi.RequestDate);//allow empty dates if empty source
|
||
dwi.workOrderItemStatusId = Getv7v8WorkOrderItemStatusIdNullOk(wi.WorkorderStatusID);
|
||
|
||
if (wi.PriorityID != Guid.Empty && V7ToV8IdMap.ContainsKey(wi.PriorityID))
|
||
dwi.workOrderItemPriorityId = V7ToV8IdMap[wi.PriorityID];
|
||
|
||
dwi.warrantyService = wi.WarrantyService;
|
||
|
||
|
||
TagFromv7Guid(wi.TypeID, witags);
|
||
SetTags(dwi, witags);
|
||
|
||
////Custom fields?
|
||
if (ShouldExportCustom)
|
||
dwi.customFields = CustomFieldData(wi, DateCustomFields);
|
||
|
||
|
||
var ravenwoitemid = util.IdFromResponse(await util.PostAsync("workorder/items", dwi.ToString()));
|
||
await util.EventLog(util.AyaType.WorkOrderItem, ravenwoitemid, await Getv7v8IdMap(wi.Creator, RootObjectTypes.User), await Getv7v8IdMap(wi.Modifier, RootObjectTypes.User), wi.Created, wi.Modified);
|
||
|
||
//##### WORKORDER ITEM UNIT
|
||
if (wi.UnitID != Guid.Empty)
|
||
{
|
||
|
||
//make sure we have a matching unit already
|
||
var ravUnitId = await Getv7v8IdMapNullOk(wi.UnitID, RootObjectTypes.Unit, true, false, await Getv7v8IdMap(c.ClientID, RootObjectTypes.Client));
|
||
if (ravUnitId != null)
|
||
{
|
||
//we have a legit unit record, make it for v8
|
||
progress.SubOp("WorkorderItemUnit " + wi.UnitID.ToString());
|
||
dynamic dwiu = new JObject();
|
||
dwiu.workOrderId = RavenId;
|
||
dwiu.workOrderItemId = ravenwoitemid;
|
||
dwiu.unitId = ravUnitId;
|
||
List<string> wiutags = new List<string>();
|
||
TagFromv7Guid(wi.WorkorderItemUnitServiceTypeID, wiutags);
|
||
SetTags(dwiu, wiutags);
|
||
long v8woitemunitid = util.IdFromResponse(await util.PostAsync("workorder/items/units", dwiu.ToString()));
|
||
//Map used for unit meter reading later, this is the ONLY use for this
|
||
//The map required is v7woitemid to v7woitemunitid for meter reading so that's what this saves
|
||
//any other use would be broken like if it needed the v8woitemid for something in future
|
||
Addv7v8IdMap(wi.ID, v8woitemunitid);
|
||
|
||
|
||
}
|
||
}
|
||
|
||
|
||
//##### WORKORDER ITEM SCHEDULED USER
|
||
foreach (WorkorderItemScheduledUser wisu in wi.ScheduledUsers)
|
||
{
|
||
progress.SubOp("WorkorderItemScheduledUser " + wisu.ID.ToString());
|
||
dynamic dwisu = new JObject();
|
||
dwisu.workOrderId = RavenId;
|
||
dwisu.workOrderItemId = ravenwoitemid;
|
||
//null is ok here for a sched user
|
||
dwisu.userId = await Getv7v8IdMapNullOk(wisu.UserID, RootObjectTypes.User);
|
||
dwisu.startDate = util.DateToV8(wisu.StartDate);
|
||
dwisu.stopDate = util.DateToV8(wisu.StopDate);
|
||
dwisu.estimatedQuantity = wisu.EstimatedQuantity;
|
||
dwisu.serviceRateId = await GetRate(false, wisu.ServiceRateID);
|
||
await util.PostAsync("workorder/items/scheduled-users", dwisu.ToString());
|
||
}
|
||
|
||
//##### WORKORDER ITEM PART
|
||
foreach (WorkorderItemPart wip in wi.Parts)
|
||
{
|
||
progress.SubOp("WorkorderItemPart " + wip.ID.ToString());
|
||
dynamic dwip = new JObject();
|
||
dwip.workOrderId = RavenId;
|
||
dwip.workOrderItemId = ravenwoitemid;
|
||
if (wip.Used)
|
||
{
|
||
dwip.quantity = wip.Quantity;
|
||
dwip.suggestedQuantity = 0;
|
||
}
|
||
else
|
||
{
|
||
dwip.quantity = 0;
|
||
dwip.suggestedQuantity = wip.Quantity;
|
||
}
|
||
var tryPartId = await Getv7v8IdMapNullOk(wip.PartID, RootObjectTypes.Part);
|
||
if (tryPartId == null)
|
||
{
|
||
dwip.partId = await GetSubstitutePartId();
|
||
if (wip.Quantity == 0 && string.IsNullOrWhiteSpace(wip.Description))
|
||
continue; //no part record, no quantity, no text at all, just skip it
|
||
}
|
||
else
|
||
dwip.partId = tryPartId;
|
||
dwip.partWarehouseId = await Getv7v8IdMap(wip.PartWarehouseID, RootObjectTypes.PartWarehouse);
|
||
dwip.taxPartSaleId = await Getv7v8IdMapNullOk(wip.TaxPartSaleID, RootObjectTypes.TaxCode);
|
||
if (wip.Discount != 0)
|
||
dwip.priceOverride = wip.Price - (wip.Price * wip.Discount);
|
||
else
|
||
dwip.priceOverride = wip.Price;
|
||
dwip.price = wip.Price;
|
||
dwip.cost = wip.Cost;
|
||
dwip.description = wip.Description;
|
||
if (wip.PartSerialID != Guid.Empty)
|
||
dwip.serials = PartSerial.GetSerialNumberFromPartSerialID(wip.PartSerialID);
|
||
await util.PostAsync("workorder/items/parts", dwip.ToString());
|
||
}
|
||
|
||
//##### WORKORDER ITEM PARTREQUEST
|
||
foreach (WorkorderItemPartRequest wipr in wi.PartRequests)
|
||
{
|
||
progress.SubOp("WorkorderItemPartRequest " + wipr.ID.ToString());
|
||
if (wipr.Quantity == 0) continue;
|
||
|
||
JObject dPO = null;
|
||
JObject v8poitemtoupdate = null;
|
||
dynamic dwipr = new JObject();
|
||
dwipr.workOrderId = RavenId;
|
||
dwipr.workOrderItemId = ravenwoitemid;
|
||
dwipr.quantity = wipr.Quantity;
|
||
dwipr.received = wipr.Received;
|
||
dwipr.partId = await Getv7v8IdMap(wipr.PartID, RootObjectTypes.Part);
|
||
|
||
dwipr.partWarehouseId = await Getv7v8IdMap(wipr.PartWarehouseID, RootObjectTypes.PartWarehouse);
|
||
|
||
poitemtowoitempartrequest pto = PoItemPartRequestMap.FirstOrDefault(z => z.v7woitempartrequestid == wipr.ID);
|
||
if (pto != null)
|
||
{
|
||
//tag this part request with the v8 poitem id that matches
|
||
//get v8 po id
|
||
//fetch po
|
||
//iterate po and look for item with the vendorpartnumber set to this woitempartid by guid string
|
||
//update the po item with the raven partrequestid
|
||
//save po
|
||
long? v8poid = await Getv7v8IdMapNullOk(pto.v7poid, RootObjectTypes.PurchaseOrder);
|
||
if (v8poid != null)
|
||
{
|
||
var a = await util.GetAsync("purchase-order/" + v8poid.ToString());
|
||
dPO = (JObject)a.ObjectResponse["data"];
|
||
if (dPO != null)
|
||
{
|
||
v8poitemtoupdate = (JObject)dPO["items"].FirstOrDefault(z => (string)z["vendorPartNumber"] == wipr.ID.ToString());
|
||
if (v8poitemtoupdate != null)
|
||
{
|
||
//update the woitempart request purchase order item id
|
||
dwipr.purchaseOrderItemId = (long)v8poitemtoupdate["id"];
|
||
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
long v8partrequestid = util.IdFromResponse(await util.PostAsync("workorder/items/part-requests", dwipr));
|
||
|
||
//SAVE PO if fetched and updated here
|
||
if (dPO != null && v8poitemtoupdate != null)
|
||
{
|
||
//save the PO
|
||
v8poitemtoupdate["workOrderItemPartRequestId"] = v8partrequestid;
|
||
v8poitemtoupdate["vendorPartNumber"] = null;
|
||
await util.PutAsync("purchase-order", dPO);
|
||
}
|
||
|
||
}
|
||
|
||
//##### WORKORDER ITEM LOANERS
|
||
foreach (WorkorderItemLoan wil in wi.Loans)
|
||
{
|
||
progress.SubOp("WorkorderItemLoan " + wil.ID.ToString());
|
||
dynamic dwil = new JObject();
|
||
dwil.workOrderId = RavenId;
|
||
dwil.workOrderItemId = ravenwoitemid;
|
||
|
||
dwil.loanUnitId = await Getv7v8IdMap(wil.LoanItemID, RootObjectTypes.LoanItem);
|
||
dwil.outDate = util.DateToV8(wil.OutDate);
|
||
dwil.dueDate = util.DateToV8(wil.DueDate);
|
||
dwil.returnDate = util.DateToV8(wil.ReturnDate);
|
||
dwil.taxCodeId = await Getv7v8IdMapNullOk(wil.TaxCodeID, RootObjectTypes.TaxCode);
|
||
dwil.quantity = wil.Quantity;
|
||
switch (wil.Rate)
|
||
{
|
||
case LoanItemRates.Days:
|
||
dwil.rate = 3;
|
||
break;
|
||
case LoanItemRates.HalfDays:
|
||
dwil.rate = 2;
|
||
break;
|
||
case LoanItemRates.Hours:
|
||
dwil.rate = 1;
|
||
break;
|
||
case LoanItemRates.Months:
|
||
dwil.rate = 5;
|
||
break;
|
||
case LoanItemRates.None:
|
||
dwil.rate = 0;
|
||
break;
|
||
case LoanItemRates.Weeks:
|
||
dwil.rate = 4;
|
||
break;
|
||
case LoanItemRates.Years:
|
||
dwil.rate = 6;
|
||
break;
|
||
}
|
||
dwil.priceOverride = wil.Charges;
|
||
dwil.notes = wil.Notes;
|
||
await util.PostAsync("workorder/items/loans", dwil.ToString());
|
||
}
|
||
|
||
//##### WORKORDER ITEM LABOR
|
||
foreach (WorkorderItemLabor wl in wi.Labors)
|
||
{
|
||
progress.SubOp("WorkorderItemLabor " + wl.ID.ToString());
|
||
dynamic dwl = new JObject();
|
||
dwl.workOrderId = RavenId;
|
||
dwl.workOrderItemId = ravenwoitemid;
|
||
//null is ok here for user
|
||
dwl.userId = await Getv7v8IdMapNullOk(wl.UserID, RootObjectTypes.User);
|
||
dwl.serviceStartDate = util.DateToV8(wl.ServiceStartDate);
|
||
dwl.serviceStopDate = util.DateToV8(wl.ServiceStopDate);
|
||
dwl.serviceRateQuantity = wl.ServiceRateQuantity;
|
||
dwl.noChargeQuantity = wl.NoChargeQuantity;
|
||
dwl.taxCodeSaleId = await Getv7v8IdMapNullOk(wl.TaxRateSaleID, RootObjectTypes.TaxCode);
|
||
dwl.serviceRateId = await GetRate(false, wl.ServiceRateID);
|
||
dwl.serviceDetails = wl.ServiceDetails;
|
||
|
||
await util.PostAsync("workorder/items/labors", dwl.ToString());
|
||
}
|
||
|
||
//##### WORKORDER ITEM TRAVEL
|
||
foreach (WorkorderItemTravel wt in wi.Travels)
|
||
{
|
||
progress.SubOp("WorkorderItemTravel " + wt.ID.ToString());
|
||
dynamic dwt = new JObject();
|
||
dwt.workOrderId = RavenId;
|
||
dwt.workOrderItemId = ravenwoitemid;
|
||
//null is ok here for user
|
||
dwt.userId = await Getv7v8IdMapNullOk(wt.UserID, RootObjectTypes.User);
|
||
dwt.travelStartDate = util.DateToV8(wt.TravelStartDate);
|
||
dwt.travelStopDate = util.DateToV8(wt.TravelStopDate);
|
||
dwt.travelRateQuantity = wt.TravelRateQuantity;
|
||
dwt.noChargeQuantity = wt.NoChargeQuantity;
|
||
dwt.taxCodeSaleId = await Getv7v8IdMapNullOk(wt.TaxRateSaleID, RootObjectTypes.TaxCode);
|
||
dwt.travelRateId = await GetRate(true, wt.TravelRateID);
|
||
dwt.travelDetails = wt.TravelDetails;
|
||
dwt.distance = wt.Distance;
|
||
await util.PostAsync("workorder/items/travels", dwt.ToString());
|
||
}
|
||
|
||
//##### WORKORDER ITEM TASKS
|
||
int nTaskSequence = 0;
|
||
foreach (WorkorderItemTask wt in wi.Tasks)
|
||
{
|
||
progress.SubOp("WorkorderItemTask " + wt.ID.ToString());
|
||
//var tg = TaskGroup.GetItem(wt.TaskGroupID);
|
||
dynamic dwt = new JObject();
|
||
dwt.workOrderId = RavenId;
|
||
dwt.workOrderItemId = ravenwoitemid;
|
||
dwt.sequence = ++nTaskSequence;
|
||
var task = AllTasks[wt.TaskID];
|
||
dwt.task = task.Name;
|
||
dwt.status = (int)wt.WorkorderItemTaskCompletionType;//same enum values in v8
|
||
await util.PostAsync("workorder/items/tasks", dwt.ToString());
|
||
}
|
||
|
||
//##### WORKORDER ITEM EXPENSES
|
||
foreach (WorkorderItemMiscExpense we in wi.Expenses)
|
||
{
|
||
progress.SubOp("WorkorderItemExpense " + we.ID.ToString());
|
||
dynamic dwe = new JObject();
|
||
dwe.workOrderId = RavenId;
|
||
dwe.workOrderItemId = ravenwoitemid;
|
||
dwe.description = we.Description;
|
||
dwe.name = we.Name;
|
||
dwe.totalCost = we.TotalCost;
|
||
dwe.chargeAmount = we.ChargeAmount;
|
||
dwe.taxPaid = we.TaxPaid;
|
||
dwe.chargeTaxCodeId = await Getv7v8IdMapNullOk(we.ChargeTaxCodeID, RootObjectTypes.TaxCode);
|
||
dwe.reimburseUser = we.ReimburseUser;
|
||
dwe.userId = await Getv7v8IdMapNullOk(we.UserID, RootObjectTypes.User);
|
||
dwe.chargeToCustomer = we.ChargeToClient;
|
||
await util.PostAsync("workorder/items/expenses", dwe.ToString());
|
||
}
|
||
|
||
//##### WORKORDER ITEM OUTSIDE SERVICE
|
||
if (wi.HasOutsideService)
|
||
{
|
||
|
||
//make sure we have a matching unit already
|
||
var ravUnitId = await Getv7v8IdMapNullOk(wi.UnitID, RootObjectTypes.Unit, true, false, await Getv7v8IdMap(c.ClientID, RootObjectTypes.Client));
|
||
if (ravUnitId != null)
|
||
{
|
||
//we have a legit unit record, make it for v8
|
||
progress.SubOp("WorkorderItemOutsideService");
|
||
dynamic dwio = new JObject();
|
||
dwio.workOrderId = RavenId;
|
||
dwio.workOrderItemId = ravenwoitemid;
|
||
dwio.unitId = ravUnitId;
|
||
dwio.notes = wi.OutsideService.Notes;
|
||
dwio.vendorSentToId = await Getv7v8IdMapNullOk(wi.OutsideService.VendorSentToID, RootObjectTypes.Vendor);
|
||
dwio.vendorSentViaId = await Getv7v8IdMapNullOk(wi.OutsideService.VendorSentViaID, RootObjectTypes.Vendor);
|
||
dwio.rMANumber = wi.OutsideService.RMANumber;
|
||
dwio.trackingNumber = wi.OutsideService.TrackingNumber;
|
||
dwio.repairCost = wi.OutsideService.RepairCost;
|
||
dwio.repairPrice = wi.OutsideService.RepairPrice;
|
||
dwio.shippingCost = wi.OutsideService.ShippingCost;
|
||
dwio.shippingPrice = wi.OutsideService.ShippingPrice;
|
||
dwio.sentDate = util.DateToV8(wi.OutsideService.DateSent);
|
||
dwio.eTADate = util.DateToV8(wi.OutsideService.DateETA);
|
||
dwio.returnDate = util.DateToV8(wi.OutsideService.DateReturned);
|
||
await util.PostAsync("workorder/items/outside-services", dwio.ToString());
|
||
}
|
||
}
|
||
|
||
|
||
}//items loop
|
||
}//wo loop
|
||
|
||
//set new seed number for work orders
|
||
_highestWorkOrderNumberExported += 1;
|
||
{
|
||
await util.PutAsync("global-biz-setting/seeds/34/" + _highestWorkOrderNumberExported.ToString());
|
||
}
|
||
}
|
||
#endregion Workorders
|
||
|
||
/*
|
||
██████╗ ██╗ ██╗ ██████╗ ████████╗███████╗
|
||
██╔═══██╗██║ ██║██╔═══██╗╚══██╔══╝██╔════╝
|
||
██║ ██║██║ ██║██║ ██║ ██║ █████╗
|
||
██║▄▄ ██║██║ ██║██║ ██║ ██║ ██╔══╝
|
||
╚██████╔╝╚██████╔╝╚██████╔╝ ██║ ███████╗
|
||
╚══▀▀═╝ ╚═════╝ ╚═════╝ ╚═╝ ╚══════╝
|
||
*/
|
||
#region Quote Workorders
|
||
int _highestQuoteNumberExported = 0;
|
||
private async System.Threading.Tasks.Task ExportQuotes(ProgressForm progress)
|
||
{
|
||
if (!progress.KeepGoing) return;
|
||
progress.Op("Start Quotes export");
|
||
progress.SubOp("");
|
||
|
||
//################## v8 quote status to mirror v7 quote status ##########################
|
||
{
|
||
|
||
if (!progress.KeepGoing) return;
|
||
progress.SubOp("Creating Quote Status types");
|
||
//InProgress 1:
|
||
await util.PostAsync("quote-status", @"{""name"": ""In progress"",
|
||
""active"": true,
|
||
""notes"": ""In process of completing this quote"",
|
||
""color"": ""#00ff00"",
|
||
""selectRoles"": 32834,
|
||
""removeRoles"": 32834,
|
||
""completed"": false,
|
||
""locked"": false}");
|
||
|
||
//Submitted 2:
|
||
await util.PostAsync("quote-status", @"{""name"": ""Submitted"",
|
||
""active"": true,
|
||
""notes"": ""Use to lock quote after given to customer and wait for approval"",
|
||
""color"": ""#c00000"",
|
||
""selectRoles"": 32834,
|
||
""removeRoles"": 32834,
|
||
""completed"": true,
|
||
""locked"": true}");
|
||
|
||
//Awarded 3:
|
||
await util.PostAsync("quote-status", @"{""name"": ""Awarded"",
|
||
""active"": true,
|
||
""notes"": ""Waiting for work order to be generated"",
|
||
""color"": ""#80ffff"",
|
||
""selectRoles"": 32834,
|
||
""removeRoles"": 32842,
|
||
""completed"": true,
|
||
""locked"": true}");
|
||
|
||
//NotAwarded 4:
|
||
await util.PostAsync("quote-status", @"{""name"": ""Not awarded"",
|
||
""active"": true,
|
||
""notes"": ""Not successful"",
|
||
""color"": ""#f2f2f2"",
|
||
""selectRoles"": 32834,
|
||
""removeRoles"": 32834,
|
||
""completed"": true,
|
||
""locked"": true}");
|
||
|
||
//New 5:
|
||
await util.PostAsync("quote-status", @"{""name"": ""New"",
|
||
""active"": true,
|
||
""notes"": ""New quote required; Sales to complete this Quote for submission"",
|
||
""color"": ""#8080ff"",
|
||
""selectRoles"": 32834,
|
||
""removeRoles"": 32834,
|
||
""completed"": false,
|
||
""locked"": false}");
|
||
|
||
|
||
//NotAwarded2 6:
|
||
await util.PostAsync("quote-status", @"{""name"": ""Beyond economical repair"",
|
||
""active"": true,
|
||
""notes"": ""Not worth repairing"",
|
||
""color"": ""#ff0000"",
|
||
""selectRoles"": 32834,
|
||
""removeRoles"": 32834,
|
||
""completed"": true,
|
||
""locked"": true}");
|
||
|
||
}
|
||
|
||
|
||
//#######################################################################################
|
||
|
||
var ObjectTypeName = "Quote";
|
||
progress.SubOp("");
|
||
//Step 1: export the CustomFields to FormCustom if applicable so that when doing individual items we can export their custom data too
|
||
var ocf = ObjectHasCustomFieldDataToExport("WorkorderItem");
|
||
bool ShouldExportCustom = ocf != null;
|
||
var DateCustomFields = await ExportCustomFieldSchema(ocf, "WorkorderItem", "WorkOrderItem");
|
||
|
||
//Step 2: export the objects
|
||
//sort by woid
|
||
var crit = @"<?xml version=""1.0"" encoding=""utf-16"" standalone=""yes""?>
|
||
<GRIDCRITERIA>
|
||
<COLUMNITEM CM=""aWorkorderQuote.aQuoteNumber"" UI=""LT_O_WorkorderQuote"" PIN=""0"" WIDTH=""92"" SORT=""ASC"" />
|
||
</GRIDCRITERIA>";
|
||
|
||
WorkorderQuoteList pl = WorkorderQuoteList.GetList(crit);
|
||
progress.Append("Exporting " + pl.Count.ToString() + " Quotes");
|
||
|
||
//task picklist used over and over
|
||
var AllTasks = TaskPickList.GetList();
|
||
|
||
foreach (WorkorderQuoteList.WorkorderQuoteListInfo i in pl)
|
||
{
|
||
if (!progress.KeepGoing) return;
|
||
List<string> tags = new List<string>();
|
||
AddImportTag(tags);
|
||
|
||
|
||
Workorder c = Workorder.GetItem(i.LT_O_WorkorderQuote.Value);
|
||
if (IsDuplicatev7v8IdMapItem(c.ID, c.WorkorderQuote.QuoteNumber.ToString(), progress)) continue;
|
||
var ObjectTID = new TypeAndID(RootObjectTypes.WorkorderQuote, c.ID);
|
||
dynamic d = new JObject();
|
||
progress.Op(ObjectTypeName + " " + c.WorkorderQuote.QuoteNumber);
|
||
progress.SubOp("");
|
||
d.serial = c.WorkorderQuote.QuoteNumber;
|
||
if (c.WorkorderQuote.QuoteNumber > _highestQuoteNumberExported)
|
||
_highestQuoteNumberExported = c.WorkorderQuote.QuoteNumber;
|
||
d.customerId = await Getv7v8IdMap(c.ClientID, RootObjectTypes.Client);
|
||
d.notes = c.Summary;
|
||
d.projectId = await Getv7v8IdMapNullOk(c.ProjectID, RootObjectTypes.Project);
|
||
d.internalReferenceNumber = c.InternalReferenceNumber;
|
||
d.customerReferenceNumber = c.CustomerReferenceNumber;
|
||
d.customerContactName = c.CustomerContactName;
|
||
d.createdDate = util.DateToV8(c.Created, true);
|
||
d.preparedById = await Getv7v8IdMap(c.WorkorderQuote.PreparedByID, RootObjectTypes.User);
|
||
d.introduction = c.WorkorderQuote.Introduction;
|
||
d.requested = util.DateToV8(c.WorkorderQuote.QuoteRequestDate);
|
||
d.validUntil = util.DateToV8(c.WorkorderQuote.ValidUntilDate);
|
||
d.submitted = util.DateToV8(c.WorkorderQuote.DateSubmitted);
|
||
d.approved = util.DateToV8(c.WorkorderQuote.DateApproved);
|
||
long? ctId = await Getv7v8IdMapNullOk(c.ContractIDResolved(), RootObjectTypes.Contract, false, true);
|
||
if (ctId == 0) ctId = null;
|
||
d.contractId = ctId;
|
||
d.lastStatusId = (int)c.WorkorderQuote.QuoteStatus;
|
||
|
||
var client = c.uiClient;
|
||
if (client != null)
|
||
{
|
||
d.postAddress = client.MailToAddress.DeliveryAddress;
|
||
d.postCity = client.MailToAddress.City;
|
||
d.postRegion = client.MailToAddress.StateProv;
|
||
d.postCode = client.MailToAddress.Postal;
|
||
d.address = client.GoToAddress.DeliveryAddress;
|
||
d.city = client.GoToAddress.City;
|
||
d.region = client.GoToAddress.StateProv;
|
||
d.country = client.GoToAddress.Country;
|
||
d.latitude = client.GoToAddress.Latitude;
|
||
d.longitude = client.GoToAddress.Longitude;
|
||
}
|
||
|
||
|
||
TagFromv7Guid(c.RegionID, tags);
|
||
TagFromv7Guid(c.WorkorderCategoryID, tags);
|
||
|
||
SetTags(d, tags);
|
||
|
||
var rMainObject = await util.PostAsync("quote", d);
|
||
long RavenId = util.IdFromResponse(rMainObject);
|
||
Addv7v8IdMap(c.ID, RavenId);
|
||
Addv7v8IdMap(c.WorkorderQuote.ID, RavenId);//some things need this id to link to
|
||
d = rMainObject.ObjectResponse["data"];
|
||
|
||
|
||
//Attachments / FILES
|
||
await ExportAttachments(ObjectTID, progress, util.AyaType.Quote);
|
||
|
||
//-----
|
||
|
||
// wiki
|
||
if (WikiPage.HasWiki(c.ID))
|
||
d.wiki = await GetWikiContent(ObjectTID);
|
||
|
||
//docs
|
||
string NonFileUrls = await ExportDocs(ObjectTID, c.Docs, progress, util.AyaType.Quote);
|
||
if (!string.IsNullOrEmpty(NonFileUrls))
|
||
d.notes = d.notes + "\n-----------------\n" + NonFileUrls;
|
||
|
||
//put the header object
|
||
await util.PutAsync("quote", d);
|
||
//-----
|
||
|
||
//Event log fixup
|
||
await util.EventLog(util.AyaType.WorkOrder, RavenId, await Getv7v8IdMap(c.Creator, RootObjectTypes.User), await Getv7v8IdMap(c.Modifier, RootObjectTypes.User), c.Created, c.Modified);
|
||
|
||
//######### STATE #######
|
||
//if closed then set that state as well but always first set the current state
|
||
{
|
||
progress.SubOp("STATE");
|
||
if (c.WorkorderQuote.QuoteStatus != WorkorderQuoteStatusTypes.NotSet)
|
||
{
|
||
dynamic state = new JObject();
|
||
state.quoteId = RavenId;
|
||
state.quoteStatusId = (int)c.WorkorderQuote.QuoteStatus;
|
||
state.created = util.DateToV8(c.Created, true);
|
||
state.userId = await Getv7v8IdMap(c.Creator, RootObjectTypes.User);
|
||
await util.PostAsync("quote/states", state.ToString());
|
||
}
|
||
|
||
|
||
}
|
||
|
||
|
||
//##### ITEMS
|
||
int nSequence = 0;
|
||
foreach (WorkorderItem wi in c.WorkorderItems)
|
||
{
|
||
List<string> witags = new List<string>();
|
||
progress.SubOp("QuoteItem " + wi.ID.ToString());
|
||
dynamic dwi = new JObject();
|
||
dwi.quoteId = RavenId;
|
||
dwi.sequence = ++nSequence;
|
||
if (!string.IsNullOrWhiteSpace(wi.Summary))//in v8 summary is reqd.
|
||
dwi.notes = wi.Summary;
|
||
else
|
||
dwi.notes = "Item " + nSequence.ToString();
|
||
dwi.techNotes = wi.TechNotes;
|
||
dwi.requestDate = util.DateToV8(wi.RequestDate);//allow empty dates if empty source
|
||
dwi.workOrderItemStatusId = Getv7v8WorkOrderItemStatusIdNullOk(wi.WorkorderStatusID);
|
||
if (wi.PriorityID != Guid.Empty && V7ToV8IdMap.ContainsKey(wi.PriorityID))
|
||
dwi.workOrderItemPriorityId = V7ToV8IdMap[wi.PriorityID];
|
||
dwi.warrantyService = wi.WarrantyService;
|
||
|
||
|
||
TagFromv7Guid(wi.TypeID, witags);
|
||
SetTags(dwi, witags);
|
||
|
||
////Custom fields?
|
||
if (ShouldExportCustom)
|
||
dwi.customFields = CustomFieldData(wi, DateCustomFields);
|
||
|
||
|
||
var ravenwoitemid = util.IdFromResponse(await util.PostAsync("quote/items", dwi.ToString()));
|
||
await util.EventLog(util.AyaType.WorkOrderItem, ravenwoitemid, await Getv7v8IdMap(wi.Creator, RootObjectTypes.User), await Getv7v8IdMap(wi.Modifier, RootObjectTypes.User), wi.Created, wi.Modified);
|
||
|
||
//##### WORKORDER ITEM UNIT
|
||
if (wi.UnitID != Guid.Empty)
|
||
{
|
||
|
||
//make sure we have a matching unit already
|
||
var ravUnitId = await Getv7v8IdMapNullOk(wi.UnitID, RootObjectTypes.Unit, true, false, await Getv7v8IdMap(c.ClientID, RootObjectTypes.Client));
|
||
if (ravUnitId != null)
|
||
{
|
||
//we have a legit unit record, make it for v8
|
||
progress.SubOp("QuoteItemUnit " + wi.UnitID.ToString());
|
||
dynamic dwiu = new JObject();
|
||
dwiu.quoteId = RavenId;
|
||
dwiu.quoteItemId = ravenwoitemid;
|
||
dwiu.unitId = ravUnitId;
|
||
List<string> wiutags = new List<string>();
|
||
TagFromv7Guid(wi.WorkorderItemUnitServiceTypeID, wiutags);
|
||
SetTags(dwiu, wiutags);
|
||
await util.PostAsync("quote/items/units", dwiu.ToString());
|
||
}
|
||
}
|
||
|
||
|
||
//##### WORKORDER ITEM SCHEDULED USER
|
||
foreach (WorkorderItemScheduledUser wisu in wi.ScheduledUsers)
|
||
{
|
||
progress.SubOp("QuoteItemScheduledUser " + wisu.ID.ToString());
|
||
dynamic dwisu = new JObject();
|
||
dwisu.quoteId = RavenId;
|
||
dwisu.quoteItemId = ravenwoitemid;
|
||
//null is ok here for a sched user
|
||
dwisu.userId = await Getv7v8IdMapNullOk(wisu.UserID, RootObjectTypes.User);
|
||
dwisu.startDate = util.DateToV8(wisu.StartDate);
|
||
dwisu.stopDate = util.DateToV8(wisu.StopDate);
|
||
dwisu.estimatedQuantity = wisu.EstimatedQuantity;
|
||
dwisu.serviceRateId = await GetRate(false, wisu.ServiceRateID);
|
||
await util.PostAsync("quote/items/scheduled-users", dwisu.ToString());
|
||
}
|
||
|
||
//##### WORKORDER ITEM PART
|
||
foreach (WorkorderItemPart wip in wi.Parts)
|
||
{
|
||
progress.SubOp("QuoteItemPart " + wip.ID.ToString());
|
||
dynamic dwip = new JObject();
|
||
dwip.quoteId = RavenId;
|
||
dwip.quoteItemId = ravenwoitemid;
|
||
dwip.quantity = wip.Quantity;
|
||
var tryPartId = await Getv7v8IdMapNullOk(wip.PartID, RootObjectTypes.Part);
|
||
if (tryPartId == null)
|
||
{
|
||
dwip.partId = await GetSubstitutePartId();
|
||
if (wip.Quantity == 0 && string.IsNullOrWhiteSpace(wip.Description))
|
||
continue; //no part record, no quantity, no text at all, just skip it
|
||
}
|
||
else
|
||
dwip.partId = tryPartId;
|
||
dwip.partWarehouseId = await Getv7v8IdMap(wip.PartWarehouseID, RootObjectTypes.PartWarehouse);
|
||
dwip.taxPartSaleId = await Getv7v8IdMapNullOk(wip.TaxPartSaleID, RootObjectTypes.TaxCode);
|
||
if (wip.Discount != 0)
|
||
dwip.priceOverride = wip.Price - (wip.Price * wip.Discount);
|
||
else
|
||
dwip.priceOverride = wip.Price;
|
||
dwip.price = wip.Price;
|
||
dwip.cost = wip.Cost;
|
||
dwip.description = wip.Description;
|
||
if (wip.PartSerialID != Guid.Empty)
|
||
dwip.serials = PartSerial.GetSerialNumberFromPartSerialID(wip.PartSerialID);
|
||
await util.PostAsync("quote/items/parts", dwip.ToString());
|
||
}
|
||
|
||
|
||
|
||
//##### WORKORDER ITEM LABOR
|
||
foreach (WorkorderItemLabor wl in wi.Labors)
|
||
{
|
||
progress.SubOp("QuoteItemLabor " + wl.ID.ToString());
|
||
dynamic dwl = new JObject();
|
||
dwl.quoteId = RavenId;
|
||
dwl.quoteItemId = ravenwoitemid;
|
||
//null is ok here for user
|
||
dwl.userId = await Getv7v8IdMapNullOk(wl.UserID, RootObjectTypes.User);
|
||
dwl.serviceStartDate = util.DateToV8(wl.ServiceStartDate);
|
||
dwl.serviceStopDate = util.DateToV8(wl.ServiceStopDate);
|
||
dwl.serviceRateQuantity = wl.ServiceRateQuantity;
|
||
dwl.noChargeQuantity = wl.NoChargeQuantity;
|
||
dwl.taxCodeSaleId = await Getv7v8IdMapNullOk(wl.TaxRateSaleID, RootObjectTypes.TaxCode);
|
||
dwl.serviceRateId = await GetRate(false, wl.ServiceRateID);
|
||
dwl.serviceDetails = wl.ServiceDetails;
|
||
|
||
await util.PostAsync("quote/items/labors", dwl.ToString());
|
||
}
|
||
|
||
//##### WORKORDER ITEM TRAVEL
|
||
foreach (WorkorderItemTravel wt in wi.Travels)
|
||
{
|
||
progress.SubOp("QuoteItemTravel " + wt.ID.ToString());
|
||
dynamic dwt = new JObject();
|
||
dwt.quoteId = RavenId;
|
||
dwt.quoteItemId = ravenwoitemid;
|
||
//null is ok here for user
|
||
dwt.userId = await Getv7v8IdMapNullOk(wt.UserID, RootObjectTypes.User);
|
||
dwt.travelStartDate = util.DateToV8(wt.TravelStartDate);
|
||
dwt.travelStopDate = util.DateToV8(wt.TravelStopDate);
|
||
dwt.travelRateQuantity = wt.TravelRateQuantity;
|
||
dwt.noChargeQuantity = wt.NoChargeQuantity;
|
||
dwt.taxCodeSaleId = await Getv7v8IdMapNullOk(wt.TaxRateSaleID, RootObjectTypes.TaxCode);
|
||
dwt.travelRateId = await GetRate(true, wt.TravelRateID);
|
||
dwt.travelDetails = wt.TravelDetails;
|
||
dwt.distance = wt.Distance;
|
||
await util.PostAsync("quote/items/travels", dwt.ToString());
|
||
}
|
||
|
||
//##### WORKORDER ITEM TASKS
|
||
int nTaskSequence = 0;
|
||
foreach (WorkorderItemTask wt in wi.Tasks)
|
||
{
|
||
progress.SubOp("QuoteItemTask " + wt.ID.ToString());
|
||
//var tg = TaskGroup.GetItem(wt.TaskGroupID);
|
||
dynamic dwt = new JObject();
|
||
dwt.quoteId = RavenId;
|
||
dwt.quoteItemId = ravenwoitemid;
|
||
dwt.sequence = ++nTaskSequence;
|
||
var task = AllTasks[wt.TaskID];
|
||
dwt.task = task.Name;
|
||
dwt.status = (int)wt.WorkorderItemTaskCompletionType;//same enum values in v8
|
||
await util.PostAsync("quote/items/tasks", dwt.ToString());
|
||
}
|
||
|
||
|
||
}//items loop
|
||
}//wo loop
|
||
|
||
//set new seed number for work orders
|
||
_highestQuoteNumberExported += 1;
|
||
{
|
||
await util.PutAsync("global-biz-setting/seeds/27/" + _highestQuoteNumberExported.ToString());
|
||
}
|
||
}
|
||
#endregion Quotes
|
||
|
||
/*
|
||
██████╗ ███╗ ███╗
|
||
██╔══██╗████╗ ████║
|
||
██████╔╝██╔████╔██║
|
||
██╔═══╝ ██║╚██╔╝██║
|
||
██║ ██║ ╚═╝ ██║
|
||
╚═╝ ╚═╝ ╚═╝
|
||
*/
|
||
#region PM Workorders
|
||
|
||
int _highestPMNumberExported = 0;
|
||
private async System.Threading.Tasks.Task ExportPMs(ProgressForm progress)
|
||
{
|
||
if (!progress.KeepGoing) return;
|
||
progress.Op("Start Preventive Maintenance export");
|
||
progress.SubOp("");
|
||
|
||
var ObjectTypeName = "PM";
|
||
progress.SubOp("");
|
||
//Step 1: export the CustomFields to FormCustom if applicable so that when doing individual items we can export their custom data too
|
||
var ocf = ObjectHasCustomFieldDataToExport("WorkorderItem");
|
||
bool ShouldExportCustom = ocf != null;
|
||
var DateCustomFields = await ExportCustomFieldSchema(ocf, "WorkorderItem", "WorkOrderItem");
|
||
|
||
//Step 2: export the objects
|
||
//sort by woid
|
||
var crit = @"<?xml version=""1.0"" encoding=""utf-16"" standalone=""yes""?>
|
||
<GRIDCRITERIA>
|
||
<COLUMNITEM CM=""aWorkorderPreventiveMaintenance.aPreventiveMaintenanceNumber"" UI=""LT_O_WorkorderWorkorderPreventiveMaintenance"" PIN=""0"" WIDTH=""92"" SORT=""ASC"" />
|
||
</GRIDCRITERIA>";
|
||
|
||
WorkorderPMList pl = WorkorderPMList.GetList(crit);
|
||
progress.Append("Exporting " + pl.Count.ToString() + " Preventive Maintenance orders");
|
||
|
||
//task picklist used over and over
|
||
var AllTasks = TaskPickList.GetList();
|
||
|
||
foreach (WorkorderPMList.WorkorderPMListInfo i in pl)
|
||
{
|
||
if (!progress.KeepGoing) return;
|
||
List<string> tags = new List<string>();
|
||
AddImportTag(tags);
|
||
|
||
|
||
Workorder c = Workorder.GetItem(i.LT_O_WorkorderPreventiveMaintenance.Value);
|
||
if (IsDuplicatev7v8IdMapItem(c.ID, c.WorkorderPreventiveMaintenance.PreventiveMaintenanceNumber.ToString(), progress)) continue;
|
||
var ObjectTID = new TypeAndID(RootObjectTypes.WorkorderPreventiveMaintenance, c.ID);
|
||
dynamic d = new JObject();
|
||
progress.Op(ObjectTypeName + " " + c.WorkorderPreventiveMaintenance.PreventiveMaintenanceNumber);
|
||
progress.SubOp("");
|
||
d.serial = c.WorkorderPreventiveMaintenance.PreventiveMaintenanceNumber;
|
||
if (c.WorkorderPreventiveMaintenance.PreventiveMaintenanceNumber > _highestPMNumberExported)
|
||
_highestPMNumberExported = c.WorkorderPreventiveMaintenance.PreventiveMaintenanceNumber;
|
||
d.customerId = await Getv7v8IdMap(c.ClientID, RootObjectTypes.Client);
|
||
d.notes = c.Summary;
|
||
d.projectId = await Getv7v8IdMapNullOk(c.ProjectID, RootObjectTypes.Project);
|
||
d.internalReferenceNumber = c.InternalReferenceNumber;
|
||
d.customerReferenceNumber = c.CustomerReferenceNumber;
|
||
d.customerContactName = c.CustomerContactName;
|
||
d.createdDate = util.DateToV8(c.Created, true);
|
||
long? ctId = await Getv7v8IdMapNullOk(c.ContractIDResolved(), RootObjectTypes.Contract, false, true);
|
||
if (ctId == 0) ctId = null;
|
||
d.contractId = ctId;
|
||
|
||
//PM specific fields
|
||
d.copyWiki = false;
|
||
d.copyAttachments = false;
|
||
d.stopGeneratingDate = util.DateToV8(c.WorkorderPreventiveMaintenance.StopGeneratingDate);
|
||
//quickly and inelegantly brute force this bitch
|
||
switch (c.WorkorderPreventiveMaintenance.DayOfTheWeek)
|
||
{
|
||
case AyaDayOfWeek.Monday:
|
||
d.excludeDaysOfWeek = util.AyaDaysOfWeek.Tuesday | util.AyaDaysOfWeek.Wednesday | util.AyaDaysOfWeek.Thursday | util.AyaDaysOfWeek.Friday | util.AyaDaysOfWeek.Saturday | util.AyaDaysOfWeek.Sunday;
|
||
break;
|
||
case AyaDayOfWeek.Tuesday:
|
||
d.excludeDaysOfWeek = util.AyaDaysOfWeek.Monday | util.AyaDaysOfWeek.Wednesday | util.AyaDaysOfWeek.Thursday | util.AyaDaysOfWeek.Friday | util.AyaDaysOfWeek.Saturday | util.AyaDaysOfWeek.Sunday;
|
||
break;
|
||
case AyaDayOfWeek.Wednesday:
|
||
d.excludeDaysOfWeek = util.AyaDaysOfWeek.Monday | util.AyaDaysOfWeek.Tuesday | util.AyaDaysOfWeek.Thursday | util.AyaDaysOfWeek.Friday | util.AyaDaysOfWeek.Saturday | util.AyaDaysOfWeek.Sunday;
|
||
break;
|
||
case AyaDayOfWeek.Thursday:
|
||
d.excludeDaysOfWeek = util.AyaDaysOfWeek.Monday | util.AyaDaysOfWeek.Tuesday | util.AyaDaysOfWeek.Wednesday | util.AyaDaysOfWeek.Friday | util.AyaDaysOfWeek.Saturday | util.AyaDaysOfWeek.Sunday;
|
||
break;
|
||
case AyaDayOfWeek.Friday:
|
||
d.excludeDaysOfWeek = util.AyaDaysOfWeek.Monday | util.AyaDaysOfWeek.Tuesday | util.AyaDaysOfWeek.Wednesday | util.AyaDaysOfWeek.Thursday | util.AyaDaysOfWeek.Saturday | util.AyaDaysOfWeek.Sunday;
|
||
break;
|
||
case AyaDayOfWeek.Saturday:
|
||
d.excludeDaysOfWeek = util.AyaDaysOfWeek.Monday | util.AyaDaysOfWeek.Tuesday | util.AyaDaysOfWeek.Wednesday | util.AyaDaysOfWeek.Thursday | util.AyaDaysOfWeek.Friday | util.AyaDaysOfWeek.Sunday;
|
||
break;
|
||
case AyaDayOfWeek.Sunday:
|
||
d.excludeDaysOfWeek = util.AyaDaysOfWeek.Monday | util.AyaDaysOfWeek.Tuesday | util.AyaDaysOfWeek.Wednesday | util.AyaDaysOfWeek.Thursday | util.AyaDaysOfWeek.Friday | util.AyaDaysOfWeek.Saturday;
|
||
break;
|
||
}
|
||
|
||
/* vet this record and if it would fail this rule then preset as inactive and put a warning into the export log
|
||
//GenerateBefore MUST be less than Repeat Interval or bad things happen
|
||
//normalizing to dates makes this easier
|
||
var dtNow = DateTime.UtcNow;
|
||
var dtGenBefore = CalculateNewDateFromSpanAndUnit(dtNow, proposedObj.GenerateBeforeUnit, proposedObj.GenerateBeforeInterval);
|
||
var dtRepeat = CalculateNewDateFromSpanAndUnit(dtNow, proposedObj.RepeatUnit, proposedObj.RepeatInterval);
|
||
if (!(dtGenBefore < dtRepeat))
|
||
{
|
||
//NOTE: this is directly tied to v8 migrate so do not change this without changing v8migrate pm export as well
|
||
AddError(ApiErrorCode.VALIDATION_INVALID_VALUE, "GenerateBeforeInterval", await Translate("ErrorGenBeforeTooSmall"));
|
||
}
|
||
*/
|
||
|
||
//sanity check to prevent runaway generations
|
||
DateTime dtNow = DateTime.UtcNow;
|
||
DateTime dtGenBefore = util.GetDateFromSpanAndUnit(dtNow, c.WorkorderPreventiveMaintenance.ThresholdSpanUnit, c.WorkorderPreventiveMaintenance.ThresholdSpan);
|
||
DateTime dtRepeat = util.GetDateFromSpanAndUnit(dtNow, c.WorkorderPreventiveMaintenance.GenerateSpanUnit, c.WorkorderPreventiveMaintenance.GenerateSpan);
|
||
if (!(dtGenBefore < dtRepeat) && c.WorkorderPreventiveMaintenance.Active == true)
|
||
{
|
||
//it's a runaway configuration, log and set inactive
|
||
d.active = false;
|
||
progress.Append("ACTION REQUIRED: PM " + c.WorkorderPreventiveMaintenance.PreventiveMaintenanceNumber.ToString()
|
||
+ " needs attention and will be set to inactive in v8. Edit in v8 and re-save to fix. (Generate before / Threshold span is too short)");
|
||
}
|
||
else
|
||
d.active = c.WorkorderPreventiveMaintenance.Active;
|
||
d.nextServiceDate = util.DateToV8(c.WorkorderPreventiveMaintenance.NextServiceDate, true);
|
||
d.repeatUnit = (int)c.WorkorderPreventiveMaintenance.GenerateSpanUnit;
|
||
d.generateBeforeUnit = (int)c.WorkorderPreventiveMaintenance.ThresholdSpanUnit;
|
||
d.repeatInterval = c.WorkorderPreventiveMaintenance.GenerateSpan;
|
||
d.generateBeforeInterval = c.WorkorderPreventiveMaintenance.ThresholdSpan;
|
||
d.stopGeneratingDate = util.DateToV8(c.WorkorderPreventiveMaintenance.StopGeneratingDate);
|
||
|
||
|
||
|
||
var client = c.uiClient;
|
||
if (client != null)
|
||
{
|
||
d.postAddress = client.MailToAddress.DeliveryAddress;
|
||
d.postCity = client.MailToAddress.City;
|
||
d.postRegion = client.MailToAddress.StateProv;
|
||
d.postCode = client.MailToAddress.Postal;
|
||
d.address = client.GoToAddress.DeliveryAddress;
|
||
d.city = client.GoToAddress.City;
|
||
d.region = client.GoToAddress.StateProv;
|
||
d.country = client.GoToAddress.Country;
|
||
d.latitude = client.GoToAddress.Latitude;
|
||
d.longitude = client.GoToAddress.Longitude;
|
||
}
|
||
|
||
|
||
TagFromv7Guid(c.RegionID, tags);
|
||
TagFromv7Guid(c.WorkorderCategoryID, tags);
|
||
|
||
SetTags(d, tags);
|
||
util.ApiResponse rMainObject = null;
|
||
rMainObject = await util.PostAsync("pm", d);
|
||
|
||
|
||
long RavenId = util.IdFromResponse(rMainObject);
|
||
Addv7v8IdMap(c.ID, RavenId);
|
||
Addv7v8IdMap(c.WorkorderPreventiveMaintenance.ID, RavenId);//some things need this id to link to
|
||
d = rMainObject.ObjectResponse["data"];
|
||
|
||
|
||
//Attachments / FILES
|
||
await ExportAttachments(ObjectTID, progress, util.AyaType.PM);
|
||
|
||
//-----
|
||
|
||
// wiki
|
||
if (WikiPage.HasWiki(c.ID))
|
||
d.wiki = await GetWikiContent(ObjectTID);
|
||
|
||
//docs
|
||
string NonFileUrls = await ExportDocs(ObjectTID, c.Docs, progress, util.AyaType.PM);
|
||
if (!string.IsNullOrEmpty(NonFileUrls))
|
||
d.notes = d.notes + "\n-----------------\n" + NonFileUrls;
|
||
|
||
//put the header object
|
||
await util.PutAsync("pm", d);
|
||
|
||
//-----
|
||
|
||
//Event log fixup
|
||
await util.EventLog(util.AyaType.WorkOrder, RavenId, await Getv7v8IdMap(c.Creator, RootObjectTypes.User), await Getv7v8IdMap(c.Modifier, RootObjectTypes.User), c.Created, c.Modified);
|
||
|
||
|
||
//##### ITEMS
|
||
int nSequence = 0;
|
||
foreach (WorkorderItem wi in c.WorkorderItems)
|
||
{
|
||
List<string> witags = new List<string>();
|
||
progress.SubOp("PMItem " + wi.ID.ToString());
|
||
dynamic dwi = new JObject();
|
||
dwi.pmId = RavenId;
|
||
dwi.sequence = ++nSequence;
|
||
if (!string.IsNullOrWhiteSpace(wi.Summary))//in v8 summary is reqd.
|
||
dwi.notes = wi.Summary;
|
||
else
|
||
dwi.notes = "Item " + nSequence.ToString();
|
||
dwi.techNotes = wi.TechNotes;
|
||
dwi.requestDate = util.DateToV8(wi.RequestDate);//allow empty dates if empty source
|
||
dwi.workOrderItemStatusId = Getv7v8WorkOrderItemStatusIdNullOk(wi.WorkorderStatusID);
|
||
if (wi.PriorityID != Guid.Empty && V7ToV8IdMap.ContainsKey(wi.PriorityID))
|
||
dwi.workOrderItemPriorityId = V7ToV8IdMap[wi.PriorityID];
|
||
dwi.warrantyService = wi.WarrantyService;
|
||
|
||
|
||
TagFromv7Guid(wi.TypeID, witags);
|
||
SetTags(dwi, witags);
|
||
|
||
////Custom fields?
|
||
if (ShouldExportCustom)
|
||
dwi.customFields = CustomFieldData(wi, DateCustomFields);
|
||
|
||
|
||
var ravenwoitemid = util.IdFromResponse(await util.PostAsync("pm/items", dwi.ToString()));
|
||
await util.EventLog(util.AyaType.WorkOrderItem, ravenwoitemid, await Getv7v8IdMap(wi.Creator, RootObjectTypes.User), await Getv7v8IdMap(wi.Modifier, RootObjectTypes.User), wi.Created, wi.Modified);
|
||
|
||
//##### WORKORDER ITEM UNIT
|
||
if (wi.UnitID != Guid.Empty)
|
||
{
|
||
|
||
//make sure we have a matching unit already
|
||
var ravUnitId = await Getv7v8IdMapNullOk(wi.UnitID, RootObjectTypes.Unit, true, false, await Getv7v8IdMap(c.ClientID, RootObjectTypes.Client));
|
||
if (ravUnitId != null)
|
||
{
|
||
//we have a legit unit record, make it for v8
|
||
progress.SubOp("PMItemUnit " + wi.UnitID.ToString());
|
||
dynamic dwiu = new JObject();
|
||
dwiu.pmId = RavenId;
|
||
dwiu.pmItemId = ravenwoitemid;
|
||
dwiu.unitId = ravUnitId;
|
||
List<string> wiutags = new List<string>();
|
||
TagFromv7Guid(wi.WorkorderItemUnitServiceTypeID, wiutags);
|
||
SetTags(dwiu, wiutags);
|
||
await util.PostAsync("pm/items/units", dwiu.ToString());
|
||
}
|
||
}
|
||
|
||
|
||
//##### WORKORDER ITEM SCHEDULED USER
|
||
foreach (WorkorderItemScheduledUser wisu in wi.ScheduledUsers)
|
||
{
|
||
progress.SubOp("PMItemScheduledUser " + wisu.ID.ToString());
|
||
dynamic dwisu = new JObject();
|
||
dwisu.pmId = RavenId;
|
||
dwisu.pmItemId = ravenwoitemid;
|
||
//null is ok here for a sched user
|
||
dwisu.userId = await Getv7v8IdMapNullOk(wisu.UserID, RootObjectTypes.User);
|
||
dwisu.startDate = util.DateToV8(wisu.StartDate);
|
||
dwisu.stopDate = util.DateToV8(wisu.StopDate);
|
||
dwisu.estimatedQuantity = wisu.EstimatedQuantity;
|
||
dwisu.serviceRateId = await GetRate(false, wisu.ServiceRateID);
|
||
await util.PostAsync("pm/items/scheduled-users", dwisu.ToString());
|
||
}
|
||
|
||
//##### WORKORDER ITEM PART
|
||
foreach (WorkorderItemPart wip in wi.Parts)
|
||
{
|
||
progress.SubOp("PMItemPart " + wip.ID.ToString());
|
||
dynamic dwip = new JObject();
|
||
dwip.pmId = RavenId;
|
||
dwip.pmItemId = ravenwoitemid;
|
||
dwip.quantity = wip.Quantity;
|
||
|
||
|
||
var tryPartId = await Getv7v8IdMapNullOk(wip.PartID, RootObjectTypes.Part);
|
||
if (tryPartId == null)
|
||
{
|
||
dwip.partId = await GetSubstitutePartId();
|
||
if (wip.Quantity == 0 && string.IsNullOrWhiteSpace(wip.Description))
|
||
continue; //no part record, no quantity, no text at all, just skip it
|
||
}
|
||
else
|
||
dwip.partId = tryPartId;
|
||
dwip.partWarehouseId = await Getv7v8IdMap(wip.PartWarehouseID, RootObjectTypes.PartWarehouse);
|
||
dwip.taxPartSaleId = await Getv7v8IdMapNullOk(wip.TaxPartSaleID, RootObjectTypes.TaxCode);
|
||
if (wip.Discount != 0)
|
||
dwip.priceOverride = wip.Price - (wip.Price * wip.Discount);
|
||
else
|
||
dwip.priceOverride = wip.Price;
|
||
dwip.price = wip.Price;
|
||
dwip.cost = wip.Cost;
|
||
dwip.description = wip.Description;
|
||
if (wip.PartSerialID != Guid.Empty)
|
||
dwip.serials = PartSerial.GetSerialNumberFromPartSerialID(wip.PartSerialID);
|
||
await util.PostAsync("pm/items/parts", dwip.ToString());
|
||
}
|
||
|
||
|
||
|
||
//##### WORKORDER ITEM LABOR
|
||
foreach (WorkorderItemLabor wl in wi.Labors)
|
||
{
|
||
progress.SubOp("PMItemLabor " + wl.ID.ToString());
|
||
dynamic dwl = new JObject();
|
||
dwl.pmId = RavenId;
|
||
dwl.pmItemId = ravenwoitemid;
|
||
//null is ok here for user
|
||
dwl.userId = await Getv7v8IdMapNullOk(wl.UserID, RootObjectTypes.User);
|
||
dwl.serviceStartDate = util.DateToV8(wl.ServiceStartDate);
|
||
dwl.serviceStopDate = util.DateToV8(wl.ServiceStopDate);
|
||
dwl.serviceRateQuantity = wl.ServiceRateQuantity;
|
||
dwl.noChargeQuantity = wl.NoChargeQuantity;
|
||
dwl.taxCodeSaleId = await Getv7v8IdMapNullOk(wl.TaxRateSaleID, RootObjectTypes.TaxCode);
|
||
dwl.serviceRateId = await GetRate(false, wl.ServiceRateID);
|
||
dwl.serviceDetails = wl.ServiceDetails;
|
||
|
||
await util.PostAsync("pm/items/labors", dwl.ToString());
|
||
}
|
||
|
||
//##### WORKORDER ITEM TRAVEL
|
||
foreach (WorkorderItemTravel wt in wi.Travels)
|
||
{
|
||
progress.SubOp("PMItemTravel " + wt.ID.ToString());
|
||
dynamic dwt = new JObject();
|
||
dwt.pmId = RavenId;
|
||
dwt.pmItemId = ravenwoitemid;
|
||
//null is ok here for user
|
||
dwt.userId = await Getv7v8IdMapNullOk(wt.UserID, RootObjectTypes.User);
|
||
dwt.travelStartDate = util.DateToV8(wt.TravelStartDate);
|
||
dwt.travelStopDate = util.DateToV8(wt.TravelStopDate);
|
||
dwt.travelRateQuantity = wt.TravelRateQuantity;
|
||
dwt.noChargeQuantity = wt.NoChargeQuantity;
|
||
dwt.taxCodeSaleId = await Getv7v8IdMapNullOk(wt.TaxRateSaleID, RootObjectTypes.TaxCode);
|
||
dwt.travelRateId = await GetRate(true, wt.TravelRateID);
|
||
dwt.travelDetails = wt.TravelDetails;
|
||
dwt.distance = wt.Distance;
|
||
await util.PostAsync("pm/items/travels", dwt.ToString());
|
||
}
|
||
|
||
//##### WORKORDER ITEM TASKS
|
||
int nTaskSequence = 0;
|
||
foreach (WorkorderItemTask wt in wi.Tasks)
|
||
{
|
||
progress.SubOp("PMItemTask " + wt.ID.ToString());
|
||
//var tg = TaskGroup.GetItem(wt.TaskGroupID);
|
||
dynamic dwt = new JObject();
|
||
dwt.pmId = RavenId;
|
||
dwt.pmItemId = ravenwoitemid;
|
||
dwt.sequence = ++nTaskSequence;
|
||
var task = AllTasks[wt.TaskID];
|
||
dwt.task = task.Name;
|
||
dwt.status = (int)wt.WorkorderItemTaskCompletionType;//same enum values in v8
|
||
await util.PostAsync("pm/items/tasks", dwt.ToString());
|
||
}
|
||
|
||
}//items loop
|
||
}//wo loop
|
||
|
||
//set new seed number for work orders
|
||
_highestPMNumberExported += 1;
|
||
{
|
||
await util.PutAsync("global-biz-setting/seeds/21/" + _highestPMNumberExported.ToString());
|
||
}
|
||
}
|
||
#endregion PM Workorders
|
||
|
||
#region ScheduleMarkers
|
||
private async System.Threading.Tasks.Task ExportScheduleMarkers(ProgressForm progress)
|
||
{
|
||
ResetUniqueNames();
|
||
if (!progress.KeepGoing) return;
|
||
progress.Op("Start ScheduleMarker export");
|
||
progress.SubOp("");
|
||
|
||
|
||
|
||
//Step 2: export the objects
|
||
ScheduleMarkerListAllUsers pl = ScheduleMarkerListAllUsers.GetList(string.Empty);
|
||
progress.Append("Exporting " + pl.Count.ToString() + " ScheduleMarkers to v8 Reminders or Reviews");
|
||
|
||
foreach (ScheduleMarkerListAllUsers.ScheduleMarkerListAllUsersInfo i in pl)
|
||
{
|
||
if (!progress.KeepGoing) return;
|
||
|
||
|
||
ScheduleMarker c = ScheduleMarker.GetItem(i.LT_O_ScheduleMarker.Value);
|
||
|
||
|
||
//Many schedulemarkers may not have related records so allow for that!!
|
||
if (c.IsFollowUp)
|
||
{
|
||
List<string> tags = new List<string>();
|
||
AddImportTag(tags);
|
||
dynamic d = new JObject();
|
||
progress.Op("FollowUp " + c.ID.ToString());
|
||
|
||
|
||
//export
|
||
//FollowUps only ever apply to a single user in v7 so no need to split them out here it's just for one user
|
||
|
||
if (!V7ToV8IdMap.ContainsKey(c.SourceID))
|
||
{
|
||
progress.Append("FollowUp User missing; skipping export: UserID " + c.SourceID.ToString());
|
||
continue;
|
||
}
|
||
|
||
//ensure has dates
|
||
string sStartDate = util.DateToV8(c.StartDate);
|
||
string sStopDate = util.DateToV8(c.StopDate);
|
||
if (sStartDate == string.Empty || sStopDate == string.Empty)
|
||
{
|
||
progress.Append("FollowUp start or stop date missing; skipping export: ID " + c.ID.ToString() + ",targetid " + c.FollowID.ToString() + ",type " + c.FollowType.ToString());
|
||
continue;
|
||
}
|
||
|
||
//Ok we have enough to do the export
|
||
if (util.DateIsPast(c.StopDate))
|
||
{
|
||
//Migrate rule case 3511
|
||
//past followups v8 reviewDate set to v7 start
|
||
//past followups assumed completed and set v8 completedate to v7 stopdate
|
||
d.reviewDate = sStartDate;
|
||
d.completedDate = sStopDate;
|
||
}
|
||
else
|
||
{
|
||
//Migrate rule case 3511
|
||
//Future followups assumed incomplete set completedDate null
|
||
//reviewDate date is v7 start date
|
||
d.reviewDate = sStartDate;
|
||
}
|
||
|
||
//completed is deprecated in v8 so to preserve that info tag the completed ones only
|
||
if (c.Completed)
|
||
tags.Add("completed.review");
|
||
|
||
d.name = c.Name;
|
||
d.notes = c.Notes;
|
||
d.userId = V7ToV8IdMap[c.SourceID];
|
||
d.assignedByUserId = await Getv7v8IdMap(c.Creator, RootObjectTypes.User);
|
||
long? v8FollowId = null;
|
||
switch (c.FollowType)
|
||
{
|
||
case RootObjectTypes.Workorder:
|
||
{
|
||
v8FollowId = await Getv7v8IdMapNullOk(c.FollowID, RootObjectTypes.Workorder, false, true);
|
||
if (v8FollowId == null)
|
||
{
|
||
progress.Append("Follow up for WO with ID: " + c.FollowID.ToString() + " skipped due to WO non-existance");
|
||
continue;
|
||
}
|
||
d.objectId = v8FollowId;
|
||
d.aType = util.AyaType.WorkOrder;
|
||
}
|
||
break;
|
||
case RootObjectTypes.WorkorderQuote:
|
||
{
|
||
Guid woid = Guid.Empty;
|
||
try
|
||
{
|
||
Workorder o = Workorder.GetWorkorderByRelativeNoMRU(RootObjectTypes.WorkorderQuote, c.FollowID);
|
||
woid = o.ID;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
if (ex is FetchException || ex is System.Security.SecurityException)//Note: workorder will throw a security exception if resulting ID is guid.empty for...reasons..
|
||
{
|
||
progress.Append("Follow up for Quote with ID: " + c.FollowID.ToString() + " skipped due to Quote non-existance");
|
||
continue;
|
||
}
|
||
throw;
|
||
}
|
||
v8FollowId = await Getv7v8IdMapNullOk(woid, RootObjectTypes.Workorder, false, true);
|
||
if (v8FollowId == null)
|
||
{
|
||
progress.Append("Follow up for Quote with ID: " + c.FollowID.ToString() + " skipped due to quote non-existance");
|
||
continue;
|
||
}
|
||
d.objectId = v8FollowId;
|
||
d.aType = util.AyaType.Quote;
|
||
}
|
||
break;
|
||
case RootObjectTypes.WorkorderPreventiveMaintenance:
|
||
{
|
||
Guid woid = Guid.Empty;
|
||
try
|
||
{
|
||
Workorder o = Workorder.GetWorkorderByRelativeNoMRU(RootObjectTypes.WorkorderPreventiveMaintenance, c.FollowID);
|
||
woid = o.ID;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
if (ex is FetchException || ex is System.Security.SecurityException)//Note: workorder will throw a security exception if resulting ID is guid.empty for...reasons..
|
||
{
|
||
progress.Append("Follow up for PM with ID: " + c.FollowID.ToString() + " skipped due to PM non-existance");
|
||
continue;
|
||
}
|
||
throw;
|
||
}
|
||
v8FollowId = await Getv7v8IdMapNullOk(woid, RootObjectTypes.Workorder, false, true);
|
||
if (v8FollowId == null)
|
||
{
|
||
progress.Append("Follow up for PM with ID: " + c.FollowID.ToString() + " skipped due to PM non-existance");
|
||
continue;
|
||
}
|
||
d.objectId = v8FollowId;
|
||
d.aType = util.AyaType.PM;
|
||
}
|
||
break;
|
||
default:
|
||
{
|
||
//It's a Review / followup tied to another object that should have already been migrated
|
||
if (!V7ToV8IdMap.ContainsKey(c.FollowID))
|
||
{
|
||
progress.Append("FollowUp target missing; skipping export: ID " + c.ID.ToString() + ", targetid " + c.FollowID.ToString() + ",type " + c.FollowType.ToString());
|
||
continue;
|
||
}
|
||
d.objectId = V7ToV8IdMap[c.FollowID];
|
||
d.aType = util.RootObjectToAyaType(c.FollowType);
|
||
}
|
||
break;
|
||
|
||
}
|
||
|
||
|
||
SetTags(d, tags);
|
||
|
||
var rMainObject = await util.PostAsync("review", d);
|
||
long RavenId = util.IdFromResponse(rMainObject);
|
||
Addv7v8IdMap(c.ID, RavenId);
|
||
await util.EventLog(util.AyaType.Review, RavenId, await Getv7v8IdMap(c.Creator, RootObjectTypes.User), await Getv7v8IdMap(c.Modifier, RootObjectTypes.User), c.Created, c.Modified);
|
||
}
|
||
else
|
||
{
|
||
//It's a Reminder schedulemarker
|
||
|
||
//ensure has dates
|
||
string sStartDate = util.DateToV8(c.StartDate);
|
||
string sStopDate = util.DateToV8(c.StopDate);
|
||
if (sStartDate == string.Empty || sStopDate == string.Empty)
|
||
{
|
||
progress.Append("ScheduleMarker start or stop date missing; skipping export: ID " + c.ID.ToString());
|
||
continue;//skip this schedulemarker entirely it's disqualified
|
||
}
|
||
|
||
foreach (Guid gUserId in c.ScheduleableUsersMarkerAppliesTo)
|
||
{
|
||
List<string> tags = new List<string>();
|
||
AddImportTag(tags);
|
||
dynamic d = new JObject();
|
||
if (!V7ToV8IdMap.ContainsKey(gUserId))
|
||
{
|
||
progress.Append("ScheduleMarker User missing; skipping export: UserID " + c.SourceID.ToString());
|
||
continue;
|
||
}
|
||
|
||
//completed is deprecated in v8 so to preserve that info tag the completed ones only
|
||
if (c.Completed)
|
||
tags.Add("completed.reminder");
|
||
|
||
d.name = c.Name;
|
||
d.notes = c.Notes;
|
||
d.userId = V7ToV8IdMap[gUserId];
|
||
d.startDate = sStartDate;
|
||
d.stopDate = sStopDate;
|
||
d.color = System.Drawing.ColorTranslator.ToHtml(System.Drawing.Color.FromArgb(c.ARGB));
|
||
SetTags(d, tags);
|
||
progress.Op("ScheduleMarker " + c.ID.ToString());
|
||
var rMainObject = await util.PostAsync("reminder", d);
|
||
long RavenId = util.IdFromResponse(rMainObject);
|
||
if (!V7ToV8IdMap.ContainsKey(c.ID))
|
||
Addv7v8IdMap(c.ID, RavenId);
|
||
await util.EventLog(util.AyaType.Reminder, RavenId, await Getv7v8IdMap(c.Creator, RootObjectTypes.User), await Getv7v8IdMap(c.Modifier, RootObjectTypes.User), c.Created, c.Modified);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
#endregion ScheduleMarkers
|
||
|
||
#region ServiceBank
|
||
//private async System.Threading.Tasks.Task ExportServiceBank(ProgressForm progress)
|
||
//{
|
||
// if (BankedObjects.Count == 0) return;
|
||
// ResetUniqueNames();
|
||
// if (!progress.KeepGoing) return;
|
||
// progress.Op("Start Service Bank export");
|
||
// progress.SubOp("");
|
||
// var ObjectTypeName = "ServiceBank";
|
||
// progress.Append("Exporting service bank records for " + BankedObjects.Count.ToString() + " banked objects");
|
||
// foreach (var bo in BankedObjects)
|
||
// {
|
||
// ServiceBankListRI pl = ServiceBankListRI.GetList(bo.Key, -1);
|
||
// progress.Append("Exporting " + pl.Count.ToString() + " service bank records for object id " + bo.Key.ToString() + " type: " + bo.Value.ToString());
|
||
|
||
// //foreach (ServiceBankListRI.ServiceBankListRIInfo i in pl)
|
||
// //for (int x = 0; x < pl.Count; x++)
|
||
// //list is backwards order most recent first
|
||
// bool FirstEntry = true;
|
||
// for (int x = pl.Count - 1; x > -1; x--)
|
||
// {
|
||
|
||
// if (!progress.KeepGoing) return;
|
||
// ServiceBankListRI.ServiceBankListRIInfo i = pl[x];
|
||
|
||
// dynamic d = new JObject();
|
||
// d.name = i.LT_ServiceBank_Label_Description;
|
||
// progress.Op(ObjectTypeName + " " + d.name);
|
||
// d.entryDate = util.DateToV8(i.LT_ServiceBank_Label_Created);
|
||
// //Guid empty is valid, particularly if it's an adjustment for a deleted workorder or something
|
||
// if (i.LT_ServiceBank_Label_SourceRootObjectType.Value == Guid.Empty)
|
||
// {
|
||
// d.sourceId = 0;
|
||
// }
|
||
// else
|
||
// {
|
||
// if (!V7ToV8IdMap.ContainsKey(i.LT_ServiceBank_Label_SourceRootObjectType.Value))
|
||
// {
|
||
// d.name = i.LT_ServiceBank_Label_Description + " v7-import: source object missing object id " + i.LT_ServiceBank_Label_SourceRootObjectType.Value.ToString() + " type: " + i.LT_ServiceBank_Label_SourceRootObjectType.RootObjectType.ToString();
|
||
// }
|
||
// else
|
||
// {
|
||
// d.sourceId = await Getv7v8IdMap(i.LT_ServiceBank_Label_SourceRootObjectType.Value, "Service bank record's source object id");
|
||
// }
|
||
// }
|
||
// //
|
||
// d.sourceType = util.RootObjectToAyaType(i.LT_ServiceBank_Label_SourceRootObjectType.RootObjectType);
|
||
|
||
// d.objectId = await Getv7v8IdMap(bo.Key, "Service bank record's root object id");
|
||
// d.objectType = util.RootObjectToAyaType(bo.Value);
|
||
// d.incidents = i.LT_ServiceBank_Label_Incidents;
|
||
// d.incidentsBalance = i.LT_ServiceBank_Label_IncidentsBalance;
|
||
// d.currency = i.LT_ServiceBank_Label_Currency;
|
||
// d.currencyBalance = i.LT_ServiceBank_Label_CurrencyBalance;
|
||
// d.hours = i.LT_ServiceBank_Label_Hours;
|
||
// d.hoursBalance = i.LT_ServiceBank_Label_HoursBalance;
|
||
// //Last values
|
||
// if (!FirstEntry)
|
||
// {
|
||
// ServiceBankListRI.ServiceBankListRIInfo iLast = pl[x + 1];
|
||
// d.lastEntryDate = util.DateToV8(iLast.LT_ServiceBank_Label_Created);
|
||
// d.lastIncidentsBalance = iLast.LT_ServiceBank_Label_IncidentsBalance;
|
||
// d.lastCurrencyBalance = iLast.LT_ServiceBank_Label_CurrencyBalance;
|
||
// d.lastHoursBalance = iLast.LT_ServiceBank_Label_HoursBalance;
|
||
// }
|
||
|
||
// var rMainObject = await util.PostAsync("service-bank/migrate", d);
|
||
// long RavenId = util.IdFromResponse(rMainObject);
|
||
// // Addv7v8IdMap(i.ID, RavenId);
|
||
|
||
|
||
// //-----
|
||
|
||
// //Event log fixup
|
||
// ServiceBank sb = ServiceBank.GetItem(i.LT_ServiceBank_Label_ID);
|
||
// await util.EventLog(util.AyaType.ServiceBank, RavenId, SaxxfeGetUserMap(sb.Creator), SaxxfeGetUserMap(sb.Creator), sb.Created, sb.Created);
|
||
// FirstEntry = false;
|
||
// }
|
||
// }
|
||
//}
|
||
#endregion ServiceBank
|
||
|
||
|
||
|
||
#region ClientServiceRequests
|
||
private async System.Threading.Tasks.Task ExportClientServiceRequests(ProgressForm progress)
|
||
{
|
||
ResetUniqueNames();
|
||
if (!progress.KeepGoing) return;
|
||
progress.Op("Start ClientServiceRequests export");
|
||
progress.SubOp("");
|
||
var ObjectTypeName = "ClientServiceRequest";
|
||
|
||
|
||
//Step 2: export the objects
|
||
ClientServiceRequestList pl = ClientServiceRequestList.GetList(string.Empty);
|
||
progress.Append("Exporting " + pl.Count.ToString() + " " + ObjectTypeName + "s");
|
||
|
||
foreach (ClientServiceRequestList.ClientServiceRequestListInfo i in pl)
|
||
{
|
||
if (!progress.KeepGoing) return;
|
||
List<string> tags = new List<string>();
|
||
AddImportTag(tags);
|
||
|
||
ClientServiceRequest c = ClientServiceRequest.GetItem(i.LT_O_ClientServiceRequest.Value);
|
||
var ObjectTID = new TypeAndID(RootObjectTypes.ClientServiceRequest, c.ID);
|
||
|
||
dynamic d = new JObject();
|
||
d.name = c.Title;
|
||
progress.Op(ObjectTypeName + " " + d.name);
|
||
if (!string.IsNullOrWhiteSpace(c.RequestedBy))
|
||
d.notes = "Requested by " + c.RequestedBy + "\n" + c.Details;
|
||
else
|
||
d.notes = c.Details;
|
||
d.dateRequested = util.DateToV8(c.Created, true);
|
||
d.customerId = await Getv7v8IdMap(c.ClientID, RootObjectTypes.Client);
|
||
d.unitId = await Getv7v8IdMapNullOk(c.UnitID, RootObjectTypes.Unit, true, false, await Getv7v8IdMap(c.ClientID, RootObjectTypes.Client));
|
||
|
||
//can't substitute a workorder item if missing so report it and export with null link and move on
|
||
if (c.WorkorderItemID != Guid.Empty)
|
||
{
|
||
if (V7ToV8IdMap.ContainsKey(c.WorkorderItemID))
|
||
{
|
||
d.workOrderItemId = V7ToV8IdMap[c.WorkorderItemID];
|
||
}
|
||
else
|
||
{
|
||
//log it as unfixable but still migrated
|
||
progress.Append("CSR '" + c.Title + "' is linked to a missing workorderitem; this CSR will be migrated but without the link to the workorder");
|
||
}
|
||
}
|
||
|
||
d.requestedByUserId = await Getv7v8IdMap(c.Creator, RootObjectTypes.User);//creator should always be a 'outside' user account
|
||
d.customerReferenceNumber = c.ClientRef;
|
||
d.priority = (int)c.Priority;//same int value
|
||
if (c.Status == ClientServiceRequestStatus.Closed)
|
||
{
|
||
//no closed status in v8 so either accepted or rejected based upon if it has a workorderitemid or not
|
||
if (c.WorkorderItemID == Guid.Empty)
|
||
|
||
//no woitem id, but is closed so guessing rejected?
|
||
d.status = (int)ClientServiceRequestStatus.Declined;
|
||
|
||
else
|
||
d.status = (int)ClientServiceRequestStatus.Accepted;
|
||
}
|
||
else
|
||
d.status = (int)c.Status;
|
||
|
||
SetTags(d, tags);
|
||
|
||
|
||
|
||
|
||
var rMainObject = await util.PostAsync("csr", d);
|
||
long RavenId = util.IdFromResponse(rMainObject);
|
||
Addv7v8IdMap(c.ID, RavenId);
|
||
|
||
//-----
|
||
|
||
//Event log fixup
|
||
await util.EventLog(util.AyaType.CustomerServiceRequest, RavenId, await Getv7v8IdMap(c.Creator, RootObjectTypes.User), await Getv7v8IdMap(c.Modifier, RootObjectTypes.User), c.Created, c.Modified);
|
||
|
||
}
|
||
}
|
||
#endregion ClientServiceRequests
|
||
|
||
#region Synchronize inventory
|
||
private async System.Threading.Tasks.Task SynchronizeInventory(ProgressForm progress)
|
||
{
|
||
if (!progress.KeepGoing) return;
|
||
if (!V7UseInventory) return;
|
||
progress.Append("Synchronizing inventory");
|
||
progress.Op("Synchronize inventory");
|
||
progress.SubOp("");
|
||
|
||
//get all parts in list
|
||
PartPickList pl = PartPickList.GetAllParts();
|
||
|
||
//iterate all parts
|
||
foreach (PartPickList.PartPickListInfo i in pl)
|
||
{
|
||
progress.SubOp("sync part " + i.Name);
|
||
PartInventoryList v7inv = PartInventoryList.GetListForSingleItem(i.ID);
|
||
long v8PartId = await Getv7v8IdMap(i.ID, RootObjectTypes.Part);
|
||
JObject v8inv = (await util.GetAsync("part/latest-inventory/" + v8PartId)).ObjectResponse;
|
||
//iterate v8 inventory
|
||
foreach (JObject v8o in v8inv["data"])
|
||
{
|
||
bool handled = false;
|
||
decimal dAdjust = 0;
|
||
decimal dMinStockLevel = 0;
|
||
decimal v8OnHand = (decimal)v8o["balance"];
|
||
foreach (PartInventoryList.PartInventoryListInfo v7o in v7inv)
|
||
{
|
||
long whsv7tov8id = await Getv7v8IdMap(v7o.LT_O_PartWarehouse.Value, RootObjectTypes.PartWarehouse);
|
||
if (whsv7tov8id == (long)v8o["partWarehouseId"])
|
||
{
|
||
//this is the match, do the fixup
|
||
//v8 inventory starts with 1 billion so every adjustment will always be *down*
|
||
|
||
if (v7o.LT_PartByWarehouseInventory_Label_QuantityOnHand < 0)
|
||
{
|
||
//don't allow negative inventory, log and set to zero instead
|
||
progress.Append("NOTE: Negative on hand for " + i.Name + " v7 (" + v7o.LT_PartByWarehouseInventory_Label_QuantityOnHand.ToString() + ") setting as zero in v8");
|
||
dAdjust = v8OnHand * -1;
|
||
}
|
||
else
|
||
{
|
||
dAdjust = v7o.LT_PartByWarehouseInventory_Label_QuantityOnHand - v8OnHand;
|
||
}
|
||
dMinStockLevel = v7o.LT_PartByWarehouseInventory_Label_MinStockLevel;
|
||
handled = true;
|
||
break;
|
||
}
|
||
}
|
||
if (!handled)
|
||
{
|
||
//no v7 matching inventory record, maybe no inventory, in any case reset to none in stock in v8
|
||
dAdjust = v8OnHand * -1;
|
||
}
|
||
|
||
//ok we have our values, send to raven
|
||
{
|
||
dynamic di = new JObject();
|
||
di.description = "v8 migrate synchronize inventory";
|
||
di.partId = v8PartId;
|
||
di.partWarehouseId = (long)v8o["partWarehouseId"];
|
||
di.quantity = dAdjust;
|
||
await util.PostAsync("part-inventory", di.ToString());
|
||
}
|
||
|
||
if (dMinStockLevel > 0)
|
||
{
|
||
JObject jsa = (await util.GetAsync("part/stock-levels/" + v8PartId)).ObjectResponse;
|
||
bool setMinStock = false;
|
||
JArray ja = (JArray)jsa["data"];
|
||
foreach (JObject js in ja)
|
||
{
|
||
if ((long)js["partWarehouseId"] == (long)v8o["partWarehouseId"])
|
||
{
|
||
js["minimumQuantity"] = dMinStockLevel;
|
||
setMinStock = true;
|
||
}
|
||
break;
|
||
}
|
||
if (!setMinStock)
|
||
{
|
||
//add a line for this record as there is no existing one (may be none at all but that's not a concern here)
|
||
dynamic dnew = new JObject();
|
||
dnew.partWarehouseId = (long)v8o["partWarehouseId"];
|
||
dnew.partId = v8PartId;
|
||
dnew.minimumQuantity = dMinStockLevel;
|
||
ja.Add(dnew);
|
||
}
|
||
|
||
await util.PutAsync("part/stock-levels/" + v8PartId, ja);//ja.ToString());
|
||
}
|
||
}
|
||
}
|
||
|
||
//Clear out inventory for special unknown missing part created in v8 for potentially missing parts
|
||
if (mSubstitutePartId != 0)
|
||
{
|
||
|
||
JObject v8inv = (await util.GetAsync("part/latest-inventory/" + mSubstitutePartId)).ObjectResponse;
|
||
//iterate v8 inventory
|
||
foreach (JObject v8o in v8inv["data"])
|
||
{
|
||
decimal dAdjust = 0;
|
||
decimal v8OnHand = (decimal)v8o["balance"];
|
||
dAdjust = v8OnHand * -1;
|
||
{
|
||
dynamic di = new JObject();
|
||
di.description = "v8 migrate synchronize inventory";
|
||
di.partId = mSubstitutePartId;
|
||
di.partWarehouseId = (long)v8o["partWarehouseId"];
|
||
di.quantity = dAdjust;
|
||
await util.PostAsync("part-inventory", di.ToString());
|
||
}
|
||
}
|
||
}
|
||
}
|
||
#endregion sync inventory
|
||
|
||
|
||
|
||
#region Global / Regional WIKIPAGES
|
||
private async System.Threading.Tasks.Task ExportGlobalRegionalWiki(ProgressForm progress)
|
||
{
|
||
progress.Append("Export Global / Regional WIKI");
|
||
if (!progress.KeepGoing) return;
|
||
progress.Op("Start Global / Regional WIKI page export");
|
||
progress.SubOp("Global WIKI check");
|
||
if (WikiPage.HasWiki(Address.GlobalAddressID))
|
||
{
|
||
progress.SubOp("Global WIKI exists, migrating to special User account");
|
||
dynamic d = new JObject();
|
||
d.name = "zV8Migrate GLOBAL_WIKI_REPOSITORY";
|
||
d.active = false;
|
||
d.userType = 2;
|
||
d.roles = 0;
|
||
d.login = util.RandomString();
|
||
d.password = util.RandomString();
|
||
d.notes = "This record created by V8Migrate utility to contain the AyaNova 7 Global WIKI which has no corresponding place in v8";
|
||
var r = await util.PostAsync("user", d);
|
||
long RavenId = util.IdFromResponse(r);
|
||
Addv7v8IdMap(Address.GlobalAddressID, RavenId);
|
||
//FILES / ATTACHMENTS FOR WIKI
|
||
var tid = new TypeAndID(RootObjectTypes.Global, Address.GlobalAddressID);
|
||
await ExportAttachments(tid, progress, util.AyaType.User);
|
||
d = r.ObjectResponse["data"];
|
||
d.wiki = await GetWikiContent(tid);
|
||
await util.PutAsync("user", d);
|
||
}
|
||
RegionList l = RegionList.GetList(string.Empty);
|
||
progress.SubOp("Regional WIKI checks");
|
||
foreach (RegionList.RegionListInfo i in l)
|
||
{
|
||
if (WikiPage.HasWiki(i.LT_Region_Label_Name.Value))
|
||
{
|
||
progress.SubOp("Region " + i.LT_Region_Label_Name.Display + " WIKI exists, migrating to special User account");
|
||
dynamic d = new JObject();
|
||
d.name = "zV8Migrate REGION_WIKI_REPOSITORY_" + i.LT_Region_Label_Name.Display;
|
||
d.active = false;
|
||
d.userType = 2;
|
||
d.roles = 0;
|
||
d.login = util.RandomString();
|
||
d.password = util.RandomString();
|
||
d.notes = "This record created by V8Migrate utility to contain the AyaNova 7 Regional WIKI which has no corresponding place in v8";
|
||
var r = await util.PostAsync("user", d);
|
||
long RavenId = util.IdFromResponse(r);
|
||
Addv7v8IdMap(i.LT_Region_Label_Name.Value, RavenId);
|
||
//FILES / ATTACHMENTS FOR WIKI
|
||
var tid = new TypeAndID(RootObjectTypes.Region, i.LT_Region_Label_Name.Value);
|
||
await ExportAttachments(tid, progress, util.AyaType.User);
|
||
d = r.ObjectResponse["data"];
|
||
d.wiki = await GetWikiContent(tid);
|
||
await util.PutAsync("user", d);
|
||
}
|
||
}
|
||
}
|
||
#endregion global regional wiki pages
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
//###############################################################################################################
|
||
//###############################################################################################################
|
||
//###############################################################################################################
|
||
//###############################################################################################################
|
||
//###############################################################################################################
|
||
//###############################################################################################################
|
||
|
||
|
||
|
||
|
||
#region locales
|
||
private async System.Threading.Tasks.Task ExportLocales(ProgressForm progress)
|
||
{
|
||
if (!progress.KeepGoing) return;
|
||
progress.Op("Start Locales export");
|
||
progress.SubOp("");
|
||
progress.Append("Exporting customized Locales");
|
||
|
||
LocaleList v7LocaleList = LocaleList.GetList();//all v7 languages
|
||
foreach (LocaleList.LocaleListInfo i in v7LocaleList)
|
||
{
|
||
ResetUniqueNames();
|
||
if (!progress.KeepGoing) return;
|
||
LocalizedTextTable lt = LocalizedTextTable.Load(i.Locale);
|
||
progress.Op("Checking if locale " + i.Locale + " is customized");
|
||
if (!util.LocaleIsCustomized(i.Locale, lt, progress)) continue;
|
||
//Guess the source language, this will always return at least "en" if nothing else
|
||
var ProbableSourceLocale = GuessCustomLocaleSourceLanguage(lt);
|
||
//collection to hold items sent to server
|
||
List<UpdateTranslationItem> exportItems = new List<UpdateTranslationItem>();
|
||
//MAKE A DUPLICATE (probable source locale here in v7 that matches Raven is duped in Raven as basis for updating with custom v7 translations)
|
||
//i.e. if we guess german is the v7 source of the customized locale then migrate it to the Raven german translation ensuring the best language for the user
|
||
var exportName = GetUniqueName(i.Locale + " (" + ProbableSourceLocale + ")");
|
||
progress.Append("ACTION REQUIRED: migrating a custom locale '" + i.Locale + "' to v8 as '" + exportName + "'. We do not recommend using migrated locales, see the Migration guide 'Localized Text' section for guidance.");
|
||
progress.SubOp("Is custom and closest match to " + ProbableSourceLocale);
|
||
progress.Op("Exporting " + i.Locale + " to " + exportName);
|
||
|
||
int x = 1;//default english locale raven id
|
||
switch (ProbableSourceLocale)
|
||
{
|
||
case "fr":
|
||
x = 3;
|
||
break;
|
||
case "de":
|
||
x = 4;
|
||
break;
|
||
case "es":
|
||
x = 2;
|
||
break;
|
||
}
|
||
var a = await util.PostAsync("translation/duplicate/" + x.ToString());
|
||
var targetTranslationId = util.IdFromResponse(a);
|
||
//now set the name
|
||
dynamic dTransName = new JObject();
|
||
dTransName.id = a.ObjectResponse["data"]["id"];
|
||
dTransName.concurrency = a.ObjectResponse["data"]["concurrency"];
|
||
dTransName.name = exportName;
|
||
dTransName.stock = false;
|
||
dTransName.baseLanguage = a.ObjectResponse["data"]["baseLanguage"];
|
||
dTransName.cjkIndex = a.ObjectResponse["data"]["cjkIndex"];
|
||
var vtemp = await util.PutAsync("translation", dTransName);
|
||
var ctoken = util.CTokenFromResponse(vtemp);
|
||
//add to maps so can set user to it on export
|
||
if (!LocaleMap.ContainsKey(i.Locale))
|
||
LocaleMap.Add(i.Locale, targetTranslationId);
|
||
//Ok, have our target locale created, now we need to insert our custom translations one by one
|
||
var trans = ((JArray)a.ObjectResponse["data"]["translationItems"]).ToObject<List<TranslationItem>>();
|
||
var DuplicateAvoidanceList = new List<string>();
|
||
//SET MATCHING KEYS TO OUR TEXT
|
||
//iterate v7 locale items
|
||
foreach (var v7item in lt.LT)
|
||
{
|
||
progress.SubOp("Processing key: " + v7item.Key);
|
||
var v8key = Translatev7TranslationKey(v7item.Key);
|
||
if (v8key == "**SKIP**") continue;
|
||
|
||
if (!DuplicateAvoidanceList.Contains(v8key)) { DuplicateAvoidanceList.Add(v8key); }
|
||
else continue;
|
||
TranslationItem v8TransItem = trans.FirstOrDefault(m => m.Key == v8key);
|
||
if (v8TransItem == null)
|
||
{
|
||
//hundreds of keys were tossed for v8 so just skip it
|
||
//throw new ArgumentOutOfRangeException("On exporting custom locale " + i.Locale + " source key " + v7item.Key + ", destination key " + v8key + " was not found.");
|
||
continue;
|
||
}
|
||
|
||
//collect
|
||
exportItems.Add(new UpdateTranslationItem
|
||
{
|
||
Id = v8TransItem.Id,
|
||
Concurrency = v8TransItem.Concurrency,
|
||
NewText = v7item.Value
|
||
});
|
||
}
|
||
//update it
|
||
progress.SubOp("Posting translation " + exportName + " to server ");
|
||
await util.PutAsync("translation/updatetranslationitemsdisplaytext",
|
||
JArray.FromObject(exportItems));// JArray.FromObject(exportItems).ToString());
|
||
progress.Op("");
|
||
progress.SubOp("");
|
||
}
|
||
progress.Op("");
|
||
progress.SubOp("");
|
||
}
|
||
|
||
|
||
#region locale utility
|
||
public class TranslationItem
|
||
{
|
||
public long Id { get; set; }
|
||
public uint Concurrency { get; set; }
|
||
public string Key { get; set; }
|
||
public string Display { get; set; }
|
||
public long TranslationId { get; set; }
|
||
}
|
||
|
||
public class UpdateTranslationItem
|
||
{
|
||
public long Id { get; set; }
|
||
public uint Concurrency { get; set; }
|
||
public string NewText { get; set; }
|
||
}
|
||
|
||
/// <summary>
|
||
/// Used by import, translate the old v7 translation key name into the new shorter version
|
||
/// </summary>
|
||
/// <param name="oldKey"></param>
|
||
/// <returns></returns>
|
||
public string Translatev7TranslationKey(string oldKey)
|
||
{
|
||
|
||
//skip keys definitely not in destination
|
||
//swaths:
|
||
if (oldKey.StartsWith("UI.ToolBar.")) return "**SKIP**";
|
||
if (oldKey.StartsWith("UI.Toolbar.")) return "**SKIP**";
|
||
// Custom English source key UI.Toolbar.ClientsToolBar, destination key ToolbarClientsToolBar was not found.
|
||
|
||
|
||
switch (oldKey)
|
||
{
|
||
// case "XXXX":
|
||
case "O.WikiPage": //skip over wikipage it's been dropped as it's an international term
|
||
case "UI.Command.LocalizedTextDesign":
|
||
case "O.WorkorderItemOutsideService":
|
||
case "O.WorkorderService.CloseByDate":
|
||
case "UI.Label.CurrentUserName":
|
||
case "UI.Go.Search":
|
||
case "ClientRequestTech.Label.ClientServiceRequestItemID":
|
||
|
||
return "**SKIP**";
|
||
}
|
||
string s = oldKey.Replace(".Label.", ".");
|
||
if (s.StartsWith("O."))
|
||
s = s.Replace("O.", "");
|
||
s = s.Replace(".ToolBar.", ".");
|
||
s = s.Replace(".Go.", ".");
|
||
s = s.Replace(".Command.", ".");
|
||
s = s.Replace(".Error.", ".");
|
||
s = s.Replace(".Object.", ".");
|
||
if (s.StartsWith("UI."))
|
||
s = s.Replace("UI.", "");
|
||
s = s.Replace(".", "");
|
||
s = s.Replace("AddressAddress", "Address");
|
||
s = s.Replace("ContactPhoneContactPhone", "ContactPhone");
|
||
s = s.Replace("ContactPhonePhone", "ContactPhone");
|
||
s = s.Replace("PurchaseOrderPurchaseOrder", "PurchaseOrder");
|
||
s = s.Replace("WorkorderItemMiscExpenseExpense", "WorkorderItemMiscExpense");
|
||
s = s.Replace("WorkorderItemTravelTravel", "WorkorderItemTravel");
|
||
s = s.Replace("DashboardDashboard", "Dashboard");
|
||
|
||
//ScheduleMarkers -> Reminder
|
||
s = s.Replace("ScheduleMarkerScheduleMarker", "ScheduleMarker");
|
||
s = s.Replace("ScheduleMarker", "Reminder");
|
||
s = s.Replace("ScheduleMarkerARGB", "ReminderARGB");
|
||
s = s.Replace("ScheduleMarkerColor", "ReminderColor");
|
||
s = s.Replace("ScheduleMarkerCompleted", "ReminderCompleted");
|
||
s = s.Replace("ScheduleMarkerEventCreated", "ReminderEventCreated");
|
||
s = s.Replace("ScheduleMarkerEventPendingAlert", "ReminderEventPendingAlert");
|
||
s = s.Replace("ScheduleMarkerFollowUp", "ReminderFollowUp");
|
||
s = s.Replace("ScheduleMarkerList", "ReminderList");
|
||
s = s.Replace("ScheduleMarkerName", "ReminderName");
|
||
s = s.Replace("ScheduleMarkerNotes", "ReminderNotes");
|
||
s = s.Replace("ScheduleMarkerRecurrence", "ReminderRecurrence");
|
||
s = s.Replace("ScheduleMarkerScheduleMarkerSourceType", "ReminderSourceType");
|
||
s = s.Replace("ScheduleMarkerSourceID", "ReminderSourceID");
|
||
s = s.Replace("ScheduleMarkerStartDate", "ReminderStartDate");
|
||
s = s.Replace("ScheduleMarkerStopDate", "ReminderStopDate");
|
||
s = s.Replace("ScheduleEditScheduleMarker", "ScheduleEditReminder");
|
||
s = s.Replace("ScheduleNewScheduleMarker", "ScheduleNewReminder");
|
||
|
||
|
||
|
||
|
||
//Custom fields were 0 to 9, now 1 to 16
|
||
s = s.Replace("Custom9", "Custom10");
|
||
s = s.Replace("Custom8", "Custom9");
|
||
s = s.Replace("Custom7", "Custom8");
|
||
s = s.Replace("Custom6", "Custom7");
|
||
s = s.Replace("Custom5", "Custom6");
|
||
s = s.Replace("Custom4", "Custom5");
|
||
s = s.Replace("Custom3", "Custom4");
|
||
s = s.Replace("Custom2", "Custom3");
|
||
if (!s.EndsWith("Custom10"))
|
||
s = s.Replace("Custom1", "Custom2");
|
||
s = s.Replace("Custom0", "Custom1");
|
||
//separate code will handle adding the new keys that didn't exist in v7 (custom 11 - 16)
|
||
|
||
//CommonActive CommonID etc remove Common
|
||
s = s.Replace("Common", "");
|
||
|
||
//Misc
|
||
s = s.Replace("FormFieldDataType", "UiFieldDataType");
|
||
s = s.Replace("UserUserType", "UserType");
|
||
s = s.Replace("UserTypesUtilityNotification", "UserTypesUtility");
|
||
|
||
//Localized -> Translation
|
||
s = s.Replace("LocaleCustomizeText", "TranslationCustomizeText");
|
||
s = s.Replace("LocaleExport", "TranslationExport");
|
||
s = s.Replace("LocaleImport", "TranslationImport");
|
||
s = s.Replace("LocaleList", "TranslationList");
|
||
s = s.Replace("LocaleLocaleFile", "TranslationFile");
|
||
s = s.Replace("LocaleUIDestLocale", "TranslationDest");
|
||
s = s.Replace("LocaleUISourceLocale", "TranslationSource");
|
||
s = s.Replace("LocaleWarnLocaleLocked", "TranslationWarnLocked");
|
||
s = s.Replace("LocalizedTextDisplayText", "TranslationDisplayText");
|
||
s = s.Replace("LocalizedTextDisplayTextCustom", "TranslationDisplayTextCustom");
|
||
s = s.Replace("LocalizedTextKey", "TranslationKey");
|
||
s = s.Replace("LocalizedTextLocale", "Translation");
|
||
s = s.Replace("LocalizedText", "TranslatedText");
|
||
|
||
//items that came up after moving this to v8 migrate plugin
|
||
s = s.Replace("DateRangeInTheLastYear", "DateRangePastYear");
|
||
s = s.Replace("ToolbarCustomizeDialog", "CustomizeDialog");
|
||
s = s.Replace("Client", "Customer");
|
||
s = s.Replace("LoanItem", "LoanUnit");
|
||
s = s.Replace("WorkorderPreventiveMaintenance", "PM");
|
||
s = s.Replace("WorkorderQuote", "Quote");
|
||
s = s.Replace("WorkorderService", "Workorder");
|
||
s = s.Replace("Workorder", "WorkOrder");
|
||
s = s.Replace("CustomerNoteCustomerNoteTypeID", "CustomerNoteTypeId");
|
||
s = s.Replace("CustomerRequestPartCustomerServiceRequestItemID", "CustomerRequestItemId");
|
||
s = s.Replace("WorkOrderItemMiscExpense", "WorkOrderItemExpense");
|
||
s = s.Replace("WorkOrderItemLoanLoan", "WorkOrderItemLoan");
|
||
s = s.Replace("WorkOrderItemLaborLabor", "WorkOrderItemLabor");
|
||
s = s.Replace("UserTypesAdministrator", "UserTypeAdministrator");
|
||
s = s.Replace("UserTypesCustomer", "UserTypeCustomer");
|
||
s = s.Replace("UserTypesHeadOffice", "UserTypeHeadOffice");
|
||
s = s.Replace("UserTypesNonSchedulable", "UserTypeNotService");
|
||
s = s.Replace("UserTypesSchedulable", "UserTypeService");
|
||
s = s.Replace("UserTypesUtility", "UserTypeUtility");
|
||
s = s.Replace("UserTypesSubContractor", "UserTypeServiceContractor");
|
||
|
||
|
||
//RateRate*
|
||
s = s.Replace("RateRate", "Rate");
|
||
s = s.Replace("WorkOrderStatusCompletedStatus", "CompletedStatus");
|
||
s = s.Replace("WorkOrderStatusName", "StatusName");
|
||
|
||
s = s.Replace("PopUpNotes", "AlertNotes");
|
||
|
||
// s = s.Replace("PartName", "PartDescription");
|
||
s = s.Replace("PartPartNumber", "PartName");
|
||
|
||
|
||
//FUTURE
|
||
//
|
||
// s = s.Replace("WASXXX", "NOWXXXX");
|
||
// s = s.Replace("WASXXX", "NOWXXXX");
|
||
// s = s.Replace("WASXXX", "NOWXXXX");
|
||
// s = s.Replace("WASXXX", "NOWXXXX");
|
||
// s = s.Replace("WASXXX", "NOWXXXX");
|
||
// s = s.Replace("WASXXX", "NOWXXXX");
|
||
// s = s.Replace("WASXXX", "NOWXXXX");
|
||
// s = s.Replace("WASXXX", "NOWXXXX");
|
||
// s = s.Replace("WASXXX", "NOWXXXX");
|
||
// s = s.Replace("WASXXX", "NOWXXXX");
|
||
// s = s.Replace("WASXXX", "NOWXXXX");
|
||
// s = s.Replace("WASXXX", "NOWXXXX");
|
||
// s = s.Replace("WASXXX", "NOWXXXX");
|
||
// s = s.Replace("WASXXX", "NOWXXXX");
|
||
// s = s.Replace("WASXXX", "NOWXXXX");
|
||
// s = s.Replace("WASXXX", "NOWXXXX");
|
||
// s = s.Replace("WASXXX", "NOWXXXX");
|
||
// s = s.Replace("WASXXX", "NOWXXXX");
|
||
// s = s.Replace("WASXXX", "NOWXXXX");
|
||
// s = s.Replace("WASXXX", "NOWXXXX");
|
||
// s = s.Replace("WASXXX", "NOWXXXX");
|
||
// s = s.Replace("WASXXX", "NOWXXXX");
|
||
// s = s.Replace("WASXXX", "NOWXXXX");
|
||
// s = s.Replace("WASXXX", "NOWXXXX");
|
||
// s = s.Replace("WASXXX", "NOWXXXX");
|
||
// s = s.Replace("WASXXX", "NOWXXXX");
|
||
|
||
return s;
|
||
}
|
||
|
||
|
||
|
||
|
||
/*
|
||
GUESS SOURCE LANGUAGE
|
||
todo: detect source language of custom and only do that one, not all languages as now
|
||
if can find a key that users can't edit becuase it's not exposed or super unlikely to be changed as not in UI
|
||
or something along those lines (or maybe 3 or four) then compare to each language to find the source of this one if possible
|
||
and detect it
|
||
|
||
Error.UnableToOpenEmailUrl
|
||
en:Unable to open email address
|
||
de:E-Mail-Adresse kann nicht geöffnet werden
|
||
es:No puede abrirse la dirección de e-mail
|
||
fr:Impossible d'ouvrir l'adresse e-mail
|
||
|
||
Global.Label.AllowScheduleConflicts
|
||
en:Allow Schedule Conflicts
|
||
de:Planungskonflikte zulassen
|
||
es:Permitir conflictos de programación
|
||
fr:Autoriser les conflits de programme
|
||
|
||
ReportEditor.Label.Properties
|
||
en:Properties
|
||
de:Eigenschaften
|
||
es:Propiedades
|
||
fr:Propriétés
|
||
|
||
StopWords3
|
||
en:would some what such make come while its use those see out who ll but get have same up well because between for all each does came just from was an these himself very under over more
|
||
de:hattest hattet hier hinter ich ihr ihre im in ist ja jede jedem jeden jeder jedes jener jenes jetzt kann kannst können könnt machen mein meine mit muß mußt musst müssen müßt nach nachdem nein nicht nun oder seid sein seine sich
|
||
es:detras detrás dia día dias días donde dónde dos durante e el él ella ellas ellos en encima enfrente enseguida entre es esa ésa esas ésas ese ése eso esos ésos esta está ésta estado estados estan están estar estas éstas este éste esto estos éstos ex
|
||
fr:font foce haut hos ici il ils je juste la le les leu là ma maintenant mais mes mine moins mon mot même ni nommés note nous nouveaux ou où pa pace paole pas pesonnes peut peu pièce plupat pou pouquoi quand que quel
|
||
|
||
UI.Help.TechSupport
|
||
en:Technical support
|
||
de:Technische Unterstützung
|
||
es:Asistencia técnica
|
||
fr:Assistance technique
|
||
*/
|
||
|
||
public string GuessCustomLocaleSourceLanguage(LocalizedTextTable lt)
|
||
{
|
||
//try to match up some likely not edited locale translations
|
||
//and give weight to the language with the most matches
|
||
//if no matches should default to en
|
||
Dictionary<string, int> d = new Dictionary<string, int>();
|
||
d.Add("en", 0);//this will default if nothing is matched as sort will just keep same order
|
||
d.Add("de", 0);
|
||
d.Add("fr", 0);
|
||
d.Add("es", 0);
|
||
|
||
|
||
{
|
||
var v = lt.LT["Error.UnableToOpenEmailUrl"];
|
||
switch (v)
|
||
{
|
||
case "Unable to open email address":
|
||
d["en"] += 1;
|
||
break;
|
||
case "E-Mail-Adresse kann nicht geöffnet werden":
|
||
d["de"] += 1;
|
||
break;
|
||
case "No puede abrirse la dirección de e-mail":
|
||
d["es"] += 1;
|
||
break;
|
||
case "Impossible d'ouvrir l'adresse e-mail":
|
||
d["fr"] += 1;
|
||
break;
|
||
}
|
||
}
|
||
|
||
{
|
||
var v = lt.LT["Global.Label.AllowScheduleConflicts"];
|
||
switch (v)
|
||
{
|
||
case "Allow Schedule Conflicts":
|
||
d["en"] += 1;
|
||
break;
|
||
case "Planungskonflikte zulassen":
|
||
d["de"] += 1;
|
||
break;
|
||
case "Permitir conflictos de programación":
|
||
d["es"] += 1;
|
||
break;
|
||
case "Autoriser les conflits de programme":
|
||
d["fr"] += 1;
|
||
break;
|
||
}
|
||
}
|
||
|
||
{
|
||
var v = lt.LT["ReportEditor.Label.Properties"];
|
||
switch (v)
|
||
{
|
||
case "Properties":
|
||
d["en"] += 1;
|
||
break;
|
||
case "Eigenschaften":
|
||
d["de"] += 1;
|
||
break;
|
||
case "Propiedades":
|
||
d["es"] += 1;
|
||
break;
|
||
case "Propriétés":
|
||
d["fr"] += 1;
|
||
break;
|
||
}
|
||
}
|
||
|
||
{
|
||
var v = lt.LT["UI.Help.TechSupport"];
|
||
switch (v)
|
||
{
|
||
case "Technical support":
|
||
d["en"] += 1;
|
||
break;
|
||
case "Technische Unterstützung":
|
||
d["de"] += 1;
|
||
break;
|
||
case "Asistencia técnica":
|
||
d["es"] += 1;
|
||
break;
|
||
case "Assistance technique":
|
||
d["fr"] += 1;
|
||
break;
|
||
}
|
||
}
|
||
|
||
{
|
||
var v = lt.LT["StopWords3"];
|
||
switch (v)
|
||
{
|
||
case "would some what such make come while its use those see out who ll but get have same up well because between for all each does came just from was an these himself very under over more":
|
||
d["en"] += 1;
|
||
break;
|
||
case "hattest hattet hier hinter ich ihr ihre im in ist ja jede jedem jeden jeder jedes jener jenes jetzt kann kannst können könnt machen mein meine mit muß mußt musst müssen müßt nach nachdem nein nicht nun oder seid sein seine sich":
|
||
d["de"] += 1;
|
||
break;
|
||
case "detras detrás dia día dias días donde dónde dos durante e el él ella ellas ellos en encima enfrente enseguida entre es esa ésa esas ésas ese ése eso esos ésos esta está ésta estado estados estan están estar estas éstas este éste esto estos éstos ex":
|
||
d["es"] += 1;
|
||
break;
|
||
case "font foce haut hos ici il ils je juste la le les leu là ma maintenant mais mes mine moins mon mot même ni nommés note nous nouveaux ou où pa pace paole pas pesonnes peut peu pièce plupat pou pouquoi quand que quel":
|
||
d["fr"] += 1;
|
||
break;
|
||
}
|
||
}
|
||
|
||
var ret = d.OrderByDescending(x => x.Value).Take(1).Single();
|
||
|
||
return ret.Key;
|
||
|
||
}
|
||
|
||
|
||
#endregion locale utility
|
||
|
||
#endregion locales
|
||
|
||
#region TAG ITEMS
|
||
#region Unitmodelcategories
|
||
private void ExportUnitModelCategories(ProgressForm progress)
|
||
{
|
||
ResetUniqueNames();
|
||
UnitModelCategories l = UnitModelCategories.GetItems();
|
||
progress.Append("Compiling " + l.Count.ToString() + " Unit model categories");
|
||
foreach (UnitModelCategory i in l) TagMap.Add(i.ID, util.NormalizeTag(GetUniqueName(i.Name) + "." + "unitmodelcategory"));
|
||
}
|
||
#endregion
|
||
|
||
#region Unitservicetypes
|
||
private void ExportUnitServiceTypes(ProgressForm progress)
|
||
{
|
||
ResetUniqueNames();
|
||
UnitServiceTypes l = UnitServiceTypes.GetItems();
|
||
progress.Append("Compiling " + l.Count.ToString() + " Unit service types");
|
||
foreach (UnitServiceType i in l)
|
||
TagMap.Add(i.ID, util.NormalizeTag(GetUniqueName(i.Name) + "." + "unitservicetype"));
|
||
}
|
||
#endregion
|
||
|
||
#region WorkorderItemTypes
|
||
private void ExportWorkorderItemTypes(ProgressForm progress)
|
||
{
|
||
ResetUniqueNames();
|
||
WorkorderItemTypes l = WorkorderItemTypes.GetItems();
|
||
progress.Append("Compiling " + l.Count.ToString() + " Workorder item types");
|
||
foreach (WorkorderItemType i in l)
|
||
TagMap.Add(i.ID, util.NormalizeTag(GetUniqueName(i.Name) + "." + "workorderitemtype"));
|
||
}
|
||
#endregion
|
||
|
||
#region REGIONS
|
||
private void ExportRegions(ProgressForm progress)
|
||
{
|
||
ResetUniqueNames();
|
||
RegionList l = RegionList.GetList(string.Empty);
|
||
progress.Append("Compiling " + l.Count.ToString() + " Regions");
|
||
foreach (RegionList.RegionListInfo i in l)
|
||
TagMap.Add(i.LT_Region_Label_Name.Value, util.NormalizeTag(GetUniqueName(i.LT_Region_Label_Name.Display) + "." + "region"));
|
||
}
|
||
#endregion
|
||
|
||
#region Client groups
|
||
private void ExportClientGroups(ProgressForm progress)
|
||
{
|
||
ResetUniqueNames();
|
||
ClientGroups l = ClientGroups.GetItems();
|
||
progress.Append("Compiling " + l.Count.ToString() + " Client groups");
|
||
foreach (ClientGroup i in l)
|
||
TagMap.Add(i.ID, util.NormalizeTag(GetUniqueName(i.Name) + "." + "clientgroup"));
|
||
}
|
||
#endregion
|
||
|
||
#region Workorder categories
|
||
private void ExportWorkorderCategories(ProgressForm progress)
|
||
{
|
||
ResetUniqueNames();
|
||
WorkorderCategories l = WorkorderCategories.GetItems();
|
||
progress.Append("Compiling " + l.Count.ToString() + " Workorder categories");
|
||
foreach (WorkorderCategory i in l)
|
||
TagMap.Add(i.ID, util.NormalizeTag(GetUniqueName(i.Name) + "." + "workordercategory"));
|
||
}
|
||
#endregion
|
||
|
||
#region Part categories
|
||
private void ExportPartCategories(ProgressForm progress)
|
||
{
|
||
ResetUniqueNames();
|
||
PartCategories l = PartCategories.GetItems();
|
||
progress.Append("Compiling " + l.Count.ToString() + " Part categories");
|
||
foreach (PartCategory i in l) TagMap.Add(i.ID, util.NormalizeTag(GetUniqueName(i.Name) + "." + "partcategory"));
|
||
}
|
||
#endregion
|
||
|
||
#region ScheduleableUserGroups
|
||
private void ExportScheduleableUserGroups(ProgressForm progress)
|
||
{
|
||
ResetUniqueNames();
|
||
ScheduleableUserGroupPickList pl = ScheduleableUserGroupPickList.GetList();
|
||
progress.Append("Compiling " + pl.Count.ToString() + " Scheduleable user groups");
|
||
foreach (ScheduleableUserGroupPickList.ScheduleableUserGroupPickListInfo i in pl)
|
||
TagMap.Add(i.ID, util.NormalizeTag(GetUniqueName(i.Name) + "." + "partcategory"));
|
||
}
|
||
#endregion clients
|
||
|
||
#region Dispatch zones
|
||
private void ExportDispatchZones(ProgressForm progress)
|
||
{
|
||
ResetUniqueNames();
|
||
DispatchZones l = DispatchZones.GetItems(false);
|
||
progress.Append("Compiling " + l.Count.ToString() + " Dispatch zones");
|
||
foreach (DispatchZone i in l)
|
||
TagMap.Add(i.ID, util.NormalizeTag(GetUniqueName(i.Name) + "." + "dispatchzone"));
|
||
}
|
||
#endregion
|
||
|
||
private void ExportUserSkills(ProgressForm progress)
|
||
{
|
||
ResetUniqueNames();
|
||
UserSkills l = UserSkills.GetItems();
|
||
progress.Append("Compiling " + l.Count.ToString() + " User skills");
|
||
foreach (UserSkill i in l)
|
||
TagMap.Add(i.ID, util.NormalizeTag(GetUniqueName(i.Name) + "." + "user-skill"));
|
||
}
|
||
|
||
private void ExportUserCertifications(ProgressForm progress)
|
||
{
|
||
ResetUniqueNames();
|
||
UserCertifications l = UserCertifications.GetItems();
|
||
progress.Append("Compiling " + l.Count.ToString() + " User certifications");
|
||
foreach (UserCertification i in l)
|
||
TagMap.Add(i.ID, util.NormalizeTag(GetUniqueName(i.Name) + "." + "user-certification"));
|
||
}
|
||
|
||
|
||
private void ExportClientNoteTypes(ProgressForm progress)
|
||
{
|
||
ResetUniqueNames();
|
||
ClientNoteTypes l = ClientNoteTypes.GetItems();
|
||
progress.Append("Compiling " + l.Count.ToString() + " Client note types");
|
||
foreach (ClientNoteType i in l)
|
||
TagMap.Add(i.ID, util.NormalizeTag(GetUniqueName(i.Name) + "." + "client-note-type"));
|
||
}
|
||
|
||
|
||
#endregion TAG ITEMS
|
||
|
||
//--------------------------------------------
|
||
#endregion object export
|
||
|
||
#region Custom fields exporter
|
||
|
||
|
||
//export objects custom field data into jobject string
|
||
private string CustomFieldData(object biz, List<int> dateFields)
|
||
{
|
||
dynamic d = new JObject();
|
||
|
||
for (int x = 0; x < 10; x++)
|
||
{
|
||
object o = (object)biz.GetType().GetProperty("Custom" + x.ToString()).GetValue(biz, null);
|
||
string s = o.ToString();
|
||
if (string.IsNullOrWhiteSpace(s))
|
||
{
|
||
s = null;
|
||
}
|
||
if (s != null && dateFields.Contains(x))
|
||
{
|
||
//parse out to UTC date
|
||
DateTime dt = new DateTime();
|
||
if (DateTime.TryParse(s, out dt))
|
||
{
|
||
s = dt.ToUniversalTime().ToString("s");
|
||
if (!string.IsNullOrWhiteSpace(s))
|
||
s = s + ".000Z";//Note the .00z forces to utc so server recognizes that, this is the simple way to accomplish it
|
||
|
||
}
|
||
}
|
||
if (!string.IsNullOrWhiteSpace(s))
|
||
d["c" + (x + 1).ToString()] = s;
|
||
|
||
}
|
||
return d.ToString();
|
||
}
|
||
|
||
static public ObjectCustomFields ObjectHasCustomFieldDataToExport(string sObject)
|
||
{
|
||
ObjectCustomFields ocf = ObjectCustomFields.GetItems(sObject);
|
||
if (ocf.Count == 0)
|
||
{
|
||
return null;
|
||
}
|
||
|
||
foreach (ObjectCustomField f in ocf)
|
||
{
|
||
if (f.Visible)
|
||
return ocf;
|
||
}
|
||
return null;
|
||
}
|
||
|
||
private async System.Threading.Tasks.Task<List<int>> ExportCustomFieldSchema(ObjectCustomFields ocf, string v7CustomFieldObjectName, string RavenCustomTranslationKeyObjectName)
|
||
{
|
||
|
||
var ret = new List<int>();
|
||
if (ocf == null) return ret;
|
||
//NOTE: this code inspired by winforApp::Util.cs PrepareCustomFieldsGrid method
|
||
dynamic d = new JObject();
|
||
var formCustomKey = RavenCustomTranslationKeyObjectName;
|
||
if (formCustomKey == "WorkOrderItem")
|
||
formCustomKey = "WorkOrder";
|
||
|
||
d.formkey = formCustomKey;
|
||
dynamic dtemplate = new JArray();
|
||
Guid Creator = Guid.Empty;
|
||
Guid Modifier = Guid.Empty;
|
||
string Created = string.Empty;
|
||
string Modified = string.Empty;
|
||
|
||
foreach (ObjectCustomField f in ocf)
|
||
{
|
||
if (Creator == Guid.Empty)
|
||
{
|
||
Creator = f.Creator;
|
||
Modifier = f.Modifier;
|
||
Created = f.Created;
|
||
Modified = f.Modified;
|
||
}
|
||
if (f.Visible)
|
||
{
|
||
int n = Convert.ToInt32(f.FieldName.Replace("Custom", "")) + 1;//raven custom fields are 1 based, v7 are zero based
|
||
//CustomFieldLocaleKeys.Add(RavenCustomTranslationKeyObjectName + n.ToString(),
|
||
// util.LocaleText.GetLocalizedText(v7CustomFieldObjectName + ".Label." + f.FieldName));
|
||
|
||
dynamic dt = new JObject();
|
||
dt.fld = RavenCustomTranslationKeyObjectName + "Custom" + n.ToString();
|
||
dt.tKey = dt.fld;
|
||
//dt.hide = false;
|
||
dt.required = false;
|
||
switch (f.FieldType)
|
||
{
|
||
case FormFieldDataTypes.Currency:
|
||
dt.type = util.AyaUiFieldDataType.Currency;
|
||
break;
|
||
case FormFieldDataTypes.DateOnly:
|
||
dt.type = util.AyaUiFieldDataType.Date;
|
||
ret.Add(n - 1);
|
||
break;
|
||
case FormFieldDataTypes.DateTime:
|
||
dt.type = util.AyaUiFieldDataType.DateTime;
|
||
ret.Add(n - 1);
|
||
break;
|
||
case FormFieldDataTypes.Number:
|
||
dt.type = util.AyaUiFieldDataType.Decimal;
|
||
break;
|
||
case FormFieldDataTypes.Text:
|
||
dt.type = util.AyaUiFieldDataType.Text;
|
||
break;
|
||
case FormFieldDataTypes.TimeOnly:
|
||
dt.type = util.AyaUiFieldDataType.Time;
|
||
ret.Add(n - 1);
|
||
break;
|
||
case FormFieldDataTypes.TrueFalse:
|
||
dt.type = util.AyaUiFieldDataType.Bool;
|
||
break;
|
||
default:
|
||
dt.type = util.AyaUiFieldDataType.Text;
|
||
break;
|
||
}
|
||
dtemplate.Add(dt);
|
||
}
|
||
}
|
||
d.template = dtemplate.ToString();
|
||
|
||
//ok, were here because there *are* custom fields available
|
||
|
||
var a = await util.GetAsync("form-custom/" + formCustomKey);
|
||
var RavenFormCustomId = util.IdFromResponse(a);
|
||
var ctoken = util.CTokenFromResponse(a);
|
||
d.concurrency = ctoken;
|
||
await util.PutAsync("form-custom/" + formCustomKey, d);
|
||
|
||
//Event log fixup
|
||
await util.EventLog(util.AyaType.FormCustom, RavenFormCustomId, await Getv7v8IdMap(Creator, RootObjectTypes.User), await Getv7v8IdMap(Modifier, RootObjectTypes.User), Created, Modified);
|
||
|
||
|
||
return ret;
|
||
}
|
||
|
||
#endregion custom fields
|
||
|
||
#region Attachments exporter
|
||
|
||
private async System.Threading.Tasks.Task ExportAttachments(TypeAndID tid, ProgressForm progress, util.AyaType specificRavenType)
|
||
{
|
||
if (!WikiPage.HasWiki(tid.ID)) return;
|
||
|
||
WikiPage w = WikiPage.GetItem(tid);
|
||
AyaFileList fl = AyaFileList.GetList(w.ID);
|
||
if (fl.Count == 0) return;
|
||
|
||
|
||
//iterate the files
|
||
foreach (AyaFileList.AyaFileListInfo i in fl)
|
||
{
|
||
if (!progress.KeepGoing) return;
|
||
var af = AyaFile.GetItem(i.LT_O_AyaFile.Value);
|
||
if (af == null) continue;
|
||
|
||
|
||
progress.SubOp("Wikifile: \"" + af.Name + "\" " + AyaBizUtils.FileSizeDisplay((decimal)af.FileSize));
|
||
if (IsDuplicatev7v8IdMapItem(i.LT_O_AyaFile.Value, i.LT_O_AyaFile.Display, progress)) continue;
|
||
|
||
if (af.FileSize > SmallestFileUploadRejectedAsTooBig)
|
||
{
|
||
progress.Append("WIKI Attachment file " + i.LT_O_AyaFile.Display + " too large (" + AyaBizUtils.FileSizeDisplay(af.FileSize) + "). " + MaxFileSizeInfo(af.FileSize));
|
||
continue;
|
||
}
|
||
|
||
//Compile the FileData property
|
||
var sDate = i.LT_Common_Label_Created.ToString();
|
||
DateTimeOffset dtLastModified = DateTime.UtcNow;
|
||
if (sDate != null)
|
||
{
|
||
//parse out to UTC date
|
||
DateTime dt = new DateTime();
|
||
if (DateTime.TryParse(sDate, out dt))
|
||
{
|
||
dtLastModified = dt.ToUniversalTime();
|
||
}
|
||
}
|
||
dynamic dFileData = new JArray();
|
||
dynamic dFile = new JObject();
|
||
dFile.name = af.Name;
|
||
dFile.lastModified = dtLastModified.ToUnixTimeMilliseconds();
|
||
dFileData.Add(dFile);
|
||
|
||
//Upload
|
||
|
||
MultipartFormDataContent formDataContent = new MultipartFormDataContent();
|
||
|
||
//Form data like the bizobject type and id
|
||
string RavenTypeAsString = "";
|
||
int ravenType = 0;//notype is default for orphaned objects
|
||
long v8Id = await Getv7v8IdMap(tid.ID, tid.RootObjectType, false, true);//default to 0 id if no match which is good for raven as it puts it into a list users can deal with in UI
|
||
if (v8Id != 0)
|
||
{
|
||
if (specificRavenType != util.AyaType.NoType)
|
||
RavenTypeAsString = ((int)specificRavenType).ToString();
|
||
else
|
||
RavenTypeAsString = ((int)util.RootObjectToAyaType(tid.RootObjectType)).ToString();
|
||
}
|
||
formDataContent.Add(new StringContent(RavenTypeAsString), name: "AttachToAType");
|
||
formDataContent.Add(new StringContent(v8Id.ToString()), name: "AttachToObjectId");
|
||
|
||
//user id override
|
||
long? attachedByUserId = await Getv7v8IdMapNullOk(i.LT_Common_Label_Creator.Value, RootObjectTypes.User);
|
||
if (attachedByUserId == null) attachedByUserId = 1;//use superuser
|
||
formDataContent.Add(new StringContent(attachedByUserId.ToString()), name: "OverrideUserId");
|
||
|
||
|
||
if (!string.IsNullOrWhiteSpace(ImportTag))
|
||
formDataContent.Add(new StringContent(ImportTag), name: "Notes");
|
||
formDataContent.Add(new StringContent(dFileData.ToString()), name: "FileData");
|
||
|
||
StreamContent AttachmentFile = new StreamContent(af.GetContent());
|
||
//try to get a more accurate mimetype than was originally
|
||
//in v7 as it uses the same method but from long ago and far far away...
|
||
string MType = af.mimeType;
|
||
try
|
||
{
|
||
MType = MimeTypeMap.GetMimeType(Path.GetExtension(af.Name));//latest up to date mime type
|
||
}
|
||
catch { }
|
||
|
||
AttachmentFile.Headers.ContentType = new MediaTypeHeaderValue(MType);
|
||
AttachmentFile.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data");
|
||
AttachmentFile.Headers.ContentDisposition.FileName = af.Name;
|
||
|
||
AttachmentFile.Headers.ContentDisposition.ModificationDate = dtLastModified;
|
||
formDataContent.Add(AttachmentFile);
|
||
|
||
//Upload
|
||
util.ApiResponse a = null;
|
||
|
||
try
|
||
{
|
||
a = await util.PostFormDataAsync("attachment", formDataContent);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
if (ex.Message.Contains("413"))
|
||
{
|
||
if (SmallestFileUploadRejectedAsTooBig > af.FileSize)
|
||
SmallestFileUploadRejectedAsTooBig = af.FileSize;
|
||
progress.Append("WIKI Attachment file " + i.LT_O_AyaFile.Display + " too large (" + AyaBizUtils.FileSizeDisplay(af.FileSize) + "). " + MaxFileSizeInfo(af.FileSize));
|
||
continue;
|
||
|
||
}
|
||
else
|
||
{
|
||
throw;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
//Map it for later processing of wiki
|
||
//attachment upload route returns all attachments for this object in file name order
|
||
//so need to find *our* most recent uploaded attachment so need to find the highest raven id number
|
||
var returnArray = ((JArray)a.ObjectResponse["data"]);
|
||
var ravenId = -1L;
|
||
foreach (JObject o in returnArray)
|
||
{
|
||
var oId = o["id"].Value<long>();
|
||
if (oId > ravenId) ravenId = oId;
|
||
}
|
||
|
||
Addv7v8IdMap(af.ID, ravenId);
|
||
}
|
||
|
||
progress.SubOp("");
|
||
|
||
}
|
||
#endregion attachments
|
||
|
||
#region Assigned docs exporter
|
||
|
||
private async System.Threading.Tasks.Task<string> ExportDocs(TypeAndID tid, AssignedDocs docs, ProgressForm progress, util.AyaType specificRavenType)
|
||
{
|
||
|
||
|
||
if (!ExportAssignedDocs) return null;
|
||
if (docs == null) return null;
|
||
|
||
if (docs.Count == 0) return null;
|
||
|
||
string NonFileUrls = string.Empty;
|
||
//iterate the files
|
||
foreach (AssignedDoc doc in docs)
|
||
{
|
||
|
||
if (!progress.KeepGoing) return null;
|
||
|
||
//is it a local file?
|
||
if (!new Uri(doc.URL).IsFile)
|
||
{
|
||
NonFileUrls += (doc.Description + "(v8Migrate) " + doc.URL + "\n");
|
||
continue;
|
||
}
|
||
//can we see it?
|
||
if (!File.Exists(doc.URL))
|
||
{
|
||
NonFileUrls += ("v8Migrate - Assigned doc. file not found: " + doc.Description + " " + doc.URL + "\n");
|
||
continue;
|
||
}
|
||
|
||
//get the file info
|
||
FileInfo fi = new FileInfo(doc.URL);
|
||
|
||
progress.SubOp("Assigned doc: \"" + doc.URL + "\" " + AyaBizUtils.FileSizeDisplay((decimal)fi.Length));
|
||
|
||
|
||
if (fi.Length > SmallestFileUploadRejectedAsTooBig)
|
||
{
|
||
progress.Append("Assigned doc. file " + doc.Description + " " + doc.URL + " too large (" + AyaBizUtils.FileSizeDisplay(fi.Length) + "). " + MaxFileSizeInfo(fi.Length));
|
||
NonFileUrls += ("v8Migrate - Assigned doc. file " + doc.Description + " " + doc.URL + " too large (" + AyaBizUtils.FileSizeDisplay(fi.Length) + ")." + MaxFileSizeInfo(fi.Length) + "\n");
|
||
continue;
|
||
}
|
||
//Compile the FileData property
|
||
|
||
DateTimeOffset dtLastModified = fi.LastWriteTimeUtc;
|
||
|
||
dynamic dFileData = new JArray();
|
||
dynamic dFile = new JObject();
|
||
dFile.name = fi.Name;
|
||
dFile.lastModified = dtLastModified.ToUnixTimeMilliseconds();
|
||
dFileData.Add(dFile);
|
||
|
||
//Upload
|
||
|
||
MultipartFormDataContent formDataContent = new MultipartFormDataContent();
|
||
|
||
//Form data like the bizobject type and id
|
||
|
||
|
||
string RavenTypeAsString = "";
|
||
int ravenType = 0;//notype is default for orphaned objects
|
||
long v8Id = await Getv7v8IdMap(tid.ID, tid.RootObjectType, false, true);//default to 0 id if no match which is good for raven as it puts it into a list users can deal with in UI
|
||
if (v8Id != 0)
|
||
{
|
||
if (specificRavenType != util.AyaType.NoType)
|
||
RavenTypeAsString = ((int)specificRavenType).ToString();
|
||
else
|
||
RavenTypeAsString = ((int)util.RootObjectToAyaType(tid.RootObjectType)).ToString();
|
||
}
|
||
|
||
|
||
// string RavenTypeAsString = "";
|
||
// if (specificRavenType != util.AyaType.NoType)
|
||
// RavenTypeAsString = ((int)specificRavenType).ToString();
|
||
// else
|
||
// RavenTypeAsString = ((int)util.RootObjectToAyaType(tid.RootObjectType)).ToString();
|
||
formDataContent.Add(new StringContent(RavenTypeAsString), name: "AttachToAType");
|
||
// formDataContent.Add(new StringContent(Map[tid.ID].ToString()), name: "AttachToObjectId");
|
||
formDataContent.Add(new StringContent(v8Id.ToString()), name: "AttachToObjectId");
|
||
|
||
|
||
|
||
formDataContent.Add(new StringContent(doc.Description + " (v8Migrate)"), name: "Notes");
|
||
formDataContent.Add(new StringContent(dFileData.ToString()), name: "FileData");
|
||
|
||
StreamContent AttachmentFile = new StreamContent(fi.OpenRead());
|
||
AttachmentFile.Headers.ContentType = new MediaTypeHeaderValue(MimeTypeMap.GetMimeType(fi.Extension));
|
||
AttachmentFile.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data");
|
||
AttachmentFile.Headers.ContentDisposition.FileName = fi.Name;
|
||
|
||
AttachmentFile.Headers.ContentDisposition.ModificationDate = dtLastModified;
|
||
formDataContent.Add(AttachmentFile);
|
||
|
||
//Upload
|
||
try
|
||
{
|
||
#if(DEBUG)
|
||
progress.Append("Sending file " + fi.Name);
|
||
#endif
|
||
await util.PostFormDataAsync("attachment", formDataContent);
|
||
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
#if(DEBUG)
|
||
progress.Append("ERROR Sending file " + fi.Name);
|
||
#endif
|
||
if (ex.Message.Contains("413"))
|
||
{
|
||
if (SmallestFileUploadRejectedAsTooBig > fi.Length)
|
||
SmallestFileUploadRejectedAsTooBig = fi.Length;
|
||
progress.Append("Assigned doc. file " + doc.Description + " " + doc.URL + " too large (" + AyaBizUtils.FileSizeDisplay(fi.Length) + "). " + MaxFileSizeInfo(fi.Length));
|
||
NonFileUrls += ("v8Migrate - Assigned doc. file " + doc.Description + " " + doc.URL + " too large (" + AyaBizUtils.FileSizeDisplay(fi.Length) + ")." + MaxFileSizeInfo(fi.Length) + "\n");
|
||
continue;
|
||
|
||
}
|
||
else
|
||
{
|
||
throw;
|
||
}
|
||
}
|
||
//No need to map it or save response as long as it works that's all that matters
|
||
#if(DEBUG)
|
||
progress.Append("File attached successfully " + fi.Name);
|
||
#endif
|
||
|
||
}
|
||
|
||
progress.SubOp("");
|
||
return NonFileUrls;
|
||
}
|
||
#endregion assigned docs
|
||
|
||
#region WIKI page exporter
|
||
|
||
|
||
//WIKI
|
||
|
||
private async System.Threading.Tasks.Task<string> GetWikiContent(TypeAndID tid)
|
||
{
|
||
//may not exist
|
||
// if (!WikiPage.HasWiki(tid.ID)) return null;
|
||
|
||
WikiPage w = WikiPage.GetItem(tid);
|
||
var content = w.GetContentAsString;
|
||
if (string.IsNullOrWhiteSpace(content)) return null;
|
||
|
||
|
||
MatchCollection mc = AyaBizUtils.rxAyaImageTags.Matches(content);
|
||
foreach (Match m in mc)
|
||
{
|
||
//this is image links to file attachments
|
||
//if there is no match then chuck it, it's not critically important and already was missing in v7 anyway
|
||
var RavenId = await Getv7v8IdMapNullOk(new Guid(m.Groups["guid"].Value), tid.RootObjectType, false, true);//Map[new Guid(m.Groups["guid"].Value)];
|
||
if (RavenId != null)
|
||
content = content.Replace(m.Value, "<img src=\"" + "[ATTACH:" + RavenId.ToString() + "]" + "\">");
|
||
else
|
||
content = content.Replace(m.Value, "[v8migrate deleted / not found]");
|
||
}
|
||
|
||
//todo: fixup font size, try to convert to heading tags instead
|
||
//figure out how to remove the inline style tag as it comes through for some reason
|
||
|
||
//TODO: Convert to Markdown format
|
||
//found 2 likely candidate libs to do this
|
||
//https://www.nuget.org/packages/Html2Markdown/
|
||
//https://www.nuget.org/packages/ReverseMarkdown/
|
||
|
||
//Not much difference at first glance, very similar stats, reverse markdown is a little more widely used and seems to have more config options
|
||
//built in, the other has to make some kind of processor for some stuff maybe so going with reverse for now
|
||
|
||
//NOTE: for this to work had to add an assembly redirect in app.config for ayanova project as it was looking for a really old version of
|
||
//the HtmlAgilityPack
|
||
|
||
var config = new ReverseMarkdown.Config
|
||
{
|
||
UnknownTags = ReverseMarkdown.Config.UnknownTagsOption.Bypass,
|
||
GithubFlavored = true, // generate GitHub flavoured markdown, supported for BR, PRE and table tags
|
||
RemoveComments = true, // will ignore all comments, (narrator: "It doesn't")
|
||
SmartHrefHandling = true // remove markdown output for links where appropriate
|
||
};
|
||
|
||
var converter = new ReverseMarkdown.Converter(config);
|
||
string res = converter.Convert(content);
|
||
//strip out comment and style chunk that is left behind by above tool
|
||
//style is an artifact of the rtf2html converter used by v7
|
||
int nStart = res.IndexOf("<!--");
|
||
int nEnd = res.IndexOf("-->");
|
||
if (nStart != -1 && nEnd != -1)
|
||
{
|
||
res = res.Substring(0, nStart) + res.Substring(nEnd + 3);
|
||
// res = newRes;
|
||
}
|
||
|
||
|
||
return res;
|
||
}
|
||
|
||
#region sample before and after
|
||
/*
|
||
* INPUT
|
||
* <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||
<html>
|
||
<head>
|
||
<title>Untitled document</title>
|
||
<meta http-equiv="Content-Type" content="text/html; charset=Windows-1252">
|
||
<style type="text/css">
|
||
<!--
|
||
span.st1
|
||
{
|
||
font-family: Arial;
|
||
font-size: 39pt;
|
||
color: #000000;
|
||
}
|
||
span.st2
|
||
{
|
||
font-family: Arial;
|
||
font-size: 8pt;
|
||
color: #000000;
|
||
}
|
||
span.st3
|
||
{
|
||
font-family: Arial;
|
||
font-size: 6pt;
|
||
color: #000000;
|
||
}
|
||
span.st4
|
||
{
|
||
font-family: Arial;
|
||
font-size: 150pt;
|
||
color: #000000;
|
||
}
|
||
span.st5
|
||
{
|
||
font-family: Arial;
|
||
font-size: 8pt;
|
||
color: #ff0000;
|
||
}
|
||
span.st6
|
||
{
|
||
font-family: Arial;
|
||
font-size: 8pt;
|
||
color: #00ff00;
|
||
}
|
||
span.st7
|
||
{
|
||
font-family: Arial;
|
||
font-size: 8pt;
|
||
color: #000080;
|
||
}
|
||
span.st8
|
||
{
|
||
font-family: Symbol;
|
||
font-size: 8pt;
|
||
color: #000080;
|
||
}
|
||
span.st9
|
||
{
|
||
font-family: Consolas;
|
||
font-size: 8pt;
|
||
color: #000000;
|
||
}
|
||
span.st10
|
||
{
|
||
font-family: Comic Sans MS;
|
||
font-size: 8pt;
|
||
color: #000000;
|
||
}
|
||
span.st11
|
||
{
|
||
font-family: Comic Sans MS;
|
||
font-size: 16pt;
|
||
color: #000000;
|
||
}
|
||
span.st12
|
||
{
|
||
font-family: Arial;
|
||
font-size: 32pt;
|
||
color: #000000;
|
||
}
|
||
span.st13
|
||
{
|
||
font-family: Times New Roman;
|
||
font-size: 12pt;
|
||
color: #000000;
|
||
}
|
||
-->
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div><span class="st1"><b><u>Here is a title</u></b></span></div>
|
||
<div><span class="st2"></span> </div>
|
||
<div><span class="st2">This document has all possible options on it except for picking every single font size so instead</span><span class="st3"> here is the smallest font size which is 6</span></div>
|
||
<div><span class="st3">and here is the</span><span class="st4"> largest font size which is 150</span></div>
|
||
<div><span class="st3"></span> </div>
|
||
<div><span class="st2"><i>italics </i>and <u>underlined </u>and <b>bold</b></span></div>
|
||
<div><span class="st2"><b></b></span> </div>
|
||
<div><span class="st2"><b></b></span><span class="st5"><b>red </b></span></div>
|
||
<div><span class="st2"><b></b></span> </div>
|
||
<div><span class="st6"><b>green </b></span></div>
|
||
<div><span class="st2"><b></b></span> </div>
|
||
<div><span class="st2"><b></b></span><span class="st7"><b>blue</b></span></div>
|
||
<div><span class="st7"><b></b></span> </div>
|
||
<div><span class="st7"><b></b></span></div>
|
||
<ul style="list-style-type: none">
|
||
<li><span class="st8"><b>· </b></span><span class="st7"><b>List one</b></span></li>
|
||
<li><span class="st7"><b></b></span><span class="st8"><b>· </b></span><span class="st7"><b>list two </b></span></li>
|
||
<li><span class="st7"><b></b></span><span class="st8"><b>· </b></span><span class="st7"><b>list three</b></span></li>
|
||
</ul><div><span class="st7"><b></b></span></div>
|
||
<div><span class="st7"><b></b></span> </div>
|
||
<div><span class="st2"><b></b></span> </div>
|
||
<div><span class="st2"><b></b></span> </div>
|
||
<div><span class="st2">Arial font</span></div>
|
||
<div><span class="st2"></span> </div>
|
||
<div><span class="st2"></span><span class="st9">Consolas font</span></div>
|
||
<div><span class="st9"></span> </div>
|
||
<div><span class="st9"></span><span class="st10">Comic sans</span></div>
|
||
<div><span class="st10"></span> </div>
|
||
<div><span class="st10"></span><span class="st11">font size 16 double default size whih is 8 </span></div>
|
||
<div><span class="st2"></span> </div>
|
||
<div><span class="st2"></span><span class="st12">font size 32 which is quadruple default of 8</span></div>
|
||
<div><span class="st2"></span> </div>
|
||
<div><span class="st2"></span> </div>
|
||
<div><span class="st2">This block is</span></div>
|
||
<div><span class="st2">left aligned</span></div>
|
||
<div><span class="st2"></span> </div>
|
||
<div><span class="st2"></span> </div>
|
||
<div align="center"><span class="st2">this block is </span></div>
|
||
<div align="center"><span class="st2">center aligned</span></div>
|
||
<div><span class="st2"></span> </div>
|
||
<div align="right"><span class="st2">This block is </span></div>
|
||
<div align="right"><span class="st2">right aligned</span></div>
|
||
<div><span class="st2"></span> </div>
|
||
<div><span class="st2"></span> </div>
|
||
<div><span class="st2"></span> </div>
|
||
<div><span class="st2"> <img src="[ATTACH:4]"></span><span class="st13">Wiki page - User: AyaNova Administrator</span></div>
|
||
<div><span class="st13"></span> </div>
|
||
<div><span class="st13">and more:</span></div>
|
||
<div><span class="st13"></span><span class="st2"> <img src="[ATTACH:4]"></span></div>
|
||
<div><span class="st2"></span> </div>
|
||
<div><span class="st2">and more</span></div>
|
||
<div><span class="st2"> <img src="[ATTACH:4]"></span></div>
|
||
<div><span class="st2"></span> </div>
|
||
<div><span class="st13"></span></div></body>
|
||
</html>
|
||
|
||
========================================
|
||
|
||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||
<html>
|
||
<head>
|
||
<title>Untitled document</title>
|
||
<meta http-equiv="Content-Type" content="text/html; charset=Windows-1252">
|
||
<style type="text/css">
|
||
<!--
|
||
span.st1
|
||
{
|
||
font-family: Times New Roman;
|
||
font-size: 20pt;
|
||
color: #000000;
|
||
}
|
||
span.st2
|
||
{
|
||
font-family: Times New Roman;
|
||
font-size: 18pt;
|
||
color: #000000;
|
||
}
|
||
span.st3
|
||
{
|
||
font-family: Times New Roman;
|
||
font-size: 10pt;
|
||
color: #000000;
|
||
}
|
||
span.st4
|
||
{
|
||
font-family: Times New Roman;
|
||
font-size: 24pt;
|
||
color: #000000;
|
||
}
|
||
span.st5
|
||
{
|
||
font-family: Times New Roman;
|
||
font-size: 12pt;
|
||
color: #000000;
|
||
}
|
||
-->
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div><span class="st1">Wiki page - User: Eva Alexander - ALL REGIONS</span></div>
|
||
<div><span class="st2"></span><span class="st3">Here is some under title text</span></div>
|
||
<div><span class="st3"></span> </div>
|
||
<div><span class="st3"></span><span class="st4">Here is another title</span></div>
|
||
<div><span class="st3"></span> </div>
|
||
<div><span class="st3">Here is some more under title text.</span></div>
|
||
<div><span class="st3">Some more lines</span></div>
|
||
<div><span class="st3">like this one</span></div>
|
||
<div><span class="st3">and this one</span></div>
|
||
<div><span class="st3"></span> </div>
|
||
<div><span class="st5"></span></div></body>
|
||
</html>
|
||
=-=-=-==-
|
||
|
||
*
|
||
*
|
||
*
|
||
output
|
||
* Untitled document<!--<br>span.st1
|
||
{
|
||
font-family: Arial;
|
||
font-size: 39pt;
|
||
color: #000000;
|
||
}<br>span.st2
|
||
{
|
||
font-family: Arial;
|
||
font-size: 8pt;
|
||
color: #000000;
|
||
}<br>span.st3
|
||
{
|
||
font-family: Arial;
|
||
font-size: 6pt;
|
||
color: #000000;
|
||
}<br>span.st4
|
||
{
|
||
font-family: Arial;
|
||
font-size: 150pt;
|
||
color: #000000;
|
||
}<br>span.st5
|
||
{
|
||
font-family: Arial;
|
||
font-size: 8pt;
|
||
color: #ff0000;
|
||
}<br>span.st6
|
||
{
|
||
font-family: Arial;
|
||
font-size: 8pt;
|
||
color: #00ff00;
|
||
}<br>span.st7
|
||
{
|
||
font-family: Arial;
|
||
font-size: 8pt;
|
||
color: #000080;
|
||
}<br>span.st8
|
||
{
|
||
font-family: Symbol;
|
||
font-size: 8pt;
|
||
color: #000080;
|
||
}<br>span.st9
|
||
{
|
||
font-family: Consolas;
|
||
font-size: 8pt;
|
||
color: #000000;
|
||
}<br>span.st10
|
||
{
|
||
font-family: Comic Sans MS;
|
||
font-size: 8pt;
|
||
color: #000000;
|
||
}<br>span.st11
|
||
{
|
||
font-family: Comic Sans MS;
|
||
font-size: 16pt;
|
||
color: #000000;
|
||
}<br>span.st12
|
||
{
|
||
font-family: Arial;
|
||
font-size: 32pt;
|
||
color: #000000;
|
||
}<br>span.st13
|
||
{
|
||
font-family: Times New Roman;
|
||
font-size: 12pt;
|
||
color: #000000;
|
||
}<br>-->
|
||
|
||
**Here is a title**
|
||
|
||
|
||
|
||
This document has all possible options on it except for picking every single font size so instead here is the smallest font size which is 6
|
||
|
||
and here is the largest font size which is 150
|
||
|
||
|
||
|
||
*italics*and underlined and **bold**
|
||
|
||
|
||
|
||
**red**
|
||
|
||
|
||
|
||
**green**
|
||
|
||
|
||
|
||
**blue**
|
||
|
||
|
||
|
||
|
||
|
||
- **·****List one**
|
||
- **·****list two**
|
||
- **·****list three**
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
Arial font
|
||
|
||
|
||
|
||
Consolas font
|
||
|
||
|
||
|
||
Comic sans
|
||
|
||
|
||
|
||
font size 16 double default size whih is 8
|
||
|
||
|
||
|
||
font size 32 which is quadruple default of 8
|
||
|
||
|
||
|
||
|
||
|
||
This block is
|
||
|
||
left aligned
|
||
|
||
|
||
|
||
|
||
|
||
this block is
|
||
|
||
center aligned
|
||
|
||
|
||
|
||
This block is
|
||
|
||
right aligned
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
Wiki page - User: AyaNova Administrator
|
||
|
||
|
||
|
||
and more:
|
||
|
||

|
||
|
||
|
||
|
||
and more
|
||
|
||

|
||
|
||
|
||
|
||
|
||
=========================
|
||
Untitled document<!--<br>span.st1
|
||
{
|
||
font-family: Times New Roman;
|
||
font-size: 20pt;
|
||
color: #000000;
|
||
}<br>span.st2
|
||
{
|
||
font-family: Times New Roman;
|
||
font-size: 18pt;
|
||
color: #000000;
|
||
}<br>span.st3
|
||
{
|
||
font-family: Times New Roman;
|
||
font-size: 10pt;
|
||
color: #000000;
|
||
}<br>span.st4
|
||
{
|
||
font-family: Times New Roman;
|
||
font-size: 24pt;
|
||
color: #000000;
|
||
}<br>span.st5
|
||
{
|
||
font-family: Times New Roman;
|
||
font-size: 12pt;
|
||
color: #000000;
|
||
}<br>-->
|
||
|
||
Wiki page - User: Eva Alexander - ALL REGIONS
|
||
|
||
Here is some under title text
|
||
|
||
|
||
|
||
Here is another title
|
||
|
||
|
||
|
||
Here is some more under title text.
|
||
|
||
Some more lines
|
||
|
||
like this one
|
||
|
||
and this one
|
||
|
||
|
||
|
||
|
||
|
||
*
|
||
*/
|
||
#endregion sample
|
||
|
||
#endregion wiki
|
||
|
||
#region TAGS
|
||
private void TagFromv7Guid(Guid g, List<string> tags)
|
||
{
|
||
if (g == Guid.Empty) return;
|
||
if (!TagMap.ContainsKey(g)) return;
|
||
var t = TagMap[g];
|
||
if (!string.IsNullOrWhiteSpace(t))
|
||
{
|
||
tags.Add(t);
|
||
}
|
||
}
|
||
|
||
private void SetTags(dynamic d, List<string> tags)
|
||
{
|
||
dynamic dtags = new JArray();
|
||
foreach (string s in tags)
|
||
dtags.Add(s);
|
||
d.tags = dtags;
|
||
}
|
||
|
||
#endregion tags
|
||
|
||
|
||
|
||
|
||
//eoc
|
||
}
|
||
|
||
//eons
|
||
}
|