mirror of
https://github.com/frappe/books.git
synced 2024-11-10 07:40:55 +00:00
feat: button to add a row
- fix error handling and insertion
This commit is contained in:
parent
16c0bfe4fc
commit
b29ee6ac16
@ -2,6 +2,24 @@ const frappe = require('frappe');
|
||||
const { getRandomString } = require('frappe/utils');
|
||||
|
||||
module.exports = {
|
||||
async isNameAutoSet(doctype) {
|
||||
const doc = frappe.getNewDoc(doctype);
|
||||
if (doc.meta.naming === 'autoincrement') {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!doc.meta.settings) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const { numberSeries } = await doc.getSettings();
|
||||
if (numberSeries) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
async setName(doc) {
|
||||
if (frappe.isServer) {
|
||||
// if is server, always name again if autoincrement or other
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { Field, FieldType } from '@/types/model';
|
||||
import frappe from 'frappe';
|
||||
import { isNameAutoSet } from 'frappe/model/naming';
|
||||
import { parseCSV } from './csvParser';
|
||||
|
||||
export const importable = [
|
||||
@ -275,7 +276,11 @@ export class Importer {
|
||||
|
||||
selectFile(text: string): boolean {
|
||||
this.csv = parseCSV(text);
|
||||
this.initialize(0, true);
|
||||
try {
|
||||
this.initialize(0, true);
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -286,13 +291,11 @@ export class Importer {
|
||||
) {
|
||||
return;
|
||||
}
|
||||
console.log('initing', labelIndex);
|
||||
|
||||
const source = this.csv.map((row) => [...row]);
|
||||
this.labelIndex = labelIndex;
|
||||
this.parsedLabels = this.csv[labelIndex];
|
||||
const values = this.csv.slice(labelIndex + 1);
|
||||
|
||||
this.parsedValues = values;
|
||||
this.parsedLabels = source[labelIndex];
|
||||
this.parsedValues = source.slice(labelIndex + 1);
|
||||
this.setAssigned();
|
||||
}
|
||||
|
||||
@ -353,10 +356,15 @@ export class Importer {
|
||||
|
||||
async importData(): Promise<Status> {
|
||||
const status: Status = { success: false, names: [], message: '' };
|
||||
const shouldDeleteName = await isNameAutoSet(this.doctype);
|
||||
|
||||
for (const docObj of this.getDocs()) {
|
||||
if (shouldDeleteName) {
|
||||
delete docObj.name;
|
||||
}
|
||||
|
||||
const doc = frappe.getNewDoc(this.doctype);
|
||||
await doc.update(docObj);
|
||||
await doc.set(docObj);
|
||||
|
||||
try {
|
||||
await doc.insert();
|
||||
@ -364,13 +372,15 @@ export class Importer {
|
||||
await doc.submit();
|
||||
}
|
||||
} 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}.`);
|
||||
|
||||
const message = (err as Error).message;
|
||||
if (message?.includes('UNIQUE constraint failed')) {
|
||||
messages.push(frappe.t`${doc.name} already exists.`);
|
||||
} else if (message) {
|
||||
messages.push(message);
|
||||
}
|
||||
|
||||
if (status.names.length) {
|
||||
@ -391,4 +401,9 @@ export class Importer {
|
||||
status.success = true;
|
||||
return status;
|
||||
}
|
||||
|
||||
addRow() {
|
||||
const emptyRow = Array(this.columnLabels.length).fill('');
|
||||
this.parsedValues.push(emptyRow);
|
||||
}
|
||||
}
|
||||
|
@ -54,14 +54,6 @@
|
||||
<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="
|
||||
@ -127,6 +119,20 @@
|
||||
@change="setLabelIndex"
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
class="w-28 bg-gray-100 focus:bg-gray-200 rounded-md"
|
||||
v-if="canReset"
|
||||
@click="
|
||||
() => {
|
||||
importer.initialize(0, true);
|
||||
canReset = false;
|
||||
}
|
||||
"
|
||||
>
|
||||
<span class="text-gray-900">
|
||||
{{ t`Reset` }}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -136,7 +142,10 @@
|
||||
<div
|
||||
class="gap-2 mt-4 grid grid-flow-col overflow-x-scroll no-scrollbar"
|
||||
>
|
||||
<div v-for="(f, k) in importer.assignableLabels" :key="f + '-' + k">
|
||||
<div
|
||||
v-for="(f, k) in importer.assignableLabels"
|
||||
:key="'assigner-' + k"
|
||||
>
|
||||
<p class="text-gray-600 text-sm mb-1">
|
||||
{{ f }}
|
||||
<span
|
||||
@ -194,7 +203,7 @@
|
||||
<!-- Data Rows -->
|
||||
<div
|
||||
v-if="importer.columnLabels.length > 0"
|
||||
style="max-height: 500px"
|
||||
style="max-height: 400px"
|
||||
>
|
||||
<div
|
||||
class="grid grid-flow-col mt-4 pb-4 border-b gap-2 items-center"
|
||||
@ -204,7 +213,12 @@
|
||||
>
|
||||
<button
|
||||
class="w-4 h-4 text-gray-600 hover:text-gray-900 cursor-pointer"
|
||||
@click="importer.dropRow(i)"
|
||||
@click="
|
||||
() => {
|
||||
importer.dropRow(i);
|
||||
canReset = true;
|
||||
}
|
||||
"
|
||||
>
|
||||
<FeatherIcon name="x" />
|
||||
</button>
|
||||
@ -220,11 +234,37 @@
|
||||
rounded
|
||||
focus:bg-gray-200
|
||||
"
|
||||
@change="(e) => onValueChange(e, i, j)"
|
||||
@change="
|
||||
(e) => {
|
||||
onValueChange(e, i, j);
|
||||
canReset = true;
|
||||
}
|
||||
"
|
||||
:key="'matrix-cell-' + i + '-' + j"
|
||||
:value="c"
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
class="
|
||||
text-gray-600
|
||||
hover:text-gray-900
|
||||
flex flex-row
|
||||
w-full
|
||||
mt-4
|
||||
outline-none
|
||||
"
|
||||
@click="
|
||||
() => {
|
||||
importer.addRow();
|
||||
canReset = true;
|
||||
}
|
||||
"
|
||||
>
|
||||
<FeatherIcon name="plus" class="w-4 h-4" />
|
||||
<p class="pl-4">
|
||||
{{ t`Add Row` }}
|
||||
</p>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -249,7 +289,7 @@
|
||||
<div class="max-h-96 overflow-y-scroll">
|
||||
<div
|
||||
v-for="(n, i) in names"
|
||||
:key="i"
|
||||
:key="'name-' + i"
|
||||
class="grid grid-cols-2 gap-2 border-b pb-2 mb-2 pr-4 text-lg w-60"
|
||||
style="grid-template-columns: 2rem auto"
|
||||
>
|
||||
@ -274,7 +314,7 @@ import FeatherIcon from '@/components/FeatherIcon.vue';
|
||||
import PageHeader from '@/components/PageHeader.vue';
|
||||
import { importable, Importer } from '@/dataImport';
|
||||
import { IPC_ACTIONS } from '@/messages';
|
||||
import { getSavePath, saveData, showMessageDialog, showToast } from '@/utils';
|
||||
import { getSavePath, saveData, showMessageDialog } from '@/utils';
|
||||
import { ipcRenderer } from 'electron';
|
||||
import frappe from 'frappe';
|
||||
export default {
|
||||
@ -287,6 +327,7 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
canReset: false,
|
||||
complete: false,
|
||||
names: ['Bat', 'Baseball', 'Other Shit'],
|
||||
file: null,
|
||||
@ -295,8 +336,8 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
labelIndex(){
|
||||
return this.importer.labelIndex
|
||||
labelIndex() {
|
||||
return this.importer.labelIndex;
|
||||
},
|
||||
requiredUnassigned() {
|
||||
return this.importer.assignableLabels.filter(
|
||||
@ -310,13 +351,7 @@ export default {
|
||||
return this.importer.assignedMatrix;
|
||||
},
|
||||
actions() {
|
||||
const cancelAction = {
|
||||
component: {
|
||||
template: '<span class="text-red-700" >{{ t`Cancel` }}</span>',
|
||||
},
|
||||
condition: () => true,
|
||||
action: this.clear,
|
||||
};
|
||||
const actions = [];
|
||||
|
||||
const secondaryAction = {
|
||||
component: {
|
||||
@ -325,7 +360,28 @@ export default {
|
||||
condition: () => true,
|
||||
action: this.handleSecondaryClick,
|
||||
};
|
||||
return [secondaryAction, cancelAction];
|
||||
actions.push(secondaryAction);
|
||||
|
||||
if (this.file) {
|
||||
actions.push({
|
||||
component: {
|
||||
template: '<span>{{ t`Change File` }}</span>',
|
||||
},
|
||||
condition: () => true,
|
||||
action: this.selectFile,
|
||||
});
|
||||
}
|
||||
|
||||
const cancelAction = {
|
||||
component: {
|
||||
template: '<span class="text-red-700" >{{ t`Cancel` }}</span>',
|
||||
},
|
||||
condition: () => true,
|
||||
action: this.clear,
|
||||
};
|
||||
actions.push(cancelAction);
|
||||
|
||||
return actions;
|
||||
},
|
||||
|
||||
fileName() {
|
||||
@ -381,8 +437,8 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
showMe() {
|
||||
const doctype = this.importer.doctype;
|
||||
this.clear();
|
||||
const doctype = this.importer?.doctype ?? 'Item';
|
||||
this.$router.push(`/list/${doctype}`);
|
||||
},
|
||||
clear() {
|
||||
@ -391,6 +447,7 @@ export default {
|
||||
this.importer = null;
|
||||
this.importType = '';
|
||||
this.complete = false;
|
||||
this.canReset = false;
|
||||
},
|
||||
handlePrimaryClick() {
|
||||
if (!this.file) {
|
||||
@ -462,9 +519,12 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
const { success, names } = await this.importer.importData();
|
||||
if (!success || !names.length) {
|
||||
// handle failure
|
||||
const { success, names, message } = await this.importer.importData();
|
||||
if (!success) {
|
||||
showMessageDialog({
|
||||
message: this.t`Import Failed`,
|
||||
description: message,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@ -489,7 +549,7 @@ export default {
|
||||
await ipcRenderer.invoke(IPC_ACTIONS.GET_FILE, options);
|
||||
|
||||
if (!success && !canceled) {
|
||||
showToast({ message: this.t`File selection failed.`, type: 'error' });
|
||||
showMessageDialog({ message: this.t`File selection failed.` });
|
||||
}
|
||||
|
||||
if (!success || canceled) {
|
||||
@ -499,9 +559,9 @@ export default {
|
||||
const text = new TextDecoder().decode(data);
|
||||
const isValid = this.importer.selectFile(text);
|
||||
if (!isValid) {
|
||||
showToast({
|
||||
message: this.t`Bad import data. Could not select file.`,
|
||||
type: 'error',
|
||||
showMessageDialog({
|
||||
message: this.t`Bad import data.`,
|
||||
description: this.t`Could not select file.`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
@ -511,8 +571,6 @@ export default {
|
||||
filePath,
|
||||
text,
|
||||
};
|
||||
|
||||
window.i = this.importer;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user