mirror of
https://github.com/frappe/books.git
synced 2025-01-23 23:28:24 +00:00
feat: added importer settings
This commit is contained in:
parent
916d0ecee4
commit
16c0bfe4fc
@ -175,6 +175,8 @@ export class Importer {
|
|||||||
requiredMap: Map = {};
|
requiredMap: Map = {};
|
||||||
labelTemplateFieldMap: LabelTemplateFieldMap = {};
|
labelTemplateFieldMap: LabelTemplateFieldMap = {};
|
||||||
shouldSubmit: boolean = false;
|
shouldSubmit: boolean = false;
|
||||||
|
labelIndex: number = -1;
|
||||||
|
csv: string[][] = [];
|
||||||
|
|
||||||
constructor(doctype: string) {
|
constructor(doctype: string) {
|
||||||
this.doctype = doctype;
|
this.doctype = doctype;
|
||||||
@ -272,23 +274,38 @@ export class Importer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
selectFile(text: string): boolean {
|
selectFile(text: string): boolean {
|
||||||
const csv = parseCSV(text);
|
this.csv = parseCSV(text);
|
||||||
this.parsedLabels = csv[0];
|
this.initialize(0, true);
|
||||||
const values = csv.slice(1);
|
|
||||||
|
|
||||||
/*
|
|
||||||
if (values.some((v) => v.length !== this.parsedLabels.length)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
this.parsedValues = values;
|
|
||||||
this._setAssigned();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
_setAssigned() {
|
initialize(labelIndex: number, force: boolean) {
|
||||||
|
if (
|
||||||
|
(typeof labelIndex !== 'number' && !labelIndex) ||
|
||||||
|
(labelIndex === this.labelIndex && !force)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log('initing', labelIndex);
|
||||||
|
|
||||||
|
this.labelIndex = labelIndex;
|
||||||
|
this.parsedLabels = this.csv[labelIndex];
|
||||||
|
const values = this.csv.slice(labelIndex + 1);
|
||||||
|
|
||||||
|
this.parsedValues = values;
|
||||||
|
this.setAssigned();
|
||||||
|
}
|
||||||
|
|
||||||
|
setAssigned() {
|
||||||
const labels = [...this.parsedLabels];
|
const labels = [...this.parsedLabels];
|
||||||
|
|
||||||
|
for (const k of Object.keys(this.assignedMap)) {
|
||||||
|
const l = this.assignedMap[k] as string;
|
||||||
|
if (!labels.includes(l)) {
|
||||||
|
this.assignedMap[k] = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
labels.forEach((l) => {
|
labels.forEach((l) => {
|
||||||
if (this.assignedMap[l] !== '') {
|
if (this.assignedMap[l] !== '') {
|
||||||
return;
|
return;
|
||||||
@ -343,6 +360,9 @@ export class Importer {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await doc.insert();
|
await doc.insert();
|
||||||
|
if (this.shouldSubmit) {
|
||||||
|
await doc.submit();
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const message = (err as Error).message;
|
const message = (err as Error).message;
|
||||||
|
|
||||||
@ -372,6 +392,3 @@ export class Importer {
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
window.gtf = getTemplateFields;
|
|
||||||
|
@ -36,35 +36,6 @@
|
|||||||
@change="setImportType"
|
@change="setImportType"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div
|
|
||||||
v-if="importType && isSubmittable"
|
|
||||||
class="
|
|
||||||
justify-center
|
|
||||||
items-center
|
|
||||||
gap-2
|
|
||||||
flex
|
|
||||||
justify-between
|
|
||||||
items-center
|
|
||||||
bg-gray-100
|
|
||||||
px-2
|
|
||||||
py-1
|
|
||||||
rounded
|
|
||||||
text-gray-900
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<p>Should Submit</p>
|
|
||||||
<FormControl
|
|
||||||
size="small"
|
|
||||||
input-class="bg-gray-100"
|
|
||||||
:df="{
|
|
||||||
fieldname: 'shouldSubmit',
|
|
||||||
label: this.t`Submit on Import`,
|
|
||||||
fieldtype: 'Check',
|
|
||||||
}"
|
|
||||||
:value="Number(importer.shouldSubmit)"
|
|
||||||
@change="(value) => (importer.shouldSubmit = !!value)"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<p
|
<p
|
||||||
class="text-base text-base ml-2"
|
class="text-base text-base ml-2"
|
||||||
:class="fileName ? 'text-gray-900 font-semibold' : 'text-gray-700'"
|
:class="fileName ? 'text-gray-900 font-semibold' : 'text-gray-700'"
|
||||||
@ -79,6 +50,86 @@
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Settings -->
|
||||||
|
<div v-if="fileName" class="">
|
||||||
|
<h2 class="text-lg font-semibold">{{ t`Importer Settings` }}</h2>
|
||||||
|
<div class="mt-4 flex gap-2">
|
||||||
|
<button
|
||||||
|
class="w-28 bg-gray-100 focus:bg-gray-200 rounded-md"
|
||||||
|
@click="importer.initialize(0, true)"
|
||||||
|
>
|
||||||
|
<span class="text-red-400">
|
||||||
|
{{ t`Reset` }}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<div
|
||||||
|
v-if="file && isSubmittable"
|
||||||
|
class="
|
||||||
|
justify-center
|
||||||
|
items-center
|
||||||
|
gap-2
|
||||||
|
flex
|
||||||
|
justify-between
|
||||||
|
items-center
|
||||||
|
bg-gray-100
|
||||||
|
px-2
|
||||||
|
rounded
|
||||||
|
text-gray-900
|
||||||
|
w-40
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<p>{{ frappe.t`Submit on Import` }}</p>
|
||||||
|
<FormControl
|
||||||
|
size="small"
|
||||||
|
input-class="bg-gray-100"
|
||||||
|
:df="{
|
||||||
|
fieldname: 'shouldSubmit',
|
||||||
|
fieldtype: 'Check',
|
||||||
|
}"
|
||||||
|
:value="Number(importer.shouldSubmit)"
|
||||||
|
@change="(value) => (importer.shouldSubmit = !!value)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="
|
||||||
|
flex flex-row
|
||||||
|
justify-center
|
||||||
|
items-center
|
||||||
|
justify-center
|
||||||
|
items-center
|
||||||
|
gap-2
|
||||||
|
flex
|
||||||
|
justify-between
|
||||||
|
items-center
|
||||||
|
bg-gray-100
|
||||||
|
pl-2
|
||||||
|
rounded
|
||||||
|
text-gray-900
|
||||||
|
w-40
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<p class="text-gray-900">{{ t`Label Index` }}</p>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
class="
|
||||||
|
bg-gray-100
|
||||||
|
outline-none
|
||||||
|
focus:bg-gray-200
|
||||||
|
px-2
|
||||||
|
py-1
|
||||||
|
rounded-md
|
||||||
|
w-10
|
||||||
|
text-right
|
||||||
|
"
|
||||||
|
min="1"
|
||||||
|
:max="importer.csv.length - 1"
|
||||||
|
:value="labelIndex + 1"
|
||||||
|
@change="setLabelIndex"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Label Assigner -->
|
<!-- Label Assigner -->
|
||||||
<div v-if="fileName" class="pb-4">
|
<div v-if="fileName" class="pb-4">
|
||||||
<h2 class="text-lg font-semibold">{{ t`Assign Imported Labels` }}</h2>
|
<h2 class="text-lg font-semibold">{{ t`Assign Imported Labels` }}</h2>
|
||||||
@ -106,7 +157,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<p
|
<p
|
||||||
class="text-red-400 text-sm mt-1 -mb-1 p-0 h-0"
|
class="text-red-400 text-sm mt-1 -mb-1 p-0 h-0"
|
||||||
v-if="requiredUnassigned"
|
v-if="isRequiredUnassigned"
|
||||||
>
|
>
|
||||||
{{ t`* required fields` }}
|
{{ t`* required fields` }}
|
||||||
</p>
|
</p>
|
||||||
@ -223,7 +274,7 @@ import FeatherIcon from '@/components/FeatherIcon.vue';
|
|||||||
import PageHeader from '@/components/PageHeader.vue';
|
import PageHeader from '@/components/PageHeader.vue';
|
||||||
import { importable, Importer } from '@/dataImport';
|
import { importable, Importer } from '@/dataImport';
|
||||||
import { IPC_ACTIONS } from '@/messages';
|
import { IPC_ACTIONS } from '@/messages';
|
||||||
import { getSavePath, saveData, showToast } from '@/utils';
|
import { getSavePath, saveData, showMessageDialog, showToast } from '@/utils';
|
||||||
import { ipcRenderer } from 'electron';
|
import { ipcRenderer } from 'electron';
|
||||||
import frappe from 'frappe';
|
import frappe from 'frappe';
|
||||||
export default {
|
export default {
|
||||||
@ -244,10 +295,16 @@ export default {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
labelIndex(){
|
||||||
|
return this.importer.labelIndex
|
||||||
|
},
|
||||||
requiredUnassigned() {
|
requiredUnassigned() {
|
||||||
return this.importer.assignableLabels
|
return this.importer.assignableLabels.filter(
|
||||||
.filter((k) => this.importer.requiredMap[k])
|
(k) => this.importer.requiredMap[k] && !this.importer.assignedMap[k]
|
||||||
.some((k) => !this.importer.assignedMap[k]);
|
);
|
||||||
|
},
|
||||||
|
isRequiredUnassigned() {
|
||||||
|
return this.requiredUnassigned.length > 0;
|
||||||
},
|
},
|
||||||
assignedMatrix() {
|
assignedMatrix() {
|
||||||
return this.importer.assignedMatrix;
|
return this.importer.assignedMatrix;
|
||||||
@ -350,6 +407,10 @@ export default {
|
|||||||
|
|
||||||
this.saveTemplate();
|
this.saveTemplate();
|
||||||
},
|
},
|
||||||
|
setLabelIndex(e) {
|
||||||
|
const labelIndex = (e.target.value ?? 1) - 1;
|
||||||
|
this.importer.initialize(labelIndex);
|
||||||
|
},
|
||||||
async saveTemplate() {
|
async saveTemplate() {
|
||||||
const template = this.importer.template;
|
const template = this.importer.template;
|
||||||
const templateName = this.importType + ' ' + this.t`Template`;
|
const templateName = this.importType + ' ' + this.t`Template`;
|
||||||
@ -382,10 +443,24 @@ export default {
|
|||||||
this.importer.updateValue(event.target.value, i, j);
|
this.importer.updateValue(event.target.value, i, j);
|
||||||
},
|
},
|
||||||
async importData() {
|
async importData() {
|
||||||
// TODO: pre import conditions
|
if (this.isRequiredUnassigned) {
|
||||||
/*
|
showMessageDialog({
|
||||||
if(){}
|
message: this.t`Required Fields not Assigned`,
|
||||||
*/
|
description: this
|
||||||
|
.t`Please assign the following fields ${this.requiredUnassigned.join(
|
||||||
|
', '
|
||||||
|
)}`,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.importer.assignedMatrix.length === 0) {
|
||||||
|
showMessageDialog({
|
||||||
|
message: this.t`No Data to Import`,
|
||||||
|
description: this.t`Please select a file with data to import.`,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const { success, names } = await this.importer.importData();
|
const { success, names } = await this.importer.importData();
|
||||||
if (!success || !names.length) {
|
if (!success || !names.length) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user