feat: import customers/vendors

This commit is contained in:
Ahmed Bouhuolia
2024-03-28 00:05:02 +02:00
parent fc1d123c6b
commit 7a3e121942
12 changed files with 437 additions and 48 deletions

View File

@@ -376,8 +376,8 @@
"customer.field.last_name": "Last name",
"customer.field.display_name": "Display name",
"customer.field.email": "Email",
"customer.field.work_phone": "Work phone",
"customer.field.personal_phone": "Personal phone",
"customer.field.work_phone": "Work Phone Number",
"customer.field.personal_phone": "Personal Phone Number",
"customer.field.company_name": "Company name",
"customer.field.website": "Website",
"customer.field.opening_balance_at": "Opening balance at",
@@ -385,7 +385,7 @@
"customer.field.created_at": "Created at",
"customer.field.balance": "Balance",
"customer.field.status": "Status",
"customer.field.currency": "Curreny",
"customer.field.currency": "Currency",
"customer.field.status.active": "Active",
"customer.field.status.inactive": "Inactive",
"customer.field.status.overdue": "Overdue",
@@ -394,8 +394,8 @@
"vendor.field.last_name": "Last name",
"vendor.field.display_name": "Display name",
"vendor.field.email": "Email",
"vendor.field.work_phone": "Work phone",
"vendor.field.personal_phone": "Personal phone",
"vendor.field.work_phone": "Work Phone Number",
"vendor.field.personal_phone": "Personal Phone Number",
"vendor.field.company_name": "Company name",
"vendor.field.website": "Website",
"vendor.field.opening_balance_at": "Opening balance at",
@@ -403,7 +403,7 @@
"vendor.field.created_at": "Created at",
"vendor.field.balance": "Balance",
"vendor.field.status": "Status",
"vendor.field.currency": "Curreny",
"vendor.field.currency": "Currency",
"vendor.field.status.active": "Active",
"vendor.field.status.inactive": "Inactive",
"vendor.field.status.overdue": "Overdue",

View File

