mirror of
https://github.com/frappe/books.git
synced 2024-09-19 19:19:02 +00:00
incr: get dataimport to work
- need to test on child table docs
This commit is contained in:
parent
0a3fe90990
commit
bd6f110553
@ -164,7 +164,7 @@ function toDocString(value: RawValue, field: Field) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function toDocDate(value: RawValue, field: Field) {
|
function toDocDate(value: RawValue, field: Field) {
|
||||||
if (value === null) {
|
if (value === null || value === '') {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,6 +181,10 @@ function toDocDate(value: RawValue, field: Field) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function toDocCurrency(value: RawValue, field: Field, fyo: Fyo) {
|
function toDocCurrency(value: RawValue, field: Field, fyo: Fyo) {
|
||||||
|
if (value === '') {
|
||||||
|
return fyo.pesa(0);
|
||||||
|
}
|
||||||
|
|
||||||
if (typeof value === 'string') {
|
if (typeof value === 'string') {
|
||||||
return fyo.pesa(value);
|
return fyo.pesa(value);
|
||||||
}
|
}
|
||||||
@ -201,6 +205,10 @@ function toDocCurrency(value: RawValue, field: Field, fyo: Fyo) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function toDocInt(value: RawValue, field: Field): number {
|
function toDocInt(value: RawValue, field: Field): number {
|
||||||
|
if (value === '') {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (typeof value === 'string') {
|
if (typeof value === 'string') {
|
||||||
value = parseInt(value);
|
value = parseInt(value);
|
||||||
}
|
}
|
||||||
@ -209,6 +217,10 @@ function toDocInt(value: RawValue, field: Field): number {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function toDocFloat(value: RawValue, field: Field): number {
|
function toDocFloat(value: RawValue, field: Field): number {
|
||||||
|
if (value === '') {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (typeof value === 'boolean') {
|
if (typeof value === 'boolean') {
|
||||||
return Number(value);
|
return Number(value);
|
||||||
}
|
}
|
||||||
|
@ -2,24 +2,24 @@ import { Fyo } from 'fyo';
|
|||||||
import NumberSeries from 'fyo/models/NumberSeries';
|
import NumberSeries from 'fyo/models/NumberSeries';
|
||||||
import { DEFAULT_SERIES_START } from 'fyo/utils/consts';
|
import { DEFAULT_SERIES_START } from 'fyo/utils/consts';
|
||||||
import { BaseError } from 'fyo/utils/errors';
|
import { BaseError } from 'fyo/utils/errors';
|
||||||
import { Field, Schema } from 'schemas/types';
|
|
||||||
import { getRandomString } from 'utils';
|
import { getRandomString } from 'utils';
|
||||||
import { Doc } from './doc';
|
import { Doc } from './doc';
|
||||||
|
|
||||||
export function getNumberSeries(schema: Schema): Field | undefined {
|
|
||||||
const numberSeries = schema.fields.find(
|
|
||||||
(f) => f.fieldname === 'numberSeries'
|
|
||||||
);
|
|
||||||
return numberSeries;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isNameAutoSet(schemaName: string, fyo: Fyo): boolean {
|
export function isNameAutoSet(schemaName: string, fyo: Fyo): boolean {
|
||||||
const schema = fyo.schemaMap[schemaName]!;
|
const schema = fyo.schemaMap[schemaName]!;
|
||||||
|
if (schema.naming === 'manual') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (schema.naming === 'autoincrement') {
|
if (schema.naming === 'autoincrement') {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const numberSeries = getNumberSeries(schema);
|
if (schema.naming === 'random') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const numberSeries = fyo.getField(schema.name, 'numberSeries');
|
||||||
if (numberSeries) {
|
if (numberSeries) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -98,9 +98,9 @@ export abstract class InvoiceItem extends Doc {
|
|||||||
const itemList = doc.parentdoc!.items as Doc[];
|
const itemList = doc.parentdoc!.items as Doc[];
|
||||||
const items = itemList.map((d) => d.item as string).filter(Boolean);
|
const items = itemList.map((d) => d.item as string).filter(Boolean);
|
||||||
|
|
||||||
let itemNotFor = 'sales';
|
let itemNotFor = 'Sales';
|
||||||
if (doc.isSales) {
|
if (doc.isSales) {
|
||||||
itemNotFor = 'purchases';
|
itemNotFor = 'Purchases';
|
||||||
}
|
}
|
||||||
|
|
||||||
const baseFilter = { for: ['not in', [itemNotFor]] };
|
const baseFilter = { for: ['not in', [itemNotFor]] };
|
||||||
|
@ -67,7 +67,7 @@ export class Item extends Doc {
|
|||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
label: fyo.t`New Sale`,
|
label: fyo.t`New Sale`,
|
||||||
condition: (doc) => !doc.notInserted && doc.for !== 'purchases',
|
condition: (doc) => !doc.notInserted && doc.for !== 'Purchases',
|
||||||
action: async (doc, router) => {
|
action: async (doc, router) => {
|
||||||
const invoice = await fyo.doc.getNewDoc('SalesInvoice');
|
const invoice = await fyo.doc.getNewDoc('SalesInvoice');
|
||||||
await invoice.append('items', {
|
await invoice.append('items', {
|
||||||
@ -80,7 +80,7 @@ export class Item extends Doc {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: fyo.t`New Purchase`,
|
label: fyo.t`New Purchase`,
|
||||||
condition: (doc) => !doc.notInserted && doc.for !== 'sales',
|
condition: (doc) => !doc.notInserted && doc.for !== 'Sales',
|
||||||
action: async (doc, router) => {
|
action: async (doc, router) => {
|
||||||
const invoice = await fyo.doc.getNewDoc('PurchaseInvoice');
|
const invoice = await fyo.doc.getNewDoc('PurchaseInvoice');
|
||||||
await invoice.append('items', {
|
await invoice.append('items', {
|
||||||
|
@ -74,20 +74,21 @@
|
|||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"options": [
|
"options": [
|
||||||
{
|
{
|
||||||
"value": "purchases",
|
"value": "Purchases",
|
||||||
"label": "Purchases"
|
"label": "Purchases"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"value": "sales",
|
"value": "Sales",
|
||||||
"label": "Sales"
|
"label": "Sales"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"value": "both",
|
"value": "Both",
|
||||||
"label": "Both"
|
"label": "Both"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"readOnly": true,
|
"readOnly": true,
|
||||||
"default": "both"
|
"required": true,
|
||||||
|
"default": "Both"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "incomeAccount",
|
"fieldname": "incomeAccount",
|
||||||
|
@ -65,7 +65,8 @@
|
|||||||
"fieldname": "hsnCode",
|
"fieldname": "hsnCode",
|
||||||
"label": "HSN/SAC",
|
"label": "HSN/SAC",
|
||||||
"fieldtype": "Int",
|
"fieldtype": "Int",
|
||||||
"placeholder": "HSN/SAC Code"
|
"placeholder": "HSN/SAC Code",
|
||||||
|
"hidden": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"tableFields": ["item", "tax", "quantity", "rate", "amount"],
|
"tableFields": ["item", "tax", "quantity", "rate", "amount"],
|
||||||
|
@ -66,7 +66,8 @@
|
|||||||
"fieldname": "hsnCode",
|
"fieldname": "hsnCode",
|
||||||
"label": "HSN/SAC",
|
"label": "HSN/SAC",
|
||||||
"fieldtype": "Int",
|
"fieldtype": "Int",
|
||||||
"placeholder": "HSN/SAC Code"
|
"placeholder": "HSN/SAC Code",
|
||||||
|
"hidden": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"tableFields": ["item", "tax", "quantity", "rate", "amount"],
|
"tableFields": ["item", "tax", "quantity", "rate", "amount"],
|
||||||
|
@ -206,6 +206,8 @@ export default {
|
|||||||
// valid selection
|
// valid selection
|
||||||
let item = this.items[this.highlightedIndex];
|
let item = this.items[this.highlightedIndex];
|
||||||
await this.selectItem(item);
|
await this.selectItem(item);
|
||||||
|
} else if (this.items.length === 1) {
|
||||||
|
await this.selectItem(this.items[0])
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
highlightItemUp() {
|
highlightItemUp() {
|
||||||
|
@ -31,7 +31,9 @@ export default {
|
|||||||
actions: { default: [] },
|
actions: { default: [] },
|
||||||
type: { type: String, default: 'secondary' },
|
type: { type: String, default: 'secondary' },
|
||||||
},
|
},
|
||||||
inject: ['doc'],
|
inject: {
|
||||||
|
doc: { default: null },
|
||||||
|
},
|
||||||
components: {
|
components: {
|
||||||
Dropdown,
|
Dropdown,
|
||||||
Button,
|
Button,
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
import { Fyo, t } from 'fyo';
|
import { Fyo, t } from 'fyo';
|
||||||
|
import { Converter } from 'fyo/core/converter';
|
||||||
import { DocValueMap } from 'fyo/core/types';
|
import { DocValueMap } from 'fyo/core/types';
|
||||||
import { Doc } from 'fyo/model/doc';
|
import { Doc } from 'fyo/model/doc';
|
||||||
import { isNameAutoSet } from 'fyo/model/naming';
|
import { isNameAutoSet } from 'fyo/model/naming';
|
||||||
import { Noun, Verb } from 'fyo/telemetry/types';
|
import { Noun, Verb } from 'fyo/telemetry/types';
|
||||||
|
import { ModelNameEnum } from 'models/types';
|
||||||
import {
|
import {
|
||||||
Field,
|
Field,
|
||||||
FieldType,
|
FieldType,
|
||||||
@ -11,16 +13,20 @@ import {
|
|||||||
SelectOption,
|
SelectOption,
|
||||||
TargetField,
|
TargetField,
|
||||||
} from 'schemas/types';
|
} from 'schemas/types';
|
||||||
import { parseCSV } from '../utils/csvParser';
|
import {
|
||||||
|
getDefaultMapFromList,
|
||||||
|
getMapFromList,
|
||||||
|
getValueMapFromList,
|
||||||
|
} from 'utils';
|
||||||
|
import { generateCSV, parseCSV } from '../utils/csvParser';
|
||||||
|
|
||||||
export const importable = [
|
export const importable = [
|
||||||
'SalesInvoice',
|
ModelNameEnum.SalesInvoice,
|
||||||
'PurchaseInvoice',
|
ModelNameEnum.PurchaseInvoice,
|
||||||
'Payment',
|
ModelNameEnum.Payment,
|
||||||
'JournalEntry',
|
ModelNameEnum.Party,
|
||||||
'Customer',
|
ModelNameEnum.Item,
|
||||||
'Supplier',
|
ModelNameEnum.JournalEntry,
|
||||||
'Item',
|
|
||||||
];
|
];
|
||||||
|
|
||||||
type Status = {
|
type Status = {
|
||||||
@ -29,16 +35,7 @@ type Status = {
|
|||||||
names: string[];
|
names: string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
type Exclusion = {
|
type Exclusion = Record<string, string[]>;
|
||||||
[key: string]: string[];
|
|
||||||
};
|
|
||||||
|
|
||||||
type Map = Record<string, unknown>;
|
|
||||||
type ObjectMap = Record<string, Map>;
|
|
||||||
|
|
||||||
type LabelTemplateFieldMap = {
|
|
||||||
[key: string]: TemplateField;
|
|
||||||
};
|
|
||||||
|
|
||||||
type LoadingStatusCallback = (
|
type LoadingStatusCallback = (
|
||||||
isMakingEntries: boolean,
|
isMakingEntries: boolean,
|
||||||
@ -56,33 +53,9 @@ interface TemplateField {
|
|||||||
parentField: string;
|
parentField: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatValue(value: string, fieldtype: FieldType): unknown {
|
|
||||||
switch (fieldtype) {
|
|
||||||
case FieldTypeEnum.Date:
|
|
||||||
if (value === '') {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
return new Date(value);
|
|
||||||
case FieldTypeEnum.Currency:
|
|
||||||
// @ts-ignore
|
|
||||||
return this.fyo.pesa(value || 0);
|
|
||||||
case FieldTypeEnum.Int:
|
|
||||||
case FieldTypeEnum.Float: {
|
|
||||||
const n = parseFloat(value);
|
|
||||||
if (!Number.isNaN(n)) {
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const exclusion: Exclusion = {
|
const exclusion: Exclusion = {
|
||||||
Item: ['image'],
|
Item: ['image'],
|
||||||
Supplier: ['address', 'outstandingAmount', 'supplier', 'image', 'customer'],
|
Party: ['address', 'outstandingAmount', 'image'],
|
||||||
Customer: ['address', 'outstandingAmount', 'supplier', 'image', 'customer'],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function getFilteredDocFields(
|
function getFilteredDocFields(
|
||||||
@ -97,48 +70,68 @@ function getFilteredDocFields(
|
|||||||
parentField = '';
|
parentField = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ts-ignore
|
const primaryFields: Field[] = fyo.schemaMap[schemaName]!.fields;
|
||||||
const primaryFields: Field[] = fyo.schemaMap[schemaName]?.fields ?? [];
|
|
||||||
const fields: TemplateField[] = [];
|
const fields: TemplateField[] = [];
|
||||||
const tableTypes: string[][] = [];
|
const tableTypes: string[][] = [];
|
||||||
const exclusionFields: string[] = exclusion[schemaName] ?? [];
|
const exclusionFields: string[] = exclusion[schemaName] ?? [];
|
||||||
|
|
||||||
primaryFields.forEach((field) => {
|
for (const field of primaryFields) {
|
||||||
const { label, fieldtype, fieldname, readOnly, required, hidden } = field;
|
const { label, fieldtype, fieldname, required } = field;
|
||||||
|
|
||||||
if (
|
if (shouldSkip(field, exclusionFields, parentField)) {
|
||||||
!(fieldname === 'name' && !parentField) &&
|
continue;
|
||||||
(readOnly ||
|
|
||||||
(hidden && typeof hidden === 'number') ||
|
|
||||||
exclusionFields.includes(fieldname))
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fieldtype === FieldTypeEnum.Table && (field as TargetField).target) {
|
if (fieldtype === FieldTypeEnum.Table) {
|
||||||
tableTypes.push([(field as TargetField).target, fieldname]);
|
const { target } = field as TargetField;
|
||||||
return;
|
tableTypes.push([target, fieldname]);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let options: SelectOption[] = [];
|
const options: SelectOption[] = (field as OptionField).options ?? [];
|
||||||
if ((field as OptionField).options !== undefined) {
|
|
||||||
options = (field as OptionField).options;
|
|
||||||
}
|
|
||||||
|
|
||||||
fields.push({
|
fields.push({
|
||||||
label,
|
label,
|
||||||
fieldname,
|
fieldname,
|
||||||
schemaName: schemaName,
|
schemaName,
|
||||||
options,
|
options,
|
||||||
fieldtype,
|
fieldtype,
|
||||||
parentField,
|
parentField,
|
||||||
required: Boolean(required ?? false),
|
required: required ?? false,
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
|
||||||
return [fields, tableTypes];
|
return [fields, tableTypes];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function shouldSkip(
|
||||||
|
field: Field,
|
||||||
|
exclusionFields: string[],
|
||||||
|
parentField: string
|
||||||
|
): boolean {
|
||||||
|
if (field.meta) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (field.fieldname === 'name' && parentField) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (field.required) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exclusionFields.includes(field.fieldname)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (field.hidden || field.readOnly) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
function getTemplateFields(schemaName: string, fyo: Fyo): TemplateField[] {
|
function getTemplateFields(schemaName: string, fyo: Fyo): TemplateField[] {
|
||||||
const fields: TemplateField[] = [];
|
const fields: TemplateField[] = [];
|
||||||
if (!schemaName) {
|
if (!schemaName) {
|
||||||
@ -147,46 +140,28 @@ function getTemplateFields(schemaName: string, fyo: Fyo): TemplateField[] {
|
|||||||
|
|
||||||
const schemaNames: string[][] = [[schemaName]];
|
const schemaNames: string[][] = [[schemaName]];
|
||||||
while (schemaNames.length > 0) {
|
while (schemaNames.length > 0) {
|
||||||
const dt = schemaNames.pop();
|
const sn = schemaNames.pop();
|
||||||
if (!dt) {
|
if (!sn) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
const [templateFields, tableTypes] = getFilteredDocFields(dt, fyo);
|
const [templateFields, tableTypes] = getFilteredDocFields(sn, fyo);
|
||||||
fields.push(...templateFields);
|
fields.push(...templateFields);
|
||||||
schemaNames.push(...tableTypes);
|
schemaNames.push(...tableTypes);
|
||||||
}
|
}
|
||||||
return fields;
|
return fields;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getLabelFieldMap(templateFields: TemplateField[]): Map {
|
|
||||||
const map: Map = {};
|
|
||||||
|
|
||||||
templateFields.reduce((acc, tf) => {
|
|
||||||
const key = tf.label as string;
|
|
||||||
acc[key] = tf.fieldname;
|
|
||||||
return acc;
|
|
||||||
}, map);
|
|
||||||
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getTemplate(templateFields: TemplateField[]): string {
|
|
||||||
const labels = templateFields.map(({ label }) => `"${label}"`).join(',');
|
|
||||||
return [labels, ''].join('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Importer {
|
export class Importer {
|
||||||
schemaName: string;
|
schemaName: string;
|
||||||
templateFields: TemplateField[];
|
templateFields: TemplateField[];
|
||||||
map: Map;
|
labelTemplateFieldMap: Record<string, TemplateField> = {};
|
||||||
template: string;
|
template: string;
|
||||||
indices: number[] = [];
|
indices: number[] = [];
|
||||||
parsedLabels: string[] = [];
|
parsedLabels: string[] = [];
|
||||||
parsedValues: string[][] = [];
|
parsedValues: string[][] = [];
|
||||||
assignedMap: Map = {}; // target: import
|
assignedMap: Record<string, string> = {}; // target: import
|
||||||
requiredMap: Map = {};
|
requiredMap: Record<string, boolean> = {};
|
||||||
labelTemplateFieldMap: LabelTemplateFieldMap = {};
|
|
||||||
shouldSubmit: boolean = false;
|
shouldSubmit: boolean = false;
|
||||||
labelIndex: number = -1;
|
labelIndex: number = -1;
|
||||||
csv: string[][] = [];
|
csv: string[][] = [];
|
||||||
@ -196,38 +171,30 @@ export class Importer {
|
|||||||
this.schemaName = schemaName;
|
this.schemaName = schemaName;
|
||||||
this.fyo = fyo;
|
this.fyo = fyo;
|
||||||
this.templateFields = getTemplateFields(schemaName, this.fyo);
|
this.templateFields = getTemplateFields(schemaName, this.fyo);
|
||||||
this.map = getLabelFieldMap(this.templateFields);
|
this.template = generateCSV([this.templateFields.map((t) => t.label)]);
|
||||||
this.template = getTemplate(this.templateFields);
|
this.labelTemplateFieldMap = getMapFromList(this.templateFields, 'label');
|
||||||
this.assignedMap = this.assignableLabels.reduce((acc: Map, k) => {
|
this.assignedMap = getDefaultMapFromList(this.templateFields, '', 'label');
|
||||||
acc[k] = '';
|
this.requiredMap = getValueMapFromList(
|
||||||
return acc;
|
this.templateFields,
|
||||||
}, {});
|
'label',
|
||||||
this.requiredMap = this.templateFields.reduce((acc: Map, k) => {
|
'required'
|
||||||
acc[k.label] = k.required;
|
) as Record<string, boolean>;
|
||||||
return acc;
|
|
||||||
}, {});
|
|
||||||
this.labelTemplateFieldMap = this.templateFields.reduce(
|
|
||||||
(acc: LabelTemplateFieldMap, k) => {
|
|
||||||
acc[k.label] = k;
|
|
||||||
return acc;
|
|
||||||
},
|
|
||||||
{}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get assignableLabels() {
|
get assignableLabels() {
|
||||||
const req: string[] = [];
|
const req: string[] = [];
|
||||||
const nreq: string[] = [];
|
const nreq: string[] = [];
|
||||||
Object.keys(this.map).forEach((k) => {
|
|
||||||
if (this.requiredMap[k]) {
|
for (const label in this.labelTemplateFieldMap) {
|
||||||
req.push(k);
|
if (this.requiredMap[label]) {
|
||||||
return;
|
req.push(label);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
nreq.push(k);
|
nreq.push(label);
|
||||||
});
|
}
|
||||||
|
|
||||||
return [...req, ...nreq];
|
return [req, nreq].flat();
|
||||||
}
|
}
|
||||||
|
|
||||||
get unassignedLabels() {
|
get unassignedLabels() {
|
||||||
@ -332,23 +299,23 @@ export class Importer {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getDocs(): Map[] {
|
getDocs(): DocValueMap[] {
|
||||||
const fields = this.columnLabels.map((k) => this.labelTemplateFieldMap[k]);
|
const fields = this.columnLabels.map((k) => this.labelTemplateFieldMap[k]);
|
||||||
const nameIndex = fields.findIndex(({ fieldname }) => fieldname === 'name');
|
const nameIndex = fields.findIndex(({ fieldname }) => fieldname === 'name');
|
||||||
|
|
||||||
const docMap: ObjectMap = {};
|
const docMap: Record<string, DocValueMap> = {};
|
||||||
|
|
||||||
const assignedMatrix = this.assignedMatrix;
|
const assignedMatrix = this.assignedMatrix;
|
||||||
for (let r = 0; r < assignedMatrix.length; r++) {
|
for (let r = 0; r < assignedMatrix.length; r++) {
|
||||||
const row = assignedMatrix[r];
|
const row = assignedMatrix[r];
|
||||||
const cts: ObjectMap = {};
|
const cts: Record<string, DocValueMap> = {};
|
||||||
const name = row[nameIndex];
|
const name = row[nameIndex];
|
||||||
|
|
||||||
docMap[name] ??= {};
|
docMap[name] ??= {};
|
||||||
|
|
||||||
for (let f = 0; f < fields.length; f++) {
|
for (let f = 0; f < fields.length; f++) {
|
||||||
const field = fields[f];
|
const field = fields[f];
|
||||||
const value = formatValue(row[f], field.fieldtype);
|
const value = Converter.toDocValue(row[f], field, this.fyo);
|
||||||
|
|
||||||
if (field.parentField) {
|
if (field.parentField) {
|
||||||
cts[field.parentField] ??= {};
|
cts[field.parentField] ??= {};
|
||||||
@ -361,7 +328,7 @@ export class Importer {
|
|||||||
|
|
||||||
for (const k of Object.keys(cts)) {
|
for (const k of Object.keys(cts)) {
|
||||||
docMap[name][k] ??= [];
|
docMap[name][k] ??= [];
|
||||||
(docMap[name][k] as Map[]).push(cts[k]);
|
(docMap[name][k] as DocValueMap[]).push(cts[k]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -423,9 +390,8 @@ export class Importer {
|
|||||||
this.parsedValues.push(emptyRow);
|
this.parsedValues.push(emptyRow);
|
||||||
}
|
}
|
||||||
|
|
||||||
async makeEntry(doc: Doc, docObj: Map) {
|
async makeEntry(doc: Doc, docObj: DocValueMap) {
|
||||||
await doc.setMultiple(docObj as DocValueMap);
|
await doc.setAndSync(docObj);
|
||||||
await doc.sync();
|
|
||||||
if (this.shouldSubmit) {
|
if (this.shouldSubmit) {
|
||||||
await doc.submit();
|
await doc.submit();
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col overflow-hidden w-full">
|
<div class="flex flex-col overflow-hidden w-full">
|
||||||
|
<!-- Header -->
|
||||||
<PageHeader :title="t`Data Import`">
|
<PageHeader :title="t`Data Import`">
|
||||||
<DropdownWithActions
|
<DropdownWithActions
|
||||||
:actions="actions"
|
:actions="actions"
|
||||||
@ -13,6 +14,7 @@
|
|||||||
>{{ primaryLabel }}</Button
|
>{{ primaryLabel }}</Button
|
||||||
>
|
>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="flex px-8 mt-2 text-base w-full flex-col gap-8"
|
class="flex px-8 mt-2 text-base w-full flex-col gap-8"
|
||||||
v-if="!complete"
|
v-if="!complete"
|
||||||
@ -21,8 +23,8 @@
|
|||||||
<div class="flex flex-row justify-start items-center w-full gap-2">
|
<div class="flex flex-row justify-start items-center w-full gap-2">
|
||||||
<FormControl
|
<FormControl
|
||||||
:df="importableDf"
|
:df="importableDf"
|
||||||
input-class="bg-gray-100 text-gray-900 text-base"
|
input-class="bg-transparent text-gray-900 text-base"
|
||||||
class="w-40"
|
class="w-40 bg-gray-100 rounded"
|
||||||
:value="importType"
|
:value="importType"
|
||||||
size="small"
|
size="small"
|
||||||
@change="setImportType"
|
@change="setImportType"
|
||||||
@ -330,9 +332,11 @@ import HowTo from 'src/components/HowTo.vue';
|
|||||||
import PageHeader from 'src/components/PageHeader.vue';
|
import PageHeader from 'src/components/PageHeader.vue';
|
||||||
import { importable, Importer } from 'src/dataImport';
|
import { importable, Importer } from 'src/dataImport';
|
||||||
import { fyo } from 'src/initFyo';
|
import { fyo } from 'src/initFyo';
|
||||||
import { getSavePath, saveData, showMessageDialog } from 'src/utils';
|
import { getSavePath, saveData } from 'src/utils/ipcCalls';
|
||||||
|
import { showMessageDialog } from 'src/utils/ui';
|
||||||
import { IPC_ACTIONS } from 'utils/messages';
|
import { IPC_ACTIONS } from 'utils/messages';
|
||||||
import Loading from '../components/Loading.vue';
|
import Loading from '../components/Loading.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
PageHeader,
|
PageHeader,
|
||||||
@ -356,6 +360,11 @@ export default {
|
|||||||
messageLoading: '',
|
messageLoading: '',
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
mounted() {
|
||||||
|
if (fyo.store.isDevelopment) {
|
||||||
|
window.di = this;
|
||||||
|
}
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
labelIndex() {
|
labelIndex() {
|
||||||
return this.importer.labelIndex;
|
return this.importer.labelIndex;
|
||||||
@ -435,14 +444,14 @@ export default {
|
|||||||
label: this.t`Import Type`,
|
label: this.t`Import Type`,
|
||||||
fieldtype: 'AutoComplete',
|
fieldtype: 'AutoComplete',
|
||||||
placeholder: this.t`Import Type`,
|
placeholder: this.t`Import Type`,
|
||||||
getList: () => importable.map((i) => fyo.models[i].label),
|
options: Object.keys(this.labelSchemaNameMap)
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
labelSchemaNameMap() {
|
labelSchemaNameMap() {
|
||||||
return importable
|
return importable
|
||||||
.map((i) => ({
|
.map((i) => ({
|
||||||
name: i,
|
name: i,
|
||||||
label: fyo.models[i].label,
|
label: fyo.schemaMap[i].label,
|
||||||
}))
|
}))
|
||||||
.reduce((acc, { name, label }) => {
|
.reduce((acc, { name, label }) => {
|
||||||
acc[label] = name;
|
acc[label] = name;
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { NounEnum, Verb } from 'fyo/telemetry/types';
|
import { NounEnum, Verb } from 'fyo/telemetry/types';
|
||||||
import ChartOfAccounts from 'src/pages/ChartOfAccounts.vue';
|
import ChartOfAccounts from 'src/pages/ChartOfAccounts.vue';
|
||||||
import Dashboard from 'src/pages/Dashboard/Dashboard.vue';
|
import Dashboard from 'src/pages/Dashboard/Dashboard.vue';
|
||||||
|
import DataImport from 'src/pages/DataImport.vue';
|
||||||
import GetStarted from 'src/pages/GetStarted.vue';
|
import GetStarted from 'src/pages/GetStarted.vue';
|
||||||
// import DataImport from 'src/pages/DataImport.vue';
|
|
||||||
import InvoiceForm from 'src/pages/InvoiceForm.vue';
|
import InvoiceForm from 'src/pages/InvoiceForm.vue';
|
||||||
import JournalEntryForm from 'src/pages/JournalEntryForm.vue';
|
import JournalEntryForm from 'src/pages/JournalEntryForm.vue';
|
||||||
import ListView from 'src/pages/ListView/ListView.vue';
|
import ListView from 'src/pages/ListView/ListView.vue';
|
||||||
@ -107,13 +107,12 @@ const routes: RouteRecordRaw[] = [
|
|||||||
edit: (route) => route.query,
|
edit: (route) => route.query,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
/*
|
|
||||||
{
|
{
|
||||||
path: '/data-import',
|
path: '/data-import',
|
||||||
name: 'Data Import',
|
name: 'Data Import',
|
||||||
component: DataImport,
|
component: DataImport,
|
||||||
},
|
},
|
||||||
*/ {
|
{
|
||||||
path: '/settings',
|
path: '/settings',
|
||||||
name: 'Settings',
|
name: 'Settings',
|
||||||
component: Settings,
|
component: Settings,
|
||||||
|
@ -87,17 +87,17 @@ function getCreateList(): SearchItem[] {
|
|||||||
{
|
{
|
||||||
label: t`Sales Items`,
|
label: t`Sales Items`,
|
||||||
schemaName: ModelNameEnum.Item,
|
schemaName: ModelNameEnum.Item,
|
||||||
filter: { for: 'sales' },
|
filter: { for: 'Sales' },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: t`Purchase Items`,
|
label: t`Purchase Items`,
|
||||||
schemaName: ModelNameEnum.Item,
|
schemaName: ModelNameEnum.Item,
|
||||||
filter: { for: 'purchases' },
|
filter: { for: 'Purchases' },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: t`Common Items`,
|
label: t`Common Items`,
|
||||||
schemaName: ModelNameEnum.Item,
|
schemaName: ModelNameEnum.Item,
|
||||||
filter: { for: 'both' },
|
filter: { for: 'Both' },
|
||||||
},
|
},
|
||||||
].map(({ label, filter, schemaName }) => {
|
].map(({ label, filter, schemaName }) => {
|
||||||
const fk = Object.keys(filter)[0] as 'for' | 'role';
|
const fk = Object.keys(filter)[0] as 'for' | 'role';
|
||||||
@ -163,15 +163,15 @@ function getListViewList(): SearchItem[] {
|
|||||||
{ label: t`Suppliers`, route: `/list/Party/role/Supplier/${t`Suppliers`}` },
|
{ label: t`Suppliers`, route: `/list/Party/role/Supplier/${t`Suppliers`}` },
|
||||||
{
|
{
|
||||||
label: t`Sales Items`,
|
label: t`Sales Items`,
|
||||||
route: `/list/Item/for/sales/${t`Sales Items`}`,
|
route: `/list/Item/for/Sales/${t`Sales Items`}`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: t`Purchase Items`,
|
label: t`Purchase Items`,
|
||||||
route: `/list/Item/for/purchases/${t`Purchase Items`}`,
|
route: `/list/Item/for/Purchases/${t`Purchase Items`}`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: t`Common Items`,
|
label: t`Common Items`,
|
||||||
route: `/list/Item/for/both/${t`Common Items`}`,
|
route: `/list/Item/for/Both/${t`Common Items`}`,
|
||||||
},
|
},
|
||||||
].map((i) => ({ ...i, group: 'List' } as SearchItem));
|
].map((i) => ({ ...i, group: 'List' } as SearchItem));
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ function getCompleteSidebar(): SidebarConfig {
|
|||||||
{
|
{
|
||||||
label: t`Sales Items`,
|
label: t`Sales Items`,
|
||||||
name: 'sales-items',
|
name: 'sales-items',
|
||||||
route: `/list/Item/for/sales/${t`Sales Items`}`,
|
route: `/list/Item/for/Sales/${t`Sales Items`}`,
|
||||||
schemaName: 'Item',
|
schemaName: 'Item',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -101,7 +101,7 @@ function getCompleteSidebar(): SidebarConfig {
|
|||||||
{
|
{
|
||||||
label: t`Purchase Items`,
|
label: t`Purchase Items`,
|
||||||
name: 'purchase-items',
|
name: 'purchase-items',
|
||||||
route: `/list/Item/for/purchases/${t`Purchase Items`}`,
|
route: `/list/Item/for/Purchases/${t`Purchase Items`}`,
|
||||||
schemaName: 'Item',
|
schemaName: 'Item',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -118,20 +118,21 @@ function getCompleteSidebar(): SidebarConfig {
|
|||||||
route: '/list/JournalEntry',
|
route: '/list/JournalEntry',
|
||||||
schemaName: 'JournalEntry',
|
schemaName: 'JournalEntry',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
label: t`Common Items`,
|
|
||||||
name: 'common-items',
|
|
||||||
route: `/list/Item/for/both/${t`Common Items`}`,
|
|
||||||
schemaName: 'Item',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
label: t`Party`,
|
label: t`Party`,
|
||||||
name: 'party',
|
name: 'party',
|
||||||
route: '/list/Party/role/Both',
|
route: '/list/Party/role/Both',
|
||||||
schemaName: 'Party',
|
schemaName: 'Party',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: t`Common Items`,
|
||||||
|
name: 'common-items',
|
||||||
|
route: `/list/Item/for/Both/${t`Common Items`}`,
|
||||||
|
schemaName: 'Item',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
/*
|
||||||
{
|
{
|
||||||
label: t`Reports`,
|
label: t`Reports`,
|
||||||
name: t`reports`,
|
name: t`reports`,
|
||||||
@ -172,6 +173,7 @@ function getCompleteSidebar(): SidebarConfig {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
*/
|
||||||
{
|
{
|
||||||
label: t`Setup`,
|
label: t`Setup`,
|
||||||
name: t`setup`,
|
name: t`setup`,
|
||||||
@ -192,7 +194,7 @@ function getCompleteSidebar(): SidebarConfig {
|
|||||||
{
|
{
|
||||||
label: t`Data Import`,
|
label: t`Data Import`,
|
||||||
name: 'data-import',
|
name: 'data-import',
|
||||||
route: '/data_import',
|
route: '/data-import',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: t`Settings`,
|
label: t`Settings`,
|
||||||
|
@ -47,16 +47,16 @@ export async function openQuickEdit({
|
|||||||
|
|
||||||
defaults = Object.assign({
|
defaults = Object.assign({
|
||||||
for:
|
for:
|
||||||
purpose === 'sales'
|
purpose === 'Sales'
|
||||||
? 'purchases'
|
? 'Purchases'
|
||||||
: purpose === 'purchases'
|
: purpose === 'Purchases'
|
||||||
? 'sales'
|
? 'Sales'
|
||||||
: 'both',
|
: 'Both',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (forWhat[0] === 'not in' && forWhat[1] === 'sales') {
|
if (forWhat[0] === 'not in' && forWhat[1] === 'Sales') {
|
||||||
defaults = Object.assign({ for: 'purchases' });
|
defaults = Object.assign({ for: 'Purchases' });
|
||||||
}
|
}
|
||||||
|
|
||||||
router[method]({
|
router[method]({
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
/**
|
/**
|
||||||
* Functions in utils/*.ts can be used by the frontend or the backends
|
|
||||||
* And so should not contain and platforma specific imports.
|
* And so should not contain and platforma specific imports.
|
||||||
*/
|
*/
|
||||||
export function getValueMapFromList<T, K extends keyof T, V extends keyof T>(
|
export function getValueMapFromList<T, K extends keyof T, V extends keyof T>(
|
||||||
@ -48,6 +47,32 @@ export function getMapFromList<T, K extends keyof T>(
|
|||||||
return acc;
|
return acc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getDefaultMapFromList<T, K extends keyof T, D>(
|
||||||
|
list: T[] | string[],
|
||||||
|
defaultValue: D,
|
||||||
|
name?: K
|
||||||
|
): Record<string, D> {
|
||||||
|
const acc: Record<string, D> = {};
|
||||||
|
if (typeof list[0] === 'string') {
|
||||||
|
for (const l of list as string[]) {
|
||||||
|
acc[l] = defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!name) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const l of list as T[]) {
|
||||||
|
const key = String(l[name]);
|
||||||
|
acc[key] = defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
|
|
||||||
export function getListFromMap<T>(map: Record<string, T>): T[] {
|
export function getListFromMap<T>(map: Record<string, T>): T[] {
|
||||||
return Object.keys(map).map((n) => map[n]);
|
return Object.keys(map).map((n) => map[n]);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user