2
0
mirror of https://github.com/frappe/books.git synced 2024-09-20 03:29:00 +00:00

incr: convertion of valueMatrix to docs

This commit is contained in:
18alantom 2023-02-13 20:48:56 +05:30
parent 0194b6c26d
commit b08c128c45
2 changed files with 158 additions and 48 deletions

View File

@ -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);

View File

@ -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) {
return false;
}
if (this.hasImporter && this.importer.valueMatrix.length) {
return true;
}
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}...`
: '';
},