Compare commits

..

1 Commits

Author SHA1 Message Date
Ahmed Bouhuolia
f395cce710 fix(script): setup script 2026-01-29 20:49:54 +02:00
48 changed files with 135 additions and 477 deletions

View File

@@ -9,21 +9,21 @@ services:
- server
- webapp
ports:
- '8085:80'
- '8443:443'
- '${PUBLIC_PROXY_PORT:-80}:80'
- '${PUBLIC_PROXY_SSL_PORT:-443}:443'
tty: true
volumes:
- ./docker/envoy/envoy.yaml:/etc/envoy/envoy.yaml
restart: on-failure
networks:
- coolify
- bigcapital_network
webapp:
container_name: bigcapital-webapp
image: bigcapitalhq/webapp:latest
restart: on-failure
networks:
- coolify
- bigcapital_network
server:
container_name: bigcapital-server
@@ -38,7 +38,7 @@ services:
- redis
restart: on-failure
networks:
- coolify
- bigcapital_network
environment:
# Mail
- MAIL_HOST=${MAIL_HOST}
@@ -144,8 +144,8 @@ services:
depends_on:
- mysql
networks:
- coolify
- bigcapital_network
mysql:
container_name: bigcapital-mysql
restart: on-failure
@@ -158,12 +158,10 @@ services:
- MYSQL_ROOT_PASSWORD=${DB_ROOT_PASSWORD}
volumes:
- mysql:/var/lib/mysql
ports:
- '3335:3306'
expose:
- '3306'
networks:
- coolify
- bigcapital_network
redis:
container_name: bigcapital-redis
@@ -175,14 +173,14 @@ services:
volumes:
- redis:/data
networks:
- coolify
- bigcapital_network
gotenberg:
image: gotenberg/gotenberg:7
expose:
- '9000'
networks:
- coolify
- bigcapital_network
# Volumes
volumes:
@@ -196,5 +194,5 @@ volumes:
# Networks
networks:
coolify:
external: true
bigcapital_network:
driver: bridge

View File

@@ -35,4 +35,4 @@ WORKDIR /app/packages/server
RUN git clone https://github.com/vishnubob/wait-for-it.git
# Once we listen the mysql port run the migration task.
CMD ./wait-for-it/wait-for-it.sh mysql:3306 -- sh -c "node dist/cli.js system:migrate:latest && node dist/cli.js tenants:migrate:latest"
CMD ./wait-for-it/wait-for-it.sh mysql:3306 -- sh -c "pnpm run system:migrate:latest && pnpm run tenants:migrate:latest"

View File

@@ -75,9 +75,6 @@ COPY --from=builder --chown=nodejs:nodejs /app/packages/server/src/i18n ./packag
COPY --from=builder --chown=nodejs:nodejs /app/packages/server/public ./packages/server/public
COPY --from=builder --chown=nodejs:nodejs /app/packages/server/static ./packages/server/static
# Copy database migration files (needed for running migrations)
COPY --from=builder --chown=nodejs:nodejs /app/packages/server/src/database ./packages/server/src/database
# Copy built shared packages (dist folders and package.json for module resolution)
COPY --from=builder --chown=nodejs:nodejs /app/shared/bigcapital-utils/dist ./shared/bigcapital-utils/dist
COPY --from=builder --chown=nodejs:nodejs /app/shared/pdf-templates/dist ./shared/pdf-templates/dist

View File

@@ -2,23 +2,10 @@
"$schema": "https://json.schemastore.org/nest-cli",
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"entryFile": "main",
"compilerOptions": {
"deleteOutDir": true,
"assets": [
{ "include": "i18n/**/*", "watchAssets": true },
{ "include": "database/**/*", "exclude": "**/*.ts", "watchAssets": true }
{ "include": "i18n/**/*", "watchAssets": true }
]
},
"projects": {
"cli": {
"type": "application",
"root": "src",
"entryFile": "cli",
"sourceRoot": "src",
"compilerOptions": {
"tsConfigPath": "tsconfig.json"
}
}
}
}

View File