@@ -4,7 +4,8 @@ import { ServiceError } from '@/exceptions';
export function allowSheetExtensions(req, file, cb) {
if (
file.mimetype !== 'text/csv' &&
file.mimetype !== 'application/vnd.ms-excel'
file.mimetype !== 'application/vnd.ms-excel' &&
file.mimetype !== 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
) {
cb(new ServiceError('IMPORTED_FILE_EXTENSION_INVALID'));

View File

@@ -1,4 +1,3 @@
export interface IModel {
name: string;
tableName: string;
@@ -40,18 +39,35 @@ export interface IModelMetaFieldCommon {
order?: number;
}
export interface IModelMetaFieldNumber {
fieldType: 'number';
export interface IModelMetaFieldText {
fieldType: 'text';
minLength?: number;
maxLength?: number;
}
export interface IModelMetaFieldOther {
fieldType: 'text' | 'boolean';
export interface IModelMetaFieldBoolean {
fieldType: 'boolean';
}
export interface IModelMetaFieldNumber {
fieldType: 'number';
min?: number;
max?: number;
}
export interface IModelMetaFieldDate {
fieldType: 'date';
}
export interface IModelMetaFieldUrl {
fieldType: 'url';
}
export type IModelMetaField = IModelMetaFieldCommon &
(IModelMetaFieldOther | IModelMetaEnumerationField | IModelMetaRelationField);
(
| IModelMetaFieldText
| IModelMetaFieldNumber
| IModelMetaFieldBoolean
| IModelMetaFieldDate
| IModelMetaFieldUrl
| IModelMetaEnumerationField
| IModelMetaRelationField
);
export interface IModelMetaEnumerationOption {
key: string;
@@ -74,9 +90,8 @@ export interface IModelMetaRelationEnumerationField {
relationEntityKey: string;
}
export type IModelMetaRelationField = IModelMetaRelationFieldCommon & (
IModelMetaRelationEnumerationField
);
export type IModelMetaRelationField = IModelMetaRelationFieldCommon &
IModelMetaRelationEnumerationField;
export interface IModelMeta {
defaultFilterField: string;

View File

@@ -6,6 +6,17 @@ export default {
sortField: 'createdAt',
},
fields: {
customerType: {
name: 'Customer Type',
column: 'contact_type',
fieldType: 'enumeration',
options: [
{ key: 'business', label: 'Business' },
{ key: 'individual', label: 'Individual' },
],
importable: true,
required: true,
},
firstName: {
name: 'customer.field.first_name',
column: 'first_name',
@@ -52,7 +63,7 @@ export default {
website: {
name: 'customer.field.website',
column: 'website',
fieldType: 'text',
fieldType: 'url',
importable: true,
},
balance: {
@@ -85,6 +96,18 @@ export default {
fieldType: 'text',
importable: true,
},
note: {
name: 'Note',
column: 'note',
fieldType: 'text',
importable: true,
},
active: {
name: 'Active',
column: 'active',
fieldType: 'boolean',
importable: true,
},
status: {
name: 'customer.field.status',
fieldType: 'enumeration',
@@ -95,7 +118,6 @@ export default {
{ key: 'unpaid', label: 'customer.field.status.unpaid' },
],
filterCustomQuery: statusFieldFilterQuery,
importable: true,
},
// Billing Address
billingAddress1: {

View File

@@ -1,51 +1,59 @@
export default {
importable: true,
defaultFilterField: 'name',
defaultSort: {
sortField: 'name',
sortOrder: 'DESC',
},
fields: {
'type': {
type: {
name: 'item.field.type',
column: 'type',
fieldType: 'enumeration',
options: [
{ key: 'inventory', label: 'item.field.type.inventory', },
{ key: 'inventory', label: 'item.field.type.inventory' },
{ key: 'service', label: 'item.field.type.service' },
{ key: 'non-inventory', label: 'item.field.type.non-inventory', },
{ key: 'non-inventory', label: 'item.field.type.non-inventory' },
],
importable: true,
},
'name': {
name: {
name: 'item.field.name',
column: 'name',
fieldType: 'text',
importable: true,
},
'code': {
code: {
name: 'item.field.code',
column: 'code',
fieldType: 'text',
importable: true,
},
'sellable': {
sellable: {
name: 'item.field.sellable',
column: 'sellable',
fieldType: 'boolean',
importable: true,
},
'purchasable': {
purchasable: {
name: 'item.field.purchasable',
column: 'purchasable',
fieldType: 'boolean',
importable: true,
},
'sell_price': {
sellPrice: {
name: 'item.field.cost_price',
column: 'sell_price',
fieldType: 'number',
importable: true,
},
'cost_price': {
costPrice: {
name: 'item.field.cost_account',
column: 'cost_price',
fieldType: 'number',
importable: true,
},
'cost_account': {
costAccount: {
name: 'item.field.sell_account',
column: 'cost_account_id',
fieldType: 'relation',
@@ -55,8 +63,10 @@ export default {
relationEntityLabel: 'name',
relationEntityKey: 'slug',
importable: true,
},
'sell_account': {
sellAccount: {
name: 'item.field.sell_description',
column: 'sell_account_id',
fieldType: 'relation',
@@ -66,8 +76,10 @@ export default {
relationEntityLabel: 'name',
relationEntityKey: 'slug',
importable: true,
},
'inventory_account': {
inventoryAccount: {
name: 'item.field.inventory_account',
column: 'inventory_account_id',
@@ -76,28 +88,34 @@ export default {
relationEntityLabel: 'name',
relationEntityKey: 'slug',
importable: true,
},
'sell_description': {
sellDescription: {
name: 'Sell description',
column: 'sell_description',
fieldType: 'text',
importable: true,
},
'purchase_description': {
purchaseDescription: {
name: 'Purchase description',
column: 'purchase_description',
fieldType: 'text',
importable: true,
},
'quantity_on_hand': {
quantityOnHand: {
name: 'item.field.quantity_on_hand',
column: 'quantity_on_hand',
fieldType: 'number',
importable: true,
},
'note': {
note: {
name: 'item.field.note',
column: 'note',
fieldType: 'text',
importable: true,
},
'category': {
category: {
name: 'item.field.category',
column: 'category_id',
@@ -106,14 +124,15 @@ export default {
relationEntityLabel: 'name',
relationEntityKey: 'id',
importable: true,
},
'active': {
active: {
name: 'item.field.active',
column: 'active',
fieldType: 'boolean',
filterable: false,
importable: true,
},
'created_at': {
createdAt: {
name: 'item.field.created_at',
column: 'created_at',
columnType: 'date',

View File

@@ -38,7 +38,7 @@ export default {
importable: true,
},
personalPhone: {
name: 'vendor.field.personal_pone',
name: 'vendor.field.personal_phone',
column: 'personal_phone',
fieldType: 'text',
importable: true,
@@ -84,6 +84,18 @@ export default {
fieldType: 'text',
importable: true,
},
note: {
name: 'Note',
column: 'note',
fieldType: 'text',
importable: true,
},
active: {
name: 'Active',
column: 'active',
fieldType: 'boolean',
importable: true,
},
status: {
name: 'vendor.field.status',
type: 'enumeration',
@@ -101,7 +113,6 @@ export default {
break;
}
},
importable: true,
},
// Billing Address
billingAddress1: {

View File

@@ -3,6 +3,7 @@ import { Importable } from '@/services/Import/Importable';
import { CreateCustomer } from './CRUD/CreateCustomer';
import { Knex } from 'knex';
import { ICustomer, ICustomerNewDTO } from '@/interfaces';
import { CustomersSampleData } from './_SampleData';
@Service()
export class CustomersImportable extends Importable {
@@ -23,4 +24,11 @@ export class CustomersImportable extends Importable {
): Promise<void> {
await this.createCustomerService.createCustomer(tenantId, createDTO, trx);
}
/**
* Retrieves the sample data of customers used to download sample sheet.
*/
public sampleData(): any[] {
return CustomersSampleData;
}
}

View File

@@ -0,0 +1,158 @@
export const CustomersSampleData = [
{
"Customer Type": "Business",
"First Name": "Nicolette",
"Last Name": "Schamberger",
"Company Name": "Homenick - Hane",
"Display Name": "Rowland Rowe",
"Email": "cicero86@yahoo.com",
"Personal Phone Number": "811-603-2235",
"Work Phone Number": "906-993-5190",
"Website": "http://google.com",
"Opening Balance": 54302.23,
"Opening Balance At": "2022-02-02",
"Opening Balance Ex. Rate": 2,
"Currency": "LYD",
"Active": "F",
"Note": "Doloribus autem optio temporibus dolores mollitia sit.",
"Billing Address 1": "862 Jessika Well",
"Billing Address 2": "1091 Dorthy Mount",
"Billing Address City": "Deckowfort",
"Billing Address Country": "Ghana",
"Billing Address Phone": "825-011-5207",
"Billing Address Postcode": "38228",
"Billing Address State": "Oregon",
"Shipping Address 1": "37626 Thiel Villages",
"Shipping Address 2": "132 Batz Avenue",
"Shipping Address City": "Pagacburgh",
"Shipping Address Country": "Albania",
"Shipping Address Phone": "171-546-3701",
"Shipping Address Postcode": "13709",
"Shipping Address State": "Georgia"
},
{
"Customer Type": "Business",
"First Name": "Hermann",
"Last Name": "Crooks",
"Company Name": "Veum - Schaefer",
"Display Name": "Harley Veum",
"Email": "immanuel56@hotmail.com",
"Personal Phone Number": "449-780-9999",
"Work Phone Number": "970-473-5785",
"Website": "http://google.com",
"Opening Balance": 54302.23,
"Opening Balance At": "2022-02-02",
"Opening Balance Ex. Rate": 2,
"Currency": "LYD",
"Active": "T",
"Note": "Doloribus dolore dolor dicta vitae in fugit nisi quibusdam.",
"Billing Address 1": "532 Simonis Spring",
"Billing Address 2": "3122 Nicolas Inlet",
"Billing Address City": "East Matteofort",
"Billing Address Country": "Holy See (Vatican City State)",
"Billing Address Phone": "366-084-8629",
"Billing Address Postcode": "41607",
"Billing Address State": "Montana",
"Shipping Address 1": "2889 Tremblay Plaza",
"Shipping Address 2": "71355 Kutch Isle",
"Shipping Address City": "D'Amorehaven",
"Shipping Address Country": "Monaco",
"Shipping Address Phone": "614-189-3328",
"Shipping Address Postcode": "09634-0435",
"Shipping Address State": "Nevada"
},
{
"Customer Type": "Business",
"First Name": "Nellie",
"Last Name": "Gulgowski",
"Company Name": "Boyle, Heller and Jones",
"Display Name": "Randall Kohler",
"Email": "anibal_frami@yahoo.com",
"Personal Phone Number": "498-578-0740",
"Work Phone Number": "394-550-6827",
"Website": "http://google.com",
"Opening Balance": 54302.23,
"Opening Balance At": "2022-02-02",
"Opening Balance Ex. Rate": 2,
"Currency": "LYD",
"Active": "T",
"Note": "Vero quibusdam rem fugit aperiam est modi.",
"Billing Address 1": "214 Sauer Villages",
"Billing Address 2": "30687 Kacey Square",
"Billing Address City": "Jayceborough",
"Billing Address Country": "Benin",
"Billing Address Phone": "332-820-1127",
"Billing Address Postcode": "16425-3887",
"Billing Address State": "Mississippi",
"Shipping Address 1": "562 Diamond Loaf",
"Shipping Address 2": "9595 Satterfield Trafficway",
"Shipping Address City": "Alexandrinefort",
"Shipping Address Country": "Puerto Rico",
"Shipping Address Phone": "776-500-8456",
"Shipping Address Postcode": "30258",
"Shipping Address State": "South Dakota"
},
{
"Customer Type": "Business",
"First Name": "Stone",
"Last Name": "Jerde",
"Company Name": "Cassin, Casper and Maggio",
"Display Name": "Clint McLaughlin",
"Email": "nathanael22@yahoo.com",
"Personal Phone Number": "562-790-6059",
"Work Phone Number": "686-838-0027",
"Website": "http://google.com",
"Opening Balance": 54302.23,
"Opening Balance At": "2022-02-02",
"Opening Balance Ex. Rate": 2,
"Currency": "LYD",
"Active": "F",
"Note": "Quis cumque molestias rerum.",
"Billing Address 1": "22590 Cathy Harbor",
"Billing Address 2": "24493 Brycen Brooks",
"Billing Address City": "Elnorashire",
"Billing Address Country": "Andorra",
"Billing Address Phone": "701-852-8005",
"Billing Address Postcode": "5680",
"Billing Address State": "Nevada",
"Shipping Address 1": "5355 Erdman Bridge",
"Shipping Address 2": "421 Jeanette Camp",
"Shipping Address City": "East Philip",
"Shipping Address Country": "Venezuela",
"Shipping Address Phone": "426-119-0858",
"Shipping Address Postcode": "34929-0501",
"Shipping Address State": "Tennessee"
},
{
"Customer Type": "Individual",
"First Name": "Lempi",
"Last Name": "Kling",
"Company Name": "Schamberger, O'Connell and Bechtelar",
"Display Name": "Alexie Barton",
"Email": "eulah.kreiger@hotmail.com",
"Personal Phone Number": "745-756-1063",
"Work Phone Number": "965-150-1945",
"Website": "http://google.com",
"Opening Balance": 54302.23,
"Opening Balance At": "2022-02-02",
"Opening Balance Ex. Rate": 2,
"Currency": "LYD",
"Active": "F",
"Note": "Maxime laboriosam hic voluptate maiores est officia.",
"Billing Address 1": "0851 Jones Flat",
"Billing Address 2": "845 Bailee Drives",
"Billing Address City": "Kamrenport",
"Billing Address Country": "Niger",
"Billing Address Phone": "220-125-0608",
"Billing Address Postcode": "30311",
"Billing Address State": "Delaware",
"Shipping Address 1": "929 Ferry Row",
"Shipping Address 2": "020 Adam Plaza",
"Shipping Address City": "West Carmellaside",
"Shipping Address Country": "Ghana",
"Shipping Address Phone": "053-333-6679",
"Shipping Address Postcode": "79221-4681",
"Shipping Address State": "Illinois"
}
]

View File

@@ -2,6 +2,7 @@ import { Importable } from '@/services/Import/Importable';
import { CreateVendor } from './CRUD/CreateVendor';
import { Knex } from 'knex';
import { Inject, Service } from 'typedi';
import { VendorsSampleData } from './_SampleData';
@Service()
export class VendorsImportable extends Importable {
@@ -21,4 +22,11 @@ export class VendorsImportable extends Importable {
): Promise<void> {
await this.createVendorService.createVendor(tenantId, createDTO, trx);
}
/**
* Retrieves the sample data of vendors sample sheet.
*/
public sampleData(): any[] {
return VendorsSampleData;
}
}

View File

@@ -0,0 +1,122 @@
export const VendorsSampleData = [
{
"First Name": "Nicolette",
"Last Name": "Schamberger",
"Company Name": "Homenick - Hane",
"Display Name": "Rowland Rowe",
"Email": "cicero86@yahoo.com",
"Personal Phone Number": "811-603-2235",
"Work Phone Number": "906-993-5190",
"Website": "http://google.com",
"Opening Balance": 54302.23,
"Opening Balance At": "2022-02-02",
"Opening Balance Ex. Rate": 2,
"Currency": "LYD",
"Active": "F",
"Note": "Doloribus autem optio temporibus dolores mollitia sit.",
"Billing Address 1": "862 Jessika Well",
"Billing Address 2": "1091 Dorthy Mount",
"Billing Address City": "Deckowfort",
"Billing Address Country": "Ghana",
"Billing Address Phone": "825-011-5207",
"Billing Address Postcode": "38228",
"Billing Address State": "Oregon",
"Shipping Address 1": "37626 Thiel Villages",
"Shipping Address 2": "132 Batz Avenue",
"Shipping Address City": "Pagacburgh",
"Shipping Address Country": "Albania",
"Shipping Address Phone": "171-546-3701",
"Shipping Address Postcode": "13709",
"Shipping Address State": "Georgia"
},
{
"First Name": "Hermann",
"Last Name": "Crooks",
"Company Name": "Veum - Schaefer",
"Display Name": "Harley Veum",
"Email": "immanuel56@hotmail.com",
"Personal Phone Number": "449-780-9999",
"Work Phone Number": "970-473-5785",
"Website": "http://google.com",
"Opening Balance": 54302.23,
"Opening Balance At": "2022-02-02",
"Opening Balance Ex. Rate": 2,
"Currency": "LYD",
"Active": "F",
"Note": "Doloribus dolore dolor dicta vitae in fugit nisi quibusdam.",
"Billing Address 1": "532 Simonis Spring",
"Billing Address 2": "3122 Nicolas Inlet",
"Billing Address City": "East Matteofort",
"Billing Address Country": "Holy See (Vatican City State)",
"Billing Address Phone": "366-084-8629",
"Billing Address Postcode": "41607",
"Billing Address State": "Montana",
"Shipping Address 1": "2889 Tremblay Plaza",
"Shipping Address 2": "71355 Kutch Isle",
"Shipping Address City": "D'Amorehaven",
"Shipping Address Country": "Monaco",
"Shipping Address Phone": "614-189-3328",
"Shipping Address Postcode": "09634-0435",
"Shipping Address State": "Nevada"
},
{
"First Name": "Nellie",
"Last Name": "Gulgowski",
"Company Name": "Boyle, Heller and Jones",
"Display Name": "Randall Kohler",
"Email": "anibal_frami@yahoo.com",
"Personal Phone Number": "498-578-0740",
"Work Phone Number": "394-550-6827",
"Website": "http://google.com",
"Opening Balance": 54302.23,
"Opening Balance At": "2022-02-02",
"Opening Balance Ex. Rate": 2,
"Currency": "LYD",
"Active": "F",
"Note": "Vero quibusdam rem fugit aperiam est modi.",
"Billing Address 1": "214 Sauer Villages",
"Billing Address 2": "30687 Kacey Square",
"Billing Address City": "Jayceborough",
"Billing Address Country": "Benin",
"Billing Address Phone": "332-820-1127",
"Billing Address Postcode": "16425-3887",
"Billing Address State": "Mississippi",
"Shipping Address 1": "562 Diamond Loaf",
"Shipping Address 2": "9595 Satterfield Trafficway",
"Shipping Address City": "Alexandrinefort",
"Shipping Address Country": "Puerto Rico",
"Shipping Address Phone": "776-500-8456",
"Shipping Address Postcode": "30258",
"Shipping Address State": "South Dakota"
},
{
"First Name": "Stone",
"Last Name": "Jerde",
"Company Name": "Cassin, Casper and Maggio",
"Display Name": "Clint McLaughlin",
"Email": "nathanael22@yahoo.com",
"Personal Phone Number": "562-790-6059",
"Work Phone Number": "686-838-0027",
"Website": "http://google.com",
"Opening Balance": 54302.23,
"Opening Balance At": "2022-02-02",
"Opening Balance Ex. Rate": 2,
"Currency": "LYD",
"Active": "F",
"Note": "Quis cumque molestias rerum.",
"Billing Address 1": "22590 Cathy Harbor",
"Billing Address 2": "24493 Brycen Brooks",
"Billing Address City": "Elnorashire",
"Billing Address Country": "Andorra",
"Billing Address Phone": "701-852-8005",
"Billing Address Postcode": "5680",
"Billing Address State": "Nevada",
"Shipping Address 1": "5355 Erdman Bridge",
"Shipping Address 2": "421 Jeanette Camp",
"Shipping Address City": "East Philip",
"Shipping Address Country": "Venezuela",
"Shipping Address Phone": "426-119-0858",
"Shipping Address Postcode": "34929-0501",
"Shipping Address State": "Tennessee"
}
]

View File

@@ -1,5 +1,5 @@
import * as Yup from 'yup';
import { upperFirst, camelCase, first } from 'lodash';
import { upperFirst, camelCase, first, isUndefined } from 'lodash';
import pluralize from 'pluralize';
import { ResourceMetaFieldsMap } from './interfaces';
import { IModelMetaField } from '@/interfaces';
@@ -36,13 +36,13 @@ export const convertFieldsToYupValidation = (fields: ResourceMetaFieldsMap) => {
fieldSchema = Yup.string().label(field.name);
if (field.fieldType === 'text') {
if (field.minLength) {
if (!isUndefined(field.minLength)) {
fieldSchema = fieldSchema.min(
field.minLength,
`Minimum length is ${field.minLength} characters`
);
}
if (field.maxLength) {
if (!isUndefined(field.maxLength)) {
fieldSchema = fieldSchema.max(
field.maxLength,
`Maximum length is ${field.maxLength} characters`
@@ -50,6 +50,13 @@ export const convertFieldsToYupValidation = (fields: ResourceMetaFieldsMap) => {
}
} else if (field.fieldType === 'number') {
fieldSchema = Yup.number().label(field.name);
if (!isUndefined(field.max)) {
fieldSchema = fieldSchema.max(field.max);
}
if (!isUndefined(field.min)) {
fieldSchema = fieldSchema.min(field.min);
}
} else if (field.fieldType === 'boolean') {
fieldSchema = Yup.boolean().label(field.name);
} else if (field.fieldType === 'enumeration') {
@@ -70,6 +77,8 @@ export const convertFieldsToYupValidation = (fields: ResourceMetaFieldsMap) => {
return moment(val, 'YYYY-MM-DD', true).isValid();
}
);
} else if (field.fieldType === 'url') {
fieldSchema = fieldSchema.url();
}
if (field.required) {
fieldSchema = fieldSchema.required();

View File

@@ -29,8 +29,14 @@ export function ImportFileUploadForm({
formProps,
}: ImportFileUploadFormProps) {
const { mutateAsync: uploadImportFile } = useImportFileUpload();
const { resource, params, setStep, setSheetColumns, setEntityColumns, setImportId } =
useImportFileContext();
const {
resource,
params,
setStep,
setSheetColumns,
setEntityColumns,
setImportId,
} = useImportFileContext();
const handleSubmit = (
values: ImportFileUploadValues,
@@ -52,7 +58,17 @@ export function ImportFileUploadForm({
setStep(ImportStepperStep.Mapping);
setSubmitting(false);
})
.catch((error) => {
.catch(({ response: { data } }) => {
if (
data.errors.find(
(er) => er.type === 'IMPORTED_FILE_EXTENSION_INVALID',
)
) {
AppToaster.show({
intent: Intent.DANGER,
message: 'The extenstion of uploaded file is not supported.',
});
}
setSubmitting(false);
});
};