From 916d0ecee4a161391c8effc45835e601f8631090 Mon Sep 17 00:00:00 2001 From: 18alantom <2.alan.tom@gmail.com> Date: Wed, 23 Feb 2022 16:01:55 +0530 Subject: [PATCH] feat: add should submit - code for insertion - success state --- src/dataImport.ts | 157 +++++++++++++++++++++++++++++++++++---- src/pages/DataImport.vue | 121 +++++++++++++++++++++++++++--- 2 files changed, 253 insertions(+), 25 deletions(-) diff --git a/src/dataImport.ts b/src/dataImport.ts index bc4cd635..953fac6f 100644 --- a/src/dataImport.ts +++ b/src/dataImport.ts @@ -12,12 +12,26 @@ export const importable = [ 'Item', ]; +type Status = { + success: boolean; + message: string; + names: string[]; +}; + type Exclusion = { [key: string]: string[]; }; type Map = { - [key: string]: string | boolean | object; + [key: string]: unknown; +}; + +type ObjectMap = { + [key: string]: Map; +}; + +type LabelTemplateFieldMap = { + [key: string]: TemplateField; }; interface TemplateField { @@ -27,6 +41,27 @@ interface TemplateField { doctype: string; options?: string[]; fieldtype: FieldType; + parentField: string; +} + +function formatValue(value: string, fieldtype: FieldType): unknown { + switch (fieldtype) { + case FieldType.Date: + return new Date(value); + case FieldType.Currency: + // @ts-ignore + return frappe.pesa(value); + case FieldType.Int: + case FieldType.Float: { + const n = parseFloat(value); + if (!Number.isNaN(n)) { + return n; + } + return 0; + } + default: + return value; + } } const exclusion: Exclusion = { @@ -35,11 +70,21 @@ const exclusion: Exclusion = { Customer: ['address', 'outstandingAmount', 'supplier', 'image', 'customer'], }; -function getFilteredDocFields(doctype: string): [TemplateField[], string[]] { - const fields: TemplateField[] = []; +function getFilteredDocFields( + df: string | string[] +): [TemplateField[], string[][]] { + let doctype = df[0]; + let parentField = df[1] ?? ''; + + if (typeof df === 'string') { + doctype = df; + parentField = ''; + } + // @ts-ignore const primaryFields: Field[] = frappe.models[doctype].fields; - const tableTypes: string[] = []; + const fields: TemplateField[] = []; + const tableTypes: string[][] = []; const exclusionFields: string[] = exclusion[doctype] ?? []; primaryFields.forEach( @@ -54,15 +99,16 @@ function getFilteredDocFields(doctype: string): [TemplateField[], string[]] { options, }) => { if ( - readOnly || - (hidden && typeof hidden === 'number') || - exclusionFields.includes(fieldname) + !(fieldname === 'name' && !parentField) && + (readOnly || + (hidden && typeof hidden === 'number') || + exclusionFields.includes(fieldname)) ) { return; } if (fieldtype === FieldType.Table && childtype) { - tableTypes.push(childtype); + tableTypes.push([childtype, fieldname]); return; } @@ -72,6 +118,7 @@ function getFilteredDocFields(doctype: string): [TemplateField[], string[]] { doctype, options, fieldtype, + parentField, required: Boolean(required ?? false), }); } @@ -85,7 +132,7 @@ function getTemplateFields(doctype: string): TemplateField[] { if (!doctype) { return []; } - const doctypes: string[] = [doctype]; + const doctypes: string[][] = [[doctype]]; while (doctypes.length > 0) { const dt = doctypes.pop(); if (!dt) { @@ -126,7 +173,8 @@ export class Importer { parsedValues: string[][] = []; assignedMap: Map = {}; // target: import requiredMap: Map = {}; - labelFieldMap: Map = {}; + labelTemplateFieldMap: LabelTemplateFieldMap = {}; + shouldSubmit: boolean = false; constructor(doctype: string) { this.doctype = doctype; @@ -141,10 +189,13 @@ export class Importer { acc[k.label] = k.required; return acc; }, {}); - this.labelFieldMap = this.templateFields.reduce((acc: Map, k) => { - acc[k.label] = k; - return acc; - }, {}); + this.labelTemplateFieldMap = this.templateFields.reduce( + (acc: LabelTemplateFieldMap, k) => { + acc[k.label] = k; + return acc; + }, + {} + ); } get assignableLabels() { @@ -225,9 +276,11 @@ export class Importer { this.parsedLabels = csv[0]; const values = csv.slice(1); + /* if (values.some((v) => v.length !== this.parsedLabels.length)) { return false; } + */ this.parsedValues = values; this._setAssigned(); @@ -244,9 +297,81 @@ export class Importer { this.assignedMap[l] = l; }); } + + getDocs(): Map[] { + const fields = this.columnLabels.map((k) => this.labelTemplateFieldMap[k]); + const nameIndex = fields.findIndex(({ fieldname }) => fieldname === 'name'); + + const docMap: ObjectMap = {}; + + for (let r = 0; r < this.assignedMatrix.length; r++) { + const row = this.assignedMatrix[r]; + const cts: ObjectMap = {}; + const name = row[nameIndex]; + + docMap[name] ??= {}; + + for (let f = 0; f < fields.length; f++) { + const field = fields[f]; + const value = formatValue(row[f], field.fieldtype); + + if (field.parentField) { + cts[field.parentField] ??= {}; + cts[field.parentField][field.fieldname] = value; + continue; + } + + docMap[name][field.fieldname] = value; + } + + for (const k of Object.keys(cts)) { + docMap[name][k] ??= []; + (docMap[name][k] as Map[]).push(cts[k]); + } + } + + // return docObjs; + return Object.keys(docMap).map((k) => docMap[k]); + } + + async importData(): Promise { + const status: Status = { success: false, names: [], message: '' }; + + for (const docObj of this.getDocs()) { + const doc = frappe.getNewDoc(this.doctype); + await doc.update(docObj); + + try { + await doc.insert(); + } catch (err) { + const message = (err as Error).message; + + const messages = [ + frappe.t`Could not import ${this.doctype} ${doc.name}.`, + ]; + if (message) { + messages.push(frappe.t`Error: ${message}.`); + } + + if (status.names.length) { + messages.push( + frappe.t`The following ${ + status.names.length + } entries were created: ${status.names.join(', ')}` + ); + } + + status.message = messages.join(' '); + return status; + } + + status.names.push(doc.name); + } + + status.success = true; + return status; + } } -// @ts-ignore -window.im = importable; // @ts-ignore window.gtf = getTemplateFields; diff --git a/src/pages/DataImport.vue b/src/pages/DataImport.vue index dbe4bc93..d4b342a1 100644 --- a/src/pages/DataImport.vue +++ b/src/pages/DataImport.vue @@ -10,10 +10,10 @@ + +