2023-02-02 08:12:09 +00:00
|
|
|
<template>
|
|
|
|
<div class="flex flex-col overflow-hidden w-full">
|
|
|
|
<!-- Header -->
|
|
|
|
<PageHeader :title="t`Import Wizard`">
|
2023-02-14 18:18:20 +00:00
|
|
|
<DropdownWithActions
|
|
|
|
:actions="actions"
|
2023-02-15 13:49:17 +00:00
|
|
|
v-if="hasImporter"
|
2023-02-15 08:32:48 +00:00
|
|
|
:disabled="isMakingEntries"
|
2023-02-14 18:18:20 +00:00
|
|
|
:title="t`More`"
|
|
|
|
/>
|
2023-02-02 08:12:09 +00:00
|
|
|
<Button
|
2023-02-15 13:49:17 +00:00
|
|
|
v-if="hasImporter"
|
2023-02-14 18:18:20 +00:00
|
|
|
:title="t`Add Row`"
|
2023-02-15 08:32:48 +00:00
|
|
|
@click="() => importer.addRow()"
|
|
|
|
:disabled="isMakingEntries"
|
2023-02-14 18:18:20 +00:00
|
|
|
:icon="true"
|
|
|
|
>
|
|
|
|
<feather-icon name="plus" class="w-4 h-4" />
|
|
|
|
</Button>
|
|
|
|
<Button
|
2023-02-15 13:49:17 +00:00
|
|
|
v-if="hasImporter"
|
2023-02-14 18:18:20 +00:00
|
|
|
:title="t`Save Template`"
|
2023-02-02 08:12:09 +00:00
|
|
|
@click="saveTemplate"
|
2023-02-14 18:18:20 +00:00
|
|
|
:icon="true"
|
2023-02-02 08:12:09 +00:00
|
|
|
>
|
2023-02-14 18:18:20 +00:00
|
|
|
<feather-icon name="download" class="w-4 h-4" />
|
2023-02-04 07:26:37 +00:00
|
|
|
</Button>
|
2023-02-02 08:12:09 +00:00
|
|
|
<Button
|
2023-02-04 07:26:37 +00:00
|
|
|
v-if="canImportData"
|
2023-02-14 18:18:20 +00:00
|
|
|
:title="t`Import Data`"
|
2023-02-02 08:12:09 +00:00
|
|
|
type="primary"
|
2023-02-04 07:26:37 +00:00
|
|
|
@click="importData"
|
2023-02-15 08:32:48 +00:00
|
|
|
:disabled="errorMessage.length > 0 || isMakingEntries"
|
2023-02-02 08:12:09 +00:00
|
|
|
>
|
2023-02-04 07:26:37 +00:00
|
|
|
{{ t`Import Data` }}
|
|
|
|
</Button>
|
|
|
|
<Button
|
|
|
|
v-if="importType && !canImportData"
|
2023-02-14 18:18:20 +00:00
|
|
|
:title="t`Select File`"
|
2023-02-04 07:26:37 +00:00
|
|
|
type="primary"
|
|
|
|
@click="selectFile"
|
|
|
|
>
|
|
|
|
{{ t`Select File` }}
|
|
|
|
</Button>
|
2023-02-02 08:12:09 +00:00
|
|
|
</PageHeader>
|
|
|
|
|
|
|
|
<!-- Main Body of the Wizard -->
|
2023-02-15 13:49:17 +00:00
|
|
|
<div class="flex text-base w-full flex-col">
|
2023-02-02 08:12:09 +00:00
|
|
|
<!-- Select Import Type -->
|
|
|
|
<div
|
|
|
|
class="
|
2023-02-14 18:18:20 +00:00
|
|
|
h-row-largest
|
2023-02-02 08:12:09 +00:00
|
|
|
flex flex-row
|
|
|
|
justify-start
|
|
|
|
items-center
|
|
|
|
w-full
|
|
|
|
gap-2
|
|
|
|
border-b
|
|
|
|
p-4
|
|
|
|
"
|
|
|
|
>
|
2023-02-02 12:22:50 +00:00
|
|
|
<AutoComplete
|
|
|
|
:df="{
|
|
|
|
fieldname: 'importType',
|
|
|
|
label: t`Import Type`,
|
|
|
|
fieldtype: 'AutoComplete',
|
|
|
|
options: importableSchemaNames.map((value) => ({
|
|
|
|
value,
|
|
|
|
label: fyo.schemaMap[value]?.label ?? value,
|
|
|
|
})),
|
|
|
|
}"
|
2023-02-02 08:12:09 +00:00
|
|
|
input-class="bg-transparent text-gray-900 text-base"
|
2023-02-02 12:22:50 +00:00
|
|
|
class="w-40"
|
|
|
|
:border="true"
|
2023-02-02 08:12:09 +00:00
|
|
|
:value="importType"
|
|
|
|
size="small"
|
|
|
|
@change="setImportType"
|
|
|
|
/>
|
|
|
|
|
2023-02-14 09:26:09 +00:00
|
|
|
<p v-if="errorMessage.length > 0" class="text-base ms-2 text-red-500">
|
|
|
|
{{ errorMessage }}
|
|
|
|
</p>
|
2023-02-02 08:12:09 +00:00
|
|
|
<p
|
2023-02-14 09:26:09 +00:00
|
|
|
v-else
|
2023-02-02 08:12:09 +00:00
|
|
|
class="text-base ms-2"
|
|
|
|
:class="fileName ? 'text-gray-900 font-semibold' : 'text-gray-700'"
|
|
|
|
>
|
2023-02-15 07:59:06 +00:00
|
|
|
<span v-if="fileName" class="font-normal">{{ t`Selected` }} </span>
|
2023-02-14 09:26:09 +00:00
|
|
|
{{ helperMessage }}{{ fileName ? ',' : '' }}
|
2023-02-02 08:12:09 +00:00
|
|
|
<span v-if="fileName" class="font-normal">
|
2023-02-15 07:59:06 +00:00
|
|
|
{{ t`check values and click on` }} </span
|
2023-02-15 04:46:00 +00:00
|
|
|
>{{ ' ' }}<span v-if="fileName">{{ t`Import Data.` }}</span>
|
|
|
|
<span
|
|
|
|
v-if="hasImporter && importer.valueMatrix.length > 0"
|
|
|
|
class="font-normal"
|
2023-02-15 07:59:06 +00:00
|
|
|
>{{
|
|
|
|
' ' +
|
|
|
|
(importer.valueMatrix.length === 2
|
|
|
|
? t`${importer.valueMatrix.length} row added.`
|
|
|
|
: t`${importer.valueMatrix.length} rows added.`)
|
|
|
|
}}</span
|
2023-02-15 04:46:00 +00:00
|
|
|
>
|
2023-02-02 08:12:09 +00:00
|
|
|
</p>
|
|
|
|
</div>
|
|
|
|
|
2023-02-15 07:59:06 +00:00
|
|
|
<!-- Assignment Row and Value Grid container -->
|
|
|
|
<div
|
|
|
|
v-if="hasImporter"
|
|
|
|
class="overflow-auto custom-scroll"
|
|
|
|
style="max-height: calc(100vh - (2 * var(--h-row-largest)) - 2px)"
|
|
|
|
>
|
|
|
|
<!-- Column Assignment Row -->
|
2023-02-14 18:18:20 +00:00
|
|
|
<div
|
2023-02-15 07:59:06 +00:00
|
|
|
class="grid sticky top-0 py-4 pe-4 bg-white border-b gap-4"
|
|
|
|
style="z-index: 1; width: fit-content"
|
|
|
|
:style="gridTemplateColumn"
|
2023-02-14 18:18:20 +00:00
|
|
|
>
|
2023-02-15 07:59:06 +00:00
|
|
|
<div class="index-cell">#</div>
|
2023-02-15 08:32:48 +00:00
|
|
|
<Select
|
2023-02-15 07:59:06 +00:00
|
|
|
v-for="index in columnIterator"
|
|
|
|
class="flex-shrink-0"
|
|
|
|
size="small"
|
|
|
|
:border="true"
|
|
|
|
:key="index"
|
|
|
|
:df="gridColumnTitleDf"
|
2023-04-03 08:21:46 +00:00
|
|
|
:value="importer.assignedTemplateFields[index]!"
|
2023-02-15 07:59:06 +00:00
|
|
|
@change="(value: string | null) => importer.setTemplateField(index, value)"
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<!-- Values Grid -->
|
|
|
|
<div
|
|
|
|
v-if="importer.valueMatrix.length"
|
|
|
|
class="grid py-4 pe-4 bg-white gap-4"
|
|
|
|
style="width: fit-content"
|
|
|
|
:style="gridTemplateColumn"
|
|
|
|
>
|
|
|
|
<!-- Grid Value Row Cells, Allow Editing Values -->
|
|
|
|
<template v-for="(row, ridx) of importer.valueMatrix" :key="ridx">
|
2023-02-02 08:12:09 +00:00
|
|
|
<div
|
2023-02-15 07:59:06 +00:00
|
|
|
class="index-cell group cursor-pointer"
|
|
|
|
@click="importer.removeRow(ridx)"
|
2023-02-02 08:12:09 +00:00
|
|
|
>
|
2023-02-15 07:59:06 +00:00
|
|
|
<feather-icon
|
|
|
|
name="x"
|
|
|
|
class="w-4 h-4 hidden group-hover:inline-block -me-1"
|
|
|
|
:button="true"
|
|
|
|
/>
|
2023-02-02 08:12:09 +00:00
|
|
|
<span class="group-hover:hidden">
|
2023-02-15 07:59:06 +00:00
|
|
|
{{ ridx + 1 }}
|
2023-02-02 08:12:09 +00:00
|
|
|
</span>
|
|
|
|
</div>
|
|
|
|
|
2023-02-15 07:59:06 +00:00
|
|
|
<template
|
|
|
|
v-for="(val, cidx) of row.slice(0, columnCount)"
|
|
|
|
:key="`cell-${ridx}-${cidx}`"
|
|
|
|
>
|
|
|
|
<!-- Raw Data Field if Column is Not Assigned -->
|
|
|
|
<Data
|
|
|
|
v-if="!importer.assignedTemplateFields[cidx]"
|
|
|
|
:title="getFieldTitle(val)"
|
|
|
|
:df="{
|
2023-04-03 08:21:46 +00:00
|
|
|
fieldtype: 'Data',
|
2023-02-15 07:59:06 +00:00
|
|
|
fieldname: 'tempField',
|
|
|
|
label: t`Temporary`,
|
|
|
|
placeholder: t`Select column`,
|
|
|
|
}"
|
|
|
|
size="small"
|
|
|
|
:border="true"
|
|
|
|
:value="
|
|
|
|
val.value != null
|
|
|
|
? String(val.value)
|
|
|
|
: val.rawValue != null
|
|
|
|
? String(val.rawValue)
|
|
|
|
: ''
|
|
|
|
"
|
|
|
|
:read-only="true"
|
|
|
|
/>
|
|
|
|
|
|
|
|
<!-- FormControl Field if Column is Assigned -->
|
|
|
|
<FormControl
|
|
|
|
v-else
|
|
|
|
:class="val.error ? 'border border-red-300 rounded-md' : ''"
|
|
|
|
:title="getFieldTitle(val)"
|
|
|
|
:df="
|
2023-02-02 08:12:09 +00:00
|
|
|
importer.templateFieldsMap.get(
|
2023-02-02 12:22:50 +00:00
|
|
|
importer.assignedTemplateFields[cidx]!
|
2023-02-02 08:12:09 +00:00
|
|
|
)
|
|
|
|
"
|
2023-02-15 07:59:06 +00:00
|
|
|
size="small"
|
|
|
|
:rows="1"
|
|
|
|
:border="true"
|
|
|
|
:value="val.error ? null : val.value"
|
2023-04-24 08:04:02 +00:00
|
|
|
:read-only="false"
|
2023-02-15 07:59:06 +00:00
|
|
|
@change="(value: DocValue)=> {
|
2023-02-02 12:22:50 +00:00
|
|
|
importer.valueMatrix[ridx][cidx]!.error = false
|
2023-02-02 08:12:09 +00:00
|
|
|
importer.valueMatrix[ridx][cidx]!.value = value
|
|
|
|
}"
|
2023-02-15 07:59:06 +00:00
|
|
|
/>
|
2023-02-02 08:12:09 +00:00
|
|
|
</template>
|
2023-02-15 07:59:06 +00:00
|
|
|
</template>
|
2023-02-02 08:12:09 +00:00
|
|
|
</div>
|
|
|
|
|
2023-02-15 08:32:48 +00:00
|
|
|
<div
|
|
|
|
v-else
|
|
|
|
class="ps-4 text-gray-700 sticky left-0 flex items-center"
|
|
|
|
style="height: 62.5px"
|
|
|
|
>
|
2023-02-15 07:59:06 +00:00
|
|
|
{{ t`No rows added. Select a file or add rows.` }}
|
|
|
|
</div>
|
|
|
|
</div>
|
2023-02-02 08:12:09 +00:00
|
|
|
</div>
|
|
|
|
|
2023-02-02 12:22:50 +00:00
|
|
|
<!-- Loading Bar when Saving Docs -->
|
2023-02-02 08:12:09 +00:00
|
|
|
<Loading
|
|
|
|
v-if="isMakingEntries"
|
|
|
|
:open="isMakingEntries"
|
|
|
|
:percent="percentLoading"
|
|
|
|
:message="messageLoading"
|
|
|
|
/>
|
2023-02-02 12:22:50 +00:00
|
|
|
|
|
|
|
<!-- Pick Column Modal -->
|
|
|
|
<Modal
|
|
|
|
:open-modal="showColumnPicker"
|
|
|
|
@closemodal="showColumnPicker = false"
|
|
|
|
>
|
|
|
|
<div class="w-form">
|
|
|
|
<!-- Pick Column Header -->
|
|
|
|
<FormHeader :form-title="t`Pick Import Columns`" />
|
|
|
|
<hr />
|
|
|
|
|
|
|
|
<!-- Pick Column Checkboxes -->
|
|
|
|
<div
|
|
|
|
class="p-4 max-h-80 overflow-auto custom-scroll"
|
|
|
|
v-for="[key, value] of columnPickerFieldsMap.entries()"
|
|
|
|
:key="key"
|
|
|
|
>
|
|
|
|
<h2 class="text-sm font-semibold text-gray-800">
|
|
|
|
{{ key }}
|
|
|
|
</h2>
|
|
|
|
<div class="grid grid-cols-3 border rounded mt-1">
|
|
|
|
<div
|
|
|
|
v-for="tf of value"
|
|
|
|
:key="tf.fieldKey"
|
|
|
|
class="flex items-center"
|
|
|
|
>
|
|
|
|
<Check
|
|
|
|
:df="{
|
2023-04-03 08:21:46 +00:00
|
|
|
fieldtype: 'Check',
|
2023-02-02 12:22:50 +00:00
|
|
|
fieldname: tf.fieldname,
|
|
|
|
label: tf.label,
|
|
|
|
}"
|
|
|
|
:show-label="true"
|
|
|
|
:read-only="tf.required"
|
|
|
|
:value="importer.templateFieldsPicked.get(tf.fieldKey)"
|
|
|
|
@change="(value:boolean) => pickColumn(tf.fieldKey, value)"
|
|
|
|
/>
|
|
|
|
<p v-if="tf.required" class="w-0 text-red-600 -ml-4">*</p>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<!-- Pick Column Footer -->
|
|
|
|
<hr />
|
|
|
|
<div class="p-4 flex justify-between items-center">
|
|
|
|
<p class="text-sm text-gray-600">
|
|
|
|
{{ t`${numColumnsPicked} fields selected` }}
|
|
|
|
</p>
|
|
|
|
<Button type="primary" @click="showColumnPicker = false">{{
|
|
|
|
t`Done`
|
|
|
|
}}</Button>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</Modal>
|
2023-02-15 04:46:00 +00:00
|
|
|
|
|
|
|
<!-- Import Completed Modal -->
|
|
|
|
<Modal :open-modal="complete" @closemodal="clear">
|
|
|
|
<div class="w-form">
|
|
|
|
<!-- Import Completed Header -->
|
|
|
|
<FormHeader :form-title="t`Import Complete`" />
|
|
|
|
<hr />
|
|
|
|
<!-- Success -->
|
|
|
|
<div v-if="success.length > 0">
|
|
|
|
<!-- Success Section Header -->
|
|
|
|
<div class="flex justify-between px-4 pt-4 pb-1">
|
|
|
|
<p class="text-base font-semibold">{{ t`Success` }}</p>
|
|
|
|
<p class="text-sm text-gray-600">
|
|
|
|
{{
|
|
|
|
success.length === 1
|
|
|
|
? t`${success.length} entry imported`
|
|
|
|
: t`${success.length} entries imported`
|
|
|
|
}}
|
|
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<!-- Success Body -->
|
|
|
|
<div class="max-h-40 overflow-auto text-gray-900">
|
|
|
|
<div
|
|
|
|
v-for="(name, i) of success"
|
|
|
|
:key="name"
|
|
|
|
class="px-4 py-1 grid grid-cols-2 text-base gap-4"
|
|
|
|
style="grid-template-columns: 1rem auto"
|
|
|
|
>
|
|
|
|
<div class="text-end">{{ i + 1 }}.</div>
|
|
|
|
<p class="whitespace-nowrap overflow-auto no-scrollbar">
|
|
|
|
{{ name }}
|
|
|
|
</p>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<hr />
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<!-- Failed -->
|
|
|
|
<div v-if="failed.length > 0">
|
|
|
|
<!-- Failed Section Header -->
|
|
|
|
<div class="flex justify-between px-4 pt-4 pb-1">
|
|
|
|
<p class="text-base font-semibold">{{ t`Failed` }}</p>
|
|
|
|
<p class="text-sm text-gray-600">
|
|
|
|
{{
|
|
|
|
failed.length === 1
|
|
|
|
? t`${failed.length} entry failed`
|
|
|
|
: t`${failed.length} entries failed`
|
|
|
|
}}
|
|
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<!-- Failed Body -->
|
|
|
|
<div class="max-h-40 overflow-auto text-gray-900">
|
|
|
|
<div
|
|
|
|
v-for="(f, i) of failed"
|
|
|
|
:key="f.name"
|
|
|
|
class="px-4 py-1 grid grid-cols-2 text-base gap-4"
|
|
|
|
style="grid-template-columns: 1rem 8rem auto"
|
|
|
|
>
|
|
|
|
<div class="text-end">{{ i + 1 }}.</div>
|
|
|
|
<p class="whitespace-nowrap overflow-auto no-scrollbar">
|
|
|
|
{{ f.name }}
|
|
|
|
</p>
|
|
|
|
<p class="whitespace-nowrap overflow-auto no-scrollbar">
|
|
|
|
{{ f.error.message }}
|
|
|
|
</p>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<hr />
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<!-- Fallback Div -->
|
|
|
|
<div
|
|
|
|
v-if="failed.length === 0 && success.length === 0"
|
|
|
|
class="p-4 text-base"
|
|
|
|
>
|
|
|
|
{{ t`No entries were imported.` }}
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<!-- Footer Button -->
|
|
|
|
<div class="flex justify-between p-4">
|
|
|
|
<Button
|
|
|
|
v-if="failed.length > 0"
|
|
|
|
@click="clearSuccessfullyImportedEntries"
|
|
|
|
>{{ t`Fix Failed` }}</Button
|
|
|
|
>
|
|
|
|
<Button
|
|
|
|
v-if="failed.length === 0 && success.length > 0"
|
|
|
|
@click="showMe"
|
|
|
|
>{{ t`Show Me` }}</Button
|
|
|
|
>
|
|
|
|
<Button @click="clear">{{ t`Done` }}</Button>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</Modal>
|
2023-02-02 08:12:09 +00:00
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
<script lang="ts">
|
|
|
|
import { DocValue } from 'fyo/core/types';
|
2023-04-03 08:21:46 +00:00
|
|
|
import { Action } from 'fyo/model/types';
|
2023-02-02 08:12:09 +00:00
|
|
|
import { ValidationError } from 'fyo/utils/errors';
|
|
|
|
import { ModelNameEnum } from 'models/types';
|
2023-02-15 13:49:17 +00:00
|
|
|
import { OptionField, RawValue, SelectOption } from 'schemas/types';
|
2023-02-02 08:12:09 +00:00
|
|
|
import Button from 'src/components/Button.vue';
|
|
|
|
import AutoComplete from 'src/components/Controls/AutoComplete.vue';
|
2023-02-02 12:22:50 +00:00
|
|
|
import Check from 'src/components/Controls/Check.vue';
|
|
|
|
import Data from 'src/components/Controls/Data.vue';
|
2023-02-02 08:12:09 +00:00
|
|
|
import FormControl from 'src/components/Controls/FormControl.vue';
|
2023-02-15 13:49:17 +00:00
|
|
|
import Select from 'src/components/Controls/Select.vue';
|
2023-02-02 08:12:09 +00:00
|
|
|
import DropdownWithActions from 'src/components/DropdownWithActions.vue';
|
2023-02-02 12:22:50 +00:00
|
|
|
import FormHeader from 'src/components/FormHeader.vue';
|
|
|
|
import Modal from 'src/components/Modal.vue';
|
2023-02-02 08:12:09 +00:00
|
|
|
import PageHeader from 'src/components/PageHeader.vue';
|
2023-02-14 09:26:09 +00:00
|
|
|
import { getColumnLabel, Importer, TemplateField } from 'src/importer';
|
2023-02-02 08:12:09 +00:00
|
|
|
import { fyo } from 'src/initFyo';
|
2023-03-25 07:46:25 +00:00
|
|
|
import { showDialog } from 'src/utils/interactive';
|
2023-02-02 08:12:09 +00:00
|
|
|
import { getSavePath, saveData, selectFile } from 'src/utils/ipcCalls';
|
|
|
|
import { docsPathMap } from 'src/utils/misc';
|
|
|
|
import { docsPathRef } from 'src/utils/refs';
|
2023-03-25 07:46:25 +00:00
|
|
|
import { selectTextFile } from 'src/utils/ui';
|
2023-02-02 08:12:09 +00:00
|
|
|
import { defineComponent } from 'vue';
|
|
|
|
import Loading from '../components/Loading.vue';
|
|
|
|
|
2023-02-14 09:35:09 +00:00
|
|
|
type ImportWizardData = {
|
2023-02-02 12:22:50 +00:00
|
|
|
showColumnPicker: boolean;
|
2023-02-02 08:12:09 +00:00
|
|
|
complete: boolean;
|
2023-02-15 04:46:00 +00:00
|
|
|
success: string[];
|
2023-02-15 05:11:22 +00:00
|
|
|
successOldName: string[];
|
2023-02-13 15:18:56 +00:00
|
|
|
failed: { name: string; error: Error }[];
|
2023-02-02 08:12:09 +00:00
|
|
|
file: null | { name: string; filePath: string; text: string };
|
|
|
|
nullOrImporter: null | Importer;
|
|
|
|
importType: string;
|
|
|
|
isMakingEntries: boolean;
|
|
|
|
percentLoading: number;
|
|
|
|
messageLoading: string;
|
|
|
|
};
|
|
|
|
|
|
|
|
export default defineComponent({
|
|
|
|
components: {
|
|
|
|
PageHeader,
|
|
|
|
FormControl,
|
|
|
|
Button,
|
|
|
|
DropdownWithActions,
|
|
|
|
Loading,
|
|
|
|
AutoComplete,
|
2023-02-02 12:22:50 +00:00
|
|
|
Data,
|
|
|
|
Modal,
|
|
|
|
FormHeader,
|
|
|
|
Check,
|
2023-02-15 08:32:48 +00:00
|
|
|
Select,
|
2023-02-02 08:12:09 +00:00
|
|
|
},
|
|
|
|
data() {
|
|
|
|
return {
|
2023-02-02 12:22:50 +00:00
|
|
|
showColumnPicker: false,
|
2023-02-02 08:12:09 +00:00
|
|
|
complete: false,
|
2023-02-15 04:46:00 +00:00
|
|
|
success: [],
|
2023-02-15 05:11:22 +00:00
|
|
|
successOldName: [],
|
2023-02-13 15:18:56 +00:00
|
|
|
failed: [],
|
2023-02-02 08:12:09 +00:00
|
|
|
file: null,
|
|
|
|
nullOrImporter: null,
|
|
|
|
importType: '',
|
|
|
|
isMakingEntries: false,
|
|
|
|
percentLoading: 0,
|
|
|
|
messageLoading: '',
|
2023-02-14 09:35:09 +00:00
|
|
|
} as ImportWizardData;
|
2023-02-02 08:12:09 +00:00
|
|
|
},
|
|
|
|
mounted() {
|
|
|
|
if (fyo.store.isDevelopment) {
|
|
|
|
// @ts-ignore
|
2023-02-02 12:22:50 +00:00
|
|
|
window.iw = this;
|
2023-02-02 08:12:09 +00:00
|
|
|
}
|
|
|
|
},
|
2023-02-15 08:32:48 +00:00
|
|
|
watch: {
|
|
|
|
columnCount(val) {
|
|
|
|
if (!this.hasImporter) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const possiblyAssigned = this.importer.assignedTemplateFields.length;
|
|
|
|
if (val >= this.importer.assignedTemplateFields.length) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (let i = val; i < possiblyAssigned; i++) {
|
|
|
|
this.importer.assignedTemplateFields[i] = null;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
2023-02-02 08:12:09 +00:00
|
|
|
computed: {
|
2023-02-15 07:59:06 +00:00
|
|
|
gridTemplateColumn(): string {
|
|
|
|
return `grid-template-columns: 4rem repeat(${this.columnCount}, 10rem)`;
|
|
|
|
},
|
2023-02-14 09:26:09 +00:00
|
|
|
duplicates(): string[] {
|
|
|
|
if (!this.hasImporter) {
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
|
|
|
|
const dupes = new Set<string>();
|
|
|
|
const assignedSet = new Set<string>();
|
|
|
|
|
|
|
|
for (const key of this.importer.assignedTemplateFields) {
|
|
|
|
if (!key) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
const tf = this.importer.templateFieldsMap.get(key);
|
|
|
|
if (assignedSet.has(key) && tf) {
|
|
|
|
dupes.add(getColumnLabel(tf));
|
|
|
|
}
|
|
|
|
|
|
|
|
assignedSet.add(key);
|
|
|
|
}
|
|
|
|
|
|
|
|
return Array.from(dupes);
|
|
|
|
},
|
|
|
|
requiredNotSelected(): string[] {
|
|
|
|
if (!this.hasImporter) {
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
|
|
|
|
const assigned = new Set(this.importer.assignedTemplateFields);
|
|
|
|
return [...this.importer.templateFieldsMap.values()]
|
2023-04-21 05:12:50 +00:00
|
|
|
.filter((f) => {
|
|
|
|
if (assigned.has(f.fieldKey) || !f.required) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (f.parentSchemaChildField && !f.parentSchemaChildField.required) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return f.required;
|
|
|
|
})
|
2023-02-14 09:26:09 +00:00
|
|
|
.map((f) => getColumnLabel(f));
|
|
|
|
},
|
|
|
|
errorMessage(): string {
|
|
|
|
if (this.duplicates.length) {
|
|
|
|
return this.t`Duplicate columns found: ${this.duplicates.join(', ')}`;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.requiredNotSelected.length) {
|
|
|
|
return this
|
|
|
|
.t`Required fields not selected: ${this.requiredNotSelected.join(
|
|
|
|
', '
|
|
|
|
)}`;
|
|
|
|
}
|
|
|
|
|
|
|
|
return '';
|
|
|
|
},
|
2023-02-04 07:26:37 +00:00
|
|
|
canImportData(): boolean {
|
2023-02-13 15:18:56 +00:00
|
|
|
if (!this.hasImporter) {
|
|
|
|
return false;
|
2023-02-04 07:26:37 +00:00
|
|
|
}
|
|
|
|
|
2023-02-13 15:18:56 +00:00
|
|
|
return this.importer.valueMatrix.length > 0;
|
2023-02-04 07:26:37 +00:00
|
|
|
},
|
|
|
|
canSelectFile(): boolean {
|
|
|
|
return !this.file;
|
|
|
|
},
|
2023-02-02 12:22:50 +00:00
|
|
|
columnCount(): number {
|
2023-02-15 08:32:48 +00:00
|
|
|
if (!this.hasImporter) {
|
|
|
|
return 0;
|
2023-02-02 12:22:50 +00:00
|
|
|
}
|
|
|
|
|
2023-02-04 07:26:37 +00:00
|
|
|
if (!this.file) {
|
|
|
|
return this.numColumnsPicked;
|
|
|
|
}
|
|
|
|
|
2023-02-15 13:49:17 +00:00
|
|
|
if (!this.importer.valueMatrix.length) {
|
|
|
|
return this.importer.assignedTemplateFields.length;
|
2023-02-15 08:32:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return Math.min(
|
|
|
|
this.importer.assignedTemplateFields.length,
|
2023-02-15 13:49:17 +00:00
|
|
|
this.importer.valueMatrix[0].length
|
2023-02-15 08:32:48 +00:00
|
|
|
);
|
2023-02-02 12:22:50 +00:00
|
|
|
},
|
|
|
|
columnIterator(): number[] {
|
|
|
|
return Array(this.columnCount)
|
|
|
|
.fill(null)
|
|
|
|
.map((_, i) => i);
|
|
|
|
},
|
2023-02-02 08:12:09 +00:00
|
|
|
hasImporter(): boolean {
|
|
|
|
return !!this.nullOrImporter;
|
|
|
|
},
|
2023-02-02 12:22:50 +00:00
|
|
|
numColumnsPicked(): number {
|
|
|
|
return [...this.importer.templateFieldsPicked.values()].filter(Boolean)
|
|
|
|
.length;
|
|
|
|
},
|
|
|
|
columnPickerFieldsMap(): Map<string, TemplateField[]> {
|
|
|
|
const map: Map<string, TemplateField[]> = new Map();
|
|
|
|
|
|
|
|
for (const value of this.importer.templateFieldsMap.values()) {
|
|
|
|
let label = value.schemaLabel;
|
|
|
|
if (value.parentSchemaChildField) {
|
|
|
|
label = `${value.parentSchemaChildField.label} (${value.schemaLabel})`;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!map.has(label)) {
|
|
|
|
map.set(label, []);
|
|
|
|
}
|
|
|
|
|
|
|
|
map.get(label)!.push(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
return map;
|
|
|
|
},
|
2023-02-02 08:12:09 +00:00
|
|
|
importer(): Importer {
|
|
|
|
if (!this.nullOrImporter) {
|
|
|
|
throw new ValidationError(this.t`Importer not set, reload tool`, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.nullOrImporter as Importer;
|
|
|
|
},
|
|
|
|
importableSchemaNames(): ModelNameEnum[] {
|
|
|
|
const importables = [
|
|
|
|
ModelNameEnum.SalesInvoice,
|
|
|
|
ModelNameEnum.PurchaseInvoice,
|
|
|
|
ModelNameEnum.Payment,
|
|
|
|
ModelNameEnum.Party,
|
|
|
|
ModelNameEnum.Item,
|
|
|
|
ModelNameEnum.JournalEntry,
|
2023-02-15 13:49:17 +00:00
|
|
|
ModelNameEnum.Tax,
|
|
|
|
ModelNameEnum.Account,
|
|
|
|
ModelNameEnum.Address,
|
|
|
|
ModelNameEnum.NumberSeries,
|
2023-02-02 08:12:09 +00:00
|
|
|
];
|
|
|
|
|
|
|
|
const hasInventory = fyo.doc.singles.AccountingSettings?.enableInventory;
|
|
|
|
if (hasInventory) {
|
|
|
|
importables.push(
|
|
|
|
ModelNameEnum.StockMovement,
|
|
|
|
ModelNameEnum.Shipment,
|
|
|
|
ModelNameEnum.PurchaseReceipt,
|
|
|
|
ModelNameEnum.Location
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return importables;
|
|
|
|
},
|
|
|
|
actions(): Action[] {
|
|
|
|
const actions: Action[] = [];
|
|
|
|
|
2023-02-04 07:26:37 +00:00
|
|
|
let selectFileLabel = this.t`Select File`;
|
2023-02-02 08:12:09 +00:00
|
|
|
if (this.file) {
|
2023-02-04 07:26:37 +00:00
|
|
|
selectFileLabel = this.t`Change File`;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.canImportData) {
|
2023-02-02 08:12:09 +00:00
|
|
|
actions.push({
|
2023-04-03 08:21:46 +00:00
|
|
|
label: selectFileLabel,
|
2023-02-02 08:12:09 +00:00
|
|
|
component: {
|
2023-02-04 07:26:37 +00:00
|
|
|
template: `<span>{{ "${selectFileLabel}" }}</span>`,
|
2023-02-02 08:12:09 +00:00
|
|
|
},
|
|
|
|
action: this.selectFile,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-02-02 12:22:50 +00:00
|
|
|
const pickColumnsAction = {
|
2023-04-03 08:21:46 +00:00
|
|
|
label: this.t`Pick Import Columns`,
|
2023-02-02 12:22:50 +00:00
|
|
|
component: {
|
|
|
|
template: '<span>{{ t`Pick Import Columns` }}</span>',
|
|
|
|
},
|
|
|
|
action: () => (this.showColumnPicker = true),
|
|
|
|
};
|
|
|
|
|
2023-02-02 08:12:09 +00:00
|
|
|
const cancelAction = {
|
2023-04-03 08:21:46 +00:00
|
|
|
label: this.t`Cancel`,
|
2023-02-02 08:12:09 +00:00
|
|
|
component: {
|
|
|
|
template: '<span class="text-red-700" >{{ t`Cancel` }}</span>',
|
|
|
|
},
|
|
|
|
action: this.clear,
|
|
|
|
};
|
2023-02-02 12:22:50 +00:00
|
|
|
actions.push(pickColumnsAction, cancelAction);
|
2023-02-02 08:12:09 +00:00
|
|
|
|
|
|
|
return actions;
|
|
|
|
},
|
|
|
|
fileName(): string {
|
|
|
|
if (!this.file) {
|
|
|
|
return '';
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.file.name;
|
|
|
|
},
|
2023-02-14 09:26:09 +00:00
|
|
|
helperMessage(): string {
|
2023-02-02 08:12:09 +00:00
|
|
|
if (!this.importType) {
|
|
|
|
return this.t`Set an Import Type`;
|
|
|
|
} else if (!this.fileName) {
|
2023-02-15 07:59:06 +00:00
|
|
|
return '';
|
2023-02-02 08:12:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return this.fileName;
|
|
|
|
},
|
|
|
|
isSubmittable(): boolean {
|
|
|
|
const schemaName = this.importer.schemaName;
|
|
|
|
return fyo.schemaMap[schemaName]?.isSubmittable ?? false;
|
|
|
|
},
|
|
|
|
gridColumnTitleDf(): OptionField {
|
|
|
|
const options: SelectOption[] = [];
|
|
|
|
for (const field of this.importer.templateFieldsMap.values()) {
|
|
|
|
const value = field.fieldKey;
|
2023-02-02 12:22:50 +00:00
|
|
|
if (!this.importer.templateFieldsPicked.get(value)) {
|
|
|
|
continue;
|
|
|
|
}
|
2023-02-02 08:12:09 +00:00
|
|
|
|
2023-02-14 09:26:09 +00:00
|
|
|
const label = getColumnLabel(field);
|
2023-02-02 08:12:09 +00:00
|
|
|
|
|
|
|
options.push({ value, label });
|
|
|
|
}
|
|
|
|
|
2023-02-15 08:32:48 +00:00
|
|
|
options.push({ value: '', label: this.t`None` });
|
2023-02-02 08:12:09 +00:00
|
|
|
return {
|
|
|
|
fieldname: 'col',
|
2023-02-15 08:32:48 +00:00
|
|
|
fieldtype: 'Select',
|
2023-02-02 08:12:09 +00:00
|
|
|
options,
|
|
|
|
} as OptionField;
|
|
|
|
},
|
|
|
|
pickedArray(): string[] {
|
|
|
|
return [...this.importer.templateFieldsPicked.entries()]
|
2023-02-02 12:22:50 +00:00
|
|
|
.filter(([_, picked]) => picked)
|
2023-02-02 08:12:09 +00:00
|
|
|
.map(([key, _]) => key);
|
|
|
|
},
|
|
|
|
},
|
|
|
|
activated(): void {
|
2023-02-14 09:35:09 +00:00
|
|
|
docsPathRef.value = docsPathMap.ImportWizard ?? '';
|
2023-02-02 08:12:09 +00:00
|
|
|
},
|
|
|
|
deactivated(): void {
|
|
|
|
docsPathRef.value = '';
|
|
|
|
if (!this.complete) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.clear();
|
|
|
|
},
|
|
|
|
methods: {
|
2023-02-13 06:59:47 +00:00
|
|
|
getFieldTitle(vmi: {
|
|
|
|
value?: DocValue;
|
|
|
|
rawValue?: RawValue;
|
|
|
|
error?: boolean;
|
2023-02-14 09:26:09 +00:00
|
|
|
}): string {
|
2023-02-13 06:59:47 +00:00
|
|
|
const title: string[] = [];
|
|
|
|
if (vmi.value != null) {
|
|
|
|
title.push(this.t`Value: ${String(vmi.value)}`);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (vmi.rawValue != null) {
|
|
|
|
title.push(this.t`Raw Value: ${String(vmi.rawValue)}`);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (vmi.error) {
|
|
|
|
title.push(this.t`Conversion Error`);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!title.length) {
|
2023-02-14 09:26:09 +00:00
|
|
|
return this.t`No Value`;
|
2023-02-13 06:59:47 +00:00
|
|
|
}
|
2023-02-14 09:26:09 +00:00
|
|
|
|
2023-02-13 06:59:47 +00:00
|
|
|
return title.join(', ');
|
|
|
|
},
|
2023-02-02 12:22:50 +00:00
|
|
|
pickColumn(fieldKey: string, value: boolean): void {
|
|
|
|
this.importer.templateFieldsPicked.set(fieldKey, value);
|
|
|
|
if (value) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const idx = this.importer.assignedTemplateFields.findIndex(
|
|
|
|
(f) => f === fieldKey
|
|
|
|
);
|
|
|
|
|
|
|
|
if (idx >= 0) {
|
|
|
|
this.importer.assignedTemplateFields[idx] = null;
|
2023-02-04 07:26:37 +00:00
|
|
|
this.reassignTemplateFields();
|
|
|
|
}
|
|
|
|
},
|
2023-02-14 09:26:09 +00:00
|
|
|
reassignTemplateFields(): void {
|
2023-02-04 07:26:37 +00:00
|
|
|
if (this.importer.valueMatrix.length) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const idx in this.importer.assignedTemplateFields) {
|
|
|
|
this.importer.assignedTemplateFields[idx] = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
let idx = 0;
|
|
|
|
for (const [fieldKey, value] of this.importer.templateFieldsPicked) {
|
|
|
|
if (!value) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.importer.assignedTemplateFields[idx] = fieldKey;
|
|
|
|
idx += 1;
|
2023-02-02 12:22:50 +00:00
|
|
|
}
|
|
|
|
},
|
2023-02-02 08:12:09 +00:00
|
|
|
showMe(): void {
|
|
|
|
const schemaName = this.importer.schemaName;
|
|
|
|
this.clear();
|
|
|
|
this.$router.push(`/list/${schemaName}`);
|
|
|
|
},
|
|
|
|
clear(): void {
|
|
|
|
this.file = null;
|
2023-02-15 04:46:00 +00:00
|
|
|
this.success = [];
|
2023-02-15 05:11:22 +00:00
|
|
|
this.successOldName = [];
|
2023-02-13 15:18:56 +00:00
|
|
|
this.failed = [];
|
2023-02-02 08:12:09 +00:00
|
|
|
this.nullOrImporter = null;
|
|
|
|
this.importType = '';
|
|
|
|
this.complete = false;
|
|
|
|
this.isMakingEntries = false;
|
|
|
|
this.percentLoading = 0;
|
|
|
|
this.messageLoading = '';
|
|
|
|
},
|
|
|
|
async saveTemplate(): Promise<void> {
|
|
|
|
const template = this.importer.getCSVTemplate();
|
|
|
|
const templateName = this.importType + ' ' + this.t`Template`;
|
|
|
|
const { canceled, filePath } = await getSavePath(templateName, 'csv');
|
|
|
|
|
|
|
|
if (canceled || !filePath) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
await saveData(template, filePath);
|
|
|
|
},
|
2023-02-14 09:26:09 +00:00
|
|
|
async preImportValidations(): Promise<boolean> {
|
2023-03-25 07:46:25 +00:00
|
|
|
const title = this.t`Cannot Import`;
|
2023-02-14 09:26:09 +00:00
|
|
|
if (this.errorMessage.length) {
|
2023-03-25 07:46:25 +00:00
|
|
|
await showDialog({
|
|
|
|
title,
|
|
|
|
type: 'error',
|
2023-02-14 09:26:09 +00:00
|
|
|
detail: this.errorMessage,
|
|
|
|
});
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
const cellErrors = this.importer.checkCellErrors();
|
|
|
|
if (cellErrors.length) {
|
2023-03-25 07:46:25 +00:00
|
|
|
await showDialog({
|
|
|
|
title,
|
|
|
|
type: 'error',
|
2023-04-21 05:12:50 +00:00
|
|
|
detail: this.t`Following cells have errors: ${cellErrors.join(
|
|
|
|
', '
|
|
|
|
)}.`,
|
2023-02-14 09:26:09 +00:00
|
|
|
});
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
const absentLinks = await this.importer.checkLinks();
|
|
|
|
if (absentLinks.length) {
|
2023-03-25 07:46:25 +00:00
|
|
|
await showDialog({
|
|
|
|
title,
|
|
|
|
type: 'error',
|
2023-02-14 09:26:09 +00:00
|
|
|
detail: this.t`Following links do not exist: ${absentLinks
|
|
|
|
.map((l) => `(${l.schemaLabel}, ${l.name})`)
|
2023-04-20 06:10:23 +00:00
|
|
|
.join(', ')}.`,
|
2023-02-14 09:26:09 +00:00
|
|
|
});
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
},
|
2023-02-02 08:12:09 +00:00
|
|
|
async importData(): Promise<void> {
|
2023-02-14 09:26:09 +00:00
|
|
|
const isValid = await this.preImportValidations();
|
|
|
|
if (!isValid || this.isMakingEntries || this.complete) {
|
2023-02-02 08:12:09 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-02-15 04:46:00 +00:00
|
|
|
this.isMakingEntries = true;
|
2023-02-14 08:04:22 +00:00
|
|
|
this.importer.populateDocs();
|
2023-02-02 08:12:09 +00:00
|
|
|
|
2023-02-15 05:11:22 +00:00
|
|
|
const shouldSubmit = await this.askShouldSubmit();
|
|
|
|
|
2023-02-13 15:18:56 +00:00
|
|
|
let doneCount = 0;
|
|
|
|
for (const doc of this.importer.docs) {
|
|
|
|
this.setLoadingStatus(doneCount, this.importer.docs.length);
|
2023-02-15 05:11:22 +00:00
|
|
|
const oldName = doc.name ?? '';
|
2023-02-13 15:18:56 +00:00
|
|
|
try {
|
|
|
|
await doc.sync();
|
2023-02-15 05:11:22 +00:00
|
|
|
if (shouldSubmit) {
|
|
|
|
await doc.submit();
|
|
|
|
}
|
2023-02-13 15:18:56 +00:00
|
|
|
doneCount += 1;
|
2023-02-02 08:12:09 +00:00
|
|
|
|
2023-02-15 04:46:00 +00:00
|
|
|
this.success.push(doc.name!);
|
2023-02-15 05:11:22 +00:00
|
|
|
this.successOldName.push(oldName);
|
2023-02-13 15:18:56 +00:00
|
|
|
} catch (error) {
|
|
|
|
if (error instanceof Error) {
|
|
|
|
this.failed.push({ name: doc.name!, error });
|
|
|
|
}
|
|
|
|
}
|
2023-02-02 08:12:09 +00:00
|
|
|
}
|
|
|
|
|
2023-02-13 15:18:56 +00:00
|
|
|
this.isMakingEntries = false;
|
2023-02-02 08:12:09 +00:00
|
|
|
this.complete = true;
|
|
|
|
},
|
2023-02-15 05:11:22 +00:00
|
|
|
async askShouldSubmit(): Promise<boolean> {
|
|
|
|
if (!this.fyo.schemaMap[this.importType]?.isSubmittable) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
let shouldSubmit = false;
|
2023-03-25 07:46:25 +00:00
|
|
|
await showDialog({
|
|
|
|
title: this.t`Submit entries?`,
|
|
|
|
type: 'info',
|
|
|
|
details: this.t`Should entries be submitted after syncing?`,
|
2023-02-15 05:11:22 +00:00
|
|
|
buttons: [
|
|
|
|
{
|
|
|
|
label: this.t`Yes`,
|
|
|
|
action() {
|
|
|
|
shouldSubmit = true;
|
|
|
|
},
|
2023-03-25 07:46:25 +00:00
|
|
|
isPrimary: true,
|
2023-02-15 05:11:22 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
label: this.t`No`,
|
|
|
|
action() {},
|
2023-03-25 07:46:25 +00:00
|
|
|
isEscape: true,
|
2023-02-15 05:11:22 +00:00
|
|
|
},
|
|
|
|
],
|
|
|
|
});
|
|
|
|
|
|
|
|
return shouldSubmit;
|
|
|
|
},
|
2023-02-15 04:46:00 +00:00
|
|
|
clearSuccessfullyImportedEntries() {
|
2023-02-15 05:11:22 +00:00
|
|
|
const schemaName = this.importer.schemaName;
|
|
|
|
const nameFieldKey = `${schemaName}.name`;
|
|
|
|
const nameIndex = this.importer.assignedTemplateFields.findIndex(
|
|
|
|
(n) => n === nameFieldKey
|
|
|
|
);
|
|
|
|
|
|
|
|
const failedEntriesValueMatrix = this.importer.valueMatrix.filter(
|
|
|
|
(row) => {
|
|
|
|
const value = row[nameIndex].value;
|
|
|
|
if (typeof value !== 'string') {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return !this.successOldName.includes(value);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
this.setImportType(this.importType);
|
|
|
|
this.importer.valueMatrix = failedEntriesValueMatrix;
|
2023-02-15 04:46:00 +00:00
|
|
|
},
|
2023-02-02 08:12:09 +00:00
|
|
|
setImportType(importType: string): void {
|
|
|
|
this.clear();
|
|
|
|
if (!importType) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.importType = importType;
|
2023-02-02 12:22:50 +00:00
|
|
|
this.nullOrImporter = new Importer(importType, fyo);
|
2023-02-02 08:12:09 +00:00
|
|
|
},
|
2023-02-13 15:18:56 +00:00
|
|
|
setLoadingStatus(entriesMade: number, totalEntries: number): void {
|
2023-02-02 08:12:09 +00:00
|
|
|
this.percentLoading = entriesMade / totalEntries;
|
2023-02-13 15:18:56 +00:00
|
|
|
this.messageLoading = this.isMakingEntries
|
2023-02-02 08:12:09 +00:00
|
|
|
? `${entriesMade} entries made out of ${totalEntries}...`
|
|
|
|
: '';
|
|
|
|
},
|
|
|
|
async selectFile(): Promise<void> {
|
2023-03-13 07:06:56 +00:00
|
|
|
const { text, name, filePath } = await selectTextFile([
|
|
|
|
{ name: 'CSV', extensions: ['csv'] },
|
|
|
|
]);
|
2023-02-02 08:12:09 +00:00
|
|
|
|
2023-03-13 07:06:56 +00:00
|
|
|
if (!text) {
|
2023-02-02 08:12:09 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const isValid = this.importer.selectFile(text);
|
|
|
|
if (!isValid) {
|
2023-03-25 07:46:25 +00:00
|
|
|
await showDialog({
|
|
|
|
title: this.t`Cannot read file`,
|
2023-04-20 06:10:23 +00:00
|
|
|
detail: this.t`Bad import data, could not read file.`,
|
2023-03-25 07:46:25 +00:00
|
|
|
type: 'error',
|
2023-02-02 08:12:09 +00:00
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.file = {
|
|
|
|
name,
|
|
|
|
filePath,
|
|
|
|
text,
|
|
|
|
};
|
|
|
|
},
|
|
|
|
},
|
|
|
|
});
|
|
|
|
</script>
|
2023-02-15 07:59:06 +00:00
|
|
|
<style scoped>
|
|
|
|
.index-cell {
|
|
|
|
@apply flex pe-4 justify-end items-center border-e bg-white sticky left-0 -my-4 text-gray-600;
|
|
|
|
}
|
|
|
|
</style>
|