Import added headoffice support, tests ok

This commit is contained in:
2022-03-27 23:26:26 +00:00
parent 7c48ffdcd5
commit 8d0e64af99
4 changed files with 258 additions and 12 deletions

2
.vscode/launch.json vendored
View File

@@ -48,7 +48,7 @@
"AYANOVA_DATA_PATH": "c:\\temp\\ravendata",
"AYANOVA_USE_URLS": "http://*:7575;",
//"AYANOVA_PERMANENTLY_ERASE_DATABASE":"true",
"AYANOVA_SERVER_TEST_MODE": "true",
"AYANOVA_SERVER_TEST_MODE": "false",
"AYANOVA_SERVER_TEST_MODE_TZ_OFFSET": "-8",
//"AYANOVA_REPORT_RENDERING_TIMEOUT":"1",
"AYANOVA_SERVER_TEST_MODE_SEEDLEVEL": "small",

View File

@@ -0,0 +1,171 @@
# Head office import / update specifications
Names of fields listed here are the exact case and spelling required to be recognized by AyaNova for importing / updating.
Any field in the import file that is not listed on this page will be removed before sending to the server for import.
## Required fields
- Name
## Key field used to match to existing records
- Name
## Fields directly importable / updateable
- Name (import only)
- Active
- Notes
- Wiki
- Tags
- WebAddress
- TechNotes
- AccountNumber
- ContractExpires
- Phone1
- Phone2
- Phone3
- Phone4
- Phone5
- EmailAddress
- PostAddress
- PostCity
- PostRegion
- PostCountry
- PostCode
- Address
- City
- Region
- Country
- Latitude
- Longitude
## Linked object fields
The following linked objects are supported for importing (update is not supported for these fields):
- Contract via "ContractViz" field which must contain the name of an existing Contract
If the Contract is specified then the ContractExpires field can be used to set the expiry date of the Contract.
If ContractExpires is omitted then it is set to the import date and time minus 1 minute so that it won't take effect until it's been set to a future date.
## JSON file format
The .json file must contain an array of Head office objects, so even a single object must be within [] square brackets in the import file.
The first Head office record here illustrates importing a Head office with a link to an existing Contract.
```JSON
[
{
"Name": "Goodwin LLC",
"Active": true,
"Notes": "Triple-buffered mission-critical website",
"Wiki": null,
"Tags": [
"jade",
"zone5",
"zone7"
],
"WebAddress": "http://example.info",
"AccountNumber": "32906249",
"ContractViz": "Bronze",
"ContractExpires": "2023-02-01T08:00:00Z",
"Phone1": "1-629-420-0186",
"Phone2": "1-807-405-5544 x2470",
"Phone3": "352-364-6323 x45752",
"Phone4": null,
"Phone5": null,
"EmailAddress": "Heloise_Farrell61@example.com",
"PostAddress": "865 Tracey Views",
"PostCity": "New Mathew",
"PostRegion": "Louisiana",
"PostCountry": "Jordan",
"PostCode": "74354-4982",
"Address": "3400 Tyree Keys",
"City": "New Mathew",
"Region": "Louisiana",
"Country": "Jordan",
"Latitude": -56.030700,
"Longitude": 169.844400
},
{
"Name": "Bahringer - Stark",
"Active": true,
"Notes": "Enhanced reciprocal matrix",
"Wiki": null,
"Tags": [
"green",
"red",
"zone8"
],
"WebAddress": "http://example.name",
"AccountNumber": "71115129",
"ContractViz": null,
"ContractExpires": null,
"Phone1": "530.785.2024 x019",
"Phone2": "418-629-2283 x34011",
"Phone3": "347.447.3645",
"Phone4": null,
"Phone5": null,
"EmailAddress": "Alysa.Mertz@example.org",
"PostAddress": "0511 Deckow Stream",
"PostCity": "Lake Cotyhaven",
"PostRegion": "Nevada",
"PostCountry": "Comoros",
"PostCode": "41680",
"Address": "2438 Sibyl Neck",
"City": "Lake Cotyhaven",
"Region": "Nevada",
"Country": "Comoros",
"Latitude": -51.706500,
"Longitude": -22.044300
},
{
"Name": "Beer, Armstrong and Wiegand",
"Active": true,
"Notes": "Compatible clear-thinking concept",
"Wiki": null,
"Tags": [
"zone3",
"zone9"
],
"WebAddress": "https://example.org",
"AccountNumber": "95120864",
"ContractViz": null,
"ContractExpires": null,
"Phone1": "986-530-1111",
"Phone2": "1-695-482-1199 x321",
"Phone3": "653-466-6627 x117",
"Phone4": null,
"Phone5": null,
"EmailAddress": "Elisa41@example.org",
"PostAddress": "15461 Deangelo Tunnel",
"PostCity": "Kuvalisland",
"PostRegion": "Florida",
"PostCountry": "Sri Lanka",
"PostCode": "15255-8577",
"Address": "4695 Sandra Tunnel",
"City": "Kuvalisland",
"Region": "Florida",
"Country": "Sri Lanka",
"Latitude": -19.439100,
"Longitude": -112.588800
}
]
```
## CSV file format
The first row of the .csv file must contain column headers that match the field names listed above.
The first Head office record here illustrates importing a Head office with a link to an existing Contract.
```
Name,Active,Notes,Wiki,Tags,WebAddress,AccountNumber,ContractViz,ContractExpires,Phone1,Phone2,Phone3,Phone4,Phone5,EmailAddress,PostAddress,PostCity,PostRegion,PostCountry,PostCode,Address,City,Region,Country,Latitude,Longitude
Goodwin LLC,true,Triple-buffered mission-critical website,,"jade,zone5,zone7",http://example.info,32906249,Bronze,2023-02-01T08:00:00Z,1-629-420-0186,1-807-405-5544 x2470,352-364-6323 x45752,,,Heloise_Farrell61@example.com,865 Tracey Views,New Mathew,Louisiana,Jordan,74354-4982,3400 Tyree Keys,New Mathew,Louisiana,Jordan,-56.0307,169.8444
Bahringer - Stark,true,Enhanced reciprocal matrix,,"green,red,zone8",http://example.name,71115129,,,530.785.2024 x019,418-629-2283 x34011,347.447.3645,,,Alysa.Mertz@example.org,0511 Deckow Stream,Lake Cotyhaven,Nevada,Comoros,41680,2438 Sibyl Neck,Lake Cotyhaven,Nevada,Comoros,-51.7065,-22.0443
"Beer, Armstrong and Wiegand",true,Compatible clear-thinking concept,,"zone3,zone9",https://example.org,95120864,,,986-530-1111,1-695-482-1199 x321,653-466-6627 x117,,,Elisa41@example.org,15461 Deangelo Tunnel,Kuvalisland,Florida,Sri Lanka,15255-8577,4695 Sandra Tunnel,Kuvalisland,Florida,Sri Lanka,-19.4391,-112.5888
```