@@ -1,6 +1,10 @@
import { Processor, WorkerHost } from '@nestjs/bullmq';
import { Scope } from '@nestjs/common';
import { SendResetPasswordMailQueue } from '../Auth.constants';
import {
SendResetPasswordMailJob,
SendResetPasswordMailQueue,
} from '../Auth.constants';
import { Process } from '@nestjs/bull';
import { Job } from 'bullmq';
import { AuthenticationMailMesssages } from '../AuthMailMessages.esrvice';
import { MailTransporter } from '@/modules/Mail/MailTransporter.service';
@@ -19,6 +23,7 @@ export class SendResetPasswordMailProcessor extends WorkerHost {
super();
}
@Process(SendResetPasswordMailJob)
async process(job: Job<SendResetPasswordMailJobPayload>) {
try {
await this.authMailMesssages.sendResetPasswordMail(

View File

@@ -1,7 +1,11 @@
import { Scope } from '@nestjs/common';
import { Job } from 'bullmq';
import { Process } from '@nestjs/bull';
import { Processor, WorkerHost } from '@nestjs/bullmq';
import { SendSignupVerificationMailQueue } from '../Auth.constants';
import {
SendSignupVerificationMailJob,
SendSignupVerificationMailQueue,
} from '../Auth.constants';
import { MailTransporter } from '@/modules/Mail/MailTransporter.service';
import { AuthenticationMailMesssages } from '../AuthMailMessages.esrvice';
@@ -17,6 +21,7 @@ export class SendSignupVerificationMailProcessor extends WorkerHost {
super();
}
@Process(SendSignupVerificationMailJob)
async process(job: Job<SendSignupVerificationMailJobPayload>) {
try {
await this.authMailMesssages.sendSignupVerificationMail(

View File

@@ -1,9 +1,11 @@
import { Process } from '@nestjs/bull';
import { UseCls } from 'nestjs-cls';
import { Processor, WorkerHost } from '@nestjs/bullmq';
import { Scope } from '@nestjs/common';
import { Job } from 'bullmq';
import {
PlaidFetchTransitonsEventPayload,
UpdateBankingPlaidTransitionsJob,
UpdateBankingPlaidTransitionsQueueJob,
} from '../types/BankingPlaid.types';
import { PlaidUpdateTransactions } from '../command/PlaidUpdateTransactions';
@@ -26,6 +28,7 @@ export class PlaidFetchTransactionsProcessor extends WorkerHost {
/**
* Triggers the function.
*/
@Process(UpdateBankingPlaidTransitionsJob)
@UseCls()
async process(job: Job<PlaidFetchTransitonsEventPayload>) {
const { plaidItemId } = job.data;

View File

@@ -7,6 +7,7 @@ import {
RecognizeUncategorizedTransactionsJobPayload,
RecognizeUncategorizedTransactionsQueue,
} from '../_types';
import { Process } from '@nestjs/bull';
@Processor({
name: RecognizeUncategorizedTransactionsQueue,
@@ -27,6 +28,7 @@ export class RegonizeTransactionsPrcessor extends WorkerHost {
/**
* Triggers sending invoice mail.
*/
@Process(RecognizeUncategorizedTransactionsQueue)
@UseCls()
async process(job: Job<RecognizeUncategorizedTransactionsJobPayload>) {
const { ruleId, transactionsCriteria } = job.data;

View File

@@ -1,8 +1,6 @@
import { ApiProperty } from '@nestjs/swagger';
import { Type } from 'class-transformer';
import { ItemEntryDto } from '@/modules/TransactionItemEntry/dto/ItemEntry.dto';
import { AttachmentLinkDto } from '@/modules/Attachments/dtos/Attachment.dto';
import { BranchResponseDto } from '@/modules/Branches/dtos/BranchResponse.dto';
import { DiscountType } from '@/common/types/Discount';
export class BillResponseDto {
@@ -91,14 +89,6 @@ export class BillResponseDto {
})
branchId?: number;
@ApiProperty({
description: 'Branch details',
type: () => BranchResponseDto,
required: false,
})
@Type(() => BranchResponseDto)
branch?: BranchResponseDto;
@ApiProperty({
description: 'The ID of the project',
example: 301,

View File

@@ -30,7 +30,6 @@ export class BillTransformer extends Transformer {
'taxes',
'entries',
'attachments',
'branch',
];
};

View File

@@ -31,12 +31,6 @@ import { ValidateBranchExistance } from './integrations/ValidateBranchExistance'
import { ManualJournalBranchesValidator } from './integrations/ManualJournals/ManualJournalsBranchesValidator';
import { CashflowTransactionsActivateBranches } from './integrations/Cashflow/CashflowActivateBranches';
import { ExpensesActivateBranches } from './integrations/Expense/ExpensesActivateBranches';
import { BillActivateBranches } from './integrations/Purchases/BillBranchesActivate';
import { VendorCreditActivateBranches } from './integrations/Purchases/VendorCreditBranchesActivate';
import { BillPaymentsActivateBranches } from './integrations/Purchases/PaymentMadeBranchesActivate';
import { BillBranchesActivateSubscriber } from './subscribers/Activate/BillBranchesActivateSubscriber';
import { VendorCreditBranchesActivateSubscriber } from './subscribers/Activate/VendorCreditBranchesActivateSubscriber';
import { PaymentMadeActivateBranchesSubscriber } from './subscribers/Activate/PaymentMadeBranchesActivateSubscriber';
import { FeaturesModule } from '../Features/Features.module';
@Module({
@@ -72,13 +66,7 @@ import { FeaturesModule } from '../Features/Features.module';
ValidateBranchExistance,
ManualJournalBranchesValidator,
CashflowTransactionsActivateBranches,
ExpensesActivateBranches,
BillActivateBranches,
VendorCreditActivateBranches,
BillPaymentsActivateBranches,
BillBranchesActivateSubscriber,
VendorCreditBranchesActivateSubscriber,
PaymentMadeActivateBranchesSubscriber
ExpensesActivateBranches
],
exports: [
BranchesSettingsService,

View File

@@ -1,14 +1,11 @@
import { Knex } from 'knex';
import { Inject, Injectable } from '@nestjs/common';
import { Injectable } from '@nestjs/common';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
import { Bill } from '@/modules/Bills/models/Bill';
@Injectable()
export class BillActivateBranches {
constructor(
@Inject(Bill.name)
private readonly billModel: TenantModelProxy<typeof Bill>,
) {}
constructor(private readonly billModel: TenantModelProxy<typeof Bill>) {}
/**
* Updates all bills transactions with the primary branch.
@@ -20,7 +17,7 @@ export class BillActivateBranches {
primaryBranchId: number,
trx?: Knex.Transaction,
) => {
// Updates the bills with primary branch.
await this.billModel().query(trx).update({ branchId: primaryBranchId });
// Updates the sale invoice with primary branch.
await Bill.query(trx).update({ branchId: primaryBranchId });
};
}

View File

@@ -1,12 +1,11 @@
import { Knex } from 'knex';
import { Inject, Injectable } from '@nestjs/common';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
import { BillPayment } from '@/modules/BillPayments/models/BillPayment';
import { Injectable } from '@nestjs/common';
@Injectable()
export class BillPaymentsActivateBranches {
constructor(
@Inject(BillPayment.name)
private readonly billPaymentModel: TenantModelProxy<typeof BillPayment>,
) {}

View File

@@ -1,12 +1,11 @@
import { Knex } from 'knex';
import { Inject, Injectable } from '@nestjs/common';
import { Injectable } from '@nestjs/common';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
import { VendorCredit } from '@/modules/VendorCredit/models/VendorCredit';
@Injectable()
export class VendorCreditActivateBranches {
constructor(
@Inject(VendorCredit.name)
private readonly vendorCreditModel: TenantModelProxy<typeof VendorCredit>,
) {}

View File

@@ -1,28 +0,0 @@
import { IBranchesActivatedPayload } from '../../Branches.types';
import { events } from '@/common/events/events';
import { Injectable } from '@nestjs/common';
import { BillActivateBranches } from '../../integrations/Purchases/BillBranchesActivate';
import { OnEvent } from '@nestjs/event-emitter';
@Injectable()
export class BillBranchesActivateSubscriber {
constructor(
private readonly billActivateBranches: BillActivateBranches,
) { }
/**
* Updates bills transactions with the primary branch once
* the multi-branches is activated.
* @param {IBranchesActivatedPayload}
*/
@OnEvent(events.branch.onActivated)
async updateBillsWithBranchOnActivated({
primaryBranch,
trx,
}: IBranchesActivatedPayload) {
await this.billActivateBranches.updateBillsWithBranch(
primaryBranch.id,
trx,
);
}
}

View File

@@ -1,28 +0,0 @@
import { IBranchesActivatedPayload } from '../../Branches.types';
import { events } from '@/common/events/events';
import { Injectable } from '@nestjs/common';
import { VendorCreditActivateBranches } from '../../integrations/Purchases/VendorCreditBranchesActivate';
import { OnEvent } from '@nestjs/event-emitter';
@Injectable()
export class VendorCreditBranchesActivateSubscriber {
constructor(
private readonly vendorCreditActivateBranches: VendorCreditActivateBranches,
) { }
/**
* Updates vendor credits transactions with the primary branch once
* the multi-branches is activated.
* @param {IBranchesActivatedPayload}
*/
@OnEvent(events.branch.onActivated)
async updateVendorCreditsWithBranchOnActivated({
primaryBranch,
trx,
}: IBranchesActivatedPayload) {
await this.vendorCreditActivateBranches.updateVendorCreditsWithBranch(
primaryBranch.id,
trx,
);
}
}

View File

@@ -22,7 +22,6 @@ export abstract class BaseCommand extends CommandRunner {
},
migrations: {
directory: this.configService.get('systemDatabase.migrationDir'),
loadExtensions: ['.js'],
},
seeds: {
directory: this.configService.get('systemDatabase.seedsDir'),
@@ -44,7 +43,6 @@ export abstract class BaseCommand extends CommandRunner {
},
migrations: {
directory: this.configService.get('tenantDatabase.migrationsDir') || './src/database/migrations',
loadExtensions: ['.js'],
},
seeds: {
directory: this.configService.get('tenantDatabase.seedsDir') || './src/database/seeds/core',

View File

@@ -24,14 +24,14 @@ export class BalanceSheetQueryDto extends FinancialSheetBranchesQueryDto {
displayColumnsType: 'total' | 'date_periods' = 'total';
@ApiProperty({
enum: ['day', 'month', 'year', 'quarter'],
enum: ['day', 'month', 'year'],
default: 'year',
description: 'Time period for column display',
})
@IsString()
@IsOptional()
@IsEnum(['day', 'month', 'year', 'quarter'])
displayColumnsBy: 'day' | 'month' | 'year' | 'quarter' = 'year';
@IsEnum(['day', 'month', 'year'])
displayColumnsBy: 'day' | 'month' | 'year' = 'year';
@ApiProperty({
description: 'Start date for the balance sheet period',

View File

@@ -34,13 +34,13 @@ export class CashFlowStatementQueryDto extends FinancialSheetBranchesQueryDto {
@ApiProperty({
description: 'Display columns by time period',
required: false,
enum: ['day', 'month', 'year', 'quarter'],
enum: ['day', 'month', 'year'],
default: 'year',
})
@IsString()
@IsOptional()
@IsEnum(['day', 'month', 'year', 'quarter'])
displayColumnsBy: 'day' | 'month' | 'year' | 'quarter' = 'year';
@IsEnum(['day', 'month', 'year'])
displayColumnsBy: 'day' | 'month' | 'year' = 'year';
@ApiProperty({
description: 'Type of column display',

View File

@@ -64,10 +64,10 @@ export class ProfitLossSheetQueryDto extends FinancialSheetBranchesQueryDto {
displayColumnsType: 'total' | 'date_periods';
@IsString()
@IsEnum(['day', 'month', 'year', 'quarter'])
@IsEnum(['day', 'month', 'year'])
@IsOptional()
@ApiProperty({ description: 'How to display columns' })
displayColumnsBy: 'day' | 'month' | 'year' | 'quarter' = 'year';
displayColumnsBy: 'day' | 'month' | 'year' = 'year';
@Transform(({ value }) => parseBoolean(value, false))
@IsBoolean()

View File

@@ -7,7 +7,11 @@ import * as moment from 'moment';
import { TenantJobPayload } from '@/interfaces/Tenant';
import { InventoryComputeCostService } from '../commands/InventoryComputeCost.service';
import { events } from '@/common/events/events';
import { ComputeItemCostQueue } from '../types/InventoryCost.types';
import {
ComputeItemCostQueue,
ComputeItemCostQueueJob,
} from '../types/InventoryCost.types';
import { Process } from '@nestjs/bull';
interface ComputeItemCostJobPayload extends TenantJobPayload {
itemId: number;
@@ -35,6 +39,7 @@ export class ComputeItemCostProcessor extends WorkerHost {
* Process the compute item cost job.
* @param {Job<ComputeItemCostJobPayload>} job - The job to process
*/
@Process(ComputeItemCostQueueJob)
@UseCls()
async process(job: Job<ComputeItemCostJobPayload>) {
const { itemId, startingDate, organizationId, userId } = job.data;

View File

@@ -1,4 +1,8 @@
import { WriteInventoryTransactionsGLEntriesQueue } from '../types/InventoryCost.types';
import { Process } from '@nestjs/bull';
import {
WriteInventoryTransactionsGLEntriesQueue,
WriteInventoryTransactionsGLEntriesQueueJob,
} from '../types/InventoryCost.types';
import { Processor, WorkerHost } from '@nestjs/bullmq';
import { Scope } from '@nestjs/common';
@@ -11,5 +15,6 @@ export class WriteInventoryTransactionsGLEntriesProcessor extends WorkerHost {
super();
}
@Process(WriteInventoryTransactionsGLEntriesQueueJob)
async process() {}
}

View File

@@ -34,7 +34,6 @@ import {
BulkDeleteItemsDto,
ValidateBulkDeleteItemsResponseDto,
} from './dtos/BulkDeleteItems.dto';
import { ItemApiErrorResponseDto } from './dtos/ItemErrorResponse.dto';
@Controller('/items')
@ApiTags('Items')
@@ -46,7 +45,6 @@ import { ItemApiErrorResponseDto } from './dtos/ItemErrorResponse.dto';
@ApiExtraModels(ItemEstimatesResponseDto)
@ApiExtraModels(ItemReceiptsResponseDto)
@ApiExtraModels(ValidateBulkDeleteItemsResponseDto)
@ApiExtraModels(ItemApiErrorResponseDto)
@ApiCommonHeaders()
export class ItemsController extends TenantController {
constructor(private readonly itemsApplication: ItemsApplicationService) {
@@ -149,13 +147,6 @@ export class ItemsController extends TenantController {
status: 200,
description: 'The item has been successfully updated.',
})
@ApiResponse({
status: 400,
description: 'Validation error. Possible error types: ITEM_NAME_EXISTS, INVENTORY_ACCOUNT_CANNOT_MODIFIED, TYPE_CANNOT_CHANGE_WITH_ITEM_HAS_TRANSACTIONS, etc.',
schema: {
$ref: getSchemaPath(ItemApiErrorResponseDto),
},
})
@ApiResponse({ status: 404, description: 'The item not found.' })
@ApiParam({
name: 'id',
@@ -213,13 +204,6 @@ export class ItemsController extends TenantController {
status: 200,
description: 'The item has been successfully created.',
})
@ApiResponse({
status: 400,
description: 'Validation error. Possible error types: ITEM_NAME_EXISTS, ITEM_CATEOGRY_NOT_FOUND, COST_ACCOUNT_NOT_COGS, SELL_ACCOUNT_NOT_INCOME, INVENTORY_ACCOUNT_NOT_INVENTORY, INCOME_ACCOUNT_REQUIRED_WITH_SELLABLE_ITEM, COST_ACCOUNT_REQUIRED_WITH_PURCHASABLE_ITEM, etc.',
schema: {
$ref: getSchemaPath(ItemApiErrorResponseDto),
},
})
// @UsePipes(new ZodValidationPipe(createItemSchema))
async createItem(
@Body() createItemDto: CreateItemDto,
@@ -235,13 +219,6 @@ export class ItemsController extends TenantController {
status: 200,
description: 'The item has been successfully deleted.',
})
@ApiResponse({
status: 400,
description: 'Cannot delete item. Possible error types: ITEM_HAS_ASSOCIATED_TRANSACTINS, ITEM_HAS_ASSOCIATED_INVENTORY_ADJUSTMENT, etc.',
schema: {
$ref: getSchemaPath(ItemApiErrorResponseDto),
},
})
@ApiResponse({ status: 404, description: 'The item not found.' })
@ApiParam({
name: 'id',

View File

@@ -1,112 +0,0 @@
import { ApiProperty } from '@nestjs/swagger';
/**
* Item API Error Types
* These error types are returned when item operations fail validation
*/
export enum ItemErrorType {
/** Item name already exists in the system */
ItemNameExists = 'ITEM_NAME_EXISTS',
/** Item category was not found */
ItemCategoryNotFound = 'ITEM_CATEOGRY_NOT_FOUND',
/** Cost account is not a Cost of Goods Sold account */
CostAccountNotCogs = 'COST_ACCOUNT_NOT_COGS',
/** Cost account was not found */
CostAccountNotFound = 'COST_ACCOUNT_NOT_FOUMD',
/** Sell account was not found */
SellAccountNotFound = 'SELL_ACCOUNT_NOT_FOUND',
/** Sell account is not an income account */
SellAccountNotIncome = 'SELL_ACCOUNT_NOT_INCOME',
/** Inventory account was not found */
InventoryAccountNotFound = 'INVENTORY_ACCOUNT_NOT_FOUND',
/** Account is not an inventory type account */
InventoryAccountNotInventory = 'INVENTORY_ACCOUNT_NOT_INVENTORY',
/** Multiple items have associated transactions */
ItemsHaveAssociatedTransactions = 'ITEMS_HAVE_ASSOCIATED_TRANSACTIONS',
/** Item has associated transactions (singular) */
ItemHasAssociatedTransactions = 'ITEM_HAS_ASSOCIATED_TRANSACTINS',
/** Item has associated inventory adjustments */
ItemHasAssociatedInventoryAdjustment = 'ITEM_HAS_ASSOCIATED_INVENTORY_ADJUSTMENT',
/** Cannot change item type to inventory */
ItemCannotChangeInventoryType = 'ITEM_CANNOT_CHANGE_INVENTORY_TYPE',
/** Cannot change type when item has transactions */
TypeCannotChangeWithItemHasTransactions = 'TYPE_CANNOT_CHANGE_WITH_ITEM_HAS_TRANSACTIONS',
/** Inventory account cannot be modified */
InventoryAccountCannotModified = 'INVENTORY_ACCOUNT_CANNOT_MODIFIED',
/** Purchase tax rate was not found */
PurchaseTaxRateNotFound = 'PURCHASE_TAX_RATE_NOT_FOUND',
/** Sell tax rate was not found */
SellTaxRateNotFound = 'SELL_TAX_RATE_NOT_FOUND',
/** Income account is required for sellable items */
IncomeAccountRequiredWithSellableItem = 'INCOME_ACCOUNT_REQUIRED_WITH_SELLABLE_ITEM',
/** Cost account is required for purchasable items */
CostAccountRequiredWithPurchasableItem = 'COST_ACCOUNT_REQUIRED_WITH_PURCHASABLE_ITEM',
/** Item not found */
NotFound = 'NOT_FOUND',
/** Items not found */
ItemsNotFound = 'ITEMS_NOT_FOUND',
}
/**
* Item API Error Response
* Returned when an item operation fails
*/
export class ItemErrorResponseDto {
@ApiProperty({
description: 'HTTP status code',
example: 400,
})
statusCode: number;
@ApiProperty({
description: 'Error type identifier',
enum: ItemErrorType,
example: ItemErrorType.ItemNameExists,
})
type: ItemErrorType;
@ApiProperty({
description: 'Human-readable error message',
example: 'The item name is already exist.',
required: false,
nullable: true,
})
message: string | null;
@ApiProperty({
description: 'Additional error payload data',
required: false,
nullable: true,
})
payload: any;
}
/**
* Item API Error Response Wrapper
*/
export class ItemApiErrorResponseDto {
@ApiProperty({
description: 'Array of error details',
type: [ItemErrorResponseDto],
})
errors: ItemErrorResponseDto[];
}

View File

@@ -2,8 +2,10 @@ import { Processor, WorkerHost } from '@nestjs/bullmq';
import { Scope } from '@nestjs/common';
import { Job } from 'bullmq';
import { ClsService, UseCls } from 'nestjs-cls';
import { Process } from '@nestjs/bull';
import {
OrganizationBuildQueue,
OrganizationBuildQueueJob,
OrganizationBuildQueueJobPayload,
} from '../Organization.types';
import { BuildOrganizationService } from '../commands/BuildOrganization.service';
@@ -20,6 +22,7 @@ export class OrganizationBuildProcessor extends WorkerHost {
super();
}
@Process(OrganizationBuildQueueJob)
@UseCls()
async process(job: Job<OrganizationBuildQueueJobPayload>) {
console.log('Processing organization build job:', job.id);

View File

@@ -22,7 +22,6 @@ const providers = [
},
migrations: {
directory: configService.get('systemDatabase.migrationDir'),
loadExtensions: ['.js'],
},
seeds: {
directory: configService.get('systemDatabase.seedsDir'),

View File

@@ -33,7 +33,6 @@ export const TenancyDatabaseProxyProvider = ClsModule.forFeatureAsync({
},
migrations: {
directory: configService.get('tenantDatabase.migrationsDir'),
loadExtensions: ['.js'],
},
seeds: {
directory: configService.get('tenantDatabase.seedsDir'),

View File

@@ -4,12 +4,7 @@ import styled from 'styled-components';
import { DataTable } from '../Datatable';
export const ReportDataTable = styled(DataTable)`
--x-table-no-results-border-color: #ddd;
.bp4-dark & {
--x-table-no-results-border-color: var(--color-dark-gray5);
}
.table .tbody .tr.no-results:last-of-type .td {
border-bottom: 1px solid var(--x-table-no-results-border-color);
border-bottom: 1px solid #ddd;
}
`;

View File

@@ -3,7 +3,7 @@ import React from 'react';
import { Menu, MenuItem, MenuDivider } from '@blueprintjs/core';
import { useHistory, useLocation } from 'react-router-dom';
import { FormattedMessage as T } from '@/components';
import { PreferencesMenu } from '@/constants/preferencesMenu';
import preferencesMenu from '@/constants/preferencesMenu';
import PreferencesSidebarContainer from './PreferencesSidebarContainer';
import '@/style/pages/Preferences/Sidebar.scss';
@@ -15,7 +15,7 @@ export default function PreferencesSidebar() {
const history = useHistory();
const location = useLocation();
const items = PreferencesMenu.map((item) =>
const items = preferencesMenu.map((item) =>
item.divider ? (
<MenuDivider title={item.title} />
) : (

View File

@@ -1,7 +1,7 @@
// @ts-nocheck
import intl from 'react-intl-universal';
export const AllocateLandedCostType = [
export default [
{ name: intl.get('bills'), value: 'Bill' },
{ name: intl.get('expenses'), value: 'Expense' },
];

View File

@@ -1,5 +1,5 @@
// @ts-nocheck
export const App = {
export default {
"app_name": "BigCapital",
"app_version": "0.0.1 (build 12344)",
}

View File

@@ -2,7 +2,7 @@
import React from 'react';
import intl from 'react-intl-universal';
export const ContactsOptions = [
export default [
{ name: intl.get('customer'), path: 'customers' },
{ name: intl.get('vendor'), path: 'vendors' },
];

View File

@@ -16,7 +16,7 @@ import {
VendorAction,
} from './abilityOption';
export const KeyboardShortcutsOptions = [
export default [
{
shortcut_key: 'Shift + I',
description: intl.get('jump_to_the_invoices'),

View File

@@ -2,7 +2,7 @@
import React from 'react';
import { FormattedMessage as T } from '@/components';
export const PreferencesMenu = [
export default [
{
text: <T id={'general'} />,
disabled: false,
@@ -13,10 +13,10 @@ export const PreferencesMenu = [
disabled: false,
href: '/preferences/branding',
},
// {
// text: 'Billing',
// href: '/preferences/billing',
// },
{
text: 'Billing',
href: '/preferences/billing',
},
{
text: <T id={'users'} />,
href: '/preferences/users',
@@ -63,11 +63,11 @@ export const PreferencesMenu = [
disabled: false,
href: '/preferences/items',
},
// {
// text: 'Integrations',
// disabled: false,
// href: '/preferences/integrations'
// },
{
text: 'Integrations',
disabled: false,
href: '/preferences/integrations'
},
{
text: 'API Keys',
disabled: false,

View File

@@ -8,11 +8,6 @@ import { If, AppToaster } from '@/components';
import { NormalCell, BalanceCell, BankBalanceCell } from './components';
import { transformTableStateToQuery, isBlank } from '@/utils';
export const DeleteAccountTypeError = {
AccountPredefined: 'account_predefined',
AccountHasAssociatedTransactions: 'account_has_associated_transactions',
};
/**
* Account name accessor.
*/
@@ -31,13 +26,13 @@ export const accountNameAccessor = (account) => {
* Handle delete errors in bulk and singular.
*/
export const handleDeleteErrors = (errors) => {
if (errors.find((e) => e.type === DeleteAccountTypeError.AccountPredefined)) {
if (errors.find((e) => e.type === 'ACCOUNT.PREDEFINED')) {
AppToaster.show({
message: intl.get('cannot_delete_predefined_accounts'),
intent: Intent.DANGER,
});
}
if (errors.find((e) => e.type === DeleteAccountTypeError.AccountHasAssociatedTransactions)) {
if (errors.find((e) => e.type === 'ACCOUNT.HAS.ASSOCIATED.TRANSACTIONS')) {
AppToaster.show({
message: intl.get('cannot_delete_account_has_associated_transactions'),
intent: Intent.DANGER,

View File

@@ -16,7 +16,7 @@ import { FormattedMessage as T, If, FFormGroup, FSelect, FRadioGroup, FInputGrou
import { handleStringChange } from '@/utils';
import { FieldRequiredHint } from '@/components';
import { CLASSES } from '@/constants/classes';
import { AllocateLandedCostType } from '@/constants/allocateLandedCostType';
import allocateLandedCostType from '@/constants/allocateLandedCostType';
import AllocateLandedCostFormBody from './AllocateLandedCostFormBody';
import {
@@ -81,7 +81,7 @@ export default function AllocateLandedCostFormFields() {
>
<FSelect
name={'transaction_type'}
items={AllocateLandedCostType}
items={allocateLandedCostType}
onItemChange={handleTransactionTypeChange}
filterable={false}
valueAccessor={'value'}

View File

@@ -9,7 +9,7 @@ import { FormattedMessage as T } from '@/components';
import { useHistory } from 'react-router-dom';
import { useContactDuplicateFromContext } from './ContactDuplicateProvider';
import { ContactsOptions } from '@/constants/contactsOptions';
import Contacts from '@/constants/contactsOptions';
import { withDialogActions } from '@/containers/Dialog/withDialogActions';
import { compose } from '@/utils';
@@ -66,7 +66,7 @@ function ContactDuplicateForm({
>
<FSelect
name={'contact_type'}
items={ContactsOptions}
items={Contacts}
placeholder={<T id={'select_contact'} />}
textAccessor={'name'}
valueAccessor={'path'}

View File

@@ -1,20 +1,8 @@
// @ts-nocheck
import React from 'react';
import { getColumnWidth } from '@/utils';
import * as R from 'ramda';
import { useGeneralLedgerContext } from './GeneralLedgerProvider';
import { Align, CLASSES } from '@/constants';
/**
* Description cell wraps value in a div with muted text class.
*/
function DescriptionCell({ cell: { value } }) {
return React.createElement(
'div',
{ className: `cell ${CLASSES.TEXT_MUTED}` },
value,
);
}
import { Align } from '@/constants';
const getTableCellValueAccessor = (index) => `cells[${index}].value`;
@@ -87,16 +75,6 @@ const transactionIdColumnAccessor = (column) => {
};
};
/**
* Description column accessor (muted text in wrapped cell).
*/
const descriptionColumnAccessor = (column) => {
return {
...column,
Cell: DescriptionCell,
};
};
const dynamiColumnMapper = R.curry((data, column) => {
const _numericColumnAccessor = numericColumnAccessor(data);
@@ -110,7 +88,6 @@ const dynamiColumnMapper = R.curry((data, column) => {
R.pathEq(['key'], 'reference_number'),
transactionIdColumnAccessor,
),
R.when(R.pathEq(['key'], 'description'), descriptionColumnAccessor),
R.when(R.pathEq(['key'], 'credit'), _numericColumnAccessor),
R.when(R.pathEq(['key'], 'debit'), _numericColumnAccessor),
R.when(R.pathEq(['key'], 'amount'), _numericColumnAccessor),

View File

@@ -1,21 +1,9 @@
// @ts-nocheck
import React from 'react';
import { Align, CLASSES } from '@/constants';
import { Align } from '@/constants';
import { getColumnWidth } from '@/utils';
import * as R from 'ramda';
import { useJournalSheetContext } from './JournalProvider';
/**
* Description cell wraps value in a div with muted text class.
*/
function DescriptionCell({ cell: { value } }) {
return React.createElement(
'span',
{ className: `cell ${CLASSES.TEXT_MUTED}` },
value,
);
}
const getTableCellValueAccessor = (index) => `cells[${index}].value`;
const getReportColWidth = (data, accessor, headerText) => {
@@ -98,16 +86,6 @@ const accountCodeColumnAccessor = (column) => {
};
};
/**
* Description column accessor (muted text in wrapped cell).
*/
const descriptionColumnAccessor = (column) => {
return {
...column,
Cell: DescriptionCell,
};
};
/**
* Dynamic column mapper.
* @param {} data -
@@ -127,7 +105,6 @@ const dynamicColumnMapper = R.curry((data, column) => {
R.pathEq(['key'], 'transaction_number'),
transactionNumberColumnAccessor,
),
R.when(R.pathEq(['key'], 'description'), descriptionColumnAccessor),
R.when(R.pathEq(['key'], 'account_code'), accountCodeColumnAccessor),
R.when(R.pathEq(['key'], 'credit'), _numericColumnAccessor),
R.when(R.pathEq(['key'], 'debit'), _numericColumnAccessor),
@@ -136,7 +113,7 @@ const dynamicColumnMapper = R.curry((data, column) => {
});
/**
* Composes the fetched dynamic columns from the server to the columns to pass it
* Composes the fetched dynamic columns from the server to the columns to pass it
* to the table component.
*/
export const dynamicColumns = (columns, data) => {

View File

@@ -14,18 +14,6 @@ import { useSettingsSelector } from '@/hooks/state';
import { transformItemFormData } from './ItemForm.schema';
import { useWatch } from '@/hooks/utils';
/**
* Error types for item operations.
*/
export const ItemErrorType = {
ItemNameExists: 'ITEM_NAME_EXISTS',
InventoryAccountCannotModified: 'INVENTORY_ACCOUNT_CANNOT_MODIFIED',
TypeCannotChangeWithItemHasTransactions: 'TYPE_CANNOT_CHANGE_WITH_ITEM_HAS_TRANSACTIONS',
ItemHasAssociatedTransactions: 'ITEM_HAS_ASSOCIATED_TRANSACTINS',
ItemHasAssociatedInventoryAdjustment: 'ITEM_HAS_ASSOCIATED_INVENTORY_ADJUSTMENT',
ItemHasAssociatedTransactionsPlural: 'ITEM_HAS_ASSOCIATED_TRANSACTIONS',
} as const;
const defaultInitialValues = {
active: 1,
name: '',
@@ -86,7 +74,7 @@ export const transitionItemTypeKeyToLabel = (itemTypeKey) => {
// handle delete errors.
export const handleDeleteErrors = (errors) => {
if (
errors.find((error) => error.type === ItemErrorType.ItemHasAssociatedTransactions)
errors.find((error) => error.type === 'ITEM_HAS_ASSOCIATED_TRANSACTINS')
) {
AppToaster.show({
message: intl.get('the_item_has_associated_transactions'),
@@ -96,7 +84,7 @@ export const handleDeleteErrors = (errors) => {
if (
errors.find(
(error) => error.type === ItemErrorType.ItemHasAssociatedInventoryAdjustment,
(error) => error.type === 'ITEM_HAS_ASSOCIATED_INVENTORY_ADJUSTMENT',
)
) {
AppToaster.show({
@@ -108,7 +96,7 @@ export const handleDeleteErrors = (errors) => {
}
if (
errors.find(
(error) => error.type === ItemErrorType.TypeCannotChangeWithItemHasTransactions,
(error) => error.type === 'TYPE_CANNOT_CHANGE_WITH_ITEM_HAS_TRANSACTIONS',
)
) {
AppToaster.show({
@@ -119,7 +107,7 @@ export const handleDeleteErrors = (errors) => {
});
}
if (
errors.find((error) => error.type === ItemErrorType.ItemHasAssociatedTransactionsPlural)
errors.find((error) => error.type === 'ITEM_HAS_ASSOCIATED_TRANSACTIONS')
) {
AppToaster.show({
message: intl.get('item.error.you_could_not_delete_item_has_associated'),
@@ -226,10 +214,10 @@ export const transformSubmitRequestErrors = (error) => {
} = error;
const fields = {};
if (errors.find((e) => e.type === ItemErrorType.ItemNameExists)) {
if (errors.find((e) => e.type === 'ITEM.NAME.ALREADY.EXISTS')) {
fields.name = intl.get('the_name_used_before');
}
if (errors.find((e) => e.type === ItemErrorType.InventoryAccountCannotModified)) {
if (errors.find((e) => e.type === 'INVENTORY_ACCOUNT_CANNOT_MODIFIED')) {
AppToaster.show({
message: intl.get('cannot_change_item_inventory_account'),
intent: Intent.DANGER,
@@ -237,7 +225,7 @@ export const transformSubmitRequestErrors = (error) => {
}
if (
errors.find(
(e) => e.type === ItemErrorType.TypeCannotChangeWithItemHasTransactions,
(e) => e.type === 'TYPE_CANNOT_CHANGE_WITH_ITEM_HAS_TRANSACTIONS',
)
) {
AppToaster.show({

View File

@@ -87,7 +87,7 @@ export function WarehousesGridItemBox({
<WarehouseBoxRoot>
<WarehouseHeader>
<WarehouseTitle>
{title} {primary ? <Icon icon={'star-18dp'} iconSize={16} /> : null}
{title} {primary && <Icon icon={'star-18dp'} iconSize={16} />}
</WarehouseTitle>
<WarehouseCode>{code}</WarehouseCode>
<WarehouseIcon>
@@ -118,21 +118,12 @@ export const WarehousesList = styled.div`
`;
export const WarehouseBoxRoot = styled.div`
--x-box-border-color: #c8cad0;
--x-box-background-color: #fff;
--x-box-hover-border-color: #0153cc;
.bp4-dark & {
--x-box-border-color: rgba(255, 255, 255, 0.2);
--x-box-background-color: var(--color-dark-gray3);
--x-box-hover-border-color: #0153cc;
}
display: flex;
flex-direction: column;
flex-shrink: 0;
border-radius: 5px;
border: 1px solid var(--x-box-border-color);
background: var(--x-box-background-color);
border: 1px solid #c8cad0;
background: #fff;
margin: 5px 5px 8px;
width: 200px;
height: 160px;
@@ -141,7 +132,7 @@ export const WarehouseBoxRoot = styled.div`
position: relative;
&:hover {
border-color: var(--x-box-hover-border-color);
border-color: #0153cc;
}
`;
@@ -152,16 +143,9 @@ export const WarehouseHeader = styled.div`
`;
export const WarehouseTitle = styled.div`
--x-title-color: #000;
--x-title-icon-color: #e1b31d;
.bp4-dark & {
--x-title-color: var(--color-light-gray5);
--x-title-icon-color: #e1b31d;
}
font-size: 14px;
font-style: inherit;
color: var(--x-title-color);
color: #000;
white-space: nowrap;
font-weight: 500;
line-height: 1;
@@ -170,19 +154,14 @@ export const WarehouseTitle = styled.div`
margin: 0;
margin-left: 2px;
vertical-align: top;
color: var(--x-title-icon-color);
color: #e1b31d;
}
`;
const WarehouseCode = styled.div`
--x-code-color: #6b7176;
.bp4-dark & {
--x-code-color: var(--color-muted-text);
}
display: block;
font-size: 11px;
color: var(--x-code-color);
color: #6b7176;
margin-top: 4px;
`;
@@ -199,13 +178,8 @@ const WarehouseContent = styled.div`
`;
const WarehouseItem = styled.div`
--x-item-color: #000;
.bp4-dark & {
--x-item-color: var(--color-light-gray1);
}
font-size: 11px;
color: var(--x-item-color);
color: #000;
text-overflow: ellipsis;
overflow: hidden;

View File

@@ -1,6 +1,6 @@
// @ts-nocheck
import React from 'react';
import { KeyboardShortcutsOptions } from '@/constants/keyboardShortcutsOptions';
import keyboardShortcuts from '@/constants/keyboardShortcutsOptions';
import { useAbilitiesFilter } from '../utils/useAbilityContext';
/**
@@ -10,7 +10,7 @@ export const useKeywordShortcuts = () => {
const abilitiesFilter = useAbilitiesFilter();
return React.useMemo(
() => abilitiesFilter(KeyboardShortcutsOptions),
() => abilitiesFilter(keyboardShortcuts),
[abilitiesFilter],
);
};

View File

@@ -3,7 +3,6 @@ import { useQuery } from 'react-query';
import { castArray, defaultTo } from 'lodash';
import { useAuthOrganizationId } from './state';
import useApiRequest from './useRequest';
import { normalizeApiPath } from '../utils';
import { useRef } from 'react';
/**
@@ -20,11 +19,7 @@ export function useRequestQuery(query, axios, props) {
const states = useQuery(
query,
() =>
apiRequest.http({
...axios,
url: `/api/${normalizeApiPath(axios.url)}`,
}),
() => apiRequest.http({ ...axios, url: `/api/${axios.url}` }),
props,
);
// Momerize the default data.

View File

@@ -7,7 +7,7 @@ import {
useSetGlobalErrors,
useAuthToken,
} from './state';
import { getCookie, normalizeApiPath } from '../utils';
import { getCookie } from '../utils';
export default function useApiRequest() {
const setGlobalErrors = useSetGlobalErrors();
@@ -93,27 +93,27 @@ export default function useApiRequest() {
http,
get(resource, params) {
return http.get(`/api/${normalizeApiPath(resource)}`, params);
return http.get(`/api/${resource}`, params);
},
post(resource, params, config) {
return http.post(`/api/${normalizeApiPath(resource)}`, params, config);
return http.post(`/api/${resource}`, params, config);
},
update(resource, slug, params) {
return http.put(`/api/${normalizeApiPath(resource)}/${slug}`, params);
return http.put(`/api/${resource}/${slug}`, params);
},
put(resource, params) {
return http.put(`/api/${normalizeApiPath(resource)}`, params);
return http.put(`/api/${resource}`, params);
},
patch(resource, params, config) {
return http.patch(`/api/${normalizeApiPath(resource)}`, params, config);
return http.patch(`/api/${resource}`, params, config);
},
delete(resource, params) {
return http.delete(`/api/${normalizeApiPath(resource)}`, params);
return http.delete(`/api/${resource}`, params);
},
}),
[http],
@@ -130,22 +130,22 @@ export function useAuthApiRequest() {
() => ({
http,
get(resource, params) {
return http.get(`/api/${normalizeApiPath(resource)}`, params);
return http.get(`/api/${resource}`, params);
},
post(resource, params, config) {
return http.post(`/api/${normalizeApiPath(resource)}`, params, config);
return http.post(`/api/${resource}`, params, config);
},
update(resource, slug, params) {
return http.put(`/api/${normalizeApiPath(resource)}/${slug}`, params);
return http.put(`/api/${resource}/${slug}`, params);
},
put(resource, params) {
return http.put(`/api/${normalizeApiPath(resource)}`, params);
return http.put(`/api/${resource}`, params);
},
patch(resource, params, config) {
return http.patch(`/api/${normalizeApiPath(resource)}`, params, config);
return http.patch(`/api/${resource}`, params, config);
},
delete(resource, params) {
return http.delete(`/api/${normalizeApiPath(resource)}`, params);
return http.delete(`/api/${resource}`, params);
},
}),
[http],

View File

@@ -11,10 +11,7 @@ export const getPreferenceRoutes = () => [
},
{
path: `${BASE_URL}/branding`,
component: lazy(
() =>
import('../containers/Preferences/Branding/PreferencesBrandingPage'),
),
component: lazy(() => import('../containers/Preferences/Branding/PreferencesBrandingPage')),
exact: true,
},
{
@@ -32,20 +29,14 @@ export const getPreferenceRoutes = () => [
{
path: `${BASE_URL}/payment-methods`,
component: lazy(
() =>
import(
'../containers/Preferences/PaymentMethods/PreferencesPaymentMethodsPage'
),
() => import('../containers/Preferences/PaymentMethods/PreferencesPaymentMethodsPage'),
),
exact: true,
},
{
path: `${BASE_URL}/payment-methods/stripe/callback`,
component: lazy(
() =>
import(
'../containers/Preferences/PaymentMethods/PreferencesStripeCallback'
),
() => import('../containers/Preferences/PaymentMethods/PreferencesStripeCallback'),
),
exact: true,
},
@@ -121,6 +112,16 @@ export const getPreferenceRoutes = () => [
component: lazy(() => import('@/containers/Preferences/Item')),
exact: true,
},
// {
// path: `${BASE_URL}/sms-message`,
// component: SMSIntegration,
// exact: true,
// },
{
path: `${BASE_URL}/billing`,
component: lazy(() => import('@/containers/Subscriptions/BillingPage')),
exact: true,
},
{
path: `${BASE_URL}/api-keys`,
component: lazy(() => import('@/containers/Preferences/ApiKeys/ApiKeys')),

View File

@@ -196,6 +196,7 @@ $ns: bp4;
--color-preferences-sidebar-head-border: #bbcbd0;
--color-preferences-sidebar-head-text: #3b3b4c;
// Preferences - Topbar.
--color-preferences-topbar-background: #fff;
--color-preferences-topbar-border: #d2dde2;
@@ -208,7 +209,7 @@ $ns: bp4;
--color-financial-sheet-title-text: rgb(31, 50, 85);
--color-financial-sheet-type-text: rgb(31, 50, 85);
--color-financial-sheet-date-text: rgb(31, 50, 85);
--color-financial-sheet-footer-text: var(--color-muted-text);
--color-financial-sheet-footer-text: rgb(31, 50, 85);
--color-financial-sheet-minimal-title-text: #333;
// Transaction locking.
@@ -513,7 +514,7 @@ body.bp4-dark {
--color-financial-sheet-title-text: var(--color-light-gray1);
--color-financial-sheet-type-text: var(--color-light-gray1);
--color-financial-sheet-date-text: var(--color-light-gray1);
--color-financial-sheet-footer-text: var(--color-muted-text);
--color-financial-sheet-footer-text: var(--color-light-gray1);
--color-financial-sheet-minimal-title-text: var(--color-white);
// Transaction locking.

View File

@@ -365,7 +365,7 @@
border-bottom: 1px solid var(--color-datatable-constrant-head-border);
padding: 0.5rem;
}
.tbody .tr .td {
background: transparent;
padding: 0.5rem 0.5rem;
@@ -375,32 +375,3 @@
}
}
}
// Sticky header: blurred transparent background so body rows don't show through
.bigcapital-datatable.has-sticky,
.bigcapital-datatable.has-sticky-header {
&.table-constrant .table .thead .th,
&.table--constrant .table .thead .th {
background: rgba(255, 255, 255, 0.25);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
body.bp4-dark & {
background: rgba(28, 33, 39, 0.6);
}
}
}
// Sticky cells in table body: blurred transparent background so content doesn't show through
.bigcapital-datatable.has-sticky {
&.table-constrant .table .tbody .tr:not(:hover) .td[data-sticky-td],
&.table--constrant .table .tbody .tr:not(:hover) .td[data-sticky-td] {
background: rgba(255, 255, 255, 0.25);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
body.bp4-dark & {
background: rgba(28, 33, 39, 0.6);
}
}
}

View File

@@ -13,9 +13,6 @@ import jsCookie from 'js-cookie';
import { deepMapKeys } from './map-key-deep';
export * from './deep';
/** Strips leading slash from a path segment to avoid double slashes when joining with a base (e.g. `/api/` + path). */
export const normalizeApiPath = (path) => (path || '').replace(/^\//, '');
export const getCookie = (name, defaultValue) =>
_.defaultTo(jsCookie.get(name), defaultValue);