mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-21 07:10:33 +00:00
feat: import sheet files on initializing demo account
This commit is contained in:
@@ -4,6 +4,7 @@ import { Request } from 'express';
|
|||||||
import TenancyService from '@/services/Tenancy/TenancyService';
|
import TenancyService from '@/services/Tenancy/TenancyService';
|
||||||
import TenantsManagerService from '@/services/Tenancy/TenantsManager';
|
import TenantsManagerService from '@/services/Tenancy/TenantsManager';
|
||||||
import rtlDetect from 'rtl-detect';
|
import rtlDetect from 'rtl-detect';
|
||||||
|
import { Tenant } from '@/system/models';
|
||||||
|
|
||||||
export default (req: Request, tenant: ITenant) => {
|
export default (req: Request, tenant: ITenant) => {
|
||||||
const { id: tenantId, organizationId } = tenant;
|
const { id: tenantId, organizationId } = tenant;
|
||||||
@@ -16,7 +17,7 @@ export default (req: Request, tenant: ITenant) => {
|
|||||||
|
|
||||||
const tenantContainer = tenantServices.tenantContainer(tenantId);
|
const tenantContainer = tenantServices.tenantContainer(tenantId);
|
||||||
|
|
||||||
tenantContainer.set('i18n', injectI18nUtils(req));
|
tenantContainer.set('i18n', injectI18nUtils());
|
||||||
|
|
||||||
const knexInstance = tenantServices.knex(tenantId);
|
const knexInstance = tenantServices.knex(tenantId);
|
||||||
const models = tenantServices.models(tenantId);
|
const models = tenantServices.models(tenantId);
|
||||||
@@ -33,14 +34,35 @@ export default (req: Request, tenant: ITenant) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const injectI18nUtils = (req) => {
|
export const injectI18nUtils = (req) => {
|
||||||
const locale = req.getLocale();
|
const globalI18n = Container.get('i18n');
|
||||||
|
const locale = globalI18n.getLocale();
|
||||||
const direction = rtlDetect.getLangDir(locale);
|
const direction = rtlDetect.getLangDir(locale);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
locale,
|
locale,
|
||||||
__: req.__,
|
__: globalI18n.__,
|
||||||
direction,
|
direction,
|
||||||
isRtl: direction === 'rtl',
|
isRtl: direction === 'rtl',
|
||||||
isLtr: direction === 'ltr',
|
isLtr: direction === 'ltr',
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const initalizeTenantServices = async (tenantId: number) => {
|
||||||
|
const tenant = await Tenant.query()
|
||||||
|
.findById(tenantId)
|
||||||
|
.withGraphFetched('metadata');
|
||||||
|
|
||||||
|
const tenantServices = Container.get(TenancyService);
|
||||||
|
const tenantsManager = Container.get(TenantsManagerService);
|
||||||
|
|
||||||
|
// Initialize the knex instance.
|
||||||
|
tenantsManager.setupKnexInstance(tenant);
|
||||||
|
|
||||||
|
const tenantContainer = tenantServices.tenantContainer(tenantId);
|
||||||
|
tenantContainer.set('i18n', injectI18nUtils());
|
||||||
|
|
||||||
|
tenantServices.knex(tenantId);
|
||||||
|
tenantServices.models(tenantId);
|
||||||
|
tenantServices.repositories(tenantId);
|
||||||
|
tenantServices.cache(tenantId);
|
||||||
|
};
|
||||||
|
|||||||
@@ -116,6 +116,7 @@ import { DecrementUncategorizedTransactionOnCategorize } from '@/services/Cashfl
|
|||||||
import { DisconnectPlaidItemOnAccountDeleted } from '@/services/Banking/BankAccounts/events/DisconnectPlaidItemOnAccountDeleted';
|
import { DisconnectPlaidItemOnAccountDeleted } from '@/services/Banking/BankAccounts/events/DisconnectPlaidItemOnAccountDeleted';
|
||||||
import { LoopsEventsSubscriber } from '@/services/Loops/LoopsEventsSubscriber';
|
import { LoopsEventsSubscriber } from '@/services/Loops/LoopsEventsSubscriber';
|
||||||
import { DeleteUncategorizedTransactionsOnAccountDeleting } from '@/services/Banking/BankAccounts/events/DeleteUncategorizedTransactionsOnAccountDeleting';
|
import { DeleteUncategorizedTransactionsOnAccountDeleting } from '@/services/Banking/BankAccounts/events/DeleteUncategorizedTransactionsOnAccountDeleting';
|
||||||
|
import { SeedInitialDemoAccountDataOnOrgBuild } from '@/services/OneClickDemo/events/SeedInitialDemoAccountData';
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
return new EventPublisher();
|
return new EventPublisher();
|
||||||
@@ -282,5 +283,8 @@ export const susbcribers = () => {
|
|||||||
|
|
||||||
// Loops
|
// Loops
|
||||||
LoopsEventsSubscriber
|
LoopsEventsSubscriber
|
||||||
|
|
||||||
|
// Demo Account
|
||||||
|
SeedInitialDemoAccountDataOnOrgBuild
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { I18n } from 'i18n';
|
|||||||
|
|
||||||
export default () => new I18n({
|
export default () => new I18n({
|
||||||
locales: ['en', 'ar'],
|
locales: ['en', 'ar'],
|
||||||
|
defaultLocale: 'en',
|
||||||
register: global,
|
register: global,
|
||||||
directory: global.__locales_dir,
|
directory: global.__locales_dir,
|
||||||
updateFiles: false,
|
updateFiles: false,
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export class ImportFileMapping {
|
|||||||
*/
|
*/
|
||||||
public async mapping(
|
public async mapping(
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
importId: number,
|
importId: string,
|
||||||
maps: ImportMappingAttr[]
|
maps: ImportMappingAttr[]
|
||||||
): Promise<ImportFileMapPOJO> {
|
): Promise<ImportFileMapPOJO> {
|
||||||
const importFile = await Import.query()
|
const importFile = await Import.query()
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export class ImportFileProcessCommit {
|
|||||||
*/
|
*/
|
||||||
public async commit(
|
public async commit(
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
importId: number
|
importId: string
|
||||||
): Promise<ImportFilePreviewPOJO> {
|
): Promise<ImportFilePreviewPOJO> {
|
||||||
const knex = this.tenancy.knex(tenantId);
|
const knex = this.tenancy.knex(tenantId);
|
||||||
const trx = await knex.transaction({ isolationLevel: 'read uncommitted' });
|
const trx = await knex.transaction({ isolationLevel: 'read uncommitted' });
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ export class ImportResourceApplication {
|
|||||||
*/
|
*/
|
||||||
public async mapping(
|
public async mapping(
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
importId: number,
|
importId: string,
|
||||||
maps: ImportMappingAttr[]
|
maps: ImportMappingAttr[]
|
||||||
) {
|
) {
|
||||||
return this.importMappingService.mapping(tenantId, importId, maps);
|
return this.importMappingService.mapping(tenantId, importId, maps);
|
||||||
@@ -77,7 +77,7 @@ export class ImportResourceApplication {
|
|||||||
* @param {number} importId
|
* @param {number} importId
|
||||||
* @returns {Promise<ImportFilePreviewPOJO>}
|
* @returns {Promise<ImportFilePreviewPOJO>}
|
||||||
*/
|
*/
|
||||||
public async process(tenantId: number, importId: number) {
|
public async process(tenantId: number, importId: string) {
|
||||||
return this.importProcessCommit.commit(tenantId, importId);
|
return this.importProcessCommit.commit(tenantId, importId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { OneClickDemo } from '@/system/models/OneclickDemo';
|
|||||||
import { SystemUser } from '@/system/models';
|
import { SystemUser } from '@/system/models';
|
||||||
import { IAuthSignInPOJO } from '@/interfaces';
|
import { IAuthSignInPOJO } from '@/interfaces';
|
||||||
import { ICreateOneClickDemoPOJO } from './interfaces';
|
import { ICreateOneClickDemoPOJO } from './interfaces';
|
||||||
|
import { initalizeTenantServices } from '@/api/middleware/TenantDependencyInjection';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class CreateOneClickDemo {
|
export class CreateOneClickDemo {
|
||||||
@@ -33,6 +34,9 @@ export class CreateOneClickDemo {
|
|||||||
const tenantId = signedIn.tenant.id;
|
const tenantId = signedIn.tenant.id;
|
||||||
const userId = signedIn.user.id;
|
const userId = signedIn.user.id;
|
||||||
|
|
||||||
|
// Injects the given tenant IoC services.
|
||||||
|
await initalizeTenantServices(tenantId);
|
||||||
|
|
||||||
// Creates a new one-click demo.
|
// Creates a new one-click demo.
|
||||||
await OneClickDemo.query().insert({ key: demoId, tenantId, userId });
|
await OneClickDemo.query().insert({ key: demoId, tenantId, userId });
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export class SeedDemoAbstract{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import { SeedDemoAbstract } from './SeedDemoAbstract';
|
||||||
|
|
||||||
|
export class SeedDemoAccountItems extends SeedDemoAbstract {
|
||||||
|
/**
|
||||||
|
* Retrieves the seeder file mapping.
|
||||||
|
*/
|
||||||
|
get mapping() {
|
||||||
|
return [
|
||||||
|
{ from: 'Item Type', to: 'type' },
|
||||||
|
{ from: 'Item Name', to: 'name' },
|
||||||
|
{ from: 'Item Code', to: 'code' },
|
||||||
|
{ from: 'Sellable', to: 'sellable' },
|
||||||
|
{ from: 'Purchasable', to: 'purchasable' },
|
||||||
|
{ from: 'Sell Price', to: 'sellPrice' },
|
||||||
|
{ from: 'Cost Price', to: 'cost_price' },
|
||||||
|
{ from: 'Cost Account', to: 'costAccount' },
|
||||||
|
{ from: 'Sell Account', to: 'sellAccount' },
|
||||||
|
{ from: 'Inventory Account', to: 'inventoryAccount' },
|
||||||
|
{ from: 'Sell Description', to: 'sellDescription' },
|
||||||
|
{ from: 'Purchase Description', to: 'purchaseDescription' },
|
||||||
|
{ from: 'Note', to: 'note' },
|
||||||
|
{ from: 'Category', to: 'category' },
|
||||||
|
{ from: 'Active', to: 'active' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the seeder file name.
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
get importFileName() {
|
||||||
|
return `items.csv`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the resource name of the seeder.
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
get resource() {
|
||||||
|
return 'Item';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
import { Inject } from 'typedi';
|
||||||
|
import { promises as fs } from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
import events from '@/subscribers/events';
|
||||||
|
import { PromisePool } from '@supercharge/promise-pool';
|
||||||
|
import { IOrganizationBuildEventPayload } from '@/interfaces';
|
||||||
|
import { SeedDemoAccountItems } from '../DemoSeeders/SeedDemoItems';
|
||||||
|
import { ImportResourceApplication } from '@/services/Import/ImportResourceApplication';
|
||||||
|
import { getImportsStoragePath } from '@/services/Import/_utils';
|
||||||
|
import { OneClickDemo } from '@/system/models/OneclickDemo';
|
||||||
|
|
||||||
|
export class SeedInitialDemoAccountDataOnOrgBuild {
|
||||||
|
@Inject()
|
||||||
|
private importApp: ImportResourceApplication;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attaches events with handlers.
|
||||||
|
*/
|
||||||
|
public attach = (bus) => {
|
||||||
|
bus.subscribe(
|
||||||
|
events.organization.build,
|
||||||
|
this.seedInitialDemoAccountDataOnOrgBuild.bind(this)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Demo account seeder.
|
||||||
|
*/
|
||||||
|
get seedDemoAccountSeeders() {
|
||||||
|
return [SeedDemoAccountItems];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the seeder sheet file to the import storage first.
|
||||||
|
* @param {string} fileName -
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
async initiateSeederFile(fileName: string) {
|
||||||
|
const destination = path.join(getImportsStoragePath(), fileName);
|
||||||
|
const source = path.join(global.__views_dir, `/demo-sheets`, fileName);
|
||||||
|
|
||||||
|
// Use the fs.promises.copyFile method to copy the file
|
||||||
|
await fs.copyFile(source, destination);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Seeds initial demo account data on organization build
|
||||||
|
* @param {IOrganizationBuildEventPayload}
|
||||||
|
*/
|
||||||
|
async seedInitialDemoAccountDataOnOrgBuild({
|
||||||
|
tenantId,
|
||||||
|
}: IOrganizationBuildEventPayload) {
|
||||||
|
const foundDemo = await OneClickDemo.query().findOne('tenantId', tenantId);
|
||||||
|
|
||||||
|
// Can't continue if the found demo is not exists.
|
||||||
|
// Means that account is not demo account.
|
||||||
|
if (!foundDemo) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const results = await PromisePool.for(this.seedDemoAccountSeeders)
|
||||||
|
.withConcurrency(1)
|
||||||
|
.process(async (SeedDemoAccountSeeder) => {
|
||||||
|
const seederInstance = new SeedDemoAccountSeeder();
|
||||||
|
|
||||||
|
await this.initiateSeederFile(seederInstance.importFileName);
|
||||||
|
|
||||||
|
const importedFile = await this.importApp.import(
|
||||||
|
tenantId,
|
||||||
|
seederInstance.resource,
|
||||||
|
seederInstance.importFileName,
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
await this.importApp.mapping(
|
||||||
|
tenantId,
|
||||||
|
importedFile.import.importId,
|
||||||
|
seederInstance.mapping
|
||||||
|
);
|
||||||
|
await this.importApp.process(tenantId, importedFile.import.importId);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (results.errors) {
|
||||||
|
throw results.errors;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
5
packages/server/views/demo-sheets/items.csv
Normal file
5
packages/server/views/demo-sheets/items.csv
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
Item Type,Item Name,Item Code,Sellable,Purchasable,Cost Price,Sell Price,Cost Account,Sell Account,Inventory Account,Sell Description,Purchase Description,Category,Note,Active
|
||||||
|
Inventory,"Hettinger, Schumm and Bartoletti",1000,T,T,10000,1000,Cost of Goods Sold,Other Income,Inventory Asset,Description ....,Description ....,sdafasdfsadf,At dolor est non tempore et quisquam.,TRUE
|
||||||
|
Inventory,Schmitt Group,1001,T,T,10000,1000,Cost of Goods Sold,Other Income,Inventory Asset,Description ....,Description ....,sdafasdfsadf,Id perspiciatis at adipisci minus accusamus dolor iure dolore.,TRUE
|
||||||
|
Inventory,Marks - Carroll,1002,T,T,10000,1000,Cost of Goods Sold,Other Income,Inventory Asset,Description ....,Description ....,sdafasdfsadf,Odio odio minus similique.,TRUE
|
||||||
|
Inventory,"VonRueden, Ruecker and Hettinger",1003,T,T,10000,1000,Cost of Goods Sold,Other Income,Inventory Asset,Description ....,Description ....,sdafasdfsadf,Quibusdam dolores illo.,TRUE
|
||||||
|
Reference in New Issue
Block a user