629 lines
16 KiB
Vue
629 lines
16 KiB
Vue
<template>
|
|
<div v-if="formState.ready">
|
|
<gz-error :error-box-message="formState.errorBoxMessage"></gz-error>
|
|
<div>
|
|
<v-row>
|
|
<v-col cols="12" sm="6" lg="4" xl="3">
|
|
<v-select
|
|
ref="ayaType"
|
|
v-model="ayaType"
|
|
:items="selectLists.importableAyaTypes"
|
|
item-text="name"
|
|
item-value="id"
|
|
:label="$ay.t('AyaType')"
|
|
data-cy="ayaType"
|
|
></v-select>
|
|
</v-col>
|
|
|
|
<v-col v-if="ayaType != 0" cols="12" sm="6" lg="4" xl="3">
|
|
<v-checkbox
|
|
v-model="doImport"
|
|
:label="$ay.t('ImportNewRecords')"
|
|
></v-checkbox>
|
|
<v-checkbox
|
|
v-model="doUpdate"
|
|
:label="$ay.t('UpdateExistingRecords')"
|
|
color="warning"
|
|
></v-checkbox>
|
|
</v-col>
|
|
|
|
<v-col
|
|
v-if="ayaType != 0 && (doImport || doUpdate)"
|
|
cols="12"
|
|
sm="6"
|
|
lg="4"
|
|
xl="3"
|
|
>
|
|
<v-file-input
|
|
v-model="uploadFile"
|
|
:label="$ay.t('FileToImport')"
|
|
accept=".json, .csv, application/json, text/csv"
|
|
prepend-icon="$ayiFileUpload"
|
|
show-size
|
|
></v-file-input
|
|
><v-btn
|
|
v-if="importable"
|
|
:loading="uploading"
|
|
color="primary"
|
|
text
|
|
@click="process"
|
|
>{{ $ay.t("Import") }}</v-btn
|
|
>
|
|
</v-col>
|
|
|
|
<v-col v-if="outputText != null" cols="12">
|
|
<v-textarea
|
|
v-model="outputText"
|
|
full-width
|
|
readonly
|
|
auto-grow
|
|
data-cy="outputText"
|
|
></v-textarea>
|
|
</v-col>
|
|
</v-row>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<script>
|
|
import Papa from "papaparse";
|
|
const FORM_KEY = "adm-import";
|
|
export default {
|
|
data() {
|
|
return {
|
|
selectLists: {
|
|
importableAyaTypes: []
|
|
},
|
|
uploadFile: [],
|
|
ayaType: 0,
|
|
doImport: false,
|
|
doUpdate: false,
|
|
outputText: null,
|
|
rights: window.$gz.role.defaultRightsObject(),
|
|
uploading: false,
|
|
formState: {
|
|
ready: false,
|
|
dirty: false,
|
|
valid: true,
|
|
readOnly: false,
|
|
loading: true,
|
|
errorBoxMessage: null,
|
|
appError: null,
|
|
serverError: {}
|
|
}
|
|
};
|
|
},
|
|
computed: {
|
|
importable() {
|
|
return (
|
|
(this.doImport || this.doUpdate) &&
|
|
this.uploadFile &&
|
|
this.uploadFile.name &&
|
|
this.ayaType != 0
|
|
);
|
|
}
|
|
},
|
|
async created() {
|
|
//NOTE:Global is what is checked for initialize to show this form and at server to allow import
|
|
this.rights = window.$gz.role.getRights(window.$gz.type.Global);
|
|
window.$gz.eventBus.$on("menu-click", clickHandler);
|
|
await fetchTranslatedText(this);
|
|
await populateSelectionLists(this);
|
|
generateMenu(this);
|
|
this.formState.ready = true;
|
|
},
|
|
beforeDestroy() {
|
|
window.$gz.eventBus.$off("menu-click", clickHandler);
|
|
},
|
|
methods: {
|
|
async process() {
|
|
if (this.uploading) {
|
|
return;
|
|
}
|
|
if (!this.uploadFile) {
|
|
return;
|
|
}
|
|
this.uploading = true;
|
|
this.outputText = null;
|
|
try {
|
|
let fileType = this.uploadFile.type.toLowerCase();
|
|
if (!fileType.includes("csv") && !fileType.includes("json")) {
|
|
throw new Error("Not supported file type, must be .csv or .json");
|
|
}
|
|
const isCSV = fileType.includes("csv");
|
|
let dat = null;
|
|
if (isCSV) {
|
|
let res = await parseCSVFile(this.uploadFile);
|
|
if (res.errors.length > 0) {
|
|
this.outputText =
|
|
"LT:CSV parsing errors:\n" + JSON.stringify(res.errors);
|
|
throw new Error("LT:Errors in CSV file import can not proceed");
|
|
}
|
|
if (res.data) {
|
|
dat = res.data;
|
|
}
|
|
//transform the input csv if it's not a direct match to json (part assembly etc)
|
|
transform(dat, this.ayaType);
|
|
//console.log("Done parseCSVFile");
|
|
} else {
|
|
dat = await parseJSONFile(this.uploadFile);
|
|
//console.log("Done parse json");
|
|
}
|
|
|
|
// console.log(
|
|
// "The processed data before cleaning is ",
|
|
// JSON.stringify(dat)
|
|
// );
|
|
|
|
//strip out any unsupported fields before transmission
|
|
cleanData(dat, this.ayaType);
|
|
|
|
// console.log(
|
|
// "The processed data AFTER cleaning is ",
|
|
// JSON.stringify(dat)
|
|
// );
|
|
// eslint-disable-next-line no-debugger
|
|
//debugger;
|
|
|
|
//console.log("Flattened:", Flatten(dat));
|
|
console.log(
|
|
"if it was csv it should look like this:\n",
|
|
Papa.unparse(dat)
|
|
);
|
|
|
|
//upload the data
|
|
await this.upload(dat);
|
|
} catch (error) {
|
|
window.$gz.errorHandler.handleFormError(error);
|
|
} finally {
|
|
this.uploading = false;
|
|
}
|
|
},
|
|
async upload(dat) {
|
|
try {
|
|
if (this.doUpdate == true) {
|
|
let dialogResult = await window.$gz.dialog.confirmGeneric(
|
|
"AdminImportUpdateWarning",
|
|
"warning"
|
|
);
|
|
if (dialogResult == false) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
const res = await window.$gz.api.post("import", {
|
|
data: dat,
|
|
atype: this.ayaType,
|
|
doImport: this.doImport,
|
|
doUpdate: this.doUpdate
|
|
});
|
|
if (res.error) {
|
|
window.$gz.errorHandler.handleFormError(res.error);
|
|
} else {
|
|
//result is an array of strings
|
|
let outText = "";
|
|
res.data.forEach(function appendImportResultItem(value) {
|
|
outText += value + "\n";
|
|
});
|
|
this.outputText = await window.$gz.translation.translateStringWithMultipleKeysAsync(
|
|
outText
|
|
);
|
|
}
|
|
} catch (error) {
|
|
window.$gz.errorHandler.handleFormError(error);
|
|
}
|
|
},
|
|
handleSelected() {}
|
|
}
|
|
};
|
|
|
|
/////////////////////////////
|
|
//
|
|
//
|
|
function clickHandler(menuItem) {
|
|
if (!menuItem) {
|
|
return;
|
|
}
|
|
const m = window.$gz.menu.parseMenuItem(menuItem);
|
|
if (m.owner == FORM_KEY && !m.disabled) {
|
|
switch (m.key) {
|
|
default:
|
|
window.$gz.eventBus.$emit(
|
|
"notify-warning",
|
|
FORM_KEY + "::context click: [" + m.key + "]"
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////
|
|
//
|
|
//
|
|
function generateMenu() {
|
|
const menuOptions = {
|
|
isMain: true,
|
|
icon: "$ayiFileImport",
|
|
title: "Import",
|
|
helpUrl: "adm-import",
|
|
menuItems: []
|
|
};
|
|
|
|
window.$gz.eventBus.$emit("menu-change", menuOptions);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////
|
|
//
|
|
// Ensures UI translated text is available
|
|
//
|
|
async function fetchTranslatedText() {
|
|
await window.$gz.translation.cacheTranslations([
|
|
"AyaType",
|
|
"ImportNewRecords",
|
|
"UpdateExistingRecords",
|
|
"AdminImportUpdateWarning",
|
|
"FileToImport"
|
|
]);
|
|
}
|
|
|
|
//////////////////////
|
|
//
|
|
//
|
|
async function populateSelectionLists(vm) {
|
|
await window.$gz.enums.fetchEnumList("importable");
|
|
vm.selectLists.importableAyaTypes = window.$gz.enums.getSelectionList(
|
|
"importable"
|
|
);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////
|
|
//
|
|
// Parse csv and return results as JSON, handle errors if any
|
|
//
|
|
async function parseCSVFile(file) {
|
|
return new Promise(function(complete, error) {
|
|
Papa.parse(file, {
|
|
header: true,
|
|
skipEmptyLines: true,
|
|
dynamicTyping: true,
|
|
worker: true,
|
|
complete,
|
|
error
|
|
});
|
|
});
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////
|
|
//
|
|
// reformat JSON that was imported for types that need
|
|
// to be transformed (partassembly etc)
|
|
//
|
|
function transform(dat, atype) {
|
|
switch (atype) {
|
|
case window.$gz.type.PartAssembly:
|
|
//json from csv needs reformatting
|
|
dat.forEach(z => {
|
|
var newItems = [];
|
|
z.Items.split(",").forEach(x => {
|
|
let o = x.split("|");
|
|
newItems.push({
|
|
PartNameViz: o[0],
|
|
Quantity: Number.parseFloat(o[1])
|
|
});
|
|
});
|
|
z.Items = newItems;
|
|
});
|
|
break;
|
|
case window.$gz.type.TaskGroup:
|
|
dat.forEach(z => {
|
|
var newItems = [];
|
|
z.Items.split(",").forEach((x, i) => {
|
|
newItems.push({
|
|
Sequence: i,
|
|
Task: x
|
|
});
|
|
});
|
|
z.Items = newItems;
|
|
});
|
|
break;
|
|
}
|
|
//console.log("transform after:", dat);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////
|
|
//
|
|
// Open local json file, read, parse and return results as JSON, handle errors if any
|
|
//
|
|
async function parseJSONFile(file) {
|
|
return new Promise(function(complete) {
|
|
const reader = new FileReader();
|
|
reader.addEventListener(
|
|
"load",
|
|
() => {
|
|
// this will then display a text file
|
|
complete(JSON.parse(reader.result));
|
|
},
|
|
false
|
|
);
|
|
if (file) {
|
|
reader.readAsText(file);
|
|
}
|
|
});
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////
|
|
//
|
|
// remove unsupported props from data
|
|
//
|
|
function cleanData(dat, atype) {
|
|
var allowedProps = [];
|
|
//Note: convention here is any ID field that is linked object we want to support gets renamed here to replace *id with *viz if viz not already present
|
|
//at back end it will attempt to match up but not create if not existing
|
|
switch (atype) {
|
|
case window.$gz.type.Customer:
|
|
allowedProps.push(
|
|
...[
|
|
"Name",
|
|
"Active",
|
|
"Notes",
|
|
"Wiki",
|
|
"Tags",
|
|
"WebAddress",
|
|
"AlertNotes",
|
|
"BillHeadOffice",
|
|
"HeadOfficeViz",
|
|
"TechNotes",
|
|
"AccountNumber",
|
|
"ContractViz",
|
|
"ContractExpires",
|
|
"Phone1",
|
|
"Phone2",
|
|
"Phone3",
|
|
"Phone4",
|
|
"Phone5",
|
|
"EmailAddress",
|
|
"PostAddress",
|
|
"PostCity",
|
|
"PostRegion",
|
|
"PostCountry",
|
|
"PostCode",
|
|
"Address",
|
|
"City",
|
|
"Region",
|
|
"Country",
|
|
"Latitude",
|
|
"Longitude"
|
|
]
|
|
);
|
|
break;
|
|
case window.$gz.type.HeadOffice:
|
|
allowedProps.push(
|
|
...[
|
|
"Name",
|
|
"Active",
|
|
"Notes",
|
|
"Wiki",
|
|
"Tags",
|
|
"WebAddress",
|
|
"TechNotes",
|
|
"AccountNumber",
|
|
"ContractViz",
|
|
"ContractExpires",
|
|
"Phone1",
|
|
"Phone2",
|
|
"Phone3",
|
|
"Phone4",
|
|
"Phone5",
|
|
"EmailAddress",
|
|
"PostAddress",
|
|
"PostCity",
|
|
"PostRegion",
|
|
"PostCountry",
|
|
"PostCode",
|
|
"Address",
|
|
"City",
|
|
"Region",
|
|
"Country",
|
|
"Latitude",
|
|
"Longitude"
|
|
]
|
|
);
|
|
break;
|
|
case window.$gz.type.Part:
|
|
allowedProps.push(
|
|
...[
|
|
"Name",
|
|
"Active",
|
|
"Description",
|
|
"Notes",
|
|
"Wiki",
|
|
"Tags",
|
|
"ManufacturerViz",
|
|
"ManufacturerNumber",
|
|
"WholeSalerViz",
|
|
"WholeSalerNumber",
|
|
"AlternativeWholeSalerViz",
|
|
"AlternativeWholeSalerNumber",
|
|
"Cost",
|
|
"Retail",
|
|
"UnitOfMeasure",
|
|
"UPC",
|
|
"PartSerialsViz"
|
|
]
|
|
);
|
|
break;
|
|
case window.$gz.type.PartAssembly:
|
|
allowedProps.push(
|
|
...[
|
|
"Name",
|
|
"Active",
|
|
"Notes",
|
|
"Wiki",
|
|
"Tags",
|
|
"Items",
|
|
"PartNameViz",
|
|
"Quantity"
|
|
]
|
|
);
|
|
break;
|
|
case window.$gz.type.PartWarehouse:
|
|
allowedProps.push(...["Name", "Active", "Notes", "Wiki", "Tags"]);
|
|
break;
|
|
case window.$gz.type.Project:
|
|
allowedProps.push(
|
|
...[
|
|
"Name",
|
|
"Active",
|
|
"Notes",
|
|
"Wiki",
|
|
"Tags",
|
|
"DateStarted",
|
|
"DateCompleted",
|
|
"ProjectOverseerViz",
|
|
"AccountNumber"
|
|
]
|
|
);
|
|
break;
|
|
case window.$gz.type.ServiceRate:
|
|
allowedProps.push(
|
|
...[
|
|
"Name",
|
|
"Active",
|
|
"Notes",
|
|
"Wiki",
|
|
"Tags",
|
|
"AccountNumber",
|
|
"Cost",
|
|
"Charge",
|
|
"Unit",
|
|
"ContractOnly"
|
|
]
|
|
);
|
|
break;
|
|
case window.$gz.type.TaskGroup:
|
|
allowedProps.push(...["Name", "Active", "Notes", "Items"]);
|
|
break;
|
|
case window.$gz.type.TravelRate:
|
|
allowedProps.push(
|
|
...[
|
|
"Name",
|
|
"Active",
|
|
"Notes",
|
|
"Wiki",
|
|
"Tags",
|
|
"AccountNumber",
|
|
"Cost",
|
|
"Charge",
|
|
"Unit",
|
|
"ContractOnly"
|
|
]
|
|
);
|
|
break;
|
|
}
|
|
|
|
//Strip out any records that have fields not on our allowed list
|
|
dat.forEach(z => {
|
|
for (const prop in z) {
|
|
if (allowedProps.includes(prop) == false) {
|
|
delete z[prop];
|
|
} else {
|
|
if (prop == "Tags") {
|
|
//if it's coming from csv then Tags will be a string with comma separated items like this: blue,white,red
|
|
//if it's json it will already be an array
|
|
if (z.Tags && typeof z.Tags === "string") {
|
|
z.Tags = z.Tags.split(",");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
/* HOW TO GET PROPERTY NAMES EASILY
|
|
Get the object from network traffic:
|
|
Assign it in code to a variable:
|
|
var v = {
|
|
Name: "XYZ Accounting",
|
|
Active: true,
|
|
Notes: "Ergonomic impactful info-mediaries",
|
|
Wiki: null,
|
|
CustomFields: null,
|
|
Tags: ["black", "zone2", "zone3"],
|
|
WebAddress: "https://example.biz",
|
|
AlertNotes: null,
|
|
BillHeadOffice: true,
|
|
HeadOfficeId: 1,
|
|
HeadOfficeViz: "XYZ Head Office",
|
|
TechNotes: null,
|
|
AccountNumber: "10402542",
|
|
ContractId: null,
|
|
ContractViz: null,
|
|
ContractExpires: null,
|
|
LastWorkOrderViz: 350,
|
|
LastServiceDateViz: "2022-03-09T00:00:00Z",
|
|
Phone1: "373-707-7322 x535",
|
|
Phone2: "281.718.4551",
|
|
Phone3: "1-452-791-5760 x84358",
|
|
Phone4: null,
|
|
Phone5: null,
|
|
EmailAddress: "Annabel.Hahn64@example.org",
|
|
PostAddress: null,
|
|
PostCity: null,
|
|
PostRegion: null,
|
|
PostCountry: null,
|
|
PostCode: null,
|
|
Address: "9723 Tiara Summit",
|
|
City: "Hansenside",
|
|
Region: "Mississippi",
|
|
Country: "Japan",
|
|
Latitude: -7.7692,
|
|
Longitude: 165.0654
|
|
};
|
|
In a javascript console use this Object.getOwnPropertyNames(<PASTE OBJECT HERE>); will output a nice array with field names
|
|
[
|
|
"Name",
|
|
"Active",
|
|
"Notes",
|
|
"Wiki",
|
|
"CustomFields",
|
|
"Tags",
|
|
"WebAddress",
|
|
"AlertNotes",
|
|
"BillHeadOffice",
|
|
"HeadOfficeId",
|
|
"HeadOfficeViz",
|
|
"TechNotes",
|
|
"AccountNumber",
|
|
"ContractId",
|
|
"ContractViz",
|
|
"ContractExpires",
|
|
"LastWorkOrderViz",
|
|
"LastServiceDateViz",
|
|
"Phone1",
|
|
"Phone2",
|
|
"Phone3",
|
|
"Phone4",
|
|
"Phone5",
|
|
"EmailAddress",
|
|
"PostAddress",
|
|
"PostCity",
|
|
"PostRegion",
|
|
"PostCountry",
|
|
"PostCode",
|
|
"Address",
|
|
"City",
|
|
"Region",
|
|
"Country",
|
|
"Latitude",
|
|
"Longitude"
|
|
]
|
|
|
|
|
|
|
|
|
|
if it was csv it should look like this:
|
|
Name,Active,Notes,Wiki,CustomFields,Tags,WebAddress,AlertNotes,BillHeadOffice,HeadOfficeViz,TechNotes,AccountNumber,ContractViz,ContractExpires,Phone1,Phone2,Phone3,Phone4,Phone5,EmailAddress,PostAddress,PostCity,PostRegion,PostCountry,PostCode,Address,City,Region,Country,Latitude,Longitude
|
|
"ACopyOf Altenwerth, Willms and Krajcik",true,Advanced web-enabled productivity,,,"black,white",https://example.info,,true,"Gerlach, Howe and Crist",,40810165,,,(868) 464-5253,1-788-655-8853 x08455,536.824.7646,,,Maximo_Ziemann10@example.net,00060 Davion Mission,East Kevonstad,Washington,Iraq,68361-2305,75163 Heller Plains,East Kevonstad,Washington,Iraq,33.3014,-157.7912
|
|
"ACopyOf Adams, Macejkovic and Stoltenberg",true,Multi-layered multi-tasking infrastructure,,,zone9,https://example.biz,,false,,,21491466,,,830-720-4993 x86044,1-306-960-6200 x0811,1-269-482-1221 x97391,,,Leanne99@example.com,4399 Hegmann Pass,South Velva,Georgia,Chile,44636-1335,6036 Reggie Pine,South Velva,Georgia,Chile,41.1883,-152.6866
|
|
|
|
*/
|
|
</script>
|