View File

@@ -165,6 +165,7 @@ Types not listed may be added in future; for needs beyond what is provided with
Each object type listed below links to a page showing the specific format required for the import file and special notes about importing that specific object:
- [Customers](adm-import-customer.md)
- [Head offices](adm-import-headoffice.md)
## Import form

View File

@@ -343,32 +343,106 @@ namespace AyaNova.Biz
public async Task<List<string>> ImportData(AyImportData importData)
public async Task<List<string>> ImportData(AyImportData importData)
{
List<string> ImportResult = new List<string>();
string ImportTag = $"imported-{FileUtil.GetSafeDateFileName()}";
string ImportTag = ImportUtil.GetImportTag();
//ignore these fields
var jsset = JsonSerializer.CreateDefault(new JsonSerializerSettings { ContractResolver = new AyaNova.Util.JsonUtil.ShouldSerializeContractResolver(new string[] { "Concurrency", "Id", "CustomFields" }) });
foreach (JObject j in importData.Data)
{
var w = j.ToObject<HeadOffice>(jsset);
if (j["CustomFields"] != null)
w.CustomFields = j["CustomFields"].ToString();
w.Tags.Add(ImportTag);//so user can find them all and revert later if necessary
var res = await CreateAsync(w);
if (res == null)
long existingId = await ct.HeadOffice.AsNoTracking().Where(z => z.Name == (string)j["Name"]).Select(x => x.Id).FirstOrDefaultAsync();
if (existingId == 0)
{
ImportResult.Add($"* {w.Name} - {this.GetErrorsAsString()}");
this.ClearErrors();
if (importData.DoImport)
{
//import this record
var o = j.ToObject<HeadOffice>(jsset);
o.Tags.Add(ImportTag);
if (!JsonUtil.JTokenIsNullOrEmpty(j["ContractViz"]))
{
o.ContractId = await ct.Contract.AsNoTracking().Where(z => z.Name == (string)j["ContractViz"]).Select(x => x.Id).FirstOrDefaultAsync();
if (o.ContractId == 0)
AddError(ApiErrorCode.NOT_FOUND, "ContractViz", $"'{(string)j["ContractViz"]}'");
if (JsonUtil.JTokenIsNullOrEmpty(j["ContractExpires"]))
o.ContractExpires = DateTime.UtcNow.Subtract(new TimeSpan(0, 1, 0));//expired one minute ago to be safe, can't guess what the contract should be
else
o.ContractExpires = (DateTime)j["ContractExpires"];
}
var res = await CreateAsync(o);
if (res == null)
{
ImportResult.Add($"❌ {o.Name}\r\n{this.GetErrorsAsString()}");
this.ClearErrors();
}
else
{
ImportResult.Add($"✔️ {o.Name}");
}
}
}
else
{
ImportResult.Add($"{w.Name} - ok");
if (importData.DoUpdate)
{
//update this record with any data provided
//load existing record
var target = await GetAsync((long)existingId);
var source = j.ToObject<HeadOffice>(jsset);
var propertiesToUpdate = j.Properties().Select(p => p.Name).ToList();
propertiesToUpdate.Remove("Name");
ImportUtil.Update(source, target, propertiesToUpdate);
var res = await PutAsync(target);
if (res == null)
{
ImportResult.Add($"❌ {target.Name} - {this.GetErrorsAsString()}");
this.ClearErrors();
}
else
{
ImportResult.Add($"✔️ {target.Name}");
}
}
}
}
return ImportResult;
}
// public async Task<List<string>> ImportData(AyImportData importData)
// {
// List<string> ImportResult = new List<string>();
// string ImportTag = $"imported-{FileUtil.GetSafeDateFileName()}";
// var jsset = JsonSerializer.CreateDefault(new JsonSerializerSettings { ContractResolver = new AyaNova.Util.JsonUtil.ShouldSerializeContractResolver(new string[] { "Concurrency", "Id", "CustomFields" }) });
// foreach (JObject j in importData.Data)
// {
// var w = j.ToObject<HeadOffice>(jsset);
// if (j["CustomFields"] != null)
// w.CustomFields = j["CustomFields"].ToString();
// w.Tags.Add(ImportTag);//so user can find them all and revert later if necessary
// var res = await CreateAsync(w);
// if (res == null)
// {
// ImportResult.Add($"* {w.Name} - {this.GetErrorsAsString()}");
// this.ClearErrors();
// }
// else
// {
// ImportResult.Add($"{w.Name} - ok");
// }
// }
// return ImportResult;
// }
////////////////////////////////////////////////////////////////////////////////////////////////
//JOB / OPERATIONS