Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ee): One-click demo account #616

Merged
merged 14 commits into from
Aug 22, 2024
Prev Previous commit
Next Next commit
feat: import sheet files on initializing demo account
  • Loading branch information
abouolia committed Aug 20, 2024
commit 408c807fc2972cb3f887c70a1a2ff27c63aa9705
28 changes: 25 additions & 3 deletions packages/server/src/api/middleware/TenantDependencyInjection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Request } from 'express';
import TenancyService from '@/services/Tenancy/TenancyService';
import TenantsManagerService from '@/services/Tenancy/TenantsManager';
import rtlDetect from 'rtl-detect';
import { Tenant } from '@/system/models';

export default (req: Request, tenant: ITenant) => {
const { id: tenantId, organizationId } = tenant;
Expand All @@ -16,7 +17,7 @@ export default (req: Request, tenant: ITenant) => {

const tenantContainer = tenantServices.tenantContainer(tenantId);

tenantContainer.set('i18n', injectI18nUtils(req));
tenantContainer.set('i18n', injectI18nUtils());

const knexInstance = tenantServices.knex(tenantId);
const models = tenantServices.models(tenantId);
Expand All @@ -33,14 +34,35 @@ export default (req: Request, tenant: ITenant) => {
};

export const injectI18nUtils = (req) => {
const locale = req.getLocale();
const globalI18n = Container.get('i18n');
const locale = globalI18n.getLocale();
const direction = rtlDetect.getLangDir(locale);

return {
locale,
__: req.__,
__: globalI18n.__,
direction,
isRtl: direction === 'rtl',
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);
};
4 changes: 4 additions & 0 deletions packages/server/src/loaders/eventEmitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ import { DecrementUncategorizedTransactionOnCategorize } from '@/services/Cashfl
import { DisconnectPlaidItemOnAccountDeleted } from '@/services/Banking/BankAccounts/events/DisconnectPlaidItemOnAccountDeleted';
import { LoopsEventsSubscriber } from '@/services/Loops/LoopsEventsSubscriber';
import { DeleteUncategorizedTransactionsOnAccountDeleting } from '@/services/Banking/BankAccounts/events/DeleteUncategorizedTransactionsOnAccountDeleting';
import { SeedInitialDemoAccountDataOnOrgBuild } from '@/services/OneClickDemo/events/SeedInitialDemoAccountData';

export default () => {
return new EventPublisher();
Expand Down Expand Up @@ -282,5 +283,8 @@ export const susbcribers = () => {

// Loops
LoopsEventsSubscriber

// Demo Account
SeedInitialDemoAccountDataOnOrgBuild
];
};
1 change: 1 addition & 0 deletions packages/server/src/loaders/i18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { I18n } from 'i18n';

export default () => new I18n({
locales: ['en', 'ar'],
defaultLocale: 'en',
register: global,
directory: global.__locales_dir,
updateFiles: false,
Expand Down
2 changes: 1 addition & 1 deletion packages/server/src/services/Import/ImportFileMapping.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export class ImportFileMapping {
*/
public async mapping(
tenantId: number,
importId: number,
importId: string,
maps: ImportMappingAttr[]
): Promise<ImportFileMapPOJO> {
const importFile = await Import.query()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export class ImportFileProcessCommit {
*/
public async commit(
tenantId: number,
importId: number
importId: string
): Promise<ImportFilePreviewPOJO> {
const knex = this.tenancy.knex(tenantId);
const trx = await knex.transaction({ isolationLevel: 'read uncommitted' });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export class ImportResourceApplication {
*/
public async mapping(
tenantId: number,
importId: number,
importId: string,
maps: ImportMappingAttr[]
) {
return this.importMappingService.mapping(tenantId, importId, maps);
Expand All @@ -77,7 +77,7 @@ export class ImportResourceApplication {
* @param {number} importId
* @returns {Promise<ImportFilePreviewPOJO>}
*/
public async process(tenantId: number, importId: number) {
public async process(tenantId: number, importId: string) {
return this.importProcessCommit.commit(tenantId, importId);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { OneClickDemo } from '@/system/models/OneclickDemo';
import { SystemUser } from '@/system/models';
import { IAuthSignInPOJO } from '@/interfaces';
import { ICreateOneClickDemoPOJO } from './interfaces';
import { initalizeTenantServices } from '@/api/middleware/TenantDependencyInjection';

@Service()
export class CreateOneClickDemo {
Expand All @@ -33,6 +34,9 @@ export class CreateOneClickDemo {
const tenantId = signedIn.tenant.id;
const userId = signedIn.user.id;

// Injects the given tenant IoC services.
await initalizeTenantServices(tenantId);

// Creates a new one-click demo.
await OneClickDemo.query().insert({ key: demoId, tenantId, userId });

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@



export class SeedDemoAbstract{

}
Original file line number Diff line number Diff line change
@@ -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';
}
}
Original file line number Diff line number Diff line change
@@ -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 changes: 5 additions & 0 deletions packages/server/views/demo-sheets/items.csv
Original file line number Diff line number Diff line change
@@ -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
Loading