mirror of
https://github.com/frappe/books.git
synced 2024-12-23 11:29:03 +00:00
incr: convertion of valueMatrix to docs
This commit is contained in:
parent
0194b6c26d
commit
b08c128c45
128
src/importer.ts
128
src/importer.ts
@ -1,6 +1,7 @@
|
||||
import { Fyo } from 'fyo';
|
||||
import { Converter } from 'fyo/core/converter';
|
||||
import { DocValue } from 'fyo/core/types';
|
||||
import { DocValue, DocValueMap } from 'fyo/core/types';
|
||||
import { Doc } from 'fyo/model/doc';
|
||||
import { getEmptyValuesByFieldTypes } from 'fyo/utils';
|
||||
import { ValidationError } from 'fyo/utils/errors';
|
||||
import {
|
||||
@ -75,6 +76,12 @@ export class Importer {
|
||||
*/
|
||||
valueMatrix: ValueMatrix;
|
||||
|
||||
/**
|
||||
* Data from the valueMatrix rows will be converted into Docs
|
||||
* which will be stored in this array.
|
||||
*/
|
||||
docs: Doc[];
|
||||
|
||||
constructor(schemaName: string, fyo: Fyo) {
|
||||
if (!fyo.schemaMap[schemaName]) {
|
||||
throw new ValidationError(
|
||||
@ -85,6 +92,7 @@ export class Importer {
|
||||
this.hasChildTables = false;
|
||||
this.schemaName = schemaName;
|
||||
this.fyo = fyo;
|
||||
this.docs = [];
|
||||
this.valueMatrix = [];
|
||||
|
||||
const templateFields = getTemplateFields(schemaName, fyo, this);
|
||||
@ -98,6 +106,124 @@ export class Importer {
|
||||
});
|
||||
}
|
||||
|
||||
pushFromValueMatrixToDocs() {
|
||||
const { dataMap, childTableMap } =
|
||||
this.getDataAndChildTableMapFromValueMatrix();
|
||||
|
||||
const schema = this.fyo.db.schemaMap[this.schemaName];
|
||||
const targetFieldnameMap = schema?.fields
|
||||
.filter((f) => f.fieldtype === FieldTypeEnum.Table)
|
||||
.reduce((acc, f) => {
|
||||
const { target, fieldname } = f as TargetField;
|
||||
acc[target] = fieldname;
|
||||
return acc;
|
||||
}, {} as Record<string, string>);
|
||||
|
||||
for (const [name, data] of dataMap.entries()) {
|
||||
const doc = this.fyo.doc.getNewDoc(this.schemaName, data, false);
|
||||
for (const schemaName in targetFieldnameMap) {
|
||||
const fieldname = targetFieldnameMap[schemaName];
|
||||
const childTable = childTableMap[name][schemaName];
|
||||
if (!childTable) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const childData of childTable.values()) {
|
||||
doc.push(fieldname, childData);
|
||||
}
|
||||
}
|
||||
|
||||
this.docs.push(doc);
|
||||
}
|
||||
}
|
||||
|
||||
getDataAndChildTableMapFromValueMatrix() {
|
||||
/**
|
||||
* Record key is the doc.name value
|
||||
*/
|
||||
const dataMap: Map<string, DocValueMap> = new Map();
|
||||
|
||||
/**
|
||||
* Record key is doc.name, childSchemaName, childDoc.name
|
||||
*/
|
||||
const childTableMap: Record<
|
||||
string,
|
||||
Record<string, Map<string, DocValueMap>>
|
||||
> = {};
|
||||
|
||||
const nameIndices = this.assignedTemplateFields
|
||||
.map((key, index) => ({ key, index }))
|
||||
.filter((f) => f.key?.endsWith('.name'))
|
||||
.reduce((acc, f) => {
|
||||
if (f.key == null) {
|
||||
return acc;
|
||||
}
|
||||
|
||||
const schemaName = f.key.split('.')[0];
|
||||
acc[schemaName] = f.index;
|
||||
return acc;
|
||||
}, {} as Record<string, number>);
|
||||
|
||||
const nameIndex = nameIndices?.[this.schemaName];
|
||||
if (nameIndex < 0) {
|
||||
return { dataMap, childTableMap };
|
||||
}
|
||||
|
||||
for (const i in this.valueMatrix) {
|
||||
const row = this.valueMatrix[i];
|
||||
const name = row[nameIndex].value;
|
||||
if (typeof name !== 'string') {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const j in row) {
|
||||
const key = this.assignedTemplateFields[j];
|
||||
const tf = this.templateFieldsMap.get(key ?? '');
|
||||
if (!tf || !key) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const isChild = this.fyo.schemaMap[tf.schemaName]?.isChild;
|
||||
const vmi = row[j];
|
||||
if (vmi.value == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isChild && !dataMap.has(name)) {
|
||||
dataMap.set(name, {});
|
||||
}
|
||||
|
||||
if (!isChild) {
|
||||
dataMap.get(name)![tf.fieldname] = vmi.value;
|
||||
continue;
|
||||
}
|
||||
|
||||
const childNameIndex = nameIndices[tf.schemaName];
|
||||
let childName = row[childNameIndex].value;
|
||||
if (typeof childName !== 'string') {
|
||||
childName = `${tf.schemaName}-${i}`;
|
||||
}
|
||||
|
||||
childTableMap[name] ??= {};
|
||||
childTableMap[name][tf.schemaName] ??= new Map();
|
||||
|
||||
const childMap = childTableMap[name][tf.schemaName];
|
||||
if (!childMap.has(childName)) {
|
||||
childMap.set(childName, {});
|
||||
}
|
||||
|
||||
const childDocValueMap = childMap.get(childName);
|
||||
if (!childDocValueMap) {
|
||||
continue;
|
||||
}
|
||||
|
||||
childDocValueMap[tf.fieldname] = vmi.value;
|
||||
}
|
||||
}
|
||||
|
||||
return { dataMap, childTableMap };
|
||||
}
|
||||
|
||||
selectFile(data: string): boolean {
|
||||
try {
|
||||
const parsed = parseCSV(data);
|
||||
|
@ -223,11 +223,13 @@
|
||||
>
|
||||
<h2 class="text-xl font-semibold mt-4">{{ t`Import Success` }} 🎉</h2>
|
||||
<p class="text-lg text-center">
|
||||
{{ t`Successfully created the following ${names.length} entries:` }}
|
||||
{{
|
||||
t`Successfully created the following ${succeeded.length} entries:`
|
||||
}}
|
||||
</p>
|
||||
<div class="max-h-96 overflow-y-auto">
|
||||
<div
|
||||
v-for="(n, i) in names"
|
||||
v-for="(n, i) in succeeded"
|
||||
:key="'name-' + i"
|
||||
class="grid grid-cols-2 gap-2 border-b pb-2 mb-2 pe-4 text-lg w-60"
|
||||
style="grid-template-columns: 2rem auto"
|
||||
@ -358,7 +360,8 @@ type DataImportData = {
|
||||
showColumnPicker: boolean;
|
||||
canReset: boolean;
|
||||
complete: boolean;
|
||||
names: string[];
|
||||
succeeded: string[];
|
||||
failed: { name: string; error: Error }[];
|
||||
file: null | { name: string; filePath: string; text: string };
|
||||
nullOrImporter: null | Importer;
|
||||
importType: string;
|
||||
@ -386,7 +389,8 @@ export default defineComponent({
|
||||
showColumnPicker: false,
|
||||
canReset: false,
|
||||
complete: false,
|
||||
names: ['Bat', 'Baseball', 'Other Shit'],
|
||||
succeeded: [],
|
||||
failed: [],
|
||||
file: null,
|
||||
nullOrImporter: null,
|
||||
importType: '',
|
||||
@ -404,15 +408,11 @@ export default defineComponent({
|
||||
},
|
||||
computed: {
|
||||
canImportData(): boolean {
|
||||
if (this.file) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.hasImporter && this.importer.valueMatrix.length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!this.hasImporter) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.importer.valueMatrix.length > 0;
|
||||
},
|
||||
canSelectFile(): boolean {
|
||||
return !this.file;
|
||||
@ -650,7 +650,8 @@ export default defineComponent({
|
||||
},
|
||||
clear(): void {
|
||||
this.file = null;
|
||||
this.names = [];
|
||||
this.succeeded = [];
|
||||
this.failed = [];
|
||||
this.nullOrImporter = null;
|
||||
this.importType = '';
|
||||
this.complete = false;
|
||||
@ -671,41 +672,29 @@ export default defineComponent({
|
||||
await saveData(template, filePath);
|
||||
},
|
||||
async importData(): Promise<void> {
|
||||
/*
|
||||
if (this.isMakingEntries || this.complete) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.isRequiredUnassigned) {
|
||||
return await showMessageDialog({
|
||||
message: this.t`Required Fields not Assigned`,
|
||||
detail: this
|
||||
.t`Please assign the following fields ${this.requiredUnassigned.join(
|
||||
', '
|
||||
)}`,
|
||||
});
|
||||
this.importer.pushFromValueMatrixToDocs();
|
||||
|
||||
let doneCount = 0;
|
||||
for (const doc of this.importer.docs) {
|
||||
this.setLoadingStatus(doneCount, this.importer.docs.length);
|
||||
try {
|
||||
await doc.sync();
|
||||
doneCount += 1;
|
||||
|
||||
this.succeeded.push(doc.name!);
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
this.failed.push({ name: doc.name!, error });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.importer.assignedMatrix.length === 0) {
|
||||
return await showMessageDialog({
|
||||
message: this.t`No Data to Import`,
|
||||
detail: this.t`Please select a file with data to import.`,
|
||||
});
|
||||
}
|
||||
|
||||
const { success, names, message } = await this.importer.importData(
|
||||
this.setLoadingStatus
|
||||
);
|
||||
if (!success) {
|
||||
return await showMessageDialog({
|
||||
message: this.t`Import Failed`,
|
||||
detail: message,
|
||||
});
|
||||
}
|
||||
|
||||
this.names = names;
|
||||
this.isMakingEntries = false;
|
||||
this.complete = true;
|
||||
*/
|
||||
},
|
||||
setImportType(importType: string): void {
|
||||
this.clear();
|
||||
@ -716,14 +705,9 @@ export default defineComponent({
|
||||
this.importType = importType;
|
||||
this.nullOrImporter = new Importer(importType, fyo);
|
||||
},
|
||||
setLoadingStatus(
|
||||
isMakingEntries: boolean,
|
||||
entriesMade: number,
|
||||
totalEntries: number
|
||||
): void {
|
||||
this.isMakingEntries = isMakingEntries;
|
||||
setLoadingStatus(entriesMade: number, totalEntries: number): void {
|
||||
this.percentLoading = entriesMade / totalEntries;
|
||||
this.messageLoading = isMakingEntries
|
||||
this.messageLoading = this.isMakingEntries
|
||||
? `${entriesMade} entries made out of ${totalEntries}...`
|
||||
: '';
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user