Skip to content

Commit

Permalink
Merge branch 'develop' into enhancement/tag-type
Browse files Browse the repository at this point in the history
  • Loading branch information
samuelmbabhazi committed Jan 13, 2025
2 parents 25895c6 + b6f49be commit 48e3384
Show file tree
Hide file tree
Showing 41 changed files with 968 additions and 703 deletions.
2 changes: 1 addition & 1 deletion apps/gauzy-e2e/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"url": "https://ever.co"
},
"dependencies": {
"@faker-js/faker": "8.0.0-alpha.0",
"@faker-js/faker": "^9.3.0",
"dayjs": "^1.11.4",
"resolve": "^1.20.0"
},
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
"migration:generate": "yarn db:migration migration:generate",
"migration:create": "yarn db:migration migration:create",
"migration:command": "cross-env NODE_ENV=development NODE_OPTIONS=--max-old-space-size=12288 yarn --cwd ./packages/core",
"seed:base": "cross-env NODE_OPTIONS=--max-old-space-size=12288 yarn run config:dev && yarn ts-node -r tsconfig-paths/register --project apps/api/tsconfig.app.json",
"seed:base": "cross-env NODE_OPTIONS=--max-old-space-size=12288 yarn run config:dev && yarn build:package:all && yarn ts-node -r tsconfig-paths/register --project apps/api/tsconfig.app.json",
"seed": "yarn seed:base ./apps/api/src/seed.ts",
"seed:ever": "yarn seed:base ./apps/api/src/seed-ever.ts",
"seed:all": "yarn seed:base ./apps/api/src/seed-all.ts",
Expand Down
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
"@apollo/subgraph": "^2.9.3",
"@aws-sdk/client-s3": "^3.717.0",
"@aws-sdk/s3-request-presigner": "^3.717.0",
"@faker-js/faker": "8.0.0-alpha.0",
"@faker-js/faker": "^9.3.0",
"@fastify/swagger": "^9.4.0",
"@gauzy/auth": "^0.1.0",
"@gauzy/common": "^0.1.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { NestModule, MiddlewareConsumer, Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { MikroOrmModule } from '@mikro-orm/nestjs';
import { TenantSetting } from '../../tenant/tenant-setting/tenant-setting.entity';
import { TenantSettingService } from '../../tenant/tenant-setting/tenant-setting.service';
import { TenantSettingsMiddleware } from './tenant-settings.middleware';
import { MikroOrmModule } from '@mikro-orm/nestjs';

@Module({
imports: [TypeOrmModule.forFeature([TenantSetting]), MikroOrmModule.forFeature([TenantSetting])],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,16 @@
export interface IDirectoryPathGenerator {
getBaseDirectory(name: string): string;
/**
* Generates a base directory path by appending the current date (in `YYYY/MM/DD` format)
* to the provided base directory name.
*
* @param baseDirname - The base directory name to which the date-based subdirectory will be appended.
*/
getBaseDirectory(baseDirname: string): string;

/**
* Generates the subdirectory path.
*
* @returns The subdirectory path as a string in `YYYY/MM/DD` format.
*/
getSubDirectory(): string;
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,18 @@ import { IDirectoryPathGenerator } from './directory-path-generator.interface';

export class DirectoryPathGenerator implements IDirectoryPathGenerator {
/**
* Generates the base directory path with the given name.
* Includes a timestamped subdirectory in the format `YYYY/MM/DD`.
* Generates a base directory path by appending the current date in `YYYY/MM/DD` format
* to the provided base directory name.
*
* @param name - The name to be used as the base directory.
* @returns The full base directory path including the timestamped subdirectory.
* @param baseDirname - The base directory name to which the date-based subdirectory will be appended.
* @returns The full directory path including the date-based subdirectory.
* @throws Error if the `baseDirname` parameter is empty or undefined.
*/
public getBaseDirectory(name: string): string {
return path.join(name, moment().format('YYYY/MM/DD'));
public getBaseDirectory(baseDirname: string): string {
if (!baseDirname) {
throw new Error('baseDirname cannot be empty');
}
return path.join(baseDirname, moment().format('YYYY/MM/DD'));
}

/**
Expand Down
7 changes: 6 additions & 1 deletion packages/core/src/lib/core/seeds/seed-data.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -807,7 +807,12 @@ export class SeedDataService {

await this.tryExecute(
'Default Payment',
createDefaultPayment(this.dataSource, this.tenant, this.defaultEmployees, this.organizations)
createDefaultPayment(
this.dataSource,
this.tenant,
this.defaultEmployees,
this.organizations
)
);

await this.tryExecute(
Expand Down
7 changes: 5 additions & 2 deletions packages/core/src/lib/employee/employee.seed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export const createDefaultEmployees = async (
users: IUser[],
defaultEmployees: any
): Promise<IEmployee[]> => {
// Precompute the organization's currency or use the default currency
// Pre compute the organization's currency or use the default currency
const currency = organization.currency || env.defaultCurrency;

// Use the default employee configurations to generate employees
Expand Down Expand Up @@ -133,7 +133,10 @@ const parseDate = (dateString?: string): Date | null => {
/**
* Fetches default employees for the given tenant.
*/
export const getDefaultEmployees = async (dataSource: DataSource, tenant: ITenant): Promise<IEmployee[]> => {
export const getDefaultEmployees = async (
dataSource: DataSource,
tenant: ITenant
): Promise<IEmployee[]> => {
// Get the default organization for the given tenant
const organization = await getDefaultOrganization(dataSource, tenant);

Expand Down
227 changes: 141 additions & 86 deletions packages/core/src/lib/invoice-item/invoice-item.seed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,12 @@ import { DataSource } from 'typeorm';
import { InvoiceItem } from './invoice-item.entity';
import { faker } from '@faker-js/faker';
import {
IEmployee,
IExpense,
IInvoice,
IInvoiceItem,
InvoiceTypeEnum,
IOrganization,
IOrganizationProject,
ITask,
ITenant
} from '@gauzy/contracts';
import { getRandomElement } from '@gauzy/utils';
import {
Employee,
Expense,
Expand All @@ -21,103 +17,162 @@ import {
Task
} from './../core/entities/internal';

/**
* Generates and saves invoice items for the specified tenant and organizations.
*
* @param dataSource - The TypeORM data source.
* @param tenant - The tenant for which invoice items are being created.
* @param organizations - The organizations to associate with the invoice items.
* @param numberOfInvoiceItemPerInvoice - The number of invoice items per invoice.
*/
const generateAndSaveInvoiceItems = async (
dataSource: DataSource,
tenant: ITenant,
organizations: IOrganization[],
numberOfInvoiceItemPerInvoice: number
): Promise<void> => {
for await (const organization of organizations) {
const invoiceItems = await generateInvoiceItemsForType(
dataSource,
tenant,
organization,
numberOfInvoiceItemPerInvoice
);
await dataSource.manager.save(invoiceItems);
}
};

/**
* Creates default invoice items for a tenant and its organizations.
*
* @param dataSource - The TypeORM data source.
* @param tenant - The tenant for which default invoice items are being created.
* @param organizations - The organizations to associate with the invoice items.
* @param numberOfInvoiceItemPerInvoice - The number of invoice items per invoice.
*/
export const createDefaultInvoiceItem = async (
dataSource: DataSource,
tenant: ITenant,
organizations: IOrganization[],
numberOfInvoiceItemPerInvoice: number
) => {
for await (const organization of organizations) {
const invoiceItems = await invoiceItemForInvoiceType(
dataSource,
tenant,
organization,
numberOfInvoiceItemPerInvoice
);
await dataSource.manager.save(invoiceItems);
}
dataSource: DataSource,
tenant: ITenant,
organizations: IOrganization[],
numberOfInvoiceItemPerInvoice: number
): Promise<void> => {
if (!tenant || !organizations || organizations.length === 0) {
throw new Error('Invalid tenant or organizations provided for default invoice item creation.');
}

await generateAndSaveInvoiceItems(dataSource, tenant, organizations, numberOfInvoiceItemPerInvoice);
};

/**
* Creates random invoice items for multiple tenants and their organizations.
*
* @param dataSource - The TypeORM data source.
* @param tenants - The tenants for which random invoice items are being created.
* @param tenantOrganizationsMap - A map of tenants to their respective organizations.
* @param numberOfInvoiceItemPerInvoice - The number of invoice items per invoice.
* @returns A promise that resolves when all invoice items are created and saved.
*/
export const createRandomInvoiceItem = async (
dataSource: DataSource,
tenants: ITenant[],
tenantOrganizationsMap: Map<ITenant, IOrganization[]>,
numberOfInvoiceItemPerInvoice: number
) => {
for await (const tenant of tenants) {
const organizations = tenantOrganizationsMap.get(tenant);
for await (const organization of organizations) {
const invoiceItems = await invoiceItemForInvoiceType(
dataSource,
tenant,
organization,
numberOfInvoiceItemPerInvoice
);
await dataSource.manager.save(invoiceItems);
}
}
dataSource: DataSource,
tenants: ITenant[],
tenantOrganizationsMap: Map<ITenant, IOrganization[]>,
numberOfInvoiceItemPerInvoice: number
): Promise<void> => {
if (!tenants || tenants.length === 0) {
throw new Error('Tenants list cannot be empty.');
}

if (!tenantOrganizationsMap) {
throw new Error('Tenant organizations map is required.');
}

for await (const tenant of tenants) {
const organizations = tenantOrganizationsMap.get(tenant);

if (!organizations || organizations.length === 0) {
console.warn(`No organizations found for tenant: ${tenant.name}`);
continue;
}

await generateAndSaveInvoiceItems(dataSource, tenant, organizations, numberOfInvoiceItemPerInvoice);
}
};

async function invoiceItemForInvoiceType(
dataSource: DataSource,
tenant: ITenant,
organization: IOrganization,
numberOfInvoiceItemPerInvoice: number
) {

const { id: tenantId } = tenant;
const { id: organizationId } = organization;

const where = {
tenantId: tenantId,
organizationId: organizationId
};
const employees: IEmployee[] = await dataSource.manager.find(Employee, { where });
const projects: IOrganizationProject[] = await dataSource.manager.find(OrganizationProject, { where });
const tasks: ITask[] = await dataSource.manager.find(Task, { where });
const products: Product[] = await dataSource.manager.find(Product, { where });
const expenses: IExpense[] = await dataSource.manager.find(Expense, { where });
const invoices: IInvoice[] = await dataSource.manager.find(Invoice, { where });

const invoiceItems: IInvoiceItem[] = [];
for await (const invoice of invoices) {
let totalValue = 0;
for (let i = 0; i < faker.number.int({ min: 1, max: numberOfInvoiceItemPerInvoice }); i++) {
const invoiceItem = new InvoiceItem();
invoiceItem.description = faker.random.words();
invoiceItem.price = faker.number.int({ min: 10, max: 50 });
invoiceItem.quantity = faker.number.int({ min: 10, max: 20 });
invoiceItem.totalValue = invoiceItem.price * invoiceItem.quantity;
invoiceItem.invoice = invoice;
/**
* Generates invoice items based on the invoice type for a given tenant and organization.
*
* @param dataSource - The TypeORM data source for database operations.
* @param tenant - The tenant for which the invoice items are generated.
* @param organization - The organization to associate with the invoice items.
* @param numberOfInvoiceItemPerInvoice - The number of invoice items to generate per invoice.
* @returns A promise that resolves to an array of generated invoice items.
*/
const generateInvoiceItemsForType = async (
dataSource: DataSource,
tenant: ITenant,
organization: IOrganization,
numberOfInvoiceItemPerInvoice: number
): Promise<IInvoiceItem[]> => {
const where = { tenantId: tenant.id, organizationId: organization.id };

// Fetch related entities in parallel
const [employees, projects, tasks, products, expenses, invoices] = await Promise.all([
dataSource.manager.find(Employee, { where }),
dataSource.manager.find(OrganizationProject, { where }),
dataSource.manager.find(Task, { where }),
dataSource.manager.find(Product, { where }),
dataSource.manager.find(Expense, { where }),
dataSource.manager.find(Invoice, { where })
]);

const invoiceItems: IInvoiceItem[] = [];

for (const invoice of invoices) {
let totalValue = 0;

for (let i = 0; i < faker.number.int({ min: 1, max: numberOfInvoiceItemPerInvoice }); i++) {
const invoiceItem = new InvoiceItem();
invoiceItem.description = faker.lorem.words();
invoiceItem.price = faker.number.int({ min: 10, max: 50 });
invoiceItem.quantity = faker.number.int({ min: 10, max: 20 });
invoiceItem.totalValue = invoiceItem.price * invoiceItem.quantity;
invoiceItem.invoice = invoice;

// Assign related entity based on the invoice type
switch (invoice.invoiceType) {
case InvoiceTypeEnum.BY_EMPLOYEE_HOURS:
invoiceItem.employee = faker.helpers.arrayElement(employees);
break;
case InvoiceTypeEnum.BY_PROJECT_HOURS:
invoiceItem.project = faker.helpers.arrayElement(projects);
invoiceItem.project = getRandomElement(projects);
break;

case InvoiceTypeEnum.BY_EMPLOYEE_HOURS:
invoiceItem.employee = getRandomElement(employees);
break;

case InvoiceTypeEnum.BY_TASK_HOURS:
invoiceItem.task = faker.helpers.arrayElement(tasks);
invoiceItem.task = getRandomElement(tasks);
break;

case InvoiceTypeEnum.BY_PRODUCTS:
invoiceItem.product = faker.helpers.arrayElement(products);
invoiceItem.product = getRandomElement(products);
break;

case InvoiceTypeEnum.BY_EXPENSES:
invoiceItem.expense = faker.helpers.arrayElement(expenses);
invoiceItem.expense = getRandomElement(expenses);
break;
}

invoiceItem.applyDiscount = faker.datatype.boolean();
invoiceItem.applyTax = faker.datatype.boolean();
invoiceItem.tenant = tenant;
invoiceItem.organization = organization;
totalValue += invoiceItem.totalValue;
invoiceItems.push(invoiceItem);
}
invoice.totalValue = totalValue;
await dataSource.manager.save(invoice);
}
return invoiceItems;
}
invoiceItem.applyDiscount = faker.datatype.boolean();
invoiceItem.applyTax = faker.datatype.boolean();
invoiceItem.tenant = tenant;
invoiceItem.organization = organization;
totalValue += invoiceItem.totalValue;
invoiceItems.push(invoiceItem);
}

// Update the invoice total value
invoice.totalValue = totalValue;
await dataSource.manager.save(invoice);
}

return invoiceItems;
};
2 changes: 1 addition & 1 deletion packages/core/src/lib/merchant/merchant.seed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ const applyRandomProperties = (
const merchant = new Merchant()
merchant.name = faker.company.name();
merchant.code = faker.string.alphanumeric();
merchant.email = getEmailWithPostfix(faker.internet.exampleEmail(merchant.name));
merchant.email = getEmailWithPostfix(faker.internet.exampleEmail({ firstName: merchant.name }));
merchant.description = faker.lorem.words();
merchant.phone = faker.phone.number();
merchant.organization = organization;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ const generateOrganizationContact = async (
? faker.number.int({ min: 500, max: 5000 })
: faker.number.int({ min: 40, max: 400 });

const email = getEmailWithPostfix(faker.internet.exampleEmail(contact.firstName, contact.lastName));
const email = getEmailWithPostfix(faker.internet.exampleEmail({ firstName: contact.firstName, lastName: contact.lastName }));
orgContact.inviteStatus = faker.helpers.arrayElement(Object.values(ContactOrganizationInviteStatus));

const phone = faker.phone.number();
Expand Down
Loading

0 comments on commit 48e3384

Please sign in to comment